terom@18: #ifndef IRC_CONN_H terom@18: #define IRC_CONN_H terom@18: terom@23: /** terom@23: * @file terom@23: * terom@23: * Support for connections to IRC servers, a rather fundamental thing. This holds the line_proto to handle the network terom@84: * communications, and then takes care of sending/receiving commands, as well as some core functionality like terom@84: * responding to PING requests, and tracking our current nickname. terom@18: */ terom@18: terom@23: struct irc_conn; terom@23: terom@23: #include "error.h" terom@155: #include "transport.h" terom@18: #include "line_proto.h" terom@91: #include "irc_queue.h" terom@18: #include "irc_line.h" terom@23: #include "irc_cmd.h" terom@21: #include terom@18: terom@25: /** terom@84: * The info required to register using irc_conn_register() terom@25: */ terom@27: struct irc_conn_register_info { terom@84: /** Nickname to use on that server */ terom@18: const char *nickname; terom@18: terom@84: /** Username to supply */ terom@18: const char *username; terom@18: terom@84: /** Realname to supply */ terom@18: const char *realname; terom@18: }; terom@18: terom@23: /** terom@27: * Connection state callbacks terom@27: */ terom@27: struct irc_conn_callbacks { terom@27: /** terom@84: * Recieved RPL_WELCOME, so irc_conn_register has completed, and we are now registered. terom@27: */ terom@27: void (*on_registered) (struct irc_conn *conn, void *arg); terom@45: terom@45: /** terom@45: * The connection has failed in some way, and can not be considered useable anymore. Sending messages won't work, terom@84: * and no more messages will be received. The connection should be destroyed, probably directly from this callback. terom@48: * terom@48: * NOTE: Implementing this callback is mandatory terom@45: */ terom@45: void (*on_error) (struct irc_conn *conn, struct error_info *err, void *arg); terom@48: terom@48: /** terom@48: * The connection was closed cleanly after irc_conn_QUIT. terom@48: * terom@84: * You probably want to destroy the irc_conn now to clean up. terom@48: */ terom@48: void (*on_quit) (struct irc_conn *conn, void *arg); terom@27: }; terom@27: terom@84: /** terom@84: * A single sock_stream connection to a single IRC server, providing a nice interface to the low-level IRC protocol. terom@84: * terom@84: * Create a irc_conn from a connected sock_stream using irc_conn_create(), providing the high-level callbacks. Then, terom@84: * register handlers for the IRC protocol messages recieved from the server using irc_conn_add_cmd_handlers() or terom@84: * irc_cmd_add and irc_conn::handlers/irc_conn::ctcp_handlers. terom@84: * terom@84: * You can then perform the registration step using irc_conn_register(), providing a irc_conn_register_info struct with terom@84: * the relevant info. terom@84: * terom@84: * Once you have succesfully registered (RPL_WELCOME recieved, irc_conn_callbacks::on_registered called, terom@84: * irc_conn::registered set), you can then send messages to the server in the form of irc_line's using the set of terom@84: * irc_conn_send functions. terom@27: */ terom@27: struct irc_conn { terom@84: /** We are a line-based protocol, this wraps the given sock_stream */ terom@27: struct line_proto *lp; terom@27: terom@91: /** Outgoing line queue */ terom@91: struct irc_queue *out_queue; terom@91: terom@84: /** High-level callbacks */ terom@27: struct irc_conn_callbacks callbacks; terom@27: terom@27: /** Opaque argument for callbacks */ terom@27: void *cb_arg; terom@27: terom@84: /** @defgroup irc_conn_status Status flags terom@84: * @{ terom@84: */ terom@27: /** Registration request sent */ terom@27: bool registering; terom@27: terom@84: /** Registered (as in, we have a working nickname) */ terom@27: bool registered; terom@48: terom@48: /** Quit message sent, waiting for server to close connection */ terom@48: bool quitting; terom@49: terom@49: /** Quit message sent, connection closed, we're done */ terom@49: bool quit; terom@37: terom@37: // @} terom@37: terom@84: /** terom@84: * Our current, actual nickname. This is set to NULL while we are still registering, but is then initialized when we terom@84: * get the first RPL_WELCOME message, and then kept up-to-date by handling relevant NICK messages. terom@84: */ terom@37: char *nickname; terom@27: terom@84: /** terom@84: * General IRC command handlers. These handlers are invoked directly for all irc_line's recieved from the IRC terom@84: * server, everything including core PING/NICK/RPL_WELCOME handling is implemented using these. You should add terom@84: * your own handlers using irc_conn_add_cmd_handlers() to implement whatever functionality it is that you want; terom@84: * see irc_cmd.h for more info about how these work. The "command" field is the literal IRC command as received terom@84: * from the server itself. terom@84: * terom@84: * So for example, the following line: terom@84: * \verbatim :nick!user@host PRIVMSG #foo :Hello everyone! \endverbatim terom@84: * terom@84: * results in the following irc_line: terom@84: * \code terom@84: * { { "nick", "user", "host" }, "PRIVMSG", { "#foo", "Hello everyone!", NULL } } terom@84: * \endcode terom@84: * terom@84: * As with all rules, there are exceptions - CTCP messages are handled differently. PRIVMSG's which contain terom@84: * CTCP extended messages are trapped before these handlers are invoked, and are instead processed using terom@84: * irc_conn::ctcp_handlers. Some built-in CTCP message handlers are provided that then submit these messages back terom@84: * to this main irc_conn::handlers list, as pseudo-irc_line's with a command field like "CTCP ACTION". These terom@84: * include: terom@84: * terom@84: * CTCP ACTION - { { "nick", "user", "host" }, "CTCP ACTION", "#foo", "sends an action message" } terom@84: * terom@84: * @see irc_cmd.h terom@84: * @see irc_line terom@84: * @see irc_conn::ctcp_handlers terom@84: */ terom@37: irc_cmd_handlers_t handlers; terom@84: terom@84: /** terom@84: * CTCP command handlers. These handlers are invoked for all PRIVMSG's recieved from the IRC server which begin terom@84: * with the CTCP X-DELIM char (in other words, a simplified CTCP spec is implemented). These PRIVMSG's will not be terom@84: * seen using irc_conn::handlers. These CTCP messages are then parsed, and a pseudo-irc_line is created, with the terom@84: * same source as the real message, the command set to the CTCP extended message tag, args[0] as the PRIVMSG's terom@84: * destination, and args[1] as the rest of the CTCP extended message payload. terom@84: * terom@84: * So for example, the following line: terom@88: * \verbatim :nick!user@host PRIVMSG #foo :\001ACTION does something lame\001 \endverbatim terom@84: * terom@84: * results in the following irc_line: terom@84: * \code terom@84: * { { "nick", "user", "host" }, "ACTION", "#foo", "does something lame" } terom@84: * \endcode terom@84: * terom@84: * Internally, some of these CTCP messages are then redirected back to handlers for convenience; currently only terom@84: * "ACTION". terom@84: * terom@84: * @see irc_cmd.h terom@84: * @see irc_line terom@84: * @see irc_conn::handlers terom@84: */ terom@84: irc_cmd_handlers_t ctcp_handlers; terom@27: }; terom@27: terom@27: /** terom@84: * Create a new irc_conn using the given sock_stream, which should be connected to an IRC server (i.e. ready for terom@84: * read/write). terom@27: * terom@84: * This does not yet send any requests to the server, it only sets up the core state. Use irc_conn_register() to terom@84: * actually register with the server. terom@18: * terom@84: * On success, the resulting irc_conn is returned via *conn_ptr with SUCCESS. Otherwise, ERR_* with error info returned terom@18: * via *err. terom@23: * terom@84: * @param conn_ptr returned new irc_conn structure terom@155: * @param transport connected transport terom@84: * @param callbacks the high-level status callbacks, required terom@84: * @param cb_arg opqaue context argument for callbacks terom@84: * @param err returned error info terom@18: */ terom@155: err_t irc_conn_create (struct irc_conn **conn_ptr, transport_t *transport, const struct irc_conn_callbacks *callbacks, terom@155: void *cb_arg, error_t *err); terom@18: terom@20: /** terom@28: * Destroy the irc_conn state, terminating any connection and releasing all resources. terom@28: * terom@28: * This does not end the session cleanly, and is intended mainly to be used after clean exit, or to clean up after errors. terom@28: */ terom@28: void irc_conn_destroy (struct irc_conn *conn); terom@28: terom@28: /** terom@23: * Add a new chain of command handler callbacks to be used to handle irc_lines from the server. The given arg will be terom@23: * passed to the callbacks as the context argument. The chain will be appended to the end of the current list of chains. terom@23: * terom@84: * @param conn the connection to use terom@84: * @param handlers the array of irc_cmd_handler structs, terminated with a NULL entry terom@23: * @param arg the context argument to use for the callbacks terom@23: */ terom@37: err_t irc_conn_add_cmd_handlers (struct irc_conn *conn, struct irc_cmd_handler *handlers, void *arg); terom@23: terom@23: /** terom@27: * Register with the IRC server using the given registration info (initial nickname etc.) terom@27: * terom@27: * This sends the NICK/USER command sequence. terom@84: * terom@84: * @param conn the connection to use terom@84: * @param info the information required to register, including nickname/username/etc terom@27: */ terom@27: err_t irc_conn_register (struct irc_conn *conn, const struct irc_conn_register_info *info); terom@27: terom@27: /** terom@150: * Compare the given nickname against our current nickname, returning true if it represents ourselves. terom@150: * terom@150: * As a convenience function, this is NULL-safe - it can safely be called with a NULL conn, NULL nickname, or a terom@150: * conn that doesn't yet have a nickname, and it will simply return false. terom@150: */ terom@150: bool irc_conn_self (struct irc_conn *conn, const char *nickname); terom@150: terom@150: /** terom@84: * Send a generic IRC message by formatting the given irc_line and sending it as a line. terom@20: * terom@84: * Use the irc_conn_COMMAND functions defined below for a more convenient interface. terom@84: * terom@84: * An error is returned if the line contains invalid data or writing the line to the sock_stream fails. Possible socket terom@84: * failures will also be reported using irc_conn_callbacks::on_error, so ignoring return values from this is usually terom@84: * OK... terom@84: * terom@84: * @param conn the IRC protocol connection terom@84: * @param line the irc_line protocol line to send terom@84: */ terom@84: err_t irc_conn_send (struct irc_conn *conn, const struct irc_line *line); terom@84: terom@84: /** terom@84: * @defgroup irc_conn_COMMAND Simple request functions terom@84: * terom@84: * These functions all simply build the corresponding irc_line struct, and then pass it on to irc_conn_send(). terom@20: * terom@20: * @{ terom@20: */ terom@20: terom@23: /** terom@84: * Send a NICK message. Usually either called indirectly via irc_conn_register, or used to change to a new nickname. terom@84: * terom@84: * If succesfull, this will result in a NICK message for our current nickname; irc_conn will handle this to update terom@84: * irc_conn::nickname. terom@23: * terom@23: * @param conn the IRC protocol connection terom@84: * @param nickname the new nickname to use terom@23: * terom@84: * Possible errors (from RFC2812): terom@84: * ERR_NONICKNAMEGIVEN terom@84: * ERR_NICKNAMEINUSE terom@84: * ERR_UNAVAILRESOURCE terom@84: * ERR_ERRONEUSNICKNAME terom@84: * ERR_NICKCOLLISION terom@84: * ERR_RESTRICTED terom@18: */ terom@18: err_t irc_conn_NICK (struct irc_conn *conn, const char *nickname); terom@18: terom@48: /** terom@84: * Send a USER message. Usually called indirectly via irc_conn_register. terom@23: * terom@84: * @param conn the IRC protocol connection terom@84: * @param username the username to register with, may be replaced with username from ident reply terom@23: * @param realname the full-name to register with terom@84: * terom@84: * Possible errors (from RFC2812): terom@84: * ERR_NEEDMOREPARAMS terom@84: * ERR_ALREADYREGISTRED terom@18: */ terom@18: err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname); terom@18: terom@48: /** terom@84: * Send a PONG message to the given target. terom@23: * terom@84: * Note that you do not need to handle the normal PING/PONG cycle, irc_conn does this for you. terom@84: * terom@84: * @param conn the IRC protocol connection terom@23: * @param target the PING source, aka. the target to send the PONG reply to terom@84: * terom@84: * Possible errors (from RFC2812): terom@84: * ERR_NOORIGIN terom@84: * ERR_NOSUCHSERVER terom@20: */ terom@20: err_t irc_conn_PONG (struct irc_conn *conn, const char *target); terom@18: terom@48: /** terom@84: * Send a simple JOIN message for the given channel. Note that this does not implement all possible arguments. terom@23: * terom@84: * If succesfull, this will result in a JOIN message for us on the given channel, plus a series of terom@84: * RPL_NAMREPLY/RPL_ENDOFNAMES/RPL_TOPIC/etc messages. terom@23: * terom@84: * @param conn the IRC protocol connection terom@23: * @param channel the full channel name to join terom@84: * terom@84: * Possible errors (from RFC2812): terom@84: * ERR_NEEDMOREPARAMS ERR_BANNEDFROMCHAN terom@84: * ERR_INVITEONLYCHAN ERR_BADCHANNELKEY terom@84: * ERR_CHANNELISFULL ERR_BADCHANMASK terom@84: * ERR_NOSUCHCHANNEL ERR_TOOMANYCHANNELS terom@84: * ERR_TOOMANYTARGETS ERR_UNAVAILRESOURCE terom@84: * terom@23: */ terom@23: err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel); terom@23: terom@48: /** terom@97: * Send a PRIVMSG message to some target, usually a channel or another user (nickname). terom@97: * terom@97: * Note that the protocol limits messages to 512 bytes in length (total, including other protocol stuff), and messages terom@97: * can't contain newlines or NULs - this will return an ERR_IRC_LINE_* in both cases. terom@97: * terom@97: * If succesfull, this won't result in any reply. If the target is a nickname, the server supports AWAY, and the target terom@97: * has marked themselves as away, this may result in an RPL_AWAY reply. terom@97: * terom@97: * @param conn the IRC protocol connection terom@97: * @param target the message target, usually either a channel name or a nickname terom@97: * @param message the message to send terom@97: * terom@97: * Possible errors (from RFC2812): terom@97: * ERR_NORECIPIENT ERR_NOTEXTTOSEND terom@97: * ERR_CANNOTSENDTOCHAN ERR_NOTOPLEVEL terom@97: * ERR_WILDTOPLEVEL ERR_TOOMANYTARGETS terom@97: * ERR_NOSUCHNICK terom@97: * terom@97: */ terom@97: err_t irc_conn_PRIVMSG (struct irc_conn *conn, const char *target, const char *message); terom@97: terom@97: /** terom@127: * Send a NOTICE messaeg to some target, usually a channel or another user (nickname). terom@127: * terom@127: * The same limitations on message length and data apply as for irc_conn_PRIVMSG. terom@127: * terom@127: * The only difference between PRIVMSG and NOTICE is that NOTICE messages should never generate an automated reply, terom@127: * hence, they are used for e.g. CTCP replies, and ideally, for IRC bot output. terom@127: * terom@127: * @param conn the IRC protocol connection terom@127: * @param target the message target, usually either a channel name or a nickname terom@127: * @param message the message to send terom@127: * terom@127: * Possible errors (from RFC2812): terom@127: * None. Servers do not ever reply to NOTICE messages. terom@127: */ terom@127: err_t irc_conn_NOTICE (struct irc_conn *conn, const char *target, const char *message); terom@127: terom@127: /** terom@84: * Send a QUIT message to the server. The server will reply with an ERROR message and close the connection. terom@48: * terom@84: * This updates our state as disconnecting, and once EOF is recieved, the irc_conn_callbacks::on_quit callback is terom@84: * called. terom@84: * terom@84: * @param conn the IRC protocol connection terom@84: * @param message the quit message, which may be displayed to other clients terom@48: */ terom@48: err_t irc_conn_QUIT (struct irc_conn *conn, const char *message); terom@48: terom@21: // @} terom@21: terom@18: #endif /* IRC_CONN_H */