terom@26: #include "irc_chan.h" terom@39: #include "irc_proto.h" terom@37: #include "log.h" terom@26: terom@26: #include terom@72: #include terom@26: #include terom@26: terom@26: const char* irc_chan_name (struct irc_chan *chan) terom@26: { terom@26: return chan->info.channel; terom@26: } terom@26: terom@37: /** terom@72: * Add or update a nickname to the irc_chan.users list. terom@72: * terom@72: * If the given nickname already exists in our users list, this does nothing. Otherwise, an irc_user is aquired using terom@72: * irc_net_get_user, and a new irc_chan_user struct added to our users list. terom@72: */ terom@72: static err_t irc_chan_add_user (struct irc_chan *chan, const char *nickname) terom@72: { terom@72: struct irc_user *user; terom@72: struct irc_chan_user *chan_user; terom@72: err_t err; terom@72: terom@72: // skip if already listed terom@72: if (irc_chan_get_user(chan, nickname)) terom@72: return SUCCESS; terom@72: terom@72: // lookup/create the irc_user state terom@72: if ((err = irc_net_get_user(chan->net, &user, nickname))) terom@72: return err; terom@72: terom@72: // alloc the new irc_chan_user terom@72: if ((chan_user = calloc(1, sizeof(*chan_user))) == NULL) { terom@72: // XXX: release irc_user terom@72: return ERR_CALLOC; terom@72: } terom@72: terom@72: // store terom@72: chan_user->user = user; terom@72: terom@72: // add to users list terom@72: LIST_INSERT_HEAD(&chan->users, chan_user, chan_users); terom@72: terom@72: // ok terom@72: return SUCCESS; terom@72: } terom@72: terom@72: /** terom@37: * :nm JOIN terom@37: */ terom@37: static void irc_chan_on_JOIN (const struct irc_line *line, void *arg) terom@37: { terom@37: struct irc_chan *chan = arg; terom@72: err_t err; terom@37: terom@37: // us? terom@37: if (irc_prefix_cmp_nick(line->prefix, chan->net->conn->nickname) == 0) { terom@37: // twiddle state terom@45: chan->joining = false; terom@45: chan->joined = true; terom@37: terom@38: // invoke callback terom@38: IRC_CHAN_INVOKE_CALLBACK(chan, on_self_join); terom@45: terom@45: } else { terom@72: char prefix_buf[IRC_PREFIX_MAX]; terom@72: struct irc_nm nm; terom@45: terom@72: // parse the nickname terom@72: if ((err = irc_nm_parse(&nm, prefix_buf, line->prefix))) terom@72: return log_warn("invalid prefix: %s", line->prefix); terom@72: terom@72: // add them terom@72: if ((err = irc_chan_add_user(chan, nm.nickname))) terom@72: return log_warn("irc_chan_add_user(%s, %s): %s", irc_chan_name(chan), nm.nickname, error_name(err)); terom@72: terom@72: // invoke callback (source) terom@72: IRC_CHAN_INVOKE_CALLBACK(chan, on_join, &nm); terom@38: } terom@38: } terom@37: terom@38: /** terom@38: * :nm PRIVMSG terom@38: */ terom@38: static void irc_chan_on_PRIVMSG (const struct irc_line *line, void *arg) terom@38: { terom@38: struct irc_chan *chan = arg; terom@45: char prefix_buf[IRC_PREFIX_MAX]; terom@67: struct irc_nm nm; terom@45: err_t err; terom@38: terom@45: const char *msg = line->args[1]; terom@45: terom@45: // parse nickmask terom@67: if ((err = irc_nm_parse(&nm, prefix_buf, line->prefix))) { terom@45: log_warn("invalid prefix: %s", line->prefix); terom@45: terom@72: // invoke callback with NULL source terom@45: IRC_CHAN_INVOKE_CALLBACK(chan, on_msg, NULL, msg); terom@45: terom@45: } else { terom@72: // invoke callback (source, message) terom@67: IRC_CHAN_INVOKE_CALLBACK(chan, on_msg, &nm, msg); terom@45: } terom@37: } terom@37: terom@37: /** terom@72: * Add/update nicknames to users list using irc_chan_add_user terom@72: * terom@72: * @see IRC_RPL_NAMREPLY terom@72: */ terom@72: static void irc_chan_on_RPL_NAMREPLY (const struct irc_line *line, void *arg) terom@72: { terom@72: struct irc_chan *chan = arg; terom@72: char chanflags[IRC_CHANFLAGS_MAX]; terom@72: const char *nickname; terom@72: err_t err; terom@72: terom@72: // the args terom@72: const char *arg_names = line->args[3]; terom@72: terom@72: // copy the nicklist to a mutable buffer terom@72: char names_buf[strlen(arg_names) + 1], *names = names_buf; terom@72: strcpy(names, arg_names); terom@72: terom@72: // iterate over each name terom@72: // XXX: nickflags terom@72: while ((nickname = strsep(&names, " "))) { terom@72: // parse off the channel flags terom@72: nickname = irc_nick_chanflags(nickname, chanflags); terom@72: terom@72: // add/update terom@72: // XXX: do something with chanflags terom@72: if ((err = irc_chan_add_user(chan, nickname))) terom@72: log_warn("irc_chan_add_user(%s, %s): %s", irc_chan_name(chan), nickname, error_name(err)); terom@72: } terom@72: } terom@72: terom@72: /** terom@72: * Channel join sequence complete terom@72: * terom@72: * @see IRC_RPL_ENDOFNAMES terom@72: */ terom@72: static void irc_chan_on_RPL_ENDOFNAMES (const struct irc_line *line, void *arg) terom@72: { terom@72: struct irc_chan *chan = arg; terom@72: terom@72: // XXX: update state terom@72: log_info("channel join sync complete"); terom@72: } terom@72: terom@72: /** terom@37: * Core command handlers terom@37: */ terom@37: struct irc_cmd_handler _cmd_handlers[] = { terom@72: { "JOIN", &irc_chan_on_JOIN }, terom@72: { "PRIVMSG", &irc_chan_on_PRIVMSG }, terom@72: { IRC_RPL_NAMREPLY, &irc_chan_on_RPL_NAMREPLY }, terom@72: { IRC_RPL_ENDOFNAMES, &irc_chan_on_RPL_ENDOFNAMES }, terom@72: { NULL, NULL } terom@37: }; terom@37: terom@26: err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, struct error_info *err) terom@26: { terom@26: struct irc_chan *chan; terom@26: terom@26: // allocate terom@26: if ((chan = calloc(1, sizeof(*chan))) == NULL) terom@26: return SET_ERROR(err, ERR_CALLOC); terom@26: terom@26: // store terom@26: chan->net = net; terom@26: chan->info = *info; terom@37: terom@37: // init terom@72: LIST_INIT(&chan->users); terom@37: irc_cmd_init(&chan->handlers); terom@38: CHAIN_INIT(&chan->callbacks); terom@37: terom@37: // add handlers terom@37: if ((ERROR_CODE(err) = irc_cmd_add(&chan->handlers, _cmd_handlers, chan))) terom@37: goto error; terom@26: terom@26: // ok terom@26: *chan_ptr = chan; terom@26: terom@26: return SUCCESS; terom@37: terom@37: error: terom@37: // cleanup terom@37: irc_chan_destroy(chan); terom@37: terom@37: return ERROR_CODE(err); terom@37: } terom@37: terom@37: void irc_chan_destroy (struct irc_chan *chan) terom@37: { terom@72: // XXX: free chan_users list terom@72: terom@37: // free command handlers terom@37: irc_cmd_free(&chan->handlers); terom@37: terom@38: // free callbacks terom@38: chain_free(&chan->callbacks); terom@38: terom@37: // free chan itself terom@37: free(chan); terom@26: } terom@26: terom@38: err_t irc_chan_add_callbacks (struct irc_chan *chan, const struct irc_chan_callbacks *callbacks, void *arg) terom@38: { terom@38: return chain_add(&chan->callbacks, callbacks, arg); terom@38: } terom@38: terom@69: void irc_chan_remove_callbacks (struct irc_chan *chan, const struct irc_chan_callbacks *callbacks, void *arg) terom@69: { terom@69: chain_remove(&chan->callbacks, callbacks, arg); terom@69: } terom@69: terom@72: struct irc_chan_user* irc_chan_get_user (struct irc_chan *chan, const char *nickname) terom@72: { terom@72: struct irc_chan_user *chan_user = NULL; terom@72: terom@72: // look for it... terom@72: LIST_FOREACH(chan_user, &chan->users, chan_users) { terom@72: if (irc_cmp_nick(nickname, chan_user->user->nickname) == 0) { terom@72: // found terom@72: return chan_user; terom@72: } terom@72: } terom@72: terom@72: // not found terom@72: return NULL; terom@72: } terom@72: terom@26: err_t irc_chan_join (struct irc_chan *chan) terom@26: { terom@26: err_t err; terom@26: terom@26: // XXX: error instead? terom@45: assert(!chan->joining && !chan->joined); terom@45: assert(chan->net->conn); terom@26: terom@26: // send JOIN message on the appropriate connection terom@26: if ((err = irc_conn_JOIN(chan->net->conn, chan->info.channel))) terom@37: // XXX: error state? terom@26: return err; terom@26: terom@37: // ok terom@45: chan->joining = true; terom@26: terom@26: return SUCCESS; terom@26: } terom@26: