terom@25: #include "irc_net.h" terom@25: #include "log.h" terom@25: terom@25: #include terom@26: #include terom@25: terom@46: /** terom@46: * Something happaned which caused our irc_conn to fail. Destroy it, and recover. XXX: somehow terom@46: */ terom@46: static void irc_net_error (struct irc_net *net, struct error_info *err) terom@46: { terom@46: struct irc_chan *chan = NULL; terom@46: terom@46: // log an error terom@46: log_err_info(err, "irc_conn failed"); terom@46: terom@46: // destroy connection and set NULL terom@46: irc_conn_destroy(net->conn); terom@46: net->conn = NULL; terom@46: terom@46: // update channel state terom@46: TAILQ_FOREACH(chan, &net->channels, node) { terom@46: // XXX: notify channel somehow terom@46: } terom@46: terom@46: // XXX: reconnect? terom@46: } terom@46: terom@46: /** terom@46: * Our irc_conn has registered. Join our channels terom@46: */ terom@27: static void irc_net_conn_registered (struct irc_conn *conn, void *arg) terom@27: { terom@27: struct irc_net *net = arg; terom@27: struct irc_chan *chan = NULL; terom@46: struct error_info err; terom@27: terom@27: (void) conn; terom@27: terom@27: // join our channels terom@27: TAILQ_FOREACH(chan, &net->channels, node) { terom@46: if ((ERROR_CODE(&err) = irc_chan_join(chan))) terom@46: // XXX: this should be some kind of irc_chan_error instead terom@46: irc_net_error(net, &err); terom@27: } terom@27: } terom@27: terom@46: /** terom@46: * Our irc_conn has spontaneously failed, destroy it terom@46: */ terom@45: static void irc_net_conn_error (struct irc_conn *conn, struct error_info *err, void *arg) terom@45: { terom@45: struct irc_net *net = arg; terom@45: terom@46: (void) conn; terom@46: terom@46: irc_net_error(net, err); terom@45: } terom@45: terom@27: /** terom@48: * Our irc_conn has quit succesfully terom@48: */ terom@48: static void irc_net_conn_quit (struct irc_conn *conn, void *arg) terom@48: { terom@48: struct irc_net *net = arg; terom@48: terom@48: // clean up the conn terom@48: irc_conn_destroy(conn); terom@48: net->conn = NULL; terom@48: terom@48: // XXX: notify user terom@48: } terom@48: terom@48: /** terom@27: * Our irc_conn_callbacks list terom@27: */ terom@27: struct irc_conn_callbacks _conn_callbacks = { terom@27: .on_registered = &irc_net_conn_registered, terom@45: .on_error = &irc_net_conn_error, terom@48: .on_quit = &irc_net_conn_quit, terom@27: }; terom@27: terom@37: /** terom@38: * Propagate the command to the appropriate irc_chan based on the given name terom@38: */ terom@38: static void irc_net_propagate_chan (struct irc_net *net, const struct irc_line *line, const char *channel) terom@37: { terom@37: struct irc_chan *chan; terom@37: terom@37: // look up channel terom@38: if ((chan = irc_net_get_chan(net, channel)) == NULL) { terom@38: log_warn("unknown channel: %s", channel); terom@37: return; terom@37: } terom@37: terom@37: // propagate terom@37: irc_cmd_invoke(&chan->handlers, line); terom@37: } terom@37: terom@37: /** terom@38: * Propagate line to irc_chan based on args[0] terom@38: */ terom@38: static void irc_net_on_chan0 (const struct irc_line *line, void *arg) terom@38: { terom@38: struct irc_net *net = arg; terom@38: terom@38: irc_net_propagate_chan(net, line, line->args[0]); terom@38: } terom@38: terom@38: /** terom@38: * :nm PRIVMSG terom@38: * terom@38: * Either propagate to channel if found, otherwise handle as a privmsg terom@38: */ terom@38: static void irc_net_on_PRIVMSG (const struct irc_line *line, void *arg) terom@38: { terom@38: struct irc_net *net = arg; terom@38: terom@38: // XXX: does two lookups terom@38: if (irc_net_get_chan(net, line->args[0])) { terom@38: // short-circuit to on_chan0 terom@38: irc_net_on_chan0(line, arg); terom@38: terom@38: } else { terom@38: // XXX: callbacks for privmsgs terom@38: terom@38: } terom@38: } terom@38: terom@38: /** terom@37: * Our irc_cmd handler list terom@37: */ terom@37: static struct irc_cmd_handler _cmd_handlers[] = { terom@38: { "JOIN", &irc_net_on_chan0 }, terom@38: { "PRIVMSG", &irc_net_on_PRIVMSG }, terom@37: { NULL, NULL } terom@37: }; terom@37: terom@25: err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err) terom@25: { terom@25: struct irc_net *net; terom@28: struct sock_stream *sock = NULL; terom@25: terom@25: // allocate terom@25: if ((net = calloc(1, sizeof(*net))) == NULL) terom@25: return SET_ERROR(err, ERR_CALLOC); terom@25: terom@26: // initialize terom@53: net->info = *info; terom@26: TAILQ_INIT(&net->channels); terom@26: terom@44: if (info->raw_sock) { terom@44: log_info("connected using raw socket: %p", info->raw_sock); terom@44: terom@44: sock = info->raw_sock; terom@44: terom@44: } else if (info->use_ssl) { terom@25: log_info("connecting to [%s]:%s using SSL", info->hostname, info->service); terom@25: terom@44: // XXX: over-simplified blocking connect terom@25: if (sock_ssl_connect(&sock, info->hostname, info->service, err)) terom@25: goto error; terom@25: terom@25: } else { terom@25: log_info("connecting to [%s]:%s", info->hostname, info->service); terom@25: terom@44: // XXX: over-simplified blocking connect terom@25: if (sock_tcp_connect(&sock, info->hostname, info->service, err)) terom@25: goto error; terom@25: terom@25: } terom@25: terom@25: // create the irc connection state terom@27: if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err)) terom@27: goto error; terom@27: terom@37: // add our command handlers terom@37: if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net))) terom@37: goto error; terom@37: terom@27: // register terom@28: log_info("connected, registering"); terom@28: terom@27: if ((ERROR_CODE(err) = irc_conn_register(net->conn, &info->register_info))) terom@25: goto error; terom@25: terom@25: // ok terom@25: *net_ptr = net; terom@25: terom@25: return SUCCESS; terom@25: terom@25: error: terom@47: if (sock && !net->conn) terom@28: // we need to clean up the socket ourself terom@28: sock_stream_release(sock); terom@28: terom@47: // cleanup main terom@47: irc_net_destroy(net); terom@25: terom@25: return ERROR_CODE(err); terom@25: } terom@25: terom@47: void irc_net_destroy (struct irc_net *net) terom@47: { terom@47: struct irc_chan *next = TAILQ_FIRST(&net->channels), *chan; terom@47: terom@47: // our conn terom@47: if (net->conn) terom@47: irc_conn_destroy(net->conn); terom@47: terom@47: // our channels terom@47: while (next) { terom@47: chan = next; terom@47: next = TAILQ_NEXT(chan, node); terom@47: terom@47: irc_chan_destroy(chan); terom@47: } terom@47: terom@47: // ourselves terom@47: free(net); terom@47: } terom@47: terom@26: struct irc_chan* irc_net_add_chan (struct irc_net *net, const struct irc_chan_info *info) terom@26: { terom@26: struct irc_chan *chan; terom@26: struct error_info err; terom@26: terom@26: // create the new irc_chan struct terom@26: if (irc_chan_create(&chan, net, info, &err)) terom@26: // XXX: we lose error info terom@26: return NULL; terom@26: terom@26: // add to network list terom@26: TAILQ_INSERT_TAIL(&net->channels, chan, node); terom@27: terom@27: // currently connected? terom@27: if (net->conn && net->conn->registered) { terom@27: // then join terom@27: if ((ERROR_CODE(&err) = irc_chan_join(chan))) terom@27: // XXX terom@27: return NULL; terom@27: } terom@26: terom@26: // ok, return terom@26: return chan; terom@26: } terom@26: terom@26: struct irc_chan* irc_net_get_chan (struct irc_net *net, const char *channel) terom@26: { terom@26: struct irc_chan *chan = NULL; terom@26: terom@26: // look for it... terom@26: TAILQ_FOREACH(chan, &net->channels, node) { terom@26: if (strcasecmp(chan->info.channel, channel) == 0) terom@26: // found it terom@26: return chan; terom@26: } terom@26: terom@26: // no such channel terom@26: return NULL; terom@26: } terom@26: terom@48: err_t irc_net_quit (struct irc_net *net, const char *message) terom@48: { terom@48: if (!net->conn) terom@48: return ERR_IRC_NET_QUIT_STATE; terom@48: terom@48: // send the QUIT message, and then we can wait for the reply terom@48: return irc_conn_QUIT(net->conn, message); terom@48: }