ref: 4a3b80329a99d6d11a070d42c0103810bfdb7625
dir: /main.c/
/*
* This work is dedicated to the public domain.
* See COPYING file for more information.
*/
#include <errno.h>
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#ifdef __gnu_linux__
#include <sys/epoll.h>
#include <bsd/err.h>
#include <bsd/stdlib.h>
#include <bsd/string.h>
#else
#include <sys/event.h>
#endif
#include "htable.h"
#include "util.c"
#define CLONE_COOLDOWN 1
#define CLONE_ADDEND 10
/* #define PING_TIMEOUT 240 */
#define EVENT_ADDEND 100
#define NET_ADDEND 10
#define USER_ADDEND 100
#define BUFSIZE 1024
#define NICK_LEN 16
#define MAX_EVENTS 10
#define EV_READ 1
#define EV_WRITE 2
enum {
IDLE = 0,
RESET,
CLONING
};
struct network {
int id; /* event index */
char *name; /* name */
char *symb; /* symbol */
char *host; /* host */
char *port; /* port */
char *chan; /* channel */
int ready; /* joined */
};
struct event {
int fd; /* event fd */
int netid; /* net index */
char *user; /* user nick */
int suffix; /* suffix count */
time_t time; /* last response */
int ready; /* joined */
};
static int debug;
static int done; /* program state */
static int fifofd; /* fifo fd */
static char *fifopath; /* fifo path */
static char msg [BUFSIZE]; /* message buffer */
static int timeout_id = -1;
#ifdef __gnu_linux__
static int epfd; /* epoll instance */
#else
static int kqfd; /* kqueue instance */
#endif
static int *fdtoid; /* fd to event id */
static int fdslen; /* maximum fd */
static struct event *events; /* events array */
static int evslen; /* array length */
static int evscap; /* array capacity */
static struct network *networks; /* networks array */
static int netlen; /* array length */
static int netcap; /* array capacity */
static int state = IDLE;
/*
* hash table of users
* key -> <user_nick> + '[' + <network_symbol> + ']'
* value -> array of all user's clones event ids indexed
* corresponding to its connected network
*/
static struct Htable *users;
/* functions prototype */
void fd_register(int, int);
void fifo_read(void);
time_t event_timeout(void);
void event_write(int);
int event_read(int);
void net_add(char *, char *, char *, char *, char *);
void net_del(char *);
void net_del_raw(int);
void net_users_add(int, char *);
void user_add(char *, int, int);
void user_del(char *, char *);
int *user_event_ids(char *, int);
int clone_add(char *, int);
int event_add(int, int, char *);
void event_del(int);
void nick_add_symb(char *, int);
void privmsg_update(char *, char *, int);
void print_table(void);
void print_htable(void);
void print_users(void);
void print_border(void);
int
main(int argc, char *argv[])
{
#ifdef __gnu_linux__
struct epoll_event epevs [MAX_EVENTS]; /* maximum event to handle */
#else
struct kevent kevs [MAX_EVENTS]; /* maximum event to handle */
struct timespec tmsp;
#endif
int i, r, fd, id, nev;
time_t timeout;
time_t ntime; /* next timeout time */
/* set stdout to unbufferd */
setvbuf(stdout, NULL, _IONBF, 0);
/* check arguments */
if (argc != 2) {
printf("usage: %s fifo\n", getprogname());
return 0;
} else {
fifopath = argv[1];
}
/* init global variables */
fdslen = evscap = EVENT_ADDEND;
netcap = NET_ADDEND;
fdtoid = ecalloc((size_t)fdslen, sizeof(int));
events = ecalloc((size_t)evscap, sizeof(struct event));
networks = ecalloc((size_t)netcap, sizeof(struct network));
users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free,
USER_ADDEND);
#ifdef __gnu_linux__
if ((epfd = epoll_create1(0)) == -1)
err(1, "epoll_create1");
#else
if ((kqfd = kqueue()) == -1)
err(1, "kqueue");
#endif
fifofd = fifo_open(fifopath);
fd_register(fifofd, EV_READ);
ntime = -1;
/* event loop */
while (!done) {
if (ntime == -1)
timeout = -1;
else if ((timeout = ntime - time(NULL)) < 0)
timeout = 0;
#ifdef __gnu_linux__
if ((nev = epoll_wait(epfd, epevs, MAX_EVENTS,
(int)timeout * 1000)) == -1)
err(1, "epoll_wait");
#else
tmsp.tv_sec = timeout;
tmsp.tv_nsec = 0;
if ((nev = kevent(kqfd, NULL, 0, kevs, MAX_EVENTS,
timeout < 0 ? NULL : &tmsp)) == -1)
err(1, "kevent");
#endif
if (nev == 0) {
ntime = event_timeout();
continue;
}
if (debug) {
printf("fds:");
for (i = 0; i < nev; i++) {
printf(" %d", epevs[i].data.fd);
}
printf("\n");
}
for (i = 0; i < nev; i++) {
#ifdef __gnu_linux__
fd = epevs[i].data.fd;
#else
fd = (int)kevs[i].ident;
#endif
id = fdtoid[fd];
if (fd == fifofd) {
fifo_read();
#ifdef __gnu_linux__
} else if (epevs[i].events & EPOLLOUT) {
#else
} else if (kevs[i].filter == EVFILT_WRITE) {
#endif
event_write(id);
#ifdef __gnu_linux__
} else if (epevs[i].events & EPOLLIN) {
#else
} else if (kevs[i].filter == EVFILT_READ) {
#endif
r = event_read(id);
if (r == 1) {
timeout_id = -1;
ntime = 0;
} else if (r == -2) {
break;
}
} else {
errx(1, "unknown event");
}
}
}
/*
* cleanup
*/
snprintf(msg, sizeof(msg), "QUIT :linker shutting down\r\n");
for (i = 0; i < evslen; i++) {
writeall(events[i].fd, msg);
close(events[i].fd);
}
free(fdtoid);
free(events);
/* delete all the networks */
for (i = 0; i < netlen; i++)
net_del_raw(i);
free(networks);
/* delete all the users */
htdestroy(users);
return 0;
}
void
fd_register(int fd, int type)
{
#ifdef __gnu_linux__
struct epoll_event ev;
if (type == EV_READ)
ev.events = EPOLLIN;
else if (type == EV_WRITE)
ev.events = EPOLLOUT;
else
errx(1, "unkown event type");
ev.data.fd = fd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1)
err(1, "epoll_ctl");
#else
struct kevent ev;
int filter;
if (type == EV_READ)
filter = EVFILT_READ;
else if (type == EV_WRITE)
filter = EVFILT_WRITE;
else
errx(1, "unkown event type");
EV_SET(&ev, fd, filter, EV_ADD, 0, 0, 0);
if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
#endif
/* printf("added fd: %d\n", fd); */
}
void
fifo_read(void)
{
char buffer [BUFSIZE];
char *buf;
char *cmd;
ssize_t n;
n = readline(fifofd, buffer, sizeof(buffer));
if (n == -1) {
err(1, "fifo: read");
} else if (n == 0) {
/* reopen fifo again */
close(fifofd);
fifofd = fifo_open(fifopath);
fd_register(fifofd, EV_READ);
return;
}
if (!*buffer)
return;
buf = buffer;
cmd = split(&buf, ' ');
if (strcmp(cmd, "netadd") == 0) {
char *name = split(&buf, ' ');
char *symb = split(&buf, ' ');
char *host = split(&buf, ' ');
char *port = split(&buf, ' ');
char *chan = buf;
if (!*name || !*symb || !*host || !*port || !*chan)
printf("usage: netadd <name> <symbol> <host> <port> <channel>\n");
else
net_add(name, symb, host, port, chan);
} else if (strcmp(cmd, "netdel") == 0) {
char *name = buf;
if (!*name)
printf("usage: netdel <name>\n");
else
net_del(name);
} else if (strcmp(cmd, "print") == 0) {
print_table();
} else if (strcmp(cmd, "htable") == 0) {
print_htable();
} else if (strcmp(cmd, "users") == 0) {
print_users();
} else if (strcmp(cmd, "exit") == 0) {
done = TRUE;
} else {
warnx("%s is not a command", cmd);
}
}
time_t
event_timeout(void)
{
static Htiter it = {0};
int i, j, *ids;
char *nick;
struct network *n;
time_t ntime;
/* if (timeout_id != -1) {
printf("%d: PING\n", events[timeout_id].fd);
snprintf(msg, sizeof(msg), "PING %s\r\n",
networks[events[timeout_id].netid].host);
writeall(events[timeout_id].fd, msg);
events[timeout_id].time = time(NULL);
} else { */
if (state == RESET) {
state = CLONING;
it.index = 0;
it.node = NULL;
}
printf("Adding %d bot\n", CLONE_ADDEND);
j = 0;
while (htiterate(users, &it)) {
nick = (char *)it.node->key;
ids = (int *)it.node->val;
for (i = 0; i < netlen; i++) {
n = &networks[i];
if (!n->ready || ids[i] != 0)
continue;
ids[i] = clone_add(nick, i);
}
if (++j >= CLONE_ADDEND)
goto calculate;
}
state = IDLE;
it.index = 0;
it.node = NULL;
/* } */
calculate:
ntime = -1;
timeout_id = -1;
if (state == CLONING)
ntime = time(NULL) + CLONE_COOLDOWN;
/* for(i = 0; i < evslen; i++) {
if ((events[i].time != 0)
&& ((ntime == -1) || (events[i].time + PING_TIMEOUT < ntime))) {
ntime = events[i].time + PING_TIMEOUT;
timeout_id = i;
}
} */
return ntime;
}
void
event_write(int id)
{
int fd = events[id].fd;
#ifdef __gnu_linux__
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = fd;
if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1)
err(1, "epoll_ctl");
#else
struct kevent ev;
EV_SET(&ev, fd, EV_WRITE, EV_DISABLE, 0, 0, 0);
if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
fd_register(fd, EV_READ);
#endif
if (events[id].user == NULL) { /* linker */
snprintf(msg, sizeof(msg), "NICK linker\r\n");
writeall(fd, msg);
snprintf(msg, sizeof(msg), "USER linker 0 * :linker\r\n");
writeall(fd, msg);
} else { /* user */
snprintf(msg, sizeof(msg), "NICK %s\r\n", events[id].user);
writeall(fd, msg);
snprintf(msg, sizeof(msg), "USER user 0 * :user\r\n");
writeall(fd, msg);
}
events[id].time = time(NULL);
}
int
event_read(int id)
{
char buffer [BUFSIZE];
char backup [BUFSIZE];
char lnick [NICK_LEN]; /* linker nick */
char *buf;
char *cmd;
char *nick;
int i, fd, netid;
ssize_t n;
fd = events[id].fd;
netid = events[id].netid;
events[id].time = time(NULL);
n = readline(fd, buffer, sizeof(buffer));
if (n == -1) {
warn("%d: read", fd);
event_del(id);
return 0;
} else if (n == 0) {
warnx("%d: connection closed", fd);
event_del(id);
return 0;
}
if (!*buffer)
return 0;
/* clone the buffer */
strlcpy(backup, buffer, sizeof(buffer));
buf = buffer;
/* set linker nick */
strlcpy(lnick, "linker", sizeof(lnick));
for (i = 0; i < events[id].suffix; i++)
strcat(lnick, "_");
/* first column */
cmd = split(&buf, ' ');
if (strcmp(cmd, "NOTICE") == 0) {
return 0;
} else if (strcmp(cmd, "ERROR") == 0) {
errx(1, "%d: %s\n", fd, backup);
} else if (strcmp(cmd, "PING") == 0) {
snprintf(msg, sizeof(msg), "PONG %s\r\n", buf);
writeall(fd, msg);
return 0;
}
/* strip nick from first column */
nick = split(&cmd, '!');
if (nick[0] == ':')
nick++;
/* second column */
cmd = split(&buf, ' ');
/* ignore all the info messages */
if ((strcmp(cmd, "002") == 0)
|| (strcmp(cmd, "003") == 0)
|| (strcmp(cmd, "004") == 0)
|| (strcmp(cmd, "005") == 0)
|| (strcmp(cmd, "003") == 0)
|| (strcmp(cmd, "251") == 0)
|| (strcmp(cmd, "252") == 0)
|| (strcmp(cmd, "253") == 0) /* unknown connection(s) */
|| (strcmp(cmd, "254") == 0)
|| (strcmp(cmd, "255") == 0)
|| (strcmp(cmd, "265") == 0)
|| (strcmp(cmd, "266") == 0)
|| (strcmp(cmd, "250") == 0)
|| (strcmp(cmd, "332") == 0)
|| (strcmp(cmd, "333") == 0)
|| (strcmp(cmd, "375") == 0)
|| (strcmp(cmd, "372") == 0)
|| (strcmp(cmd, "376") == 0)
|| (strcmp(cmd, "396") == 0)
|| (strcmp(cmd, "366") == 0)
|| (strcmp(cmd, "MODE") == 0)
|| (strcmp(cmd, "TOPIC") == 0)
|| (strcmp(cmd, "NOTICE") == 0)) {
return 0;
} else if (strcmp(cmd, "432") == 0) { /* Erroneous Nickname */
errx(1, "%d: %s\n", fd, backup);
} else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */
split(&buf, ' ');
nick = split(&buf, ' ');
strcat(nick, "_");
events[id].suffix++;
if (strlen(nick) > NICK_LEN) {
warnx("nick '%s' is too big", nick);
if (strcmp(nick, lnick) == 0) {
net_del(networks[netid].name);
} else {
snprintf(msg, sizeof(msg), "QUIT :nick is too big\r\n");
user_del(nick, msg);
}
} else {
snprintf(msg, sizeof(msg), "NICK %s\r\n", nick);
writeall(fd, msg);
}
return 0;
} else if (strcmp(cmd, "001") == 0) {
snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan);
writeall(fd, msg);
return 0;
} else if (strcmp(cmd, "PRIVMSG") == 0) {
char privmsg [BUFSIZE] = "";
int *ids;
if (events[id].user == NULL) { /* if linker */
nick_add_symb(nick, netid);
if ((ids = htsearch(users, nick)) == NULL)
return 0;
split(&buf, ':'); /* set buf to msg */
privmsg_update(privmsg, buf, netid);
for (i = 0; i < netlen; i++) {
if (ids[i] > 0) {
snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg);
writeall(events[ids[i]].fd, msg);
}
}
} else {
char *netsymb;
char *user = split(&buf, ' ');
/* ignore messages from channel (it is handled by linker) */
if ((user[0] == '#') || (user[0] == '&'))
return 0;
nick_add_symb(nick, netid);
if ((ids = htsearch(users, nick)) == NULL)
return 0;
/* split user nick and network symbol */
*strrchr(user, ']') = '\0';
netsymb = strrchr(user, '[');
*netsymb++ = '\0';
/* get the network index */
for (i = 0; i < netlen; i++) {
if (strcmp(netsymb, networks[i].symb) == 0)
break;
}
split(&buf, ':'); /* set buf to msg */
privmsg_update(privmsg, buf, netid);
snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg);
writeall(events[ids[i]].fd, msg);
}
return 0;
}
/* these messages are handled by linker */
if (events[id].user != NULL) { /* if clone */
if (strcmp(cmd, "353") == 0) {
events[id].ready = TRUE;
return 0;
} else if ((strcmp(cmd, "JOIN") == 0)
|| (strcmp(cmd, "QUIT") == 0)
|| (strcmp(cmd, "PART") == 0)
|| (strcmp(cmd, "KICK") == 0)
|| (strcmp(cmd, "NICK") == 0)) {
return 0;
}
} else if (strcmp(cmd, "353") == 0) {
char *nick;
split(&buf, ':');
networks[netid].ready = TRUE;
state = RESET;
/* then add all new users */
while (*(nick = split(&buf, ' ')) != '\0') {
if (*nick == '@'
|| *nick == '&'
|| *nick == '~'
|| *nick == '%'
|| *nick == '+'
|| *nick == '\\')
nick++;
if (strcmp(nick, lnick) != 0)
user_add(nick, netid, FALSE);
}
return 1;
} else if (strcmp(cmd, "JOIN") == 0) {
if ((strcmp(nick, lnick) != 0)
&& (user_event_ids(nick, netid) == NULL)) { /* if not clone */
if (state != IDLE)
warnx("ignoring user '%s' due to network cloning", nick);
else
user_add(nick, netid, TRUE);
}
return 0;
} else if ((strcmp(cmd, "QUIT") == 0)
|| (strcmp(cmd, "PART") == 0)) {
snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf);
nick_add_symb(nick, netid);
if (htsearch(users, nick) != NULL)
user_del(nick, msg);
return -2;
} else if (strcmp(cmd, "NICK") == 0) {
int *ids, i;
char *newnick;
nick_add_symb(nick, netid);
if ((ids = htsearch(users, nick)) == NULL)
return 0;
/* set buf to new nick */
split(&buf, ':');
/* allocate a newnick and append the netsym and then replace the old */
newnick = ecalloc(strlen(buf) + strlen(networks[netid].symb) + 2 + 1, sizeof(char));
sprintf(newnick, "%s[%s]", buf, networks[netid].symb);
htsetkey(users, nick, newnick);
snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick);
for (i = 0; i < netlen; i++) {
events[ids[i]].user = newnick;
if (ids[i] > 0)
writeall(events[ids[i]].fd, msg);
}
return 0;
} else if (strcmp(cmd, "KICK") == 0) {
/* :<nick_which_is_kicking>!~user@host KICK <channel> <nick_which_has_been_kicked> :<kick_msg> */
int *ids;
char *chan = split(&buf, ' ');
char *user = split(&buf, ' ');
/* set the quit msg */
snprintf(msg, sizeof(msg), "QUIT : kicked by %s\r\n", nick);
/* delete whole network if it is the linker */
if (strcmp(user, lnick) == 0) {
net_del(networks[netid].name);
return 0;
}
/* delete the user if the message from the same network */
if ((ids = user_event_ids(user, netid)) == NULL) {
nick_add_symb(user, netid);
user_del(user, msg);
return 0;
}
/* close the kicked fd */
writeall(fd, msg);
event_del(id);
/*
* send notice in the channel through linker
*/
/* get the original user netid */
for (i = 0; i < netlen; i++) {
if (ids[i] == -1)
break;
}
/* set buf to msg */
split(&buf, ':');
/* remove netsymb and suffix */
*strrchr(user, '[') = '\0';
/* send notice */
snprintf(msg, sizeof(msg),
"PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]\r\n",
networks[i].chan, user, nick, networks[netid].name, chan, buf);
writeall(events[networks[i].id].fd, msg);
return 0;
}
printf("%d: %s\n", fd, backup);
return 0;
}
void
net_add(char *name, char *symb, char *host, char *port, char *chan)
{
struct network *n;
int i, fd;
/* if name, symbol or configuration already exists */
for (i = 0; i < netlen; i++) {
if (strcmp(networks[i].name, name) == 0) {
warnx("network name '%s' already exists", name);
return;
}
if (strcmp(networks[i].symb, symb) == 0) {
warnx("network symbol '%s' already exists", symb);
return;
}
if ((strcmp(networks[i].host, host) == 0)
&& (strcmp(networks[i].port, port) == 0)
&& (strcmp(networks[i].chan, chan) == 0)) {
warnx("network configuration already exists");
return;
}
}
/* resize if full */
if (netlen == netcap) {
Htiter it = {0};
networks = realloc0(networks, sizeof(struct network) * (size_t)netcap,
sizeof(struct network) * (size_t)(netcap + NET_ADDEND));
while (htiterate(users, &it))
it.node->val = realloc0(it.node->val, sizeof(int) * (size_t)netcap,
sizeof(int) * (size_t)(netcap + NET_ADDEND));
netcap += NET_ADDEND;
}
/* connect */
if ((fd = dial(host, port)) == -1)
return;
/* add a network */
n = &networks[netlen];
n->id = event_add(fd, netlen, NULL);
n->name = strdup(name);
n->symb = strdup(symb);
n->host = strdup(host);
n->port = strdup(port);
n->chan = strdup(chan);
n->ready = FALSE;
netlen++;
printf("%d: network '%s' added\n", fd, name);
}
void
net_del(char *name)
{
int i, netid, *ids;
Htiter it = {0}; /* current iterator */
Htiter lastit = {0}; /* last iterator */
/* get netid */
netid = -1;
for (i = 0; i < netlen; i++) {
if (strcmp(name, networks[i].name) == 0) {
netid = i;
break;
}
}
if (netid == -1) {
warnx("network '%s' doesn't exist", name);
return;
}
/* set the quit msg */
snprintf(msg, sizeof(msg), "QUIT :unlinking network %s\r\n", name);
/* reconstruct the user-clones table */
while (htiterate(users, &it)) {
ids = (int *)it.node->val;
/* delete all the users of deleting network */
if (ids[netid] == -1) {
user_del(it.node->key, msg);
/* this node is deleted */
it = lastit;
/* delete the clones */
} else {
if (ids[netid] > 0) {
writeall(events[ids[netid]].fd, msg);
event_del(ids[netid]);
}
/* swap last with current one */
ids[netid] = ids[netlen-1];
}
lastit = it;
}
/* set last netid of events to current netid. */
for (i = 0; i < evslen; i++) {
if (events[i].netid == netlen-1)
events[i].netid = netid;
}
writeall(events[networks[netid].id].fd, msg);
event_del(networks[netid].id);
net_del_raw(netid);
printf("%d: network '%s' deleted\n", events[networks[netid].id].fd, name);
/* swap the network with the last */
networks[netid] = networks[netlen-1];
netlen--;
}
void
net_del_raw(int netid)
{
struct network *n = &networks[netid];
free(n->name);
free(n->symb);
free(n->host);
free(n->port);
free(n->chan);
}
void
user_add(char *unick, int netid, int clone)
{
size_t len;
char *nick;
int *ids, i;
len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1;
/* too long nick */
if (len-1 > NICK_LEN) {
warnx("nick '%s' is too big", unick);
return;
}
/* resize hash table if storage is low */
if ((users->cap - users->len) < USER_ADDEND)
htresize(users, users->cap + USER_ADDEND);
/* allocate a new user */
nick = ecalloc(len, sizeof(char));
ids = ecalloc((size_t)netcap, sizeof(int));
sprintf(nick, "%s[%s]", unick, networks[netid].symb);
ids[netid] = -1;
if (debug)
printf("useradd: %s\n", nick);
if (clone) {
/* clone the user on all other network */
for (i = 0; i < netlen; i++) {
if (networks[i].ready && i != netid)
ids[i] = clone_add(nick, i);
}
}
/* insert it to the users hash table */
if (htinsert(users, nick, ids) == -1)
/* this shouldn't happen as it was already checked */
errx(1, "user '%s' already exists", nick);
}
void
user_del(char *nick, char *msg)
{
int i, *ids;
if ((ids = htsearch(users, nick)) == NULL)
errx(1, "user '%s' 1 doesn't exists", nick);
for (i = 0; i < netlen; i++) {
if (ids[i] <= 0)
continue;
printf("%d: del[%s]: %s\n", events[ids[i]].fd,
networks[i].symb, nick);
if (events[ids[i]].ready)
writeall(events[ids[i]].fd, msg);
event_del(ids[i]);
}
htremove(users, nick);
}
int *
user_event_ids(char *nick, int netid)
{
unsigned int s;
int *ids = NULL;
/* count suffix */
for (s = 0; nick[strlen(nick)-s-1] == '_'; s++);
/* remove suffix */
if (s > 0)
nick[strlen(nick)-s] = '\0';
ids = htsearch(users, nick);
/* if match but suffix doesn't match */
if ((ids != NULL) && (events[ids[netid]].suffix != (int)s))
ids = NULL;
/* add suffix back */
if (s > 0)
nick[strlen(nick)] = '_';
return ids;
}
int
clone_add(char *nick, int netid)
{
struct network *n;
int fd;
n = &networks[netid];
if ((fd = dial(n->host, n->port)) == -1) {
warn("enable to connect to %s\n", n->host);
return -1;
}
printf("%d: add[%s]: %s\n", fd, n->symb, nick);
return event_add(fd, netid, nick);
}
int
event_add(int fd, int netid, char *user)
{
int i = evslen;
if (evslen == evscap) {
events = realloc0(events, sizeof(struct event) * (size_t)evscap,
sizeof(struct event) * (size_t)(evscap + EVENT_ADDEND));
evscap += EVENT_ADDEND;
}
events[i].fd = fd;
events[i].netid = netid;
events[i].user = user;
events[i].suffix = 0;
events[i].time = 0;
events[i].ready = FALSE;
fd_register(fd, EV_WRITE);
if (fd == fdslen - 1) {
fdtoid = realloc0(fdtoid, sizeof(int) * (size_t)fdslen,
sizeof(int) * (size_t)fdslen * 2);
fdslen *= 2;
}
fdtoid[fd] = i;
return evslen++;
}
void
event_del(int id)
{
int *ids;
int l = evslen - 1; /* last id */
/* swap id */
if (events[l].user == NULL) { /* if linker */
networks[events[l].netid].id = id;
} else { /* else user */
if ((ids = htsearch(users, events[l].user)) == NULL)
errx(1, "user '%s' 2 doesn't exists", events[l].user);
ids[events[l].netid] = id;
}
/* disable id */
if (events[id].user == NULL) { /* if linker */
networks[events[id].netid].id = -2;
} else { /* else user */
if ((ids = htsearch(users, events[id].user)) == NULL)
errx(1, "user '%s' 3 doesn't exists", events[id].user);
ids[events[id].netid] = -2;
}
close(events[id].fd);
events[id] = events[l];
evslen--;
fdtoid[events[id].fd] = id;
}
void
nick_add_symb(char *nick, int netid)
{
strcat(nick, "[");
strcat(nick, networks[netid].symb);
strcat(nick, "]");
}
/*
* trim all the nicknames to original nick
* src will be destructed
*/
void
privmsg_update(char *dst, char *src, int netid)
{
char d; /* delimiter */
char *n;
while (src != NULL) {
n = strpbrk(src, " :;,<>@&~%+\\");
if (n == NULL) {
d = '\0';
} else {
d = *n;
*n = '\0';
n++;
}
/* check if the word is nick */
if (user_event_ids(src, netid) != NULL)
*strrchr(src, '[') = '\0';
strcat(dst, src);
strncat(dst, &d, 1);
src = n;
}
}
void
print_table(void)
{
int i, *ids, diff, tabs;
Htiter it = {0};
char *nick;
if (netlen == 0)
return;
print_border();
/* print networks */
printf("struct networks\t\t");
for (i = 0; i < netlen; i++)
printf("%s(%d)\t", networks[i].symb, events[networks[i].id].fd);
printf("\n");
while (htiterate(users, &it)) {
ids = (int *)it.node->val;
nick = (char *)it.node->key;
/* print tabbed user nick */
printf("%s", nick);
diff = 24 - (int)strlen(nick);
tabs = ((diff / 8) + (diff % 8 > 0));
printf("%.*s", tabs, "\t\t\t");
/* print tabbed clones ids */
for (i = 0; i < netlen; i++) {
printf("%d", ids[i]);
/* print suffix */
if ((ids[i] > 0) && (events[ids[i]].suffix > 0))
printf("(%d)", events[ids[i]].suffix);
printf("\t");
}
printf("\n");
}
print_border();
}
void
print_htable(void)
{
Htiter it = {0};
int index = -1;
print_border();
while (htiterate(users, &it)) {
if (index != (int)it.index) {
/* ignore first new line */
if (index != -1)
printf("\n");
printf("%d", it.index);
index = (int)it.index;
}
printf(" -> %s", (char *)it.node->key);
}
printf("\n");
print_border();
}
void
print_users(void)
{
Htiter it = {0};
int i = 0;
print_border();
while (htiterate(users, &it))
printf("%d: %s\n", i++, (char *)it.node->key);
print_border();
}
void
print_border(void)
{
int i;
for (i = 0; i < 64; i++)
printf("-");
printf("\n");
}