ref: 8df402e53d02ca2c0b976715ce57c28750dd99d9
parent: 5ff9649e6c51bae0dd09f218e4943fa5f4d458be
author: libredev <libredev@ircforever.org>
date: Wed Nov 16 13:14:04 EST 2022
code refactor
--- a/Makefile
+++ b/Makefile
@@ -12,15 +12,11 @@
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)
--- 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 @@
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 @@
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;
--- 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 @@
char *chan; /* channel */
} Network;
+static int isrunning = 1;
+
static fd_set fdset; /* fd_set (select) */
static int nfds; /* number of fds */
@@ -42,389 +45,181 @@
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;
-/*
- * hash table of users
- * key -> <nickname> + '[' + <network_symbol> + ']'
- * value -> array of linked clones fds indexed
- * corresponding to its connected network
- */
-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);
-static int isrunning = 1;
-
-/* 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);
-
-void
-print_table(void)
-{
- int i, *fds;
- Htiter it = {0};
-
- if (netlen == 0) return;
-
- /* 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");
-
- 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");
- }
- printf("\n");
- }
-}
-
-
-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);
- }
- printf("\n");
-}
-
int
-fdadd(char *host, char *port)
+main(int argc, char *argv[])
{
- int fd;
- if ((fd = dial(host, port)) == -1)
- return -1;
+ int fifo_fd, r, i;
+ fd_set rdset;
+ struct stat fifo_st;
- FD_SET(fd, &fdset);
- if (fd+1 > nfds)
- nfds = fd+1;
+ /* set stdout to unbufferd */
+ setvbuf(stdout, NULL, _IONBF, 0);
- return fd;
-}
-
-void
-fddel(int fd, char *msg)
-{
- ircsend(fd, "QUIT :%s", msg);
- close(fd);
- FD_CLR(fd, &fdset);
- fdnet[fd] = -1;
-}
-
-void
-netadd(char *name, char *symb, char *host, char *port, char *chan)
-{
- int i, fd;
- Network *n;
- Htiter it = {0};
-
- /* resize */
- if (netlen < netcap) {
- netcap += NET_ADDEND;
- networks = realloc(networks, (size_t)netcap * sizeof(Network));
-
- while (htiterate(users, &it))
- it.node->val = realloc(it.node->val, (size_t)netcap * sizeof(int));
+ /* check arguments */
+ if (argc != 2) {
+ printf("usage: %s <fifo>\n", argv[0]);
+ return 1;
}
- /* 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;
- }*/
- }
+ /* init networks and users */
+ netcap = NET_ADDEND;
+ networks = ecalloc((size_t)netcap, sizeof(Network));
- 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;
+ /*
+ * 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);
- 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++;
-}
+ /* init fdnet array to -1 */
+ for (i = 0; i < MAX_FD; i++)
+ fdnet[i] = -1;
-void
-__netdel(int index)
-{
- Network *n = &networks[index];
- free(n->name);
- free(n->symb);
- free(n->host);
- free(n->port);
- free(n->chan);
-}
-
-void
-netdel(char *name)
-{
- int i, netindex = -1, *fds;
- char quit_msg[BUFFER_LEN];
-
- Htiter it = {0}; /* current iterator */
- Htiter lastit = {0}; /* last iterator */
-
- /* check if the netname exists */
- for (i = 0; i < netlen; i++) {
- if (strcmp(name, networks[i].name) == 0) {
- netindex = i;
- break;
+ /* 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;
}
+ } else if (mkfifo(argv[1], S_IRWXU) != 0) {
+ printf("error: failed to create fifo file '%s'\n", argv[1]);
+ return 1;
}
- if (netindex == -1) {
- printf("error: network name '%s' doesn't exist\n", name);
- return;
+ fifo_fd = open(argv[1], O_RDWR);
+ if (fifo_fd < 0) {
+ printf("error: cannot open() '%s'\n", argv[1]);
+ return 1;
}
- printf("info: deleting network '%s'\n", name);
+ /* initialize fdset */
+ FD_ZERO(&fdset);
+ FD_SET(fifo_fd, &fdset);
+ nfds = fifo_fd + 1;
- /* 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] = netindex;
- }
-
- 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 */
- } else {
- if (fds[netindex] > 0)
- fddel(fds[netindex], quit_msg);
-
- /* swap last index with the netindex */
- fds[netindex] = fds[netlen-1];
- fds[netlen-1] = 0;
+ /* 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);
}
- lastit = it;
- }
- fddel(networks[netindex].fd, quit_msg);
- __netdel(netindex);
-
- /* swap the network with the last */
- networks[netindex] = networks[netlen-1];
- netlen--;
-}
-
-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);
+ 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 */
}
}
-
- if (htinsert(users, nick, fds) == -1)
- fatal("error: user '%s' already exist", nick);
+ terminate(0);
+ return 0;
}
void
-userdel(char *nick, char *msg)
+handle_fifo_input(int fd)
{
- int *fds, i;
- if ((fds = htsearch(users, nick)) == NULL)
- return;
+ char buffer[BUFFER_LEN];
+ char *buf;
+ char *cmd;
- for (i = 0; i < netlen; i++) {
- if (fds[i] > 0)
- fddel(fds[i], msg);
+ if (readline(fd, buffer, sizeof(buffer)) < 1) {
+ printf("error: %d: failed to read from fifo: %s\n", fd, strerror(errno));
+ terminate(1);
}
- htremove(users, nick);
-}
+ /* printf("fifo: %s\n", buffer); */
-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;
+ 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);
}
}
void
-handle_server(int fd)
+handle_server_output(int fd)
{
char backup [BUFFER_LEN];
char buffer [BUFFER_LEN];
+ char linker_nick [MAX_NICK_LEN];
char *buf;
char *cmd;
char *nick;
int i;
- char linker[BUFFER_LEN] = "linker";
+ if (readline(fd, buffer, sizeof(buffer)) < 1) {
+ printf("error: %d: remote host closed connection: %s\n", fd, strerror(errno));
+ terminate(1);
+ }
- if (dreadline(fd, buffer, sizeof(buffer)) < 1)
- fatal("error: %d: remote host closed connection: %s", fd, strerror(errno));
-
+ /* clone the buffer */
strcpy(backup, buffer);
buf = buffer;
+ /* 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') {
@@ -440,7 +235,7 @@
} else if (strcmp(cmd, "ERROR") == 0) {
goto printbuffer;
} else if (strcmp(cmd, "PING") == 0) {
- ircsend(fd, "PONG %s", buf);
+ fdprintf(fd, "PONG %s\r\n", buf);
return;
}
/* strip nick from first column */
@@ -450,20 +245,7 @@
/* second column */
cmd = split(&buf, ' ');
- /* these messages are handled by linker */
- if (fdtype[fd] == FD_TYPE_CLONE) {
- if ((strcmp(cmd, "353") == 0)
- || (strcmp(cmd, "JOIN") == 0)
- || (strcmp(cmd, "QUIT") == 0)
- || (strcmp(cmd, "PART") == 0)
- || (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)
@@ -472,6 +254,7 @@
|| (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)
@@ -490,17 +273,79 @@
nick = split(&buf, ' ');
strcat(nick, "_");
fdsuf[fd]++;
- ircsend(fd, "NICK %s", nick);
+ 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) {
- ircsend(fd, "JOIN %s", networks[fdnet[fd]].chan);
+ fdprintf(fd, "JOIN %s\r\n", networks[fdnet[fd]].chan);
return;
+ } else if (strcmp(cmd, "PRIVMSG") == 0) {
+ char msg[BUFFER_LEN] = "";
+ int *fds;
- /* ========================= only linker ========================= */
+ if (fdtype[fd] == FD_TYPE_LINKER) {
+ nick_add_symb(nick, fd);
+ if ((fds = htsearch(users, nick)) == NULL)
+ return;
+
+ 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 {
+ char *netsymb;
+ char *user = split(&buf, ' ');
+
+ /* ignore messages from channel (it is handled by linker) */
+ if ((user[0] == '#') || (user[0] == '&'))
+ return;
+
+ nick_add_symb(nick, fd);
+ if ((fds = htsearch(users, nick)) == NULL)
+ return;
+
+ /* 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(msg, buf, fd);
+ fdprintf(fds[i], "PRIVMSG %s :%s\r\n", user, msg);
+ }
+ return;
+ }
+ /* these messages are handled by linker */
+ if (fdtype[fd] == FD_TYPE_CLONE) {
+ if ((strcmp(cmd, "353") == 0)
+ || (strcmp(cmd, "JOIN") == 0)
+ || (strcmp(cmd, "QUIT") == 0)
+ || (strcmp(cmd, "PART") == 0)
+ || (strcmp(cmd, "KICK") == 0)
+ || (strcmp(cmd, "NICK") == 0))
+ return;
} 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 @@
|| *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 @@
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 @@
/* 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 @@
/* 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;
+ Network *n = &networks[index];
+ free(n->name);
+ free(n->symb);
+ free(n->host);
+ free(n->port);
+ free(n->chan);
+}
- if (dreadline(fd, buffer, sizeof(buffer)) == -1)
- fatal("error: dreadline() failed");
+void
+net_resize(void)
+{
+ Htiter it = {0};
- /* printf("fifo: %s\n", buffer); */
-
- 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 @@
/* 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");
}
--- /dev/null
+++ b/util.c
@@ -1,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;
+}
--- a/utils.c
+++ /dev/null
@@ -1,152 +1,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;
-}
--- a/utils.h
+++ /dev/null
@@ -1,13 +1,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);