/* IRCfs - IRC FileServ for *nix. * Copyright (C) 2002 Nick 'Zaf' Clifford * For licensing details, refer to the LICENSE file in the source * code directory. */ /* TODO * Rewrite channel code so you create a channel by creating a channel * object, the object sends the join. You get an event when the channel * is finally joined */ #include "module.h" #include "server.h" #include "channel.h" #include "list.h" #include "user.h" #include "select.h" #include "magic.h" #define MAGIC_CHANNEL_USER 0xAABBCCDD #define MAGIC_CHANNEL 0x1144DD00 #define MAGIC_CHANNEL_SERVER_DATA 0x83271743 struct channel_user { MAGIC magic; struct channel *channel; struct user *user; char mode; }; struct channel { MAGIC magic; POOL pool; char *name; int in_names_list; struct server *server; struct list *user_list; //struct channel_user list struct list *new_user_list; //struct channel_user list struct list *event_handlers; // channel_event_node list }; struct channel_server_data { MAGIC magic; struct list *channel_list; }; static int channel_server_dies(struct eventmsg *msg); static int channel_user_quits(struct eventmsg *msg); static int channel_cleanup_server(struct server *s); static void channel_start_cleanup(); static int channel_new_server(struct eventmsg *msg); static int channel_list_find(const char *name, struct channel *c); static int channel_command_join(struct server *s, const char *command, int argc, char *argv[], void *appdata); static int channel_command_part(struct server *s, const char *command, int argc, char *argv[], void *appdata); static int channel_command_kick(struct server *s, const char *command, int argc, char *argv[], void *appdata); static int channel_command_privmsg(struct server *s, const char *command, int argc, char *argv[], void *appdata); static int channel_each_server(struct server *s); void channel_join(struct server *server, const char *name); void channel_part(struct channel *c); static int channel_code_353(struct server *s, int code, int argc, char *argv[], void *appdata); static int channel_code_366(struct server *s, int code, int argc, char *argv[], void *appdata); static void channel_destroy(struct channel *c); static int channel_raise_event(struct channel *c, enum channel_events event, struct user *from, struct user *to, const char *txt); static int channel_event_filter(struct eventmsg *msg, void *filter); MODULE_NAME("channel"); MODULE_INIT(channel_init); MODULE_DESTROY(channel_unload); MODULE_DEPENDS("server","select","user"); MODULE_REGISTER(channel); static DEBUG channel_debug; static struct eventobj *channel_eventobj; int channel_init() { channel_debug = debug_register("channel"); channel_eventobj = event_create_obj(NULL,NULL); event_set_filter_func(channel_eventobj, channel_event_filter); server_foreach(channel_each_server); event_add_listener(server_get_event_obj(), server_event_new, NULL, channel_new_server, NULL); register_post_func(channel_start_cleanup); return 0; } int channel_unload() { return -1; } int channel_is_prefix(char ch) { switch(ch) { case '#': case '&': case '!': case '+': return 0; default: return -1; } } struct eventobj *channel_get_eventobj() { return channel_eventobj; } int channel_cleanup_server(struct server *s) { struct channel_server_data *csd; csd = server_get_module_data(s,"channel"); ASSERT(is_magic(csd,MAGIC_CHANNEL_SERVER_DATA)); if (csd) { list_cleanup(csd->channel_list); } return 0; } void channel_start_cleanup() { server_foreach(channel_cleanup_server); } int channel_cleanup(struct list *l, struct channel *c) { POOL p; ASSERT(is_magic(c,MAGIC_CHANNEL)); debug(channel_debug,"Channel(%s) deleting", c->name); p = c->pool; assert(p != NULL); c->pool = NULL; pool_destroy(p); return 0; } int channel_list_find(const char *name, struct channel *c) { ASSERT(is_magic(c,MAGIC_CHANNEL)); if (name && c && strcasecmp(name,c->name) == 0) return 0; return -1; } int channel_new_server(struct eventmsg *msg) { struct server *s = (struct server *)msg->msg; return channel_each_server(s); } int channel_each_server(struct server *s) { struct channel_server_data *csd; POOL p = server_get_pool(s); csd = XMALLOC(p,struct channel_server_data); set_magic(csd,MAGIC_CHANNEL_SERVER_DATA); csd->channel_list = list_create(p); server_set_module_data(s,"channel",csd); list_delete_func(csd->channel_list, (list_delete_func_t)channel_cleanup); event_add_listener(server_get_event_obj(), server_event_disconnected, s, channel_server_dies, NULL); server_set_command_handler(s,"JOIN",0,NULL,channel_command_join,NULL); server_set_command_handler(s,"PART",0,NULL,channel_command_part,NULL); server_set_command_handler(s,"KICK",0,NULL,channel_command_kick,NULL); server_set_command_handler(s,"PRIVMSG",0,NULL, channel_command_privmsg,NULL); server_set_code_handler(s,353,0,NULL,channel_code_353,NULL); server_set_code_handler(s,366,0,NULL,channel_code_366,NULL); return 0; } int channel_raise_event(struct channel *c, enum channel_events event, struct user *from, struct user *to, const char *txt) { struct channel_event_msg msg; set_magic(&msg, MAGIC_CHANNEL_EVENT_MSG); msg.server = c->server; msg.channel = c; msg.to = to; msg.from = from; msg.msg = txt; ASSERT(is_magic(c,MAGIC_CHANNEL)); return event_raise(channel_eventobj, event, &msg); } static int channel_event_filter(struct eventmsg *msg, void *filter) { struct channel *c; struct channel_event_msg *cem; c = (struct channel *) filter; cem = msg->msg; ASSERT(is_magic(c,MAGIC_CHANNEL)); ASSERT(is_magic(cem,MAGIC_CHANNEL_EVENT_MSG)); if (c == cem->channel) { return 0; } return -1; } int channel_server_dies(struct eventmsg *msg) { struct server *s; struct channel_server_data *csd; /* Remove each of the users from the user list, * posting their departure as we go */ s = msg->msg; csd = server_get_module_data(s,"channel"); ASSERT(is_magic(csd,MAGIC_CHANNEL_SERVER_DATA)); list_foreach_simple(csd->channel_list, (list_simple_callback_t)channel_destroy); return 0; } void channel_join(struct server *server, const char *name) { server_send(server,"JOIN %s",name); } static int list_dump_chan(struct list *l, struct channel *c, void *d) { ASSERT(is_magic(c,MAGIC_CHANNEL)); debug(channel_debug,"Channel(%s)",c->name); return 0; } static int channel_list_members(struct list *l, struct channel_user *cu, struct channel *c) { ASSERT(is_magic(c,MAGIC_CHANNEL)); ASSERT(is_magic(cu,MAGIC_CHANNEL_USER)); debug(channel_debug,"Channel(%s) User(%s) Mode: %c", cu->channel->name, user_get_nick(cu->user), cu->mode ? cu->mode : ' '); return 0; } void channel_dump_chaninfo(struct server *server, const char *name) { struct channel *c; c = channel_find(server,name); if (!c) { debug(channel_debug,"Err: Channel not found \"%s\"", name); return; } debug(channel_debug,"Channel(%s) Members:",c->name); list_foreach(c->user_list,(list_callback_t)channel_list_members,c); debug(channel_debug,"End of Channel Members"); } void channel_dump_chans(struct server *server) { struct channel_server_data *csd; debug(channel_debug,"Channel list"); csd = server_get_module_data(server,"channel"); if (!csd) { debug(channel_debug,"No csd"); return; } list_foreach(csd->channel_list,(list_callback_t)list_dump_chan,NULL); debug(channel_debug,"End of chan list"); } struct channel *channel_create(struct server *server, const char *name) { struct channel_server_data *csd; struct channel *c; POOL p; csd = server_get_module_data(server,"channel"); ASSERT(is_magic(csd,MAGIC_CHANNEL_SERVER_DATA)); debug(channel_debug,"Creating channel %s on %s", name, server_get_name(server)); c = list_find(csd->channel_list, (void *)name, (list_compare_t)channel_list_find); if (c != NULL) { debug(channel_debug,"Found previous channel %s, returning it", name); ASSERT(is_magic(c,MAGIC_CHANNEL)); return c; } p = pool_new(server_get_pool(server)); c = XMALLOC(p,struct channel); set_magic(c,MAGIC_CHANNEL); c->pool = p; c->name = pstrdup(c->pool,name); c->server = server; c->user_list = list_create(c->pool); c->event_handlers = list_create(c->pool); list_add_end(csd->channel_list, c); server_send(c->server,"JOIN %s", c->name); return c; } struct channel *channel_find(struct server *server, const char *name) { struct channel_server_data *csd; struct channel *c; csd = server_get_module_data(server,"channel"); ASSERT(is_magic(csd,MAGIC_CHANNEL_SERVER_DATA)); c = list_find(csd->channel_list, (void *)name, (list_compare_t)channel_list_find); if (c) ASSERT(is_magic(c,MAGIC_CHANNEL)); return c; } void channel_part(struct channel *c) { ASSERT(c); ASSERT(is_magic(c,MAGIC_CHANNEL)); server_send(c->server,"PART %s", c->name); } void channel_destroy(struct channel *c) { struct channel_server_data *csd; ASSERT(c); ASSERT(is_magic(c,MAGIC_CHANNEL)); csd = server_get_module_data(c->server,"channel"); assert(csd); ASSERT(is_magic(csd,MAGIC_CHANNEL_SERVER_DATA)); /* Sanity Check */ c = list_find_data(csd->channel_list, c); ASSERT(is_magic(c,MAGIC_CHANNEL)); debug(channel_debug,"Channel(%s) destroy",c->name); channel_raise_event(c,channel_event_close,NULL,NULL,NULL); list_mark_current(csd->channel_list); } const char *channel_get_name(struct channel *c) { ASSERT(c); ASSERT(is_magic(c,MAGIC_CHANNEL)); return c->name; } int channel_compare_user(struct user *what, struct channel_user *node) { ASSERT(is_magic(node,MAGIC_CHANNEL_USER)); if (what == node->user) return 0; return -1; } int channel_user_quits(struct eventmsg *msg) { struct user *u; struct channel *c; struct user *tmp; struct user_event_msg *uem; uem = msg->msg; c = msg->appdata; ASSERT(is_magic(uem,MAGIC_USER_EVENT_MSG)); ASSERT(is_magic(c,MAGIC_CHANNEL)); u = uem->user; /* Delete any events, corisponding to this user and this channel * matching any function */ event_del_listener_by_data(user_get_event_obj(), event_code_any, u, NULL, c); tmp = list_find(c->user_list, u, (list_compare_t)channel_compare_user); if (tmp) { debug(channel_debug,"Channel(%s) User(%s) quits", c->name, user_get_nick(u)); list_del_current(c->user_list); } return 0; } struct user *channel_find_user(struct channel *c, const char *name) { struct channel_user *tmp; ASSERT(is_magic(c,MAGIC_CHANNEL)); tmp = list_find(c->user_list, (void *)name, (list_compare_t)user_compare_byname); ASSERT(is_magic(tmp,MAGIC_CHANNEL_USER)); return tmp->user; } static int channel_user_compare_byname(const char *str, struct channel_user *cu) { ASSERT(is_magic(cu,MAGIC_CHANNEL_USER)); if (user_compare_byname(str,cu->user) == 0) return 0; return -1; } int channel_command_privmsg(struct server *s, const char *command, int argc, char *argv[], void *appdata) { /* [0|FROM] [1|PRIVMSG] [2|#CHAN] [3|USER] [4|REASON] */ struct channel *c; struct channel_user *cu; /* We ignore all non channel msgs */ if (channel_is_prefix(*argv[2]) == -1) { return 0; } c = channel_find(s,argv[2]); if (!c) { debug(channel_debug,"Privmsg for unknown channel (%s)", argv[2]); return 0; } ASSERT(is_magic(c,MAGIC_CHANNEL)); cu = list_find(c->user_list, (void *)argv[0], (list_compare_t)channel_user_compare_byname); if (!cu) { debug(channel_debug,"Channel(%s) privmsg for unknown user (%s)", c->name, argv[0]); return 0; } ASSERT(is_magic(cu,MAGIC_CHANNEL_USER)); user_update_info(cu->user, argv[0]); debug(channel_debug,"Channel(%s) User(%s) Msg: %s", c->name, user_get_nick(cu->user), argv[3]); channel_raise_event(c,channel_event_msg,cu->user, NULL,argv[3]); return 0; } int channel_command_kick(struct server *s, const char *command, int argc, char *argv[], void *appdata) { /* [0|FROM] [1|KICK] [2|#CHAN] [3|USER] [4|REASON] */ struct channel *c; struct channel_user *cu; struct channel_user *cutmp; c = channel_find(s,argv[2]); if (!c) { debug(channel_debug,"Kick for unknown channel (%s)", argv[2]); return 0; } ASSERT(is_magic(c,MAGIC_CHANNEL)); cu = list_find(c->user_list, (void *)argv[3], (list_compare_t)channel_user_compare_byname); if (!cu) { debug(channel_debug,"Channel(%s) Kick for unknown user (%s)", c->name, argv[0]); return 0; } ASSERT(is_magic(cu,MAGIC_CHANNEL_USER)); debug(channel_debug,"Channel(%s) User(%s) Kicked by %s (%s)", c->name, user_get_nick(cu->user), argv[0], argv[4] ? argv[4] : ""); cutmp = list_find(c->user_list, (void *)argv[0], (list_compare_t)channel_user_compare_byname); if (!cutmp) { debug(channel_debug,"Channel(%s) Kick by unknown user (%s)", c->name, argv[0]); } else { channel_raise_event(c,channel_event_kick,cutmp->user, cu->user,argv[4] ? argv[4] : ""); } cu = list_find(c->user_list, (void *)argv[3], (list_compare_t)channel_user_compare_byname); ASSERT(cu); list_del_current(c->user_list); if (strcasecmp(user_get_nick(cu->user), server_getnick(s)) == 0) { debug(channel_debug,"Channel(%s) We've left.", c->name); channel_destroy(c); } return 0; } int channel_command_part(struct server *s, const char *command, int argc, char *argv[], void *appdata) { struct channel *c; struct channel_user *cu; c = channel_find(s,argv[2]); if (!c) { debug(channel_debug,"Part for unknown channel (%s)", argv[2]); return 0; } ASSERT(is_magic(c,MAGIC_CHANNEL)); cu = list_find(c->user_list, (void *)argv[0], (list_compare_t)channel_user_compare_byname); if (!cu) { debug(channel_debug,"Channel(%s) Part for unknown user (%s)", c->name, argv[0]); return 0; } ASSERT(is_magic(cu,MAGIC_CHANNEL_USER)); debug(channel_debug,"Channel(%s) User(%s) Parts: %s", c->name, user_get_nick(cu->user), argv[3] ? argv[3] : ""); channel_raise_event(c,channel_event_part,cu->user, NULL,argv[3] ? argv[3] : ""); cu = list_find(c->user_list, (void *)argv[0], (list_compare_t)channel_user_compare_byname); ASSERT(cu); list_del_current(c->user_list); if (strcasecmp(user_get_nick(cu->user), server_getnick(s)) == 0) { debug(channel_debug,"Channel(%s) We've left.", c->name); channel_destroy(c); } return 0; } int channel_command_join(struct server *s, const char *command, int argc, char *argv[], void *appdata) { struct channel *c; struct user *u; struct channel_user *cu; c = channel_find(s,argv[2]); if (c == NULL) { c = channel_create(s,argv[2]); } u = user_find(s,argv[0]); if (u == NULL) { u = user_create(s,argv[0]); } if (u == server_get_me(s)) { debug(channel_debug,"I have joined %s", c->name); channel_raise_event(c,channel_event_open,NULL,NULL,NULL); } cu = XMALLOC(c->pool,struct channel_user); set_magic(cu,MAGIC_CHANNEL_USER); cu->user = u; cu->channel = c; event_add_listener(user_get_event_obj(), user_event_quit, u, channel_user_quits, c); debug(channel_debug,"Channel(%s) User(%s) joins", c->name, user_get_nick(u)); list_add_end(c->user_list, cu); channel_raise_event(c,channel_event_join,cu->user, NULL,NULL); return 0; } /* Names list */ int channel_code_353(struct server *s, int code, int argc, char *argv[], void *appdata) { /*[0|localhost] [1|353] [2|ME] [3|=] [4|#CHAN] [5|@USER1 USER2 USER3]*/ struct channel *c; static char tempbuf[512]; struct user *u; struct channel_user *cu; char *cp,*word,mode; c = channel_find(s,argv[4]); if (c == NULL) { return 0; } if (!c->in_names_list) { c->new_user_list = list_create(c->pool); c->in_names_list = 1; } strncpy(tempbuf, argv[5], sizeof(tempbuf)); tempbuf[sizeof(tempbuf)-1] = 0; cp = tempbuf; while(*cp) { word = cp; mode = 0; while(*cp && !isspace(*cp)) cp++; if (*cp) { *cp = 0; cp++; while(isspace(*cp)) cp++; } if (!is_nickchar(*word)) { mode = *word; word++; } u = user_find(s,word); if (u == NULL) { u = user_create(s,word); } cu = XMALLOC(c->pool,struct channel_user); set_magic(cu,MAGIC_CHANNEL_USER); cu->user = u; cu->channel = c; cu->mode = mode; event_add_listener(user_get_event_obj(), user_event_quit, u, channel_user_quits, c); list_add_end(c->new_user_list, cu); debug(channel_debug,"Channel(%s) Adding user (%s)", c->name, user_get_nick(u)); } return 0; } /* End of Names */ int channel_code_366(struct server *s, int code, int argc, char *argv[], void *appdata) { /*[0|localhost] [1|353] [2|ME] [3|#CHAN] [4|End of /NAMES]*/ struct channel *c; c = channel_find(s,argv[3]); if (c == NULL) { return 0; } if (!c->in_names_list) { debug(channel_debug,"Got end of /NAMES without begining"); return 0; } list_destroy(c->user_list); c->user_list = c->new_user_list; c->new_user_list = NULL; debug(channel_debug,"End of names list"); return 0; }