terom@18: #include "irc_conn.h" terom@21: #include "irc_cmd.h" terom@39: #include "irc_proto.h" terom@22: #include "log.h" terom@18: terom@18: #include terom@18: #include terom@18: terom@37: /** terom@45: * Handle an async error on this IRC connection that we could not recover from any other way, the protocol is now dead, terom@45: * and should be considered as destroyed after this returns. terom@45: * terom@45: * For conveniance, this returns the ERROR_CODE terom@21: */ terom@45: static err_t irc_conn_set_error (struct irc_conn *conn, struct error_info *err) terom@45: { terom@45: // notify user callback terom@45: conn->callbacks.on_error(conn, err, conn->cb_arg); terom@37: terom@45: return ERROR_CODE(err); terom@45: } terom@45: terom@37: terom@37: /** terom@37: * Update irc_conn.nickname terom@37: */ terom@37: static err_t irc_conn_set_nickname (struct irc_conn *conn, const char *nickname) terom@21: { terom@37: struct error_info err; terom@37: terom@37: // strdup terom@37: if ((conn->nickname = strdup(nickname)) == NULL) { terom@37: SET_ERROR(&err, ERR_STRDUP); terom@37: terom@37: // notify terom@45: return irc_conn_set_error(conn, &err); terom@37: } terom@37: terom@37: // ok terom@37: return SUCCESS; terom@37: } terom@37: terom@37: /** terom@37: * 001 :Welcome to the Internet Relay Network !@ terom@37: */ terom@37: static void on_RPL_WELCOME (const struct irc_line *line, void *arg) terom@37: { terom@37: struct irc_conn *conn = arg; terom@24: terom@21: // update state terom@27: conn->registering = false; terom@21: conn->registered = true; terom@37: terom@37: // set our real nickname from the message target terom@37: if (irc_conn_set_nickname(conn, line->args[0])) terom@37: return; terom@22: terom@27: // trigger callback terom@27: if (conn->callbacks.on_registered) terom@27: conn->callbacks.on_registered(conn, conn->cb_arg); terom@21: } terom@21: terom@37: /** terom@20: * PING [ ] terom@20: * terom@20: * Send a 'PONG ` reply right away. terom@20: */ terom@37: static void on_PING (const struct irc_line *line, void *arg) terom@20: { terom@37: struct irc_conn *conn = arg; terom@24: terom@20: // just reply terom@20: irc_conn_PONG(conn, line->args[0]); terom@20: } terom@20: terom@37: /** terom@37: * NICK terom@37: * terom@37: * If the prefix is us, then update our nickname terom@37: */ terom@37: static void on_NICK (const struct irc_line *line, void *arg) terom@37: { terom@37: struct irc_conn *conn = arg; terom@37: char nickname[IRC_NICK_MAX]; terom@37: terom@37: // parse nickname, ignoring errors terom@37: if (irc_prefix_parse_nick(line->prefix, nickname)) { terom@37: log_warn("invalid prefix: %s", line->prefix); terom@37: return; terom@37: } terom@37: terom@37: // ignore if it's not us terom@37: if (irc_cmp_nick(nickname, conn->nickname)) terom@37: return; terom@37: terom@37: // update our nickname terom@37: irc_conn_set_nickname(conn, nickname); terom@37: } terom@37: terom@37: /** terom@20: * Our command handlers terom@20: */ terom@37: static struct irc_cmd_handler _cmd_handlers[] = { terom@37: { IRC_RPL_WELCOME, &on_RPL_WELCOME }, terom@37: { "PING", &on_PING }, terom@37: { "NICK", &on_NICK }, terom@37: { NULL, NULL, }, terom@20: }; terom@20: terom@33: /** terom@23: * Incoming line handler terom@23: */ terom@18: void irc_conn_on_line (char *line_buf, void *arg) terom@18: { terom@20: struct irc_conn *conn = arg; terom@18: struct irc_line line; terom@18: int err; terom@18: terom@18: // log terom@22: log_debug("%s", line_buf); terom@18: terom@18: // parse terom@20: if ((err = irc_line_parse(&line, line_buf))) { terom@23: log_warn("invalid line: %s: %s\n", line_buf, error_name(err)); terom@20: return; terom@20: } terom@18: terom@37: // invoke command handlers terom@37: irc_cmd_invoke(&conn->handlers, &line); terom@18: } terom@18: terom@33: /** terom@33: * Transport failed terom@33: */ terom@33: void irc_conn_on_error (struct error_info *err, void *arg) terom@33: { terom@33: struct irc_conn *conn = arg; terom@33: terom@33: // log terom@33: log_err_info(err, "line_proto error"); terom@37: terom@45: // trash ourselves terom@45: irc_conn_set_error(conn, err); terom@33: } terom@33: terom@32: static struct line_proto_callbacks _lp_callbacks = { terom@32: .on_line = &irc_conn_on_line, terom@33: .on_error = &irc_conn_on_error, terom@32: }; terom@32: terom@27: err_t irc_conn_create (struct irc_conn **conn_ptr, struct sock_stream *sock, const struct irc_conn_callbacks *callbacks, terom@27: void *cb_arg, struct error_info *err) terom@18: { terom@18: struct irc_conn *conn; terom@18: terom@18: // alloc new state struct terom@18: if ((conn = calloc(1, sizeof(struct irc_conn))) == NULL) terom@18: return SET_ERROR(err, ERR_CALLOC); terom@18: terom@27: // init state terom@27: conn->callbacks = *callbacks; terom@27: conn->cb_arg = cb_arg; terom@27: terom@23: // initialize command handlers terom@37: irc_cmd_init(&conn->handlers); terom@23: terom@23: // add the core handlers terom@37: if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers(conn, _cmd_handlers, conn))) terom@28: goto error; terom@23: terom@18: // create the line_proto, with our on_line handler terom@32: if (line_proto_create(&conn->lp, sock, IRC_LINE_MAX * 1.5, &_lp_callbacks, conn, err)) terom@28: goto error; terom@18: terom@18: // ok terom@18: *conn_ptr = conn; terom@18: terom@18: return SUCCESS; terom@28: terom@28: error: terom@28: // release terom@28: irc_conn_destroy(conn); terom@28: terom@28: return ERROR_CODE(err); terom@28: } terom@28: terom@28: void irc_conn_destroy (struct irc_conn *conn) terom@28: { terom@28: // release the line_proto terom@28: if (conn->lp) terom@28: line_proto_release(conn->lp); terom@37: terom@37: // free the command handlers terom@37: irc_cmd_free(&conn->handlers); terom@28: terom@28: // free the irc_conn itself terom@28: free(conn); terom@18: } terom@18: terom@37: err_t irc_conn_add_cmd_handlers (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg) terom@23: { terom@37: // use the irc_cmd stuff terom@37: return irc_cmd_add(&conn->handlers, handlers, arg); terom@23: } terom@23: terom@27: err_t irc_conn_register (struct irc_conn *conn, const struct irc_conn_register_info *info) terom@27: { terom@27: err_t err; terom@27: terom@27: // assert state terom@27: if (conn->registering || conn->registered) terom@27: // XXX: stupid error code terom@27: return ERR_IRC_CONN_REGISTER_STATE; terom@27: terom@27: // send the initial messages terom@27: if ( terom@27: (err = irc_conn_NICK(conn, info->nickname)) terom@27: || (err = irc_conn_USER(conn, info->username, info->realname)) terom@27: ) terom@27: return err; terom@27: terom@27: // set state terom@27: conn->registering = true; terom@27: terom@27: // ok terom@27: return SUCCESS; terom@27: } terom@27: terom@18: err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line) terom@18: { terom@18: char line_buf[IRC_LINE_MAX + 2]; terom@18: err_t err; terom@24: int ret; terom@18: terom@18: // format terom@18: if ((err = irc_line_build(line, line_buf))) terom@18: return err; terom@18: terom@18: // log terom@22: log_debug("%s", line_buf); terom@18: terom@18: // add CRLF terom@18: strcat(line_buf, "\r\n"); terom@18: terom@18: // send using line_proto terom@19: // XXX: ignore output-buffering terom@24: return (ret = line_proto_send(conn->lp, line_buf)) < 0 ? -ret : SUCCESS; terom@18: } terom@18: terom@18: err_t irc_conn_NICK (struct irc_conn *conn, const char *nickname) terom@18: { terom@18: // NICK terom@18: struct irc_line line = { terom@18: NULL, "NICK", { nickname, NULL } terom@18: }; terom@18: terom@18: return irc_conn_send(conn, &line); terom@18: } terom@18: terom@18: err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname) terom@18: { terom@18: // USER terom@18: struct irc_line line = { terom@20: NULL, "USER", { username, "0", "*", realname, NULL } terom@18: }; terom@18: terom@18: return irc_conn_send(conn, &line); terom@18: } terom@18: terom@20: err_t irc_conn_PONG (struct irc_conn *conn, const char *target) terom@20: { terom@20: // PONG [ ] terom@29: // params are actually the wrong way around now, but nobody cares terom@20: struct irc_line line = { terom@20: NULL, "PONG", { target, NULL } terom@20: }; terom@20: terom@20: return irc_conn_send(conn, &line); terom@20: } terom@20: terom@23: err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel) terom@23: { terom@23: // JOIN ( *( "," ) [ *( "," ) ] ) / "0" terom@23: struct irc_line line = { terom@23: NULL, "JOIN", { channel, NULL } terom@23: }; terom@23: terom@23: return irc_conn_send(conn, &line); terom@23: } terom@23: