src/irc_conn.c
changeset 37 4fe4a3c4496e
parent 33 e5139b339b18
child 39 a4891d71aca9
equal deleted inserted replaced
36:791d7a5532e2 37:4fe4a3c4496e
     1 #include "irc_conn.h"
     1 #include "irc_conn.h"
     2 #include "irc_cmd.h"
     2 #include "irc_cmd.h"
       
     3 #include "irc_nm.h"
     3 #include "log.h"
     4 #include "log.h"
     4 
     5 
     5 #include <stdlib.h>
     6 #include <stdlib.h>
     6 #include <string.h>
     7 #include <string.h>
     7 
     8 
     8 /*
     9 /**
     9  * "Welcome to the Internet Relay Network <nick>!<user>@<host>"
    10  * @group Forward-declerations
    10  */
    11  * @{
    11 static void on_RPL_WELCOME (struct irc_conn *conn, const struct irc_line *line, void *arg)
    12  */
    12 {
    13 
    13     (void) line;
    14 /**
    14     (void) arg;
    15  * Handle an event-based error on this IRC connection.
       
    16  */
       
    17 static void irc_conn_handle_error (struct irc_conn *conn, struct error_info *err);
       
    18 
       
    19 /**
       
    20  * Update irc_conn.nickname
       
    21  */
       
    22 static err_t irc_conn_set_nickname (struct irc_conn *conn, const char *nickname)
       
    23 {
       
    24     struct error_info err;
       
    25 
       
    26     // strdup
       
    27     if ((conn->nickname = strdup(nickname)) == NULL) {
       
    28         SET_ERROR(&err, ERR_STRDUP);
       
    29 
       
    30         // notify
       
    31         irc_conn_handle_error(conn, &err);
       
    32 
       
    33         return ERROR_CODE(&err);
       
    34     }
       
    35 
       
    36     // ok
       
    37     return SUCCESS;
       
    38 }
       
    39 
       
    40 // @}
       
    41 
       
    42 /**
       
    43  * 001 <nick> :Welcome to the Internet Relay Network <nick>!<user>@<host>
       
    44  */
       
    45 static void on_RPL_WELCOME (const struct irc_line *line, void *arg)
       
    46 {
       
    47     struct irc_conn *conn = arg;
    15 
    48 
    16     // update state
    49     // update state
    17     conn->registering = false;
    50     conn->registering = false;
    18     conn->registered = true;
    51     conn->registered = true;
       
    52     
       
    53     // set our real nickname from the message target
       
    54     if (irc_conn_set_nickname(conn, line->args[0]))
       
    55         return;
    19 
    56 
    20     // trigger callback
    57     // trigger callback
    21     if (conn->callbacks.on_registered)
    58     if (conn->callbacks.on_registered)
    22         conn->callbacks.on_registered(conn, conn->cb_arg);
    59         conn->callbacks.on_registered(conn, conn->cb_arg);
    23 }
    60 }
    24 
    61 
    25 /*
    62 /**
    26  * PING <server1> [ <server2> ]
    63  * PING <server1> [ <server2> ]
    27  *
    64  *
    28  * Send a 'PONG <server1>` reply right away.
    65  * Send a 'PONG <server1>` reply right away.
    29  */ 
    66  */ 
    30 static void on_PING (struct irc_conn *conn, const struct irc_line *line, void *arg)
    67 static void on_PING (const struct irc_line *line, void *arg)
    31 {
    68 {
    32     (void) arg;
    69     struct irc_conn *conn = arg;
    33 
    70 
    34     // just reply
    71     // just reply
    35     irc_conn_PONG(conn, line->args[0]);
    72     irc_conn_PONG(conn, line->args[0]);
    36 }
    73 }
    37 
    74 
    38 /*
    75 /**
       
    76  * NICK <nickname>
       
    77  *
       
    78  * If the prefix is us, then update our nickname
       
    79  */
       
    80 static void on_NICK (const struct irc_line *line, void *arg)
       
    81 {
       
    82     struct irc_conn *conn = arg;
       
    83     char nickname[IRC_NICK_MAX];
       
    84 
       
    85     // parse nickname, ignoring errors
       
    86     if (irc_prefix_parse_nick(line->prefix, nickname)) {
       
    87         log_warn("invalid prefix: %s", line->prefix);
       
    88         return;
       
    89     }
       
    90 
       
    91     // ignore if it's not us
       
    92     if (irc_cmp_nick(nickname, conn->nickname))
       
    93         return;
       
    94 
       
    95     // update our nickname
       
    96     irc_conn_set_nickname(conn, nickname);
       
    97 }
       
    98 
       
    99 /**
    39  * Our command handlers
   100  * Our command handlers
    40  */
   101  */
    41 struct irc_cmd_handler _cmd_handlers[] = {
   102 static struct irc_cmd_handler _cmd_handlers[] = {
    42     { IRC_RPL_WELCOME,  on_RPL_WELCOME      },
   103     {   IRC_RPL_WELCOME,    &on_RPL_WELCOME     },
    43     { "PING",           on_PING             },
   104     {   "PING",             &on_PING            },
    44     { NULL,             NULL,               },
   105     {   "NICK",             &on_NICK            },
       
   106     {   NULL,               NULL,               },
    45 };
   107 };
    46 
   108 
    47 /**
   109 /**
    48  * Incoming line handler
   110  * Incoming line handler
    49  */
   111  */
    50 void irc_conn_on_line (char *line_buf, void *arg) 
   112 void irc_conn_on_line (char *line_buf, void *arg) 
    51 {
   113 {
    52     struct irc_conn *conn = arg;
   114     struct irc_conn *conn = arg;
    53     struct irc_line line;
   115     struct irc_line line;
    54     struct irc_cmd_chain *chain;
       
    55     struct irc_cmd_handler *handler;
       
    56     int err;
   116     int err;
    57     
   117     
    58     // log
   118     // log
    59     log_debug("%s", line_buf);
   119     log_debug("%s", line_buf);
    60 
   120 
    61     // parse
   121     // parse
    62     if ((err = irc_line_parse(&line, line_buf))) {
   122     if ((err = irc_line_parse(&line, line_buf))) {
    63         log_warn("invalid line: %s: %s\n", line_buf, error_name(err));
   123         log_warn("invalid line: %s: %s\n", line_buf, error_name(err));
    64         return;
   124         return;
    65     }
   125     }
    66     
   126 
    67     // run each handler chain
   127     // invoke command handlers
    68     STAILQ_FOREACH(chain, &conn->handlers, node) {
   128     irc_cmd_invoke(&conn->handlers, &line);
    69         // look up appropriate handler
       
    70         for (handler = chain->handlers; handler->command; handler++) {
       
    71             // the command is alpha-only, so normal case-insensitive cmp is fine
       
    72             if (strcasecmp(handler->command, line.command) == 0) {
       
    73                 // invoke the func
       
    74                 handler->func(conn, &line, chain->arg);
       
    75 
       
    76                 // ...only one per chain
       
    77                 break;
       
    78             }
       
    79         }
       
    80     }
       
    81 }
   129 }
    82 
   130 
    83 /**
   131 /**
    84  * Transport failed
   132  * Transport failed
    85  */
   133  */
    87 {
   135 {
    88     struct irc_conn *conn = arg;
   136     struct irc_conn *conn = arg;
    89 
   137 
    90     // log
   138     // log
    91     log_err_info(err, "line_proto error");
   139     log_err_info(err, "line_proto error");
    92 
   140     
    93     // XXX: notify user
   141     // 'handle'
       
   142     irc_conn_handle_error(conn, err);
    94 }
   143 }
    95 
   144 
    96 static struct line_proto_callbacks _lp_callbacks = {
   145 static struct line_proto_callbacks _lp_callbacks = {
    97     .on_line        = &irc_conn_on_line,
   146     .on_line        = &irc_conn_on_line,
    98     .on_error       = &irc_conn_on_error,
   147     .on_error       = &irc_conn_on_error,
    99 };
   148 };
   100 
   149 
       
   150 static void irc_conn_handle_error (struct irc_conn *conn, struct error_info *err)
       
   151 {
       
   152     // XXX: notify user callback
       
   153 }
       
   154 
   101 err_t irc_conn_create (struct irc_conn **conn_ptr, struct sock_stream *sock, const struct irc_conn_callbacks *callbacks, 
   155 err_t irc_conn_create (struct irc_conn **conn_ptr, struct sock_stream *sock, const struct irc_conn_callbacks *callbacks, 
   102         void *cb_arg, struct error_info *err)
   156         void *cb_arg, struct error_info *err)
   103 {
   157 {
   104     struct irc_conn *conn;
   158     struct irc_conn *conn;
   105 
   159 
   110     // init state
   164     // init state
   111     conn->callbacks = *callbacks;
   165     conn->callbacks = *callbacks;
   112     conn->cb_arg = cb_arg;
   166     conn->cb_arg = cb_arg;
   113 
   167 
   114     // initialize command handlers
   168     // initialize command handlers
   115     STAILQ_INIT(&conn->handlers);
   169     irc_cmd_init(&conn->handlers);
   116     
   170     
   117     // add the core handlers 
   171     // add the core handlers 
   118     if ((ERROR_CODE(err) = irc_conn_register_handler_chain(conn, _cmd_handlers, NULL)))
   172     if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers(conn, _cmd_handlers, conn)))
   119         goto error;
   173         goto error;
   120 
   174 
   121     // create the line_proto, with our on_line handler
   175     // create the line_proto, with our on_line handler
   122     if (line_proto_create(&conn->lp, sock, IRC_LINE_MAX * 1.5, &_lp_callbacks, conn, err))
   176     if (line_proto_create(&conn->lp, sock, IRC_LINE_MAX * 1.5, &_lp_callbacks, conn, err))
   123         goto error;
   177         goto error;
   134     return ERROR_CODE(err);    
   188     return ERROR_CODE(err);    
   135 }
   189 }
   136 
   190 
   137 void irc_conn_destroy (struct irc_conn *conn)
   191 void irc_conn_destroy (struct irc_conn *conn)
   138 {
   192 {
   139     struct irc_cmd_chain *next = STAILQ_FIRST(&conn->handlers);
       
   140 
       
   141     // release the line_proto
   193     // release the line_proto
   142     if (conn->lp)
   194     if (conn->lp)
   143         line_proto_release(conn->lp);
   195         line_proto_release(conn->lp);
   144 
   196     
   145     // clean up any handler chains
   197     // free the command handlers
   146     while (next) {
   198     irc_cmd_free(&conn->handlers);
   147         struct irc_cmd_chain *node = next;
       
   148 
       
   149         // update next
       
   150         next = STAILQ_NEXT(node, node);
       
   151 
       
   152         // free
       
   153         free(node);
       
   154     }
       
   155 
   199 
   156     // free the irc_conn itself
   200     // free the irc_conn itself
   157     free(conn);
   201     free(conn);
   158 }
   202 }
   159 
   203 
   160 err_t irc_conn_register_handler_chain (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg)
   204 err_t irc_conn_add_cmd_handlers (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg)
   161 {
   205 {
   162     struct irc_cmd_chain *item;
   206     // use the irc_cmd stuff
   163 
   207     return irc_cmd_add(&conn->handlers, handlers, arg);
   164     // allocate the chain item
       
   165     if ((item = calloc(1, sizeof(*item))) == NULL)
       
   166         return ERR_CALLOC;
       
   167 
       
   168     // store
       
   169     item->handlers = handlers;
       
   170     item->arg = arg;
       
   171 
       
   172     // append
       
   173     STAILQ_INSERT_TAIL(&conn->handlers, item, node);
       
   174 
       
   175     // ok
       
   176     return SUCCESS;
       
   177 }
   208 }
   178 
   209 
   179 err_t irc_conn_register (struct irc_conn *conn, const struct irc_conn_register_info *info)
   210 err_t irc_conn_register (struct irc_conn *conn, const struct irc_conn_register_info *info)
   180 {
   211 {
   181     err_t err;
   212     err_t err;