void sendbuf(struct client_data *client) {
send_log(client->FD, databuf.buf, databuf.len, 0);
}
+void sendbuf_chan(struct channel *chan) {
+ struct trie_find *f = NULL;
+ if (trie_findfirst(&f, &chan->members) == 1)
+ do {
+ struct client_data *cl = f->n->value;
+ sendbuf(cl);
+ } while (trie_findnext(&f) == 1);
+}
int parsePacket(struct client_data *client) {
char *source = NULL;
char *command = NULL;
chan->topic_when = time(NULL);
}
msg_topic(client, chan);
- struct trie_find *f = NULL;
- if (trie_findfirst(&f, &chan->members) == 1)
- do {
- struct client_data *cl = f->n->value;
- sendbuf(cl);
- } while (trie_findnext(&f) == 1);
+ sendbuf_chan(chan);
}
} else if(strcmp(command, "PRIVMSG") == 0) {
if (!is_registered(client))
} else {
struct client_data *cl = findClient(args[0]);
if (!cl) {
- copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, args[0]);
+ err_nosuchnick(client, args[0]);
sendbuf(client);
return 0;
} else {
if (argc == 1) { // get mode
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);
- sendbuf(client);
- return 0;
- }
+ if (!cl)
+ return err_nosuchnick(client, nick), sendbuf(client), 0;
copy_strf(&databuf, ":%s 221 %s +", hostname, client->nick);
if (cl->umode & UMODE_INVISIBLE) append_str(&databuf, "i");
if (cl->umode & UMODE_OPER) append_str(&databuf, "o");
append_str(&databuf, "\r\n");
sendbuf(client);
} else {
- copy_strf(&databuf, ":%s 324 %s %s +nt\n", hostname, client->nick, nick);
+ struct channel *chan = findChannel(nick);
+ if (!chan)
+ return err_nosuchchannel(client, nick), sendbuf(client), 0;
+ copy_strf(&databuf, ":%s 324 %s %s +", hostname, client->nick, nick);
+ if (chan->dmode & DMODE_PERSIST) append_str(&databuf, "P");
+ if (chan->dmode & DMODE_INVITEONLY) append_str(&databuf, "i");
+ if (chan->dmode & DMODE_MODERATED) append_str(&databuf, "m");
+ if (chan->dmode & DMODE_NOEXTERNAL) append_str(&databuf, "n");
+ if (chan->dmode & DMODE_SECRET) append_str(&databuf, "s");
+ if (chan->dmode & DMODE_TOPICPROT) append_str(&databuf, "t");
+ if (chan->member_limit != -1) append_str(&databuf, "l");
+ if (chan->chankey) append_str(&databuf, "k");
+ if (chan->member_limit != -1) append_strf(&databuf, " %u", chan->member_limit);
+ if (chan->chankey) append_strf(&databuf, " %s", chan->chankey);
+ append_str(&databuf, "\r\n");
+ sendbuf(client);
+ // FIXME: 64-bit time_t support
+ copy_strf(&databuf, ":%s 329 %s %s %u\n", hostname, client->nick, chan->name, (unsigned)chan->creation);
sendbuf(client);
}
} else { // set mode
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);
- sendbuf(client);
- return 0;
- }
+ if (!cl)
+ return err_nosuchnick(client, nick), sendbuf(client), 0;
if (client->FD != cl->FD) {
copy_strf(&databuf, ":%s 502 %s :Can't change mode for other users\n", hostname, client->nick);
sendbuf(client);
return 0;
}
- int off;
+ int is_plus;
char *p = args[1];
switch (*p++) {
- case '+': off = 0; break;
- case '-': off = 1; break;
+ case '+': is_plus = 1; break;
+ case '-': is_plus = 0; break;
default: goto unknown;
}
while (*p) {
switch (*p) {
+ case '+': is_plus = 1; break;
+ case '-': is_plus = 0; break;
case 'i':
- if (off) {
- copy_strf(&databuf, ":%s MODE %s -i\n", hostname, client->nick);
- cl->umode &= ~UMODE_INVISIBLE;
- } else {
- copy_strf(&databuf, ":%s MODE %s +i\n", hostname, client->nick);
- cl->umode |= UMODE_INVISIBLE;
- }
+ SETBIT(cl->umode, UMODE_INVISIBLE, is_plus);
+ copy_strf(&databuf, ":%s MODE %s %si\n", hostname, client->nick, is_plus?"+":"-");
sendbuf(client);
break;
default:
p++;
}
} else {
+ struct channel *chan = findChannel(nick);
+ if (!chan)
+ return err_nosuchchannel(client, nick), sendbuf(client), 0;
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]);
return 0;
}
}
- goto unknown;
+ int is_plus;
+ char *p = args[1];
+ switch (*p++) {
+ case '+': is_plus = 1; break;
+ case '-': is_plus = 0; break;
+ default: goto unknown;
+ }
+ char **argp = &args[2];
+ while (*p) {
+ switch (*p) {
+ case '+': is_plus = 1; break;
+ case '-': is_plus = 0; break;
+#define MSG_CHANDMODE(m) \
+ copy_strf(&databuf, ":%s!%s@%s MODE %s %s"m"\r\n", \
+ client->nick, client->username, client->hostname, \
+ chan->name, is_plus?"+":"-")
+ case 'P':
+ if (!(client->umode & UMODE_OPER))
+ break;
+ SETBIT(chan->dmode, DMODE_PERSIST, is_plus);
+ MSG_CHANDMODE("P");
+ sendbuf_chan(chan);
+ break;
+ case 'i':
+ SETBIT(chan->dmode, DMODE_INVITEONLY, is_plus);
+ MSG_CHANDMODE("i");
+ sendbuf_chan(chan);
+ break;
+ case 'm':
+ SETBIT(chan->dmode, DMODE_MODERATED, is_plus);
+ MSG_CHANDMODE("m");
+ sendbuf_chan(chan);
+ break;
+ case 'n':
+ SETBIT(chan->dmode, DMODE_NOEXTERNAL, is_plus);
+ MSG_CHANDMODE("n");
+ sendbuf_chan(chan);
+ break;
+ case 's':
+ SETBIT(chan->dmode, DMODE_SECRET, is_plus);
+ MSG_CHANDMODE("s");
+ sendbuf_chan(chan);
+ break;
+ case 't':
+ SETBIT(chan->dmode, DMODE_TOPICPROT, is_plus);
+ MSG_CHANDMODE("t");
+ sendbuf_chan(chan);
+ break;
+ case 'l':
+ if (is_plus) {
+ int x = 0;
+ if (!*argp) {
+ // FIXME: not enough args error
+ } else if (sscanf(*argp, "%u", &x) != 1 || x < 0) {
+ // FIXME: bad arg error
+ argp++;
+ } else {
+ chan->member_limit = x;
+ copy_strf(&databuf, ":%s!%s@%s MODE %s +l %u\r\n",
+ client->nick, client->username, client->hostname,
+ chan->name, x);
+ sendbuf_chan(chan);
+ argp++;
+ }
+ } else {
+ chan->member_limit = -1;
+ MSG_CHANDMODE("l");
+ sendbuf_chan(chan);
+ }
+ break;
+ case 'k':
+ if (is_plus) {
+ if (!*argp) {
+ // FIXME: not enough args error
+ break;
+ } else if (strchr(*argp, ',') || strchr(*argp, ' ')) {
+ copy_strf(&databuf, ":%s 525 %s %s :Key is not well-formed\r\n", hostname, client->nick, nick);
+ sendbuf(client);
+ argp++;
+ break;
+ }
+ if (chan->chankey)
+ free(chan->chankey);
+ chan->chankey = mystrdup(*argp);
+ if (!chan->chankey)
+ abort();
+ } else {
+ free(chan->chankey);
+ chan->chankey = NULL;
+ if (*argp) // unused argument
+ argp++;
+ }
+ copy_strf(&databuf, ":%s!%s@%s MODE %s %sk %s\r\n",
+ client->nick, client->username, client->hostname,
+ chan->name, is_plus?"+":"-", is_plus?*argp:"*");
+ sendbuf_chan(chan);
+ break;
+ case 'b':
+ case 'e':
+ case 'I':
+ default:
+ goto unknown;
+ }
+ p++;
+ }
}
}
} else if(strcmp(command, "LIST") == 0) {
copy_strf(&databuf, ":%s 318 %s %s :End of /WHOIS list\n", hostname, client->nick, cl->nick);
sendbuf(client);
} else {
- copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\n", hostname, client->nick, nick);
+ err_nosuchnick(client, nick);
sendbuf(client);
}
} else if(strcmp(command, "AWAY") == 0) {
}
} else {
unknown:
- // Inform user of error
- copy_strf(&databuf, ":%s 421 %s :Unknown command %s\n", hostname, client->nick, command);
- sendbuf(client);
+ err_unknowncommand(client, command), sendbuf(client);
}
return 0;
}
// TODO: perform basic sanitization
if (strlen(nickname) >= MAX_NICKLEN || nickname[0] == '\0') {
- copy_strf(&databuf, ":%s 432 * %s :Erroneous nickname\n", hostname, nickname);
- sendbuf(client);
+ err_erroneusnickname(client, nickname), sendbuf(client);
return;
}
const char *forbidden = " ,*?!@$:#&.";
while (*forbidden)
if (strchr(nickname, *forbidden++)) {
- copy_strf(&databuf, ":%s 432 * %s :Erroneous nickname\n", hostname, nickname);
- sendbuf(client);
+ err_erroneusnickname(client, nickname), sendbuf(client);
return;
}
stringlower(key);
if (trie_get(&client_map, key)) {
- // Nickname is already taken
- copy_strf(&databuf, ":%s 443 * %s :Nickname is already in use\n", hostname, nickname);
- sendbuf(client);
+ err_nicknameinuse(client, nickname), sendbuf(client);
return;
}
chan = malloc(sizeof(struct channel));
if (!chan) abort();
memset(chan, 0, sizeof(struct channel));
+ chan->creation = time(NULL);
+ chan->member_limit = -1;
+ chan->dmode = DMODE_NOEXTERNAL | DMODE_TOPICPROT;
chan->key = mystrdup(key);
if (!chan->key) abort();
chan->name = mystrdup(channel);
chan->member_count++;
// send JOIN to everyone
msg_join(client, chan);
- struct trie_find *f = NULL;
- if (trie_findfirst(&f, &chan->members) == 1)
- do {
- struct client_data *cl = f->n->value;
- sendbuf(cl);
- } while(trie_findnext(&f) == 1);
+ sendbuf_chan(chan);
// send topic
channel_sendtopic(client, chan, 1);
// send names
{
if (reason) {
msg_part(client, chan, *reason ? reason : NULL);
- struct trie_find *f = NULL;
- if (trie_findfirst(&f, &chan->members) == 1)
- do {
- struct client_data *cl = f->n->value;
- sendbuf(cl);
- } while (trie_findnext(&f) == 1);
+ sendbuf_chan(chan);
}
trie_set(&client->channels, chan->key, NULL, NULL);
trie_set(&chan->members, client->key, NULL, NULL);
trie_free(&chan->members);
trie_free(&chan->o);
trie_free(&chan->v);
+ free(chan->chankey);
free(chan);
}
}
}
void rpl_isupport(struct client_data *client) // 005
{
- copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=%u CHANTYPES=#& PREFIX=(ov)@+ :are supported by this server\n", hostname, client->nick, MAX_NICKLEN-1);
+ copy_strf(&databuf, ":%s 005 %s CASEMAPPING=rfc1459 NICKLEN=%u CHANMODES=beI,k,l,Pimnst CHANTYPES=#& PREFIX=(ov)@+ :are supported by this server\n", hostname, client->nick, MAX_NICKLEN-1);
+
}
// 010 RPL_BOUNC
// 221 RPL_UMODEIS
// 348 RPL_EXCEPTLIST
// 349 RPL_ENDOFEXCEPTLIST
// 351 RPL_VERSION
-// 353 RPL_NAMREPLY
int rpl_namreply(struct client_data *client, struct channel *chan, struct client_data *cl) // 353
{
if (!cl) {
// 381 RPL_YOUREOPER
// 382 RPL_REHASHING
// 391 RPL_TIME
-// 400 ERR_UNKNOWNERROR
-// 401 ERR_NOSUCHNICK
-// 402 ERR_NOSUCHSERVER
-// 403 ERR_NOSUCHCHANNEL
-// 404 ERR_CANNOTSENDTOCHAN
-// 405 ERR_TOOMANYCHANNELS
-// 406 ERR_WASSNOSUCHNICK
-// 409 ERR_NOORIGIN
-// 417 ERR_INPUTTOOLONG
-// 421 ERR_UNKNOWNCOMMAND
-// 422 ERR_NOMOTD
-// 432 ERR_ERRONEUSNICKNAME
-// 433 ERR_NICKNAMEINUSE
-// 441 ERR_USERNOTINCHANNEL
-// 442 ERR_NOTONCHANNEL
-// 443 ERR_USERONCHANNEL
+#if 0
+void err_unknownerror(struct client_data *client, const char *command, const char *msg) // 400
+{
+ copy_strf(&databuf, ":%s 400 %s %s :%s\r\n", hostname, client->nick, command, msg);
+}
+#endif
+void err_nosuchnick(struct client_data *client, const char *nick) // 401
+{
+ copy_strf(&databuf, ":%s 401 %s %s :No such nick/channel\r\n", hostname, client->nick, nick);
+}
+#if 0
+void err_nosuchserver(struct client_data *client, const char *server) // 402
+{
+ copy_strf(&databuf, ":%s 402 %s %s :No such server\r\n", hostname, client->nick, server);
+}
+#endif
+void err_nosuchchannel(struct client_data *client, const char *channel) // 403
+{
+ copy_strf(&databuf, ":%s 403 %s %s :No such channel\r\n", hostname, client->nick, channel);
+}
+#if 0
+void err_cannotsendtochan(struct client_data *client, const char *channel) // 404
+{
+ copy_strf(&databuf, ":%s 404 %s %s :Cannot send to channel\r\n", hostname, client->nick, channel);
+}
+void err_toomanychannels(struct client_data *client, const char *channel) // 405
+{
+ copy_strf(&databuf, ":%s 405 %s %s :You have joined too many channels\r\n", hostname, client->nick, channel);
+}
+void err_wasnosuchnick(struct client_data *client) // 406
+{
+ copy_strf(&databuf, ":%s 406 %s :There was no such nickname\r\n", hostname, client->nick);
+}
+void err_noorigin(struct client_data *client) // 409
+{
+ copy_strf(&databuf, ":%s 409 %s :No origin specified\r\n", hostname, client->nick);
+}
+void err_inputtoolong(struct client_data *client) // 417
+{
+ copy_strf(&databuf, ":%s 417 %s :Input line was too long\r\n", hostname, client->nick);
+}
+#endif
+void err_unknowncommand(struct client_data *client, const char *command) // 421
+{
+ copy_strf(&databuf, ":%s 421 %s %s :Unknown command\r\n", hostname, client->nick, command);
+}
+#if 0
+void err_nomotd(struct client_data *client) // 422
+{
+ copy_strf(&databuf, ":%s 422 %s :MOTD File is missing\r\n", hostname, client->nick);
+}
+#endif
+void err_erroneusnickname(struct client_data *client, const char *nick) // 432
+{
+ copy_strf(&databuf, ":%s 432 %s %s :Erroneus nickname\r\n", hostname, client->nick, nick);
+}
+void err_nicknameinuse(struct client_data *client, const char *nick) // 433
+{
+ copy_strf(&databuf, ":%s 433 %s %s :Nickname is already in use\r\n", hostname, client->nick, nick);
+}
+#if 0
+void err_usernotinchannel(struct client_data *client, const char *nick, struct channel *chan) // 441
+{
+ copy_strf(&databuf, ":%s 441 %s %s %s :They aren't on that channel\r\n", hostname, client->nick, nick, chan->name);
+}
+void err_notonchannel(struct client_data *client, struct channel *chan) // 442
+{
+ copy_strf(&databuf, ":%s 442 %s %s :is already on channel\r\n", hostname, client->nick, chan->name);
+}
+void err_useronchannel(struct client_data *client, const char *nick, struct channel *chan) // 443
+{
+ copy_strf(&databuf, ":%s 443 %s %s %s :is already on channel\r\n", hostname, client->nick, nick, chan->name);
+}
+#endif
void err_notregistered(struct client_data *client) // 451
{
(void)client;