ticl

tiny irc channel linker
git clone git://git.ircforever.org/ticl
Log | Files | Refs | Submodules | README | LICENSE

commit 8df402e53d02ca2c0b976715ce57c28750dd99d9
parent 5ff9649e6c51bae0dd09f218e4943fa5f4d458be
Author: libredev <libredev@ircforever.org>
Date:   Wed, 16 Nov 2022 18:14:04 +0530

code refactor

Diffstat:
MMakefile | 10+++-------
Mhtable.c | 27++++++++++++++++++++-------
Mmain.c | 1192+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Autil.c | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dutils.c | 152-------------------------------------------------------------------------------
Dutils.h | 13-------------
6 files changed, 783 insertions(+), 725 deletions(-)

diff --git a/Makefile b/Makefile @@ -12,15 +12,11 @@ CFLAGS = -g -std=c89 -Wall -Wextra -pedantic -Wconversion\ VERSION != date '+%Y-%m-%d' PROGRAM = ticl -SOURCES = main.c htable.c utils.c -HEADERS = htable.h utils.h +SOURCES = main.c htable.c +HEADERS = htable.h util.c OBJECTS = $(SOURCES:.c=.o) -all: $(PROGRAM) - -main.o: htable.o utils.o -htable.o: htable.c htable.h -utils.o: utils.c utils.h +all: clean $(PROGRAM) $(PROGRAM): $(OBJECTS) $(CC) -o $@ $(OBJECTS) $(LDFLAGS) diff --git a/htable.c b/htable.c @@ -3,11 +3,12 @@ * See COPYING file for more information. */ +#include <errno.h> #include <stdlib.h> #include <stdio.h> +#include <string.h> #include "htable.h" -#include "utils.h" static unsigned int hash(Htable *ht, void *key) @@ -28,11 +29,20 @@ htcreate(KeyLenFn *keylenfn, KeyCmpFn *keycmpfn, FreeKeyFn *freekeyfn, FreeValFn unsigned int i; Htable *ht; - if (keylenfn == NULL) fatal("keylenfn cannot be NULL"); - if (keycmpfn == NULL) fatal("keycmpfn cannot be NULL"); + if (keylenfn == NULL || keycmpfn == NULL || freekeyfn == NULL || freevalfn == NULL) { + printf("error: callback function(s) cannot be NULL\n"); + return NULL; + } - ht = ecalloc(1, sizeof(Htable)); - ht->nodes = ecalloc(cap, sizeof(Htnode)); + /* create a hash table */ + if ((ht = calloc(1, sizeof(Htable))) == NULL) { + printf("error: calloc: %s\n", strerror(errno)); + return NULL; + } + if ((ht->nodes = calloc(cap, sizeof(Htnode))) == NULL) { + printf("error: calloc: %s\n", strerror(errno)); + return NULL; + } ht->cap = cap; ht->len = 0; ht->keylenfn = keylenfn; @@ -103,13 +113,16 @@ htinsert(Htable *ht, void *key, void *val) ht->freevalfn(n->val); n->key = key; n->val = val; - return -1; + return 0; } pn = n; } /* create a new node */ - n = ecalloc(1, sizeof(Htnode)); + if ((n = calloc(1, sizeof(Htnode))) == NULL) { + printf("error: calloc: %s\n", strerror(errno)); + return -1; + } n->key = key; n->val = val; n->next = NULL; diff --git a/main.c b/main.c @@ -12,14 +12,15 @@ #include <sys/stat.h> #include <unistd.h> -#include "utils.h" #include "htable.h" +#include "util.c" #define BUFFER_LEN 1024 #define MAX_FD 1024 +#define MAX_NICK_LEN 16 #define NET_ADDEND 10 -#define USER_ADDEND 100 +#define USER_ADDEND 5 #define FD_TYPE_LINKER 1 #define FD_TYPE_CLONE 2 @@ -33,6 +34,8 @@ typedef struct { char *chan; /* channel */ } Network; +static int isrunning = 1; + static fd_set fdset; /* fd_set (select) */ static int nfds; /* number of fds */ @@ -42,414 +45,294 @@ static int fdsuf [MAX_FD]; /* fd -> nick suffix */ static Network *networks; /* linked list of networks */ static int netlen; /* current network length */ -static int netcap = NET_ADDEND; /* total memory allocated */ +static int netcap; /* total memory allocated */ +static Htable *users; + +/* functions prototype */ +void handle_fifo_input(int); +void handle_server_output(int); +void net_add(char *, char *, char *, char *, char *); +void net_del(char *); +void net_del_raw(int); +void net_resize(void); +void net_update(void); +void user_add(char *, int); +void user_del(char *, char *); +int *user_fds(char *, int); +int clone_add(int, char *); +int fd_add(char *, char *); +void fd_del(int, char *); +void nick_add_symb(char *, int); +void privmsg_update(char *, char *, int); +void terminate(int); +void print_table(void); +void print_htable(void); +void print_users(void); +void print_border(void); -/* - * hash table of users - * key -> <nickname> + '[' + <network_symbol> + ']' - * value -> array of linked clones fds indexed - * corresponding to its connected network - */ -static Htable *users; +int +main(int argc, char *argv[]) +{ + int fifo_fd, r, i; + fd_set rdset; + struct stat fifo_st; -static int isrunning = 1; + /* set stdout to unbufferd */ + setvbuf(stdout, NULL, _IONBF, 0); -/* prototype */ -void print_table(void); -void print_users(void); -int fdadd(char *host, char *port); -void fddel(int fd, char *msg); -void netadd(char *name, char *symb, char *host, char *port, char *chan); -void __netdel(int index); -void netdel(char *name); -int cloneadd(int netindex, char *nick); -void clonesupdate(void); -void useradd(char *unick, int nfd); -void userdel(char *nick, char *msg); -void append_netsymb(char *nick, int fd); -int *getuserfds(char *nick, int nfd); -void privmsg_update(char *dst, char *src, int fd); -void handle_server(int fd); -void handle_fifo(int fd); + /* check arguments */ + if (argc != 2) { + printf("usage: %s <fifo>\n", argv[0]); + return 1; + } -void -print_table(void) -{ - int i, *fds; - Htiter it = {0}; + /* init networks and users */ + netcap = NET_ADDEND; + networks = ecalloc((size_t)netcap, sizeof(Network)); - if (netlen == 0) return; + /* + * hash table of users + * key -> <nickname> + '[' + <network_symbol> + ']' + * value -> array of linked clones fds indexed + * corresponding to its connected network + */ + users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND); - /* print networks */ - printf("Networks\t"); - for (i = 0; i < netlen; i++) - printf("%s[%s](%d)\t", networks[i].name, networks[i].symb, networks[i].fd); - printf("\n"); + /* init fdnet array to -1 */ + for (i = 0; i < MAX_FD; i++) + fdnet[i] = -1; - while (htiterate(users, &it)) { - fds = (int *)it.node->val; - /* print user nick */ - printf("%s\t", (char *)it.node->key); - /* print clones fd separated by tabs */ - for (i = 0; i < netlen; i++) { - printf("%d", fds[i]); - if ((fds[i] > 0) && (fdsuf[fds[i]] > 0)) - printf("(%d)", fdsuf[fds[i]]); - printf("\t"); + /* crate or/and open fifo */ + if (stat(argv[1], &fifo_st) == 0) { + if (!(fifo_st.st_mode & S_IFIFO)) { + printf("error: '%s' is not a fifo file\n", argv[1]); + return 1; } - printf("\n"); + } else if (mkfifo(argv[1], S_IRWXU) != 0) { + printf("error: failed to create fifo file '%s'\n", argv[1]); + return 1; } -} - - -void -print_users(void) -{ - Htiter it = {0}; - int index = -1; - while (htiterate(users, &it)) { - if (index != (int)it.index) { - printf("\n%d", it.index); - index = (int)it.index; - } - printf(" -> %s", (char *)it.node->key); + fifo_fd = open(argv[1], O_RDWR); + if (fifo_fd < 0) { + printf("error: cannot open() '%s'\n", argv[1]); + return 1; } - printf("\n"); -} - -int -fdadd(char *host, char *port) -{ - int fd; - if ((fd = dial(host, port)) == -1) - return -1; - FD_SET(fd, &fdset); - if (fd+1 > nfds) - nfds = fd+1; + /* initialize fdset */ + FD_ZERO(&fdset); + FD_SET(fifo_fd, &fdset); + nfds = fifo_fd + 1; - return fd; -} + /* select loop */ + while (isrunning) { + rdset = fdset; + r = select(nfds, &rdset, 0, 0, NULL); + if (r < 0) { + if (errno == EINTR) + continue; + printf("error: select: %s\n", strerror(errno)); + terminate(1); + } else if (r == 0) { + printf("error: select timeout\n"); + terminate(1); + } -void -fddel(int fd, char *msg) -{ - ircsend(fd, "QUIT :%s", msg); - close(fd); - FD_CLR(fd, &fdset); - fdnet[fd] = -1; + for (i = 0; i < nfds; i++) { + if (!FD_ISSET(i, &rdset)) + continue; + if (i == fifo_fd) + handle_fifo_input(i); + else + handle_server_output(i); + break; /* one fd at a time because of FD_CLR */ + } + } + terminate(0); + return 0; } void -netadd(char *name, char *symb, char *host, char *port, char *chan) +handle_fifo_input(int fd) { - int i, fd; - Network *n; - Htiter it = {0}; - - /* resize */ - if (netlen < netcap) { - netcap += NET_ADDEND; - networks = realloc(networks, (size_t)netcap * sizeof(Network)); + char buffer[BUFFER_LEN]; + char *buf; + char *cmd; - while (htiterate(users, &it)) - it.node->val = realloc(it.node->val, (size_t)netcap * sizeof(int)); + if (readline(fd, buffer, sizeof(buffer)) < 1) { + printf("error: %d: failed to read from fifo: %s\n", fd, strerror(errno)); + terminate(1); } + /* printf("fifo: %s\n", buffer); */ - /* if name or symbol or host:port is already taken */ - for (i = 0; i < netlen; i++) { - if (strcmp(networks[i].name, name) == 0) { - printf("error: network name '%s' already taken\n", name); - return; - } - if (strcmp(networks[i].symb, symb) == 0) { - printf("error: network symbol '%s' already taken\n", symb); - return; - } - /* if ((strcmp(networks[i].host, host) == 0) - && (strcmp(networks[i].port, port) == 0)) { - printf("error: network host '%s' and port '%s' already taken by network '%s'\n", host, port, networks[i].name); - 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> <hostname> <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) { + isrunning = 0; + } else { + printf("error: %s is not a command\n", cmd); } - - printf("info: adding network '%s'\n", name); - if ((fd = fdadd(host, port)) == -1) - return; - /* send NICK and USER commands */ - ircsend(fd, "NICK linker"); - ircsend(fd, "USER linker localhost %s :%s", host, name); - /* add a network */ - fdnet[fd] = netlen; - fdtype[fd] = FD_TYPE_LINKER; - - n = &networks[netlen]; - n->fd = fd; - n->name = strdup(name); - n->symb = strdup(symb); - n->host = strdup(host); - n->port = strdup(port); - n->chan = strdup(chan); - netlen++; } void -__netdel(int index) +handle_server_output(int fd) { - Network *n = &networks[index]; - free(n->name); - free(n->symb); - free(n->host); - free(n->port); - free(n->chan); -} + char backup [BUFFER_LEN]; + char buffer [BUFFER_LEN]; + char linker_nick [MAX_NICK_LEN]; + char *buf; + char *cmd; + char *nick; + int i; -void -netdel(char *name) -{ - int i, netindex = -1, *fds; - char quit_msg[BUFFER_LEN]; + if (readline(fd, buffer, sizeof(buffer)) < 1) { + printf("error: %d: remote host closed connection: %s\n", fd, strerror(errno)); + terminate(1); + } - Htiter it = {0}; /* current iterator */ - Htiter lastit = {0}; /* last iterator */ + /* clone the buffer */ + strcpy(backup, buffer); + buf = buffer; - /* check if the netname exists */ - for (i = 0; i < netlen; i++) { - if (strcmp(name, networks[i].name) == 0) { - netindex = i; + /* set linker nick */ + strcpy(linker_nick, "linker"); + for (i = 0; i < fdsuf[fd]; i++) + strcat(linker_nick, "_"); + + /* remove CRLFs */ + for (i = 0; i < (int)strlen(buf); i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = '\0'; break; } } - if (netindex == -1) { - printf("error: network name '%s' doesn't exist\n", name); + + /* first column */ + cmd = split(&buf, ' '); + if (strcmp(cmd, "NOTICE") == 0) { + return; + } else if (strcmp(cmd, "ERROR") == 0) { + goto printbuffer; + } else if (strcmp(cmd, "PING") == 0) { + fdprintf(fd, "PONG %s\r\n", buf); return; } + /* strip nick from first column */ + nick = split(&cmd, '!'); + if (nick[0] == ':') + nick++; - printf("info: deleting network '%s'\n", name); + /* second column */ + cmd = split(&buf, ' '); - /* set the quit msg */ - snprintf(quit_msg, BUFFER_LEN, "unlinking network %s", name); + /* 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, "375") == 0) + || (strcmp(cmd, "372") == 0) + || (strcmp(cmd, "376") == 0) + || (strcmp(cmd, "396") == 0) + || (strcmp(cmd, "366") == 0) + || (strcmp(cmd, "MODE") == 0) + || (strcmp(cmd, "NOTICE") == 0)) { + return; + } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ + split(&buf, ' '); + nick = split(&buf, ' '); + strcat(nick, "_"); + fdsuf[fd]++; + if (strlen(nick) > MAX_NICK_LEN) { + /* remove suffix from the nick */ + nick[strlen(nick) - (size_t)fdsuf[fd]] = '\0'; + printf("error: cannot append suffix, nick '%s' is too big\n", nick); + if (strcmp(nick, "linker") == 0) { + net_del(networks[fdnet[fd]].name); + } else { + user_del(nick, "nick is too big"); + } + } else { + fdprintf(fd, "NICK %s\r\n", nick); + } + return; + } else if (strcmp(cmd, "001") == 0) { + fdprintf(fd, "JOIN %s\r\n", networks[fdnet[fd]].chan); + return; + } else if (strcmp(cmd, "PRIVMSG") == 0) { + char msg[BUFFER_LEN] = ""; + int *fds; - /* set all fds with last network index to the current index. */ - for (i = 0; i < nfds; i++) { - if (fdnet[i] == netlen-1) - fdnet[i] = netindex; - } + if (fdtype[fd] == FD_TYPE_LINKER) { + nick_add_symb(nick, fd); + if ((fds = htsearch(users, nick)) == NULL) + return; - while (htiterate(users, &it)) { - fds = (int *)it.node->val; - /* delete the user */ - if (fds[netindex] == -1) { - userdel(it.node->key, quit_msg); - /* this node is deleted and has no next */ - it = lastit; - /* delete the clone */ + split(&buf, ':'); /* set buf to msg */ + privmsg_update(msg, buf, fd); + for (i = 0; i < netlen; i++) { + if (fds[i] > 0) + fdprintf(fds[i], "PRIVMSG %s :%s\r\n", networks[i].chan, msg); + } } else { - if (fds[netindex] > 0) - fddel(fds[netindex], quit_msg); + char *netsymb; + char *user = split(&buf, ' '); - /* swap last index with the netindex */ - fds[netindex] = fds[netlen-1]; - fds[netlen-1] = 0; - } - lastit = it; - } + /* ignore messages from channel (it is handled by linker) */ + if ((user[0] == '#') || (user[0] == '&')) + return; - fddel(networks[netindex].fd, quit_msg); - __netdel(netindex); + nick_add_symb(nick, fd); + if ((fds = htsearch(users, nick)) == NULL) + return; - /* swap the network with the last */ - networks[netindex] = networks[netlen-1]; - netlen--; -} + /* split user nick and network symbol */ + *strrchr(user, ']') = '\0'; + netsymb = strrchr(user, '['); + *netsymb++ = '\0'; -int -cloneadd(int netindex, char *nick) -{ - int fd; - Network *n = &networks[netindex]; - - printf("info: adding clone '%s' on network '%s'\n", nick, n->name); - if ((fd = fdadd(n->host, n->port)) == -1) - return -1; - /* send NICK and USER commands */ - ircsend(fd, "NICK %s", nick); - ircsend(fd, "USER username localhost %s :real name", n->host); - /* add a user */ - fdnet[fd] = netindex; - fdtype[fd] = FD_TYPE_CLONE; - fdsuf[fd] = 0; - return fd; -} - -/* update clones in the last network added */ -void -clonesupdate(void) -{ - int *fds; - Htiter it = {0}; - - while (htiterate(users, &it)) { - fds = (int *)it.node->val; - /* netlen-1 can be occupied if networks are added quickly - * and has not enough time to connect to it's server */ - if (fds[netlen-1] == 0) - fds[netlen-1] = cloneadd(netlen-1, it.node->key); - } -} - -void -useradd(char *unick, int nfd) -{ - int i, *fds; - Network *n; - size_t l; - char *nick; - - n = &networks[fdnet[nfd]]; - - /* too long name */ - l = strlen(unick) + strlen(n->symb) + 2 + 1; - if (l > 16) - return; - - nick = ecalloc(l, sizeof(char)); - fds = ecalloc((size_t)netcap, sizeof(int)); - sprintf(nick, "%s[%s]", unick, n->symb); - - /* resize */ - if ((users->cap - users->len) < USER_ADDEND) - htresize(users, users->cap + USER_ADDEND); - - for (i = 0; i < netlen; i++) { - /* set -1 on user network */ - if (i == fdnet[nfd]) { - fds[i] = -1; - } else { - fds[i] = cloneadd(i, nick); - } - } - - if (htinsert(users, nick, fds) == -1) - fatal("error: user '%s' already exist", nick); -} - -void -userdel(char *nick, char *msg) -{ - int *fds, i; - if ((fds = htsearch(users, nick)) == NULL) - return; - - for (i = 0; i < netlen; i++) { - if (fds[i] > 0) - fddel(fds[i], msg); - } - htremove(users, nick); -} - -void -append_netsymb(char *nick, int fd) -{ - strcat(nick, "["); - strcat(nick, networks[fdnet[fd]].symb); - strcat(nick, "]"); -} - -int * -getuserfds(char *nick, int nfd) -{ - unsigned int l; - int *fds = NULL; - - for (l = 0; nick[strlen(nick)-l-1] == '_'; l++); - if (l > 0) - nick[strlen(nick)-l] = '\0'; - - fds = htsearch(users, nick); - /* if match but suffix doesn't match */ - if ((fds != NULL) && (fdsuf[fds[fdnet[nfd]]] != (int)l)) - fds = NULL; - - /* add the removed delimiter */ - if (l > 0) - nick[strlen(nick)] = '_'; - - return fds; -} - -/* trim all the nicknames to original nick - * this src will be destructed */ -void -privmsg_update(char *dst, char *src, int fd) -{ - 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 (getuserfds(src, fd) != NULL) - *strrchr(src, '[') = '\0'; - - strcat(dst, src); - strncat(dst, &d, 1); - - src = n; - } -} - -void -handle_server(int fd) -{ - char backup [BUFFER_LEN]; - char buffer [BUFFER_LEN]; - char *buf; - char *cmd; - char *nick; - int i; - - char linker[BUFFER_LEN] = "linker"; - - if (dreadline(fd, buffer, sizeof(buffer)) < 1) - fatal("error: %d: remote host closed connection: %s", fd, strerror(errno)); - - strcpy(backup, buffer); - buf = buffer; + /* get the network index */ + for (i = 0; i < netlen; i++) { + if (strcmp(netsymb, networks[i].symb) == 0) + break; + } - /* remove CRLFs */ - for (i = 0; i < (int)strlen(buf); i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = '\0'; - break; + split(&buf, ':'); /* set buf to msg */ + privmsg_update(msg, buf, fd); + fdprintf(fds[i], "PRIVMSG %s :%s\r\n", user, msg); } - } - - /* first column */ - cmd = split(&buf, ' '); - if (strcmp(cmd, "NOTICE") == 0) { - return; - } else if (strcmp(cmd, "ERROR") == 0) { - goto printbuffer; - } else if (strcmp(cmd, "PING") == 0) { - ircsend(fd, "PONG %s", buf); return; } - /* strip nick from first column */ - nick = split(&cmd, '!'); - if (nick[0] == ':') - nick++; - - /* second column */ - cmd = split(&buf, ' '); /* these messages are handled by linker */ if (fdtype[fd] == FD_TYPE_CLONE) { if ((strcmp(cmd, "353") == 0) @@ -459,48 +342,10 @@ handle_server(int fd) || (strcmp(cmd, "KICK") == 0) || (strcmp(cmd, "NICK") == 0)) return; - } - - /* set linker nick */ - for (i = 0; i < fdsuf[fd]; i++) - strcat(linker, "_"); - /* 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, "254") == 0) - || (strcmp(cmd, "255") == 0) - || (strcmp(cmd, "265") == 0) - || (strcmp(cmd, "266") == 0) - || (strcmp(cmd, "250") == 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, "NOTICE") == 0)) { - return; - } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ - split(&buf, ' '); - nick = split(&buf, ' '); - strcat(nick, "_"); - fdsuf[fd]++; - ircsend(fd, "NICK %s", nick); - return; - } else if (strcmp(cmd, "001") == 0) { - ircsend(fd, "JOIN %s", networks[fdnet[fd]].chan); - return; - - /* ========================= only linker ========================= */ } else if (strcmp(cmd, "353") == 0) { char *nick; split(&buf, ':'); - clonesupdate(); + net_update(); while (*(nick = split(&buf, ' ')) != '\0') { if (*nick == '@' || *nick == '&' @@ -509,25 +354,25 @@ handle_server(int fd) || *nick == '+' || *nick == '\\') nick++; - if (strcmp(nick, linker) != 0) - useradd(nick, fd); + if (strcmp(nick, linker_nick) != 0) + user_add(nick, fd); } return; } else if (strcmp(cmd, "JOIN") == 0) { - if ((strcmp(nick, linker) != 0) - && (getuserfds(nick, fd) == NULL)) /* if not clone */ - useradd(nick, fd); + if ((strcmp(nick, linker_nick) != 0) + && (user_fds(nick, fd) == NULL)) /* if not clone */ + user_add(nick, fd); return; } else if ((strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0)) { - append_netsymb(nick, fd); - userdel(nick, buf); + nick_add_symb(nick, fd); + user_del(nick, buf); return; } else if (strcmp(cmd, "NICK") == 0) { int *fds, i; char *newnick; - append_netsymb(nick, fd); + nick_add_symb(nick, fd); if ((fds = htsearch(users, nick)) == NULL) return; @@ -540,7 +385,7 @@ handle_server(int fd) for (i = 0; i < netlen; i++) { if (fds[i] > 0) - ircsend(fds[i], "NICK %s", newnick); + fdprintf(fds[i], "NICK %s\r\n", newnick); } return; } else if (strcmp(cmd, "KICK") == 0) { @@ -553,15 +398,15 @@ handle_server(int fd) /* set the quit msg */ snprintf(quit_msg, BUFFER_LEN, "kicked by %s", nick); - if (strcmp(user, linker) == 0) { - netdel(networks[fdnet[fd]].name); + if (strcmp(user, linker_nick) == 0) { + net_del(networks[fdnet[fd]].name); return; } /* delete the user if the message from the same network */ - if ((fds = getuserfds(user, fd)) == NULL) { - append_netsymb(user, fd); - userdel(user, quit_msg); + if ((fds = user_fds(user, fd)) == NULL) { + nick_add_symb(user, fd); + user_del(user, quit_msg); return; } @@ -577,173 +422,344 @@ handle_server(int fd) /* set buf to msg */ split(&buf, ':'); /* send notice in the channel through linker */ - ircsend(networks[i].fd, "PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]", + fdprintf(networks[i].fd, "PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]\r\n", networks[i].chan, user, nick, networks[fdnet[fd]].name, chan, buf); /* close the kicked fd */ - fddel(fds[fdnet[fd]], quit_msg); + fd_del(fds[fdnet[fd]], quit_msg); fds[fdnet[fd]] = -2; return; - /* ========================= only linker ========================= */ + } +printbuffer: + printf("%d: %s\n", fd, backup); +} - } else if (strcmp(cmd, "PRIVMSG") == 0) { - char msg[BUFFER_LEN] = ""; - int *fds; +void +net_add(char *name, char *symb, char *host, char *port, char *chan) +{ + int i, fd; + Network *n; - if (fdtype[fd] == FD_TYPE_LINKER) { - append_netsymb(nick, fd); - if ((fds = htsearch(users, nick)) == NULL) - return; + /* if name or symbol or configuration already exists */ + for (i = 0; i < netlen; i++) { + if (strcmp(networks[i].name, name) == 0) { + printf("error: network name '%s' already exists\n", name); + return; + } + if (strcmp(networks[i].symb, symb) == 0) { + printf("error: network symbol '%s' already exists\n", symb); + return; + } + if ((strcmp(networks[i].host, host) == 0) + && (strcmp(networks[i].port, port) == 0) + && (strcmp(networks[i].chan, chan) == 0)) { + printf("error: network configuration already exists\n"); + return; + } + } - split(&buf, ':'); /* set buf to msg */ - privmsg_update(msg, buf, fd); - for (i = 0; i < netlen; i++) { - if (fds[i] > 0) - ircsend(fds[i], "PRIVMSG %s :%s", networks[i].chan, msg); - } - } else { - char *netsymb; - char *user = split(&buf, ' '); + /* resize if no space */ + if (netlen == netcap) + net_resize(); - /* ignore messages from channel (it is handled by linker) */ - if ((user[0] == '#') || (user[0] == '&')) - return; + /* connect */ + if ((fd = fd_add(host, port)) == -1) + return; + /* send NICK and USER commands */ + fdprintf(fd, "NICK linker\r\n"); + fdprintf(fd, "USER linker 0 * :linker\r\n"); + /* add a network */ + fdnet[fd] = netlen; + fdtype[fd] = FD_TYPE_LINKER; - append_netsymb(nick, fd); - if ((fds = htsearch(users, nick)) == NULL) - return; + n = &networks[netlen]; + n->fd = fd; + n->name = strdup(name); + n->symb = strdup(symb); + n->host = strdup(host); + n->port = strdup(port); + n->chan = strdup(chan); + netlen++; + printf("%d: network '%s' added\n", fd, name); +} - /* split user nick and network symbol */ - *strrchr(user, ']') = '\0'; - netsymb = strrchr(user, '['); - *netsymb++ = '\0'; +void +net_del(char *name) +{ + int i, index, *fds; + char quit_msg [BUFFER_LEN]; - /* get the network index */ - for (i = 0; i < netlen; i++) { - if (strcmp(netsymb, networks[i].symb) == 0) - break; - } + Htiter it = {0}; /* current iterator */ + Htiter lastit = {0}; /* last iterator */ - split(&buf, ':'); /* set buf to msg */ - privmsg_update(msg, buf, fd); - ircsend(fds[i], "PRIVMSG %s :%s", user, msg); + /* check if the name exists */ + index = -1; + for (i = 0; i < netlen; i++) { + if (strcmp(name, networks[i].name) == 0) { + index = i; + break; } + } + if (index == -1) { + printf("error: network name '%s' doesn't exist\n", name); return; } -printbuffer: - printf("%d: %s\n", fd, backup); -} + /* set the quit msg */ + snprintf(quit_msg, BUFFER_LEN, "unlinking network %s", name); + + /* set all fds with last network index to the current index. */ + for (i = 0; i < nfds; i++) { + if (fdnet[i] == netlen-1) + fdnet[i] = index; + } + + while (htiterate(users, &it)) { + fds = (int *)it.node->val; + /* delete the user */ + if (fds[index] == -1) { + user_del(it.node->key, quit_msg); + /* this node is deleted and has no next */ + it = lastit; + /* delete the clone */ + } else { + if (fds[index] > 0) + fd_del(fds[index], quit_msg); + /* swap last index with the index */ + fds[index] = fds[netlen-1]; + fds[netlen-1] = 0; + } + lastit = it; + } + + fd_del(networks[index].fd, quit_msg); + net_del_raw(index); + printf("%d: network '%s' deleted\n", networks[index].fd, name); + /* swap the network with the last */ + networks[index] = networks[netlen-1]; + netlen--; +} void -handle_fifo(int fd) +net_del_raw(int index) { - char buffer[BUFFER_LEN]; - char *buf; - char *cmd; - - if (dreadline(fd, buffer, sizeof(buffer)) == -1) - fatal("error: dreadline() failed"); + Network *n = &networks[index]; + free(n->name); + free(n->symb); + free(n->host); + free(n->port); + free(n->chan); +} - /* printf("fifo: %s\n", buffer); */ +void +net_resize(void) +{ + Htiter it = {0}; - 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 == '\0') || (*symb == '\0') || (*host == '\0') || (*port == '\0') || (*chan == '\0')) - printf("usage: netadd <name> <symbol> <hostname> <port> <channel>\n"); - netadd(name, symb, host, port, chan); - } else if (strcmp(cmd, "netdel") == 0) { - char *name = buf; - if (*name == '\0') - printf("usage: netdel <name>\n"); - netdel(name); - } else if (strcmp(cmd, "print") == 0) { - print_table(); - } else if (strcmp(cmd, "users") == 0) { - print_users(); - } else if (strcmp(cmd, "exit") == 0) { - isrunning = 0; - } else { - printf("error: %s is not a command\n", cmd); + /* resize networks array */ + if ((networks = realloc(networks, ((size_t)netcap + NET_ADDEND) * sizeof(Network))) == NULL) + goto error; + memset(networks + netcap, 0, NET_ADDEND * sizeof(Network)); + /* also resize all the user's array */ + while (htiterate(users, &it)) { + if ((it.node->val = realloc(it.node->val, ((size_t)netcap + NET_ADDEND) * sizeof(int))) == NULL) + goto error; + memset((int *)it.node->val + netcap, 0, NET_ADDEND * sizeof(int)); } + netcap += NET_ADDEND; + return; +error: + printf("error: realloc: %s\n", strerror(errno)); + terminate(1); } -int -main(int argc, char *argv[]) +void +net_update(void) { - int fifofd, r, i; - fd_set rdset; - struct stat fifo_st; + int *fds; + Htiter it = {0}; - /* check arguments */ - if (argc != 2) - fatal("usage: %s <fifo>", argv[0]); + while (htiterate(users, &it)) { + fds = (int *)it.node->val; + /* netlen-1 can be occupied if networks are added quickly + * and has not enough time to connect to it's server */ + if (fds[netlen-1] == 0) { + fds[netlen-1] = clone_add(netlen-1, it.node->key); + } + } +} - /* warning */ - printf("WARNING: This software is provided 'as-is', without any express or " - "implied warranty.\nIn no event shall the author(s) be liable " - "for any damages arising from the use of this software.\n"); +void +user_add(char *unick, int nfd) +{ + int i, *fds; + Network *n; + size_t len; + char *nick; - /* init networks and users */ - networks = ecalloc((size_t)netcap, sizeof(Network)); - users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND); - /* init fdnet array to -1 */ - for (i = 0; i < MAX_FD; i++) - fdnet[i] = -1; + n = &networks[fdnet[nfd]]; + len = strlen(unick) + strlen(n->symb) + 2 + 1; - /* crate or/and open fifo */ - if (stat(argv[1], &fifo_st) == 0) { - if (!(fifo_st.st_mode & S_IFIFO)) - fatal("error: file %s is not a fifo", argv[1]); - } else if (mkfifo(argv[1], S_IRWXU) != 0) { - fatal("error: cannot create fifo file %s", argv[1]); + /* too long nick */ + if (len-1 > MAX_NICK_LEN) { + printf("error: user nick '%s' is too big\n", unick); + return; } - fifofd = open(argv[1], O_RDWR); - if (fifofd < 0) - fatal("error: cannot open() '%s'", argv[1]); - /* set stdout to unbufferd */ - setvbuf(stdout, NULL, _IONBF, 0); - /* initialize fdset */ - FD_ZERO(&fdset); - FD_SET(fifofd, &fdset); - nfds = fifofd + 1; + /* resize hash table if store is low */ + if ((users->cap - users->len) < USER_ADDEND) + htresize(users, users->cap + USER_ADDEND); - /* select loop */ - while (isrunning) { - rdset = fdset; - r = select(nfds, &rdset, 0, 0, NULL); - if (r < 0) { - if (errno == EINTR) - continue; - fatal("error: select: %s", strerror(errno)); - } else if (r == 0) { - fatal("error: select() timeout"); + /* allocate a new user */ + nick = ecalloc(len, sizeof(char)); + fds = ecalloc((size_t)netcap, sizeof(int)); + sprintf(nick, "%s[%s]", unick, n->symb); + + /* clone the user on all other network */ + for (i = 0; i < netlen; i++) { + /* set -1 on user's network */ + if (i == fdnet[nfd]) { + fds[i] = -1; + } else { + fds[i] = clone_add(i, nick); } + } + /* insert it to the users hash table */ + if (htinsert(users, nick, fds) == -1) { + /* this shouldn't happen as it was already checked */ + printf("error: user '%s' already exists\n", nick); + terminate(1); + } +} - for (i = 0; i < nfds; i++) { - if (FD_ISSET(i, &rdset)) { - if (i == fifofd) - handle_fifo(i); - else - handle_server(i); +void +user_del(char *nick, char *msg) +{ + int *fds, i; + if ((fds = htsearch(users, nick)) == NULL) { + printf("error: user '%s' doesn't exits\n", nick); + return; + } - /* handle one fd at one time because FD_CLR */ - break; - } + for (i = 0; i < netlen; i++) { + if (fds[i] > 0) + fd_del(fds[i], msg); + } + htremove(users, nick); +} + +int * +user_fds(char *nick, int nfd) +{ + unsigned int suf; + int *fds = NULL; + + /* count suffix */ + for (suf = 0; nick[strlen(nick)-suf-1] == '_'; suf++); + /* remove suffix */ + if (suf > 0) + nick[strlen(nick)-suf] = '\0'; + + fds = htsearch(users, nick); + /* if match but suffix doesn't match */ + if ((fds != NULL) && (fdsuf[fds[fdnet[nfd]]] != (int)suf)) + fds = NULL; + + /* add back suffix */ + if (suf > 0) + nick[strlen(nick)] = '_'; + + return fds; +} + +int +clone_add(int netindex, char *nick) +{ + int fd; + Network *n = &networks[netindex]; + + if ((fd = fd_add(n->host, n->port)) == -1) + return -1; + /* send NICK and USER commands */ + fdprintf(fd, "NICK %s\r\n", nick); + fdprintf(fd, "USER %s 0 * :%s\r\n", n->name, n->name); + /* add a user */ + fdnet[fd] = netindex; + fdtype[fd] = FD_TYPE_CLONE; + fdsuf[fd] = 0; + return fd; +} + +int +fd_add(char *host, char *port) +{ + int fd; + if ((fd = dial(host, port)) == -1) + return -1; + + FD_SET(fd, &fdset); + if (fd+1 > nfds) + nfds = fd+1; + + return fd; +} + +void +fd_del(int fd, char *msg) +{ + fdprintf(fd, "QUIT :%s\r\n", msg); + close(fd); + FD_CLR(fd, &fdset); + fdnet[fd] = -1; +} + +void +nick_add_symb(char *nick, int fd) +{ + strcat(nick, "["); + strcat(nick, networks[fdnet[fd]].symb); + strcat(nick, "]"); +} + +/* + * trim all the nicknames to original nick + * src will be destructed + */ +void +privmsg_update(char *dst, char *src, int fd) +{ + 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_fds(src, fd) != NULL) + *strrchr(src, '[') = '\0'; + + strcat(dst, src); + strncat(dst, &d, 1); + + src = n; } +} +void +terminate(int status) +{ + int i; for (i = 0; i < nfds; i++) { - if (fdnet[i] > 0) { - printf("shutting down %d\n", i); - fddel(i, "linker shutting down"); + if (fdnet[i] >= 0) { + /* printf("%d: shutting down\n", i); */ + fd_del(i, "linker shutting down"); } } /* delete all the users */ @@ -751,8 +767,92 @@ main(int argc, char *argv[]) /* delete all the networks */ for (i = 0; i < netlen; i++) - __netdel(i); + net_del_raw(i); free(networks); - return 0; + if (status == 0) + printf("exit successfully\n"); + else + printf("aborted\n"); + exit(1); +} + +void +print_table(void) +{ + int i, *fds, diff, tabs; + Htiter it = {0}; + char *nick; + + if (netlen == 0) + return; + + print_border(); + /* print networks */ + printf("Networks\t\t"); + for (i = 0; i < netlen; i++) + printf("%s(%d)\t", networks[i].symb, networks[i].fd); + printf("\n"); + + while (htiterate(users, &it)) { + fds = (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 fds */ + for (i = 0; i < netlen; i++) { + printf("%d", fds[i]); + /* print suffix */ + if ((fds[i] > 0) && (fdsuf[fds[i]] > 0)) + printf("(%d)", fdsuf[fds[i]]); + 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"); } diff --git a/util.c b/util.c @@ -0,0 +1,114 @@ +/* + * This work is dedicated to the public domain. + * See COPYING file for more information. + */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <unistd.h> +#include <errno.h> + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + if ((p = calloc(nmemb, size)) == NULL) { + printf("error: calloc: %s\n", strerror(errno)); + return NULL; + } + return p; +} + +char* +split(char **str, char ch) +{ + char *token = *str; + + if (**str == '\0') return *str; + + while (**str != ch && **str != '\0') + (*str)++; + if (**str == '\0') + return token; + **str = '\0'; + (*str)++; + while(**str == ch && **str != '\0') + (*str)++; + return token; +} + +ssize_t +fdprintf(int fd, const char *fmt, ...) +{ + va_list args; + char buf[1024]; + size_t len; + ssize_t n, i = 0; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + len = strlen(buf); + while (i < (ssize_t)len) { + if ((n = write(fd, buf + i, len - (size_t)i)) == -1) { + printf("error: write failed: %s\n", strerror(errno)); + return -1; + } + i += n; + } + return i; +} + +int +dial(char *host, char *port) +{ + static struct addrinfo hints, *res = NULL, *res0; + int fd = -1, r; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((r = getaddrinfo(host, port, &hints, &res0)) != 0) { + printf("error: getaddrinfo: %s\n", gai_strerror(r)); + return -1; + } + for (res = res0; res != NULL; res = res->ai_next) { + if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) + continue; + if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) { + close(fd); + fd = -1; + continue; + } + break; + } + if (fd == -1) + printf("error: cannot connect to host '%s'\n", host); + + freeaddrinfo(res0); + return fd; +} + +ssize_t +readline(int fd, char *buffer, size_t size) +{ + ssize_t n, i = 0; + char c; + + do { + if ((n = read(fd, &c, sizeof(char))) != sizeof(char)) + return n; + buffer[i++] = c; + } while ((i < (ssize_t)size) && (c != '\n') && (c != '\0')); + + buffer[i-1] = '\0'; + return i; +} diff --git a/utils.c b/utils.c @@ -1,152 +0,0 @@ -/* - * This work is dedicated to the public domain. - * See COPYING file for more information. - */ - -#include <ctype.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <unistd.h> - -#include "utils.h" - -void -fatal(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if (fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - exit(1); -} - -void * -ecalloc(size_t nmemb, size_t size) -{ - void *p; - if ((p = calloc(nmemb, size)) == NULL) - fatal("calloc:"); - return p; -} - -char* -split(char **str, char ch) -{ - char *token = *str; - - if (**str == '\0') return *str; - - while (**str != ch && **str != '\0') - (*str)++; - if (**str == '\0') - return token; - **str = '\0'; - (*str)++; - while(**str == ch && **str != '\0') - (*str)++; - return token; -} - -void -ircsend(int fd, const char *format, ...) -{ - static char buf[4096]; - va_list args; - va_start(args, format); - vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - strcat(buf, "\r\n"); - send(fd, buf, strlen(buf), 0); -} - -int -dial(char *host, char *port) -{ - static struct addrinfo hints, *res = NULL, *res0; - int fd = -1, r; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if ((r = getaddrinfo(host, port, &hints, &res0)) != 0) { - printf("error: getaddrinfo: %s\n", gai_strerror(r)); - return -1; - } - - for (res = res0; res != NULL; res = res->ai_next) { - if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) - continue; - if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) { - close(fd); - fd = -1; - continue; - } - break; - } - if (fd == -1) { - printf("error: cannot connect to host '%s'\n", host); - return -1; - } - freeaddrinfo(res0); - return fd; -} - -int -isstrnumber(char *str) -{ - int i; - for (i = 0; str[i]!= '\0'; i++) { - if (isdigit(str[i]) == 0) - return 0; - } - return 1; -} - -size_t -strlcpy(char *dst, const char *src, size_t size) -{ - size_t i; - - if (size == 0) - return 0; - - for (i = 0; i < size; i++) { - dst[i] = src[i]; - if (src[i] == '\0') - return i; - } - - dst[size-1] = '\0'; - return size-1; -} - - -ssize_t -dreadline(int fd, char *buffer, size_t size) -{ - size_t i = 0; - char c; - ssize_t l; - - do { - if ((l = read(fd, &c, sizeof(char))) != sizeof(char)) - return l; - buffer[i++] = c; - } while ((i < size) && (c != '\n') && (c != '\0')); - - buffer[i-1] = '\0'; - return (ssize_t)i; -} diff --git a/utils.h b/utils.h @@ -1,13 +0,0 @@ -/* - * This work is dedicated to the public domain. - * See COPYING file for more information. - */ - -void fatal(const char *fmt, ...); -void *ecalloc(size_t nmemb, size_t size); -char *split(char **str, char ch); -void ircsend(int fd, const char *format, ...); -int dial(char *host, char *port); -int isstrnumber(char *str); -size_t strlcpy(char *dst, const char *src, size_t size); -ssize_t dreadline(int fd, char *buffer, size_t size);