irctk

libircclient binding for scripts
git clone https://a3nm.net/git/irctk/
Log | Files | Refs | README

commit 182d990623383bb7573607ef4fc02c7df34edc15
parent a719be5a9881c0e1a4257752ad0861e526db9bb1
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Sat, 16 Jun 2012 18:14:25 +0200

loads of cleanup

Diffstat:
README | 6+++---
irctk.c | 411++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
2 files changed, 230 insertions(+), 187 deletions(-)

diff --git a/README b/README @@ -2,14 +2,14 @@ irctk is a general-purpose IRC wrapper. It connects to an IRC server specified as [NICK@]SERVER[:PORT] as a CLI argument, posts what it receives from stdin and displays what is said to stdout. -Beware, this code is not yet secure or clean. I have been using it regularly for -some time, but your needs might be different from mine. - irctk is not related to IrcTK by Max Countryman <https://github.com/maxcountryman/irctk>. To compile, you need libircclient 1.6 <http://www.ulduzsoft.com/libircclient/>. +Warning, the code has not been thoroughly audited. SSL support does not really +work, and any certificates will be accepted. + TODO these examples aren't secure TODO use -m -c -n diff --git a/irctk.c b/irctk.c @@ -4,26 +4,19 @@ */ -#include <stdio.h> +#include <argp.h> +#include <errno.h> +#include <pthread.h> #include <stdarg.h> -#include <string.h> +#include <stdio.h> #include <stdlib.h> -#include <errno.h> -#include <argp.h> -#include <unistd.h> +#include <string.h> #include <sys/time.h> +#include <unistd.h> #include <libircclient/libircclient.h> #include <libircclient/libirc_rfcnumeric.h> -#include <unistd.h> -#include <pthread.h> - -#define CREATE_THREAD(id,func,param) (pthread_create (id, 0, func, (void *) param) != 0) -#define THREAD_FUNCTION(funcname) static void * funcname (void * arg) -#define thread_id_t pthread_t -#define _GNU_SOURCE - #define E_SESSION 1 #define E_CONNECT 2 #define E_THREAD 3 @@ -31,26 +24,23 @@ #define E_BADLINE 5 enum default_destinations {DEFAULT_FIRST, DEFAULT_LAST_IN, DEFAULT_LAST_OUT, DEFAULT_ALL}; - enum event_tos {NOTHING, COMMAND, MESSAGE}; // TODO get rid of that #define MAX_LEN 4096 #define MAX_NICK_LEN 4096 -// TODO tracking of nicks +#define max(a, b) ((b) > (a) ? (b) : (a)) -//TODO const char *argp_program_version = "irctk 0.1"; -//TODO const char *argp_program_bug_address = "<a3nm@a3nm.net>"; -const char *argp_program_version = NULL; -const char *argp_program_bug_address = NULL; +const char *argp_program_version = "irctk 0.1"; +const char *argp_program_bug_address = "<a3nm@a3nm.net>"; /* Program documentation. */ static char doc[] = "irctk -- an IRC toolkit"; /* A description of the arguments we accept. */ -static char args_doc[] = "[NICK[:PASSWORD]@]SERVER[:PORT] [CHANNEL]..."; +static char args_doc[] = "[NICK[:PASSWORD]@]SERVER[:PORT] [CHANNEL[:PASSWORD]]..."; #define STANDARD 0 #define MISC 1 @@ -60,27 +50,31 @@ static char args_doc[] = "[NICK[:PASSWORD]@]SERVER[:PORT] [CHANNEL]..."; #define DISPLAY_SELECTION 5 #define PARSING 6 #define NAME 7 + /* The options we understand. */ static struct argp_option options[] = { - {"verbose", 'v', 0, 0, - "Produce debug output on stderr", STANDARD, }, - {"quiet", 'q', 0, 0, - "Don't even display errors on stderr, fail silently", STANDARD, }, - + {"verbose", 'v', 0, 0, + "Produce debug output on stderr", + STANDARD}, + {"quiet", 'q', 0, 0, + "Don't even display errors on stderr, fail silently", + STANDARD}, + + /* TODOponey ideally should not delay for bots, only for things piped, is there a + * better way such as only waiting at the end? */ {"interval", 'i', "secs", 0, - "Wait delay between posted messages (default: 0 for tty stdin, 1 for file/pipe)", - /* TODOponey ideally should not delay for bots, only for things piped, is there a - * better way such as only waiting at the end? */ + "Wait delay between posted messages (default: 0 for tty stdin, 1 for" + " file/pipe)", MISC}, - /*{"no-auto-join", 0, 0, 0, - "Refuse to send any message to channels not specified on the command line (default is to join as needed)", MISC }, // TODO*/ {"retry-after", 'y', "secs", 0, "Wait delay before trying to reconnect (default: 5)", MISC}, {"retry-factor", 'Y', "secs", 0, "Retry delay multiplication factor after each failed attempt (default: 2)", MISC}, + /*{"no-auto-join", 0, 0, 0, + "Refuse to send any message to channels not specified on the command line (default is to join as needed)", MISC }, // TODO*/ /*{"synchronous", 's', 0, 0, "Will wait for a multiline, blankline-terminated answer on stdin to all lines passed on stdout, and will bufferize activity before that", COM_MODE }, // TODO*/ @@ -90,70 +84,78 @@ static struct argp_option options[] = { "Run this command on each message, passing the channel, nick and message as $1, $2, and $3", COM_MODE }, */ - {"default-always-first", '0', 0, 0, + {"default-always-first", '0', 0, 0, "Post messages to the first specified channel by default", - CHANNEL_SELECTION}, - {"default-last-active", 'a', 0, 0, + CHANNEL_SELECTION}, + {"default-last-active", 'a', 0, 0, "Post messages to the last active channel by default (default)", - CHANNEL_SELECTION}, - {"default-last-posted", 'p', 0, 0, + CHANNEL_SELECTION}, + {"default-last-posted", 'p', 0, 0, "Post messages to the last posted channel by default", - CHANNEL_SELECTION}, - {"default-all", 'l', 0, 0, + CHANNEL_SELECTION}, + {"default-all", 'l', 0, 0, "Post messages to all specified channels by default", - CHANNEL_SELECTION}, - {"show-inferred", 'P', 0, 0, - "When inferring a next chan with --default-last-posted, display it on stderr", - CHANNEL_SELECTION}, - - {"reply", 'r', 0, 0, - "When inferring a destination channel automatically with --last-active, also infer a destination nick, and prefix lines with this nick (blank line indicates the end of an answer)", BOT_OPTIONS }, + CHANNEL_SELECTION}, + {"show-inferred", 'P', 0, 0, + "When inferring a next chan with --default-last-posted, display it on" + " stderr", + CHANNEL_SELECTION}, + + {"reply", 'r', 0, 0, + "When inferring a destination channel automatically with --last-active," + " also infer a destination nick, and prefix lines with this nick (blank" + " line indicates the end of an answer)", + BOT_OPTIONS}, /* TODO externalize those two features as a wrapper script */ - {"filter", 'f', 0, 0, - "Only keep messages directly addressed to us (in private channel or with lines prefixed by our nick)", BOT_OPTIONS }, - {"filter-prune", 'F', 0, 0, - "Only give the message body without the channel, nick or address (useful with -fr)", BOT_OPTIONS }, - - {"with-host", 'w', 0, 0, - "Keep the host info in pseudos" , - DISPLAY_SELECTION}, - /* TODO test, and make compatible with --own */ - {"own", 'o', 0, 0, + {"filter", 'f', 0, 0, + "Only keep messages directly addressed to us (in private channel or with" + " lines prefixed by our nick)", + BOT_OPTIONS }, + {"filter-prune", 'F', 0, 0, + "Only give the message body without the channel, nick or address (useful" + " with -fr)", + BOT_OPTIONS }, + + /* TODO test, and make compatible with --own */ + {"with-host", 'w', 0, 0, + "Keep the host info in pseudos", + DISPLAY_SELECTION}, + {"own", 'o', 0, 0, "Also display messages posted by self", - DISPLAY_SELECTION}, + DISPLAY_SELECTION}, - /*{"no-command-to-event", 0, 0, 0, - "Don't generate events from messages with \"/quit\", \"/me\"...", - PARSING, }, */ // TODO - {"no-destination-prefix", 'D', 0, 0, + {"no-destination-prefix", 'D', 0, 0, "Don't look for []", - PARSING}, - {"event-to-nothing", 'n', 0, 0, + PARSING}, + {"event-to-nothing", 'n', 0, 0, "Ignore server events (default).", - PARSING}, - {"event-to-command", 'c', 0, 0, + PARSING}, + {"event-to-command", 'c', 0, 0, "Translate server events to messages with \"/quit\", \"/me\"...", - PARSING}, - {"event-to-message", 'm', 0, 0, + PARSING}, + {"event-to-message", 'm', 0, 0, "Translate server events to a human-readable description", - PARSING}, + PARSING}, + /*{"no-command-to-event", 0, 0, 0, + "Don't generate events from messages with \"/quit\", \"/me\"...", + PARSING, }, */ // TODO {"username", 'U', "name", 0, "Unix username (default: same as nick)", - NAME}, + NAME}, {"realname", 'R', "name", 0, "Full name (default: same as nick)", - NAME}, + NAME}, {"force-nick", 'N', 0, 0, - "Abort when the intended nick is unavailable or invalid instead of choosing another one", - NAME}, + "Abort when the intended nick is unavailable or invalid instead of picking" + " another one automatically", + NAME}, { 0 } }; -/* - * We store data in IRC session context. - */ + +/* The structure to store program settings */ struct arguments { char nick[MAX_NICK_LEN]; @@ -198,9 +200,10 @@ struct arguments typedef struct arguments irc_ctx_t; -/* The arguments */ +/* The program settings */ struct arguments args; +/* The initial argument values */ void initialize_args() { strncpy(args.nick, "irctk", MAX_NICK_LEN-1); @@ -240,7 +243,9 @@ void initialize_args() } -void set_nick_and_names(struct arguments *arguments) { +/* set our nick, and names if not already set */ +void set_nick_and_names(struct arguments *arguments) +{ strncat(arguments->nick, arguments->raw_dest, MAX_NICK_LEN-1); if (!arguments->username_set) arguments->username = arguments->raw_dest; @@ -249,6 +254,7 @@ void set_nick_and_names(struct arguments *arguments) { } +/* The command-line argument parsing function for argp */ static error_t parse_opt (int key, char *arg, struct argp_state *state) { struct arguments *arguments = state->input; @@ -397,51 +403,57 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) return 0; } +/* argp structure */ static struct argp argp = { options, parse_opt, args_doc, doc }; -/* TODO2 refactor those 3 */ -void info(const char *err, ...) +/* Emit a message with a verbosity level, and optionally terminate. + * Verbosity is >=1 for debug, 0 for info, -n for error n-1 */ +void message(int verbosity, const char *err, va_list ap) { - va_list ap; - va_start(ap, err); - if (args.verbosity >= 0) + if (args.verbosity >= verbosity) { - fprintf(stderr, "info: "); + fprintf(stderr, "%s: ", verbosity > 0 ? "debug" : + verbosity >= 0 ? "info" : "error"); vfprintf(stderr, err, ap); fprintf(stderr, "\n"); } + if (verbosity < 0) + exit(1-verbosity); +} + +void info(const char *err, ...) +{ + va_list ap; + va_start(ap, err); + return message(0, err, ap); } void debug(const char *err, ...) { va_list ap; va_start(ap, err); - if (args.verbosity > 0) - { - fprintf(stderr, "debug: "); - vfprintf(stderr, err, ap); - fprintf(stderr, "\n"); - } + return message(1, err, ap); } void die(int val, const char *err, ...) { - /* TODO avoid dying, rather retry with info messages */ va_list ap; va_start(ap, err); - fprintf(stderr, "error: "); - vfprintf(stderr, err, ap); - fprintf(stderr, "\n"); - exit(val); + return message(-val - 1, err, ap); } + void debug_args() { - debug("n_channels = %d\ninterval = %d\nnick = %s\nserver = %s\nport = %d\npw = %s\n", - args.n_channels, args.interval, args.nick, args.server, args.port, args.password); + debug("n_channels = %d\ninterval = %d\nnick = %s\nserver = %s\nport = %d\n" + "pw = %s\n", args.n_channels, args.interval, args.nick, args.server, + args.port, args.password); } + +/* Our main chan is either the first CLI chan or our own chan if there are no + * CLI chans */ char* first_chan() { if (args.n_channels) @@ -449,7 +461,9 @@ char* first_chan() else return args.nick; } -int join_channel(irc_session_t *s, char* chan) { +/* Join a channel, optionally suffixed with :PASSWORD */ +int join_channel(irc_session_t *s, char* chan) +{ char *password = chan; int pos = strlen(password); int ret; @@ -468,9 +482,11 @@ int join_channel(irc_session_t *s, char* chan) { return ret; } +/* Send a message or do a command */ int do_cmd_msg(irc_session_t *s, char* chan, char* line) { int rsl = -1; + int is_local = 1; /* TODOponey ask the server to notify, if possible */ /* TODO2 find a way to get real host name (part after !) */ @@ -479,20 +495,20 @@ int do_cmd_msg(irc_session_t *s, char* chan, char* line) join_channel (s, chan); - // TODO also other commands (/kick, /topic...) /* TODO2 provide a means to escape '/' */ if (line[0] == '/' && args.command_to_event) { - if ( strstr (line + 1, "nick ") == line + 1 ) + if (strstr(line + 1, "nick ") == line + 1 ) { + is_local = 0; rsl = irc_cmd_nick (s, line+6); - } else if (strstr (line + 1, "part") == line + 1) { + } else if (strstr(line + 1, "part") == line + 1) { if (!line[5] || line[5] == '\n') { rsl = irc_cmd_part(s, chan); } else if (line[5] == ' ') { rsl = irc_cmd_part(s, line + 6); } - } else if (strstr (line + 1, "join") == line + 1) { + } else if (strstr(line + 1, "join") == line + 1) { if (!line[5] || line[5] == '\n') { debug("join %s", chan); @@ -501,18 +517,23 @@ int do_cmd_msg(irc_session_t *s, char* chan, char* line) debug("join %s", line+6); rsl = join_channel(s, line + 6); } - } else if (strstr (line + 1, "topic ") == line + 1) { + } else if (strstr(line + 1, "topic ") == line + 1) { debug("topic %s", line+6); rsl = irc_cmd_topic(s, chan, line + 6); - } else if (strstr (line + 1, "quit") == line + 1) { + } else if (strstr(line + 1, "quit") == line + 1) { /* TODO notify main thread that it is over */ + is_local = 0; if (!line[5] || line[5] == '\n') { rsl = irc_cmd_quit(s, NULL); } else if (line[5] == ' ') { rsl = irc_cmd_quit(s, line + 6); } - } // TODO: invite, names, list, topic, chanmode, kick, usermode, whois, raw + } else if (line[1] == ' ') { + // escaped message "/ /message" + rsl = irc_cmd_msg (s, chan, line + 2); + } + // TODO: invite, names, list, topic, chanmode, kick, usermode, whois, raw... } else { rsl = irc_cmd_msg (s, chan, line); } @@ -522,14 +543,14 @@ int do_cmd_msg(irc_session_t *s, char* chan, char* line) if (args.own) { - /* TODO join, quit, etc. take place in a special '[]' channel */ - printf("[%s] <%s> %s", chan, args.nick, line); + printf("[%s] <%s> %s", is_local ? chan : "", args.nick, line); fflush(stdout); } return 0; } +// Send a message or do a command, possibly to multiple chans int cmd_msg_chan(irc_session_t *s, char *target, char* line) { int i = 0; @@ -548,8 +569,8 @@ int cmd_msg_chan(irc_session_t *s, char *target, char* line) cont = 0; tmp = target[i]; target[i] = 0; - /* TODO manage errors correctly! */ - rsl = do_cmd_msg(s, one_target, line); + // return value is the max of seen return values, ie. some kind of "or" + rsl = max(rsl, do_cmd_msg(s, one_target, line)); target[i] = tmp; one_target = target + i + 1; } @@ -559,9 +580,11 @@ int cmd_msg_chan(irc_session_t *s, char *target, char* line) return rsl; } +// Handle a command request which might not specify a destination int cmd_msg(irc_session_t *s, char* target, char* line) { int i = 0; + int ret; char *msg[2*MAX_LEN+2]; /* Manage the fact that target may be "" */ @@ -596,9 +619,9 @@ int cmd_msg(irc_session_t *s, char* target, char* line) //TODO2: ugly, could have generated the comma-separated string //like for last_chans_out! for (i = 0; i < args.n_channels; i++) - cmd_msg_chan(s, args.channels[i], line); + ret = max(ret, cmd_msg_chan(s, args.channels[i], line)); /* TODO return value */ - return 0; + return ret; default: return 42; // won't happen @@ -607,42 +630,34 @@ int cmd_msg(irc_session_t *s, char* target, char* line) return cmd_msg_chan(s, target, line); } } - -THREAD_FUNCTION(irc_listen) -{ - irc_session_t * sp = (irc_session_t *) arg; - // TODO, also do you need '#' or not? - irc_option_set( sp, LIBIRC_OPTION_SSL_NO_VERIFY ); - irc_run(sp); - return 0; -} - -void dump_event (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) + +// Manage an event +void manage_event (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { int cnt = 0; int rsl; // TODO use if (atoi(event) == LIBIRC_RFC_ERR_ERRONEUSNICKNAME) { - /* bad nick */ if (args.force_nick) { - die(E_BADNAME, "Invalid nick!"); + die(E_BADNAME, "Invalid nick"); } else { // TODO does not work info("Invalid nick, reverting to \"irctk\""); strncpy(args.nick, "irctk", MAX_NICK_LEN-1); rsl = irc_cmd_nick (session, "irctk"); } - } else if (atoi(event) == LIBIRC_RFC_ERR_NICKCOLLISION || atoi(event) == LIBIRC_RFC_ERR_NICKNAMEINUSE) { - /* bad nick */ + } else if (atoi(event) == LIBIRC_RFC_ERR_NICKCOLLISION + || atoi(event) == LIBIRC_RFC_ERR_NICKNAMEINUSE) { if (args.force_nick) { - die(E_BADNAME, "Nickname already in use!"); + die(E_BADNAME, "Nickname already in use"); } else { int len = strlen(args.nick); if (len >= MAX_NICK_LEN-1) { - die(E_BADNAME, "No available nickname found."); + die(E_BADNAME, "No available nickname found"); } else { args.nick[len] = '_'; args.nick[len+1] = 0; @@ -661,7 +676,9 @@ void dump_event (irc_session_t * session, const char * event, const char * origi debug("Event \"%s\", origin: \"%s\", params: %d", event, origin ? origin : "NULL", cnt); } -void event_nick (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a nick event +void event_nick (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { if (args.event_to == COMMAND) printf("[] <%s> /nick %s\n", origin, params[0]); @@ -669,10 +686,12 @@ void event_nick (irc_session_t * session, const char * event, const char * origi printf("[] -!- %s is now known as %s\n", origin, params[0]); } -void event_join (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a join event +void event_join (irc_session_t * session, const char *event, const char *origin, + const char ** params, unsigned int count) { // TODO check if it is us! - dump_event (session, event, origin, params, count); + manage_event(session, event, origin, params, count); irc_cmd_user_mode (session, "+i"); if (args.event_to == COMMAND) @@ -683,23 +702,31 @@ void event_join (irc_session_t * session, const char * event, const char * origi debug("Joined!"); } -void event_part (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a part event +void event_part (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { if (args.event_to == COMMAND) printf("[%s] <%s> /part %s\n", params[0], origin, params[1]); if (args.event_to == MESSAGE) - printf("[%s] -!- %s has left %s (%s)\n", params[0], origin, params[0], params[1]); + printf("[%s] -!- %s has left %s (%s)\n", params[0], origin, params[0], + params[1]); } -void event_topic (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a topic event +void event_topic (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { if (args.event_to == COMMAND) printf("[%s] <%s> /topic %s\n", params[0], origin, params[1]); if (args.event_to == MESSAGE) - printf("[%s] -!- %s changed the topic of %s to %s\n", params[0], origin, params[0], params[1]); + printf("[%s] -!- %s changed the topic of %s to %s\n", params[0], origin, + params[0], params[1]); } -void event_quit (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a quit event +void event_quit (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { if (args.event_to == COMMAND) printf("[] <%s> /quit %s\n", origin, params[0]); @@ -707,7 +734,9 @@ void event_quit (irc_session_t * session, const char * event, const char * origi printf("[] -!- %s has quit (%s)\n", origin, params[1]); } -void event_ctcp_action (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle an action +void event_ctcp_action (irc_session_t *session, const char *event, + const char *origin, const char **params, unsigned int count) { //TODO where is the channel?! if (args.event_to == COMMAND) @@ -716,25 +745,30 @@ void event_ctcp_action (irc_session_t * session, const char * event, const char printf("[%s] *** %s %s\n", params[0], origin, params[1]); } -void event_kick (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a kick event +void event_kick (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { // TODO check for optional params if (args.event_to == COMMAND) printf("[%s] <%s> /kick %s %s\n", params[0], origin, params[1], params[2]); if (args.event_to == MESSAGE) - printf("[%s] -!- %s was kicked from %s by %s (%s)\n", params[0], params[1], params[0], origin, params[2]); + printf("[%s] -!- %s was kicked from %s by %s (%s)\n", params[0], params[1], + params[0], origin, params[2]); } - -void event_connect (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a connect event +void event_connect (irc_session_t *session, const char *event, const char *origin, + const char **params, unsigned int count) { irc_ctx_t * ctx = (irc_ctx_t *) irc_get_ctx (session); - dump_event (session, event, origin, params, count); + int i; + + manage_event(session, event, origin, params, count); ctx->ready = 1; debug("Connected!"); - int i; for (i = 0; i < ctx->n_channels; i++) { debug("Attempt to join %s", ctx->channels[i]); @@ -742,14 +776,14 @@ void event_connect (irc_session_t * session, const char * event, const char * or } } - -void event_channel (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a channel event +void event_channel (irc_session_t *session, const char *event, + const char *origin, const char **params, unsigned int count) { - int i=0; - int ok = 1, ok2; + int i = 0, ok = 1, ok2; const char* pruned; - if ( count != 2 ) - return; + + if (count != 2) return; if (args.filter) if (strcmp(params[0], args.nick)) @@ -760,10 +794,8 @@ void event_channel (irc_session_t * session, const char * event, const char * or /* TODO test if ',' or ':' to avoid prefix nicks */ } - if (ok) - { - if (!args.prune) - { + if (ok) { + if (!args.prune) { printf ("[%s] <", params[0]); if (!origin) printf("someone"); @@ -778,10 +810,8 @@ void event_channel (irc_session_t * session, const char * event, const char * or } else { pruned = params[1]; ok2 = 1; - while(pruned[0] != ':') - { - if (!pruned[0] || pruned[0] == ' ') - { + while(pruned[0] != ':') { + if (!pruned[0] || pruned[0] == ' ') { ok2 = 0; break; } @@ -797,33 +827,36 @@ void event_channel (irc_session_t * session, const char * event, const char * or printf("%s\n", pruned); } - if (strcmp(params[0], args.nick)) // if addressed in our private chan, reply on the sender's priv chan + // if addressed in our private chan, reply on the sender's priv chan + if (strcmp(params[0], args.nick)) strncpy(args.last_chan_in, params[0], MAX_LEN-1); else strncpy(args.last_chan_in, origin?origin:"someone", MAX_LEN-1); strncpy(args.last_nick_in, origin?origin:"someone", MAX_LEN-1); fflush(stdout); } - } - -void event_privmsg (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +// Handle a privmsg +void event_privmsg (irc_session_t *session, const char *event, + const char *origin, const char **params, unsigned int count) { - dump_event (session, event, origin, params, count); + manage_event(session, event, origin, params, count); /* TODO sure? */ event_channel (session, event, origin, params, count); } - -void event_numeric (irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count) +// Handle a numeric event +void event_numeric (irc_session_t *session, unsigned int event, + const char *origin, const char **params, unsigned int count) { char buf[24]; sprintf (buf, "%d", event); - dump_event (session, buf, origin, params, count); + manage_event(session, buf, origin, params, count); } + irc_session_t* do_connect() { irc_session_t *s; @@ -835,17 +868,17 @@ irc_session_t* do_connect() callbacks.event_nick = event_nick; callbacks.event_quit = event_quit; callbacks.event_part = event_part; - callbacks.event_mode = dump_event; + callbacks.event_mode = manage_event; callbacks.event_topic = event_topic; callbacks.event_kick = event_kick; callbacks.event_channel = event_channel; callbacks.event_privmsg = event_privmsg; - callbacks.event_notice = dump_event; - callbacks.event_invite = dump_event; - callbacks.event_umode = dump_event; - callbacks.event_ctcp_rep = dump_event; + callbacks.event_notice = manage_event; + callbacks.event_invite = manage_event; + callbacks.event_umode = manage_event; + callbacks.event_ctcp_rep = manage_event; callbacks.event_ctcp_action = event_ctcp_action; - callbacks.event_unknown = dump_event; + callbacks.event_unknown = manage_event; callbacks.event_numeric = event_numeric; //TODO callbacks.event_dcc_chat_req = irc_event_dcc_chat; @@ -856,7 +889,6 @@ irc_session_t* do_connect() if (!s) die(E_SESSION, "Could not create session"); - if (!args.with_host) irc_option_set (s, LIBIRC_OPTION_STRIPNICKS); irc_set_ctx (s, &args); @@ -865,17 +897,28 @@ irc_session_t* do_connect() debug("Connecting to %s port %d with nick %s...", args.server, args.port, args.nick); - - if (irc_connect (s, args.server, args.port, args.password, args.nick, args.username, args.realname)) - die(E_CONNECT, "Could not connect: %s\n", irc_strerror (irc_errno(s))); + if (irc_connect(s, args.server, args.port, args.password, args.nick, + args.username, args.realname)) + die(E_CONNECT, "Could not connect: %s", irc_strerror (irc_errno(s))); return s; } +// The IRC thread entry point +static void* irc_listen (void *arg) +{ + irc_session_t * sp = (irc_session_t *) arg; + // TODO, also do you need '#' or not? + irc_option_set(sp, LIBIRC_OPTION_SSL_NO_VERIFY); + irc_run(sp); + return 0; +} + +// Start the IRC thread and monitor stdin int start (int max_wait) { irc_session_t * s; - thread_id_t tid; + pthread_t tid; char *line = NULL; int res, size=100; struct timeval tp1, tp2; @@ -889,8 +932,8 @@ int start (int max_wait) debug("Starting thread..."); - if (CREATE_THREAD (&tid, irc_listen, s)) - die(E_THREAD, "CREATE_THREAD failed: %s", strerror(errno)); // TODO spurious \n + if (pthread_create (&tid, 0, irc_listen, (void*) s) != 0) + die(E_THREAD, "Could not create thread: %s", strerror(errno)); debug("Thread started!"); @@ -901,12 +944,10 @@ int start (int max_wait) gettimeofday(&tp1, NULL); - if (!args.ready) - { + if (!args.ready) { waiting = 0; debug("Waiting for the connection to be ready..."); - while (waiting < max_wait && !args.ready) - { + while (waiting < max_wait && !args.ready) { debug("waiting (%d)", waiting); sleep(1); waiting++; @@ -923,11 +964,9 @@ int start (int max_wait) } } - while ( (res = getline((char**) &line, (size_t*) &size, stdin)) != -1 ) - { + while ((res = getline((char**) &line, (size_t*) &size, stdin)) != -1) { debug("startloop : got %s, waiting", line); - if (!args.ready) - { + if (!args.ready) { info("Connection lost, reconnecting..."); return start(max_wait); } @@ -954,7 +993,7 @@ int start (int max_wait) while (line[i] != ']' && line[i]) i++; if (!line[i]) - die(E_BADLINE, "Malformed address prefix."); + die(E_BADLINE, "Malformed address prefix"); msg = line + i; if (msg[0] == ' ') msg++; @@ -985,13 +1024,17 @@ int start (int max_wait) int main (int argc, char **argv) { + // initialize the default option values initialize_args(); + // parse command line arguments to set option values argp_parse (&argp, argc, argv, 0, 0, &args); + // initialize last_chan_in and last_chan_out strncpy(args.last_chan_in, first_chan(), MAX_LEN-1); strncpy(args.last_chans_out, first_chan(), MAX_LEN-1); + // start trying to connet with the initial retry interval return start(args.retry_after); }