struct dynarr databuf;
struct trie_node *client_map = NULL;
+struct trie_node *channel_map = NULL;
const char *hostname = "irc.patchysicp.xyz";
int processPacket(struct client_data *client) {
return 0;
if (!args[0]) // FIXME: error message
return 0;
- for (char *p = args[0]; *p; p++) // TODO: multiple channels
- if (*p == ',') {
- *p = '\0';
- break;
+ if (!strcmp(args[0], "0")) {
+ struct trie_find *f = NULL;
+ if (trie_findfirst(&f, &client->channels) == 1)
+ do {
+ struct channel *chan = f->n->value;
+ channel_part(client, chan, "I fell for a classic prank");
+ } while(trie_findfirst(&f, &client->channels) == 1);
+ free(f);
+ return 0;
+ }
+ char *p = strchr(args[0], ','); // TODO: multiple channels
+ if (p)
+ *p = '\0';
+ channel_join(client, args[0], NULL);
+ } else if(strcmp(command, "PART") == 0) {
+ if (!client->nick) // FIXME: error message
+ return 0;
+ if (!args[0]) // FIXME: error message
+ return 0;
+ char *p = strchr(args[0], ','); // TODO: multiple channels
+ if (p)
+ *p = '\0';
+ struct channel *chan = findChannel(args[0]);
+ if (!chan) // TODO: error message
+ return 0;
+ channel_part(client, chan, args[1] ? args[1] : "");
+ } else if(strcmp(command, "TOPIC") == 0) {
+ if (!client->nick) // FIXME: error message
+ return 0;
+ if (!args[0]) // FIXME: error message
+ return 0;
+ struct channel *chan = findChannel(args[0]);
+ if (!chan) // TODO: error message
+ return 0;
+ if (!args[1]) {
+ // TODO: +s
+ channel_sendtopic(client, chan);
+ } else {
+ // TODO: +t check, membership check
+ free(chan->topic);
+ free(chan->topic_who);
+ if (!*args[1]) {
+ chan->topic = NULL;
+ chan->topic_who = NULL;
+ chan->topic_when = 0;
+ } else {
+ chan->topic = mystrdup(args[1]);
+ if (!chan->topic) abort();
+ chan->topic_who = mystrdup(client->nick);
+ if (!chan->topic_who) abort();
+ chan->topic_when = time(NULL);
}
- copy_strf(&databuf, ":%s!%s@%s JOIN :%s\n", client->nick, client->username, client->hostname, args[0]);
- // End GitHub Copilot
- send_log(client->FD, databuf.buf, databuf.len, 0);
- sendall_log(client->FD, databuf.buf, databuf.len, 0);
- struct trie_find *f;
- if (trie_findfirst(&f, &client_map) == 1)
- do {
- struct client_data *cl = f->n->value;
- copy_strf(&databuf, ":%s 353 %s = %s :%s\n", hostname, client->nick, args[0], cl->nick);
- send_log(client->FD, databuf.buf, databuf.len, 0);
- } while(trie_findnext(&f) == 1);
- copy_strf(&databuf, ":%s 366 %s :End of /NAMES list\n", hostname, client->nick);
- send_log(client->FD, databuf.buf, databuf.len, 0);
+ copy_strf(&databuf, ":%s!%s@%s TOPIC %s :%s\n", client->nick, client->username, client->hostname, chan->name, args[1]);
+ struct trie_find *f = NULL;
+ if (trie_findfirst(&f, &chan->members) == 1)
+ do {
+ struct client_data *cl = f->n->value;
+ send_log(cl->FD, databuf.buf, databuf.len, 0);
+ } while (trie_findnext(&f) == 1);
+ }
} else if(strcmp(command, "PRIVMSG") == 0) {
if (!client->nick) // FIXME: error message
return 0;
if (argc < 2) // FIXME: error message
return 0;
copy_strf(&databuf, ":%s!%s@%s PRIVMSG %s :%s\n", client->nick, client->username, client->hostname, args[0], args[1]);
- if (args[0][0] == '#') {
+ if (args[0][0] == '#' || args[0][0] == '&') {
sendall_log(client->FD, databuf.buf, databuf.len, 0);
} else {
struct client_data *cl = findClient(args[0]);
return 0;
char *nick = args[0];
if (argc == 1) { // get mode
- if (*nick != '#') {
+ if (*nick != '#' && *nick != '&') {
struct client_data *cl = findClient(nick);
if (!cl) {
copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, nick);
send_log(client->FD, databuf.buf, databuf.len, 0);
}
} else { // set mode
- if (*nick != '#') {
+ if (*nick != '#' && *nick != '&') {
struct client_data *cl = findClient(nick);
if (!cl) {
copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, nick);
p++;
}
} else {
+ if (argc == 2) { // Type A channel modes
+ if (!strcmp(args[1], "+b")) {
+ copy_strf(&databuf, ":%s 368 %s %s :End of channel ban list\n", hostname, client->nick, args[1]);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ return 0;
+ }
+ if (!strcmp(args[1], "+e")) {
+ copy_strf(&databuf, ":%s 349 %s %s :End of channel exception list\n", hostname, client->nick, args[1]);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ return 0;
+ }
+ if (!strcmp(args[1], "+I")) {
+ copy_strf(&databuf, ":%s 347 %s %s :End of Channel Invite Exception List\n", hostname, client->nick, args[1]);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ return 0;
+ }
+ }
goto unknown;
}
}
return 0;
if (argc == 0) // FIXME: error message
return 0;
- struct trie_find *f;
+ struct trie_find *f = NULL;
if (trie_findfirst(&f, &client_map) == 1)
do {
struct client_data *cl = f->n->value;
copy_strf(&databuf, ":%s 004 %s PatchyIRCD 0.0.1 i nt\n", hostname, client->nick);
send_log(client->FD, databuf.buf, databuf.len, 0);
// FIXME: output nicklen from the actual macro
- copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=31 CHANTYPES=# :are supported by this server\n", hostname, client->nick);
+ copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=31 CHANTYPES=#& PREFIX=(ov)@+ :are supported by this server\n", hostname, client->nick);
send_log(client->FD, databuf.buf, databuf.len, 0);
copy_strf(&databuf, ":%s 251 %s :This server exists\n", hostname, client->nick);
send_log(client->FD, databuf.buf, databuf.len, 0);
return;
}
+void channel_sendtopic(struct client_data *client, struct channel *chan)
+{
+ if (chan->topic) {
+ copy_strf(&databuf, ":%s 332 %s %s :%s\n", hostname, client->nick, chan->name, chan->topic);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ // FIXME: needs %d
+ //copy_strf(&databuf, ":%s 333 %s %s %s %d\n", hostname, client->nick, chan->name, chan->topic_who, chan->topic_when);
+ copy_strf(&databuf, ":%s 333 %s %s %s 0\n", hostname, client->nick, chan->name, chan->topic_who);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ } else {
+ copy_strf(&databuf, ":%s 331 %s %s :No topic is set\n", hostname, client->nick, chan->name);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ }
+}
+void channel_join(struct client_data *client, const char *channel, const char *chankey)
+{
+ (void)chankey;
+ // TODO: check channel limit?
+ if (channel[0] != '#' && channel[0] != '&')
+ return; // FIXME: error
+ if (strchr(channel, '\7') || strchr(channel, ' ') || strchr(channel, ','))
+ return; // FIXME: error
+ if (strlen(channel) > 32)
+ return; // FIXME: error
+ char key[33];
+ strcpy(key, channel);
+ stringlower(key);
+ struct channel *chan = trie_get(&channel_map, key);
+ if (!chan) {
+ chan = malloc(sizeof(struct channel));
+ if (!chan) abort();
+ memset(chan, 0, sizeof(struct channel));
+ chan->key = mystrdup(key);
+ if (!chan->key) abort();
+ chan->name = mystrdup(channel);
+ if (!chan->name) abort();
+ if (trie_set(&channel_map, key, chan, NULL) == -1)
+ abort();
+ if (trie_set(&chan->o, client->key, client, NULL) == -1)
+ abort();
+ } else {
+ // TODO: check key
+ // TODO: check limit
+ // TODO: check ban
+ // TODO: check ban exemption
+ // TODO: check invite
+ // TODO: check invite exemption
+ }
+ if (trie_set(&chan->members, client->key, client, NULL) == -1)
+ abort();
+ if (trie_set(&client->channels, key, chan, NULL) == -1)
+ abort();
+ chan->member_count++;
+ // send JOIN to everyone
+ copy_strf(&databuf, ":%s!%s@%s JOIN :%s\n", client->nick, client->username, client->hostname, chan->name);
+ struct trie_find *f = NULL;
+ if (trie_findfirst(&f, &chan->members) == 1)
+ do {
+ struct client_data *cl = f->n->value;
+ send_log(cl->FD, databuf.buf, databuf.len, 0);
+ } while(trie_findnext(&f) == 1);
+ // send topic
+ channel_sendtopic(client, chan);
+ // send names
+ if (trie_findfirst(&f, &client_map) == 1)
+ do {
+ struct client_data *cl = f->n->value;
+ const char *prefix = NULL;
+ if (trie_get(&chan->o, cl->key))
+ prefix = "@";
+ else if (trie_get(&chan->v, cl->key))
+ prefix = "+";
+ else
+ prefix = "";
+ copy_strf(&databuf, ":%s 353 %s = %s :%s%s\n", hostname, client->nick, chan->name, prefix, cl->nick);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+ } while(trie_findnext(&f) == 1);
+ copy_strf(&databuf, ":%s 366 %s %s :End of /NAMES list\n", hostname, client->nick, chan->name);
+ send_log(client->FD, databuf.buf, databuf.len, 0);
+}
+void channel_part(struct client_data *client, struct channel *chan, const char *reason)
+{
+ if (reason) {
+ if (*reason)
+ copy_strf(&databuf, ":%s!%s@%s PART %s :%s\n", client->nick, client->username, client->hostname, chan->name, reason);
+ else
+ copy_strf(&databuf, ":%s!%s@%s PART %s\n", client->nick, client->username, client->hostname, chan->name);
+ struct trie_find *f = NULL;
+ if (trie_findfirst(&f, &chan->members) == 1)
+ do {
+ struct client_data *cl = f->n->value;
+ send_log(cl->FD, databuf.buf, databuf.len, 0);
+ } while (trie_findnext(&f) == 1);
+ }
+ trie_set(&client->channels, chan->key, NULL, NULL);
+ trie_set(&chan->members, client->key, NULL, NULL);
+ trie_set(&chan->o, client->key, NULL, NULL);
+ trie_set(&chan->v, client->key, NULL, NULL);
+ chan->member_count--;
+ if (!chan->member_count) {
+ trie_set(&channel_map, chan->key, NULL, NULL);
+ free(chan->key);
+ free(chan->name);
+ free(chan->topic);
+ free(chan->topic_who);
+ trie_free(&chan->members);
+ trie_free(&chan->o);
+ trie_free(&chan->v);
+ free(chan);
+ }
+}
struct client_data *findClient(const char *nickname)
{
char key[MAX_NICKLEN];
stringlower(key);
return trie_get(&client_map, key);
}
+struct channel *findChannel(const char *channel)
+{
+ if (strlen(channel) > 32)
+ return NULL;
+ char key[33];
+ strcpy(key, channel);
+ stringlower(key);
+ return trie_get(&channel_map, key);
+}