irctk

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

commit 1f16695574ac466cdb0f22a498168980e101685f
parent b679437ed414082935d9a88c2df7a5e1761793d5
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Sun,  1 Jul 2012 19:01:36 +0200

first version, seems to work

Diffstat:
TODO | 6+++++-
irctk.c | 326++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 295 insertions(+), 37 deletions(-)

diff --git a/TODO b/TODO @@ -1,4 +1,3 @@ -handle nick following ssl support password support exit when killed, advertising the signal @@ -7,3 +6,8 @@ si on est full unbuffered, attendre qu'un write sur stdout ait fini de bloquer avant de considérer que le write a été vu pour last_nick et autres sinon, vérifier que le delimit par lignes vides est opérationnel http://www.iagora.com/~espel/sirc/sirc.html + +tracking: +- use user list given when joining +- handle multiple addresses +- use output_nick in channel names diff --git a/irctk.c b/irctk.c @@ -26,6 +26,7 @@ enum default_destinations {DEFAULT_FIRST, DEFAULT_LAST_IN, DEFAULT_LAST_OUT, DEFAULT_ALL}; enum event_tos {NOTHING, COMMAND, MESSAGE}; +enum track {NO, YES, UNIQUE}; // TODO get rid of that #define MAX_LEN 4096 @@ -34,6 +35,8 @@ enum event_tos {NOTHING, COMMAND, MESSAGE}; #define INITIAL_CHAN_ALLOC 2 +#define ERR_BADCHANNAME 479 // hybrid + #define max(a, b) ((b) > (a) ? (b) : (a)) const char *argp_program_version = "irctk 0.1"; @@ -47,7 +50,7 @@ static char doc[] = static char args_doc[] = "[NICK[:PASSWORD]@]SERVER[:PORT] [CHANNEL[:PASSWORD]]..."; enum arg_types {STANDARD, MISC, COM_MODE, CHANNEL_SELECTION, BOT_OPTIONS, - DISPLAY_SELECTION, PARSING, NAME}; + TRACKING, DISPLAY_SELECTION, PARSING, NAME}; /* The options we understand. */ static struct argp_option options[] = { @@ -110,10 +113,20 @@ static struct argp_option options[] = { " 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)", + "Only give the message body without the channel, nick or address (implies -f, useful" + " with -r)", BOT_OPTIONS }, + {"no-track-renames", 'T', 0, 0, + "When sending to a user, do not track him if he changes nick (default)", + TRACKING }, // TODO + {"track-renames", 't', 0, 0, + "When sending to a user, track him if he changes nick", + TRACKING }, // TODO + {"unique-names", 'u', 0, 0, + "Track nick changes and expose unique names for users", + TRACKING }, // TODO + /* TODO test, and make compatible with --own */ {"with-host", 'w', 0, 0, "Keep the host info in pseudos", @@ -177,6 +190,7 @@ struct arguments int own; enum event_tos event_to; + enum track track; char *username; int username_set; // to get nick as default username @@ -225,6 +239,7 @@ void initialize_args() args.own = 0; args.event_to = NOTHING; + args.track = NO; args.username_set = 0; args.username = args.nick; @@ -313,6 +328,15 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) arguments->filter = 1; arguments->prune = 1; break; + case 'T': + arguments->track = NO; + break; + case 't': + arguments->track = YES; + break; + case 'u': + arguments->track = UNIQUE; + break; case 'a': arguments->default_destination = DEFAULT_LAST_IN; break; @@ -477,6 +501,7 @@ typedef struct fifo_set { fifo_ctrl ctrl; fifo *fifos; char **chans; + char **surface_name; char *was_pushed; } fifo_set; @@ -495,6 +520,8 @@ void init_fifo_set (fifo_set *s) { assert(s->fifos); s->was_pushed = malloc(s->allocated * sizeof(char)); assert(s->was_pushed); + s->surface_name = malloc(s->allocated * sizeof(char*)); + assert(s->surface_name); init_fifo_ctrl(&(s->ctrl)); } @@ -511,6 +538,9 @@ void destroy_fifo_set (fifo_set *s) { free(s->chans[i]); free(s->chans); free(s->was_pushed); + for (i = 0; i < s->n ; i++) + free(s->surface_name[i]); + free(s->surface_name); destroy_fifo_ctrl(&(s->ctrl)); } @@ -603,14 +633,16 @@ void realloc_chans(fifo_set *s) { debug("realloc chans"); s->allocated *= 2; s->chans = realloc(s->chans, s->allocated*sizeof(char*)); - s->was_pushed = realloc(s->was_pushed, s->allocated*sizeof(char*)); - s->fifos = realloc(s->fifos, s->allocated*sizeof(fifo)); assert(s->chans); + s->was_pushed = realloc(s->was_pushed, s->allocated*sizeof(char*)); assert(s->was_pushed); + s->fifos = realloc(s->fifos, s->allocated*sizeof(fifo)); assert(s->fifos); + s->surface_name = realloc(s->surface_name, s->allocated*sizeof(char*)); + assert(s->surface_name); } -int push_chan(fifo_set *s, char *chan) { +int push_chan_alias(fifo_set *s, const char *chan, const char *alias) { if (s->n == s->allocated) realloc_chans(s); s->chans[s->n] = malloc(MAX_LEN * sizeof(chan)); @@ -618,28 +650,117 @@ int push_chan(fifo_set *s, char *chan) { strncpy(s->chans[s->n], chan, MAX_LEN-1); init_fifo(&s->fifos[s->n]); s->was_pushed[s->n] = 0; + s->surface_name[s->n] = malloc(MAX_LEN * sizeof(chan)); + assert(s->surface_name[s->n]); + strncpy(s->surface_name[s->n], alias, MAX_LEN-1); return (s->n)++; } -int index_of_chan(fifo_set *s, char *chan) { +int push_chan(fifo_set *s, const char *chan) { + return push_chan_alias(s, chan, chan); +} + +int scan_collection(fifo_set *s, const char **collection, const char *chan) { int i; debug("asking for ioc for <%s>", chan); for (i=0; i<s->n; i++) { - debug("compare to %d of %d value %s", i, s->n, s->chans[i]); - if (!strcmp(s->chans[i], chan)) { + debug("compare to %d of %d value %s surface %s", i, s->n, + s->chans[i], s->surface_name[i]); + if (!strcmp(collection[i], chan)) { debug("found ioc %d", i); return i; } } - debug("must push"); - return push_chan(s, chan); + return -1; +} + +int index_of_chan_get(fifo_set *s, const char *chan) { + return scan_collection(s, (const char **) s->chans, chan); +} + +int index_of_chan_surface(fifo_set *s, const char *chan) { + int result = scan_collection(s, (const char **) s->surface_name, chan); + if (result >= 0) { + return result; + } else { + return push_chan(s, chan); + } +} + +int index_of_chan(fifo_set *s, const char *chan) { + int result = index_of_chan_get(s, chan); + if (result >= 0) { + return result; + } else { + return push_chan(s, chan); + } +} + +int rename_user(fifo_set *s, const char *from, const char *to) { + int pos; + debug("rename %s -> %s", from, to); + pthread_mutex_lock(&s->ctrl.mutex); + pos = scan_collection(s, (const char **) s->surface_name, from); + assert(pos >= 0); + debug("found %s at pos %d", from, pos); + strncpy(s->surface_name[pos], to, MAX_NICK_LEN-1); + debug("set surface name to %s", to); + pthread_mutex_unlock(&s->ctrl.mutex); + return pos; } +int surface_nick_exists(fifo_set *s, const char *nick) { + int result = 0; + pthread_mutex_lock(&s->ctrl.mutex); + if (scan_collection(s, (const char **) s->surface_name, nick) >= 0) + result = 1; + pthread_mutex_unlock(&s->ctrl.mutex); + return result; +} + +int nick_exists(fifo_set *s, const char *nick) { + int result = 0; + if (scan_collection(s, (const char **) s->chans, nick) >= 0 || + scan_collection(s, (const char **) s->surface_name, nick) >= 0) + result = 1; + return result; +} + +int register_nick(fifo_set *s, const char *nick) { + int result; + pthread_mutex_lock(&s->ctrl.mutex); + // we assume that nick is not a current surface nick + if (scan_collection(s, (const char **) s->chans, nick) < 0) { + debug("No need to generate fresh name."); + result = push_chan(s, nick); + } else { + // nick is already taken, generate a fresh id + int suffix = 2; + char new[MAX_NICK_LEN], num[MAX_NICK_LEN]; + while (1) { + strncpy(new, nick, MAX_NICK_LEN/2); + sprintf(num, "%d", suffix++); + strncat(new, num, MAX_NICK_LEN/2-1); + debug("Trying to register under %s", new); + if (!nick_exists(s, new)) { + break; + } + } + debug("Register under %s %s", new, nick); + result = push_chan_alias(s, new, nick); + } + pthread_mutex_unlock(&s->ctrl.mutex); + return result; +} void push_one(fifo_set *s, char *fl, char *dest, char *l, int next_dep_fifoidx, int next_dep_lineidx, int *fifoidx, int *lineidx) { - int c = index_of_chan(s, dest); + int c; + if (args.track != UNIQUE) + c = index_of_chan_surface(s, dest); + else + c = index_of_chan(s, dest); fifo *f = &(s->fifos[c]); if (s->was_pushed[c]) return; // already pushed @@ -813,7 +934,7 @@ void pop_if_sendable(fifo_set *s, int c, int *n_result, action *result) { if (f->queue[f->hd].is_sendable && f->queue[f->hd].line) { result[*n_result].line = f->queue[f->hd].line; result[*n_result].full_line = f->queue[f->hd].full_line; - result[*n_result].destination = s->chans[c]; + result[*n_result].destination = s->surface_name[c]; (*n_result)++; f->hd++; f->hd %= LINE_BUFFER; @@ -901,6 +1022,34 @@ int join_channel(irc_session_t *s, char* chan) return ret; } +const char *input_nick(const char *nick) { + int c; + const char *result; + if (args.track == NO) + return nick; + pthread_mutex_lock(&fifos.ctrl.mutex); + c = scan_collection(&fifos, (const char **) fifos.surface_name, nick); + if (c < 0) + result = nick; // dest changed nick, but we're not supposed to track that + else + result = fifos.chans[c]; + pthread_mutex_unlock(&fifos.ctrl.mutex); + return result; +} + +const char *output_nick(const char *nick) { + int c; + char *result; + if (args.track != UNIQUE) + return nick; + pthread_mutex_lock(&fifos.ctrl.mutex); + c = scan_collection(&fifos, (const char **) fifos.surface_name, nick); + assert(c >= 0); + result = fifos.chans[c]; + pthread_mutex_unlock(&fifos.ctrl.mutex); + return result; +} + /* Send a message or do a command */ int do_cmd_msg(irc_session_t *s, char* chan, char* line) { @@ -955,8 +1104,41 @@ int do_cmd_msg(irc_session_t *s, char* chan, char* line) rsl = irc_cmd_msg (s, chan, line + 2); } // TODO: invite, names, list, topic, chanmode, kick, usermode, whois, raw... + // with correct user tracking! } else { - rsl = irc_cmd_msg (s, chan, line); + if (args.track != NO) { + char lline[MAX_LEN + MAX_NICK_LEN + 2]; + int seppos = -1; + int i; + for (i=0; i<strlen(line); i++) { + if (line[i] == ',' || line[i] == ':') { + seppos = i; + break; + } + if (line[i] == ' ') + break; + } + if (seppos == -1) { + rsl = irc_cmd_msg (s, chan, line); + } else { + char sep; + sep = line[seppos]; + line[seppos] = 0; + pthread_mutex_lock(&fifos.ctrl.mutex); + int c = scan_collection(&fifos, (const char **) fifos.chans, line); + if (c < 0) + strncpy(lline, line, MAX_NICK_LEN-1); + else + strncpy(lline, fifos.surface_name[c], MAX_NICK_LEN-1); + debug("CURRENT LINE %s", lline); + pthread_mutex_unlock(&fifos.ctrl.mutex); + line[seppos] = sep; + strncat(lline, line + seppos, MAX_LEN-1); + rsl = irc_cmd_msg (s, chan, lline); + } + } else { + rsl = irc_cmd_msg (s, chan, line); + } } /* TODO manage errors */ if (rsl) @@ -964,7 +1146,8 @@ int do_cmd_msg(irc_session_t *s, char* chan, char* line) if (args.own) { - printf("[%s] <%s> %s", is_local ? chan : "", args.nick, line); + printf("[%s] <%s> %s", is_local ? chan : "", output_nick(args.nick), + line); fflush(stdout); } @@ -977,6 +1160,38 @@ void cmd_msg(char *full_line, char *target, char *line) char *msg; debug("cmd_msg %s \"%s\" (%p), last_in %s", target, line, line, args.last_chan_in); + + if (args.track != NO) { + char lline[MAX_LEN + MAX_NICK_LEN + 2]; + int seppos = -1; + int i; + for (i=0; i<strlen(line); i++) { + if (line[i] == ',' || line[i] == ':') { + seppos = i; + break; + } + if (line[i] == ' ') + break; + } + if (seppos >= 0) { + char sep; + sep = line[seppos]; + line[seppos] = 0; + pthread_mutex_lock(&fifos.ctrl.mutex); + int c = scan_collection(&fifos, (const char **) fifos.surface_name, line); + if (c >= 0) + strncpy(lline, fifos.chans[c], MAX_NICK_LEN-1); + pthread_mutex_unlock(&fifos.ctrl.mutex); + line[seppos] = sep; + if (c >= 0) { + strncpy(lline, line + seppos, MAX_LEN-1); + debug("COPY TO LLINE"); + free(full_line); + line = lline; + full_line = lline; + } + } + } /* Manage the fact that target may be "" */ if (!target[0]) { @@ -993,7 +1208,12 @@ void cmd_msg(char *full_line, char *target, char *line) msg = malloc((2*MAX_LEN + 2)* sizeof(char)); assert(msg); msg[0] = 0; - strncat((char*) msg, args.last_nick_in, MAX_LEN-1); + if (args.track == YES) { + strncat((char*) msg, input_nick(args.last_nick_in), + MAX_LEN-1); + } else { + strncat((char*) msg, args.last_nick_in, MAX_LEN-1); + } strcat((char*) msg, ": "); strncat((char*) msg, line, MAX_LEN-1); free(full_line); @@ -1025,6 +1245,14 @@ void cmd_msg(char *full_line, char *target, char *line) push_fifo_set(&fifos, full_line, target, line); } } + +void saw_user(const char *nick) { + if (!surface_nick_exists(&fifos, nick)) { + // unknown user, register him + debug("Register this new user"); + register_nick(&fifos, nick); + } +} // Manage an event void manage_event (irc_session_t *session, const char *event, const char *origin, @@ -1032,6 +1260,8 @@ void manage_event (irc_session_t *session, const char *event, const char *origin { int rsl; // TODO use + saw_user(origin); + if (atoi(event) == LIBIRC_RFC_ERR_ERRONEUSNICKNAME) { if (args.force_nick) @@ -1055,6 +1285,8 @@ void manage_event (irc_session_t *session, const char *event, const char *origin } else { args.nick[len] = '_'; args.nick[len+1] = 0; + // no need to rename self because the surface_name of ourself points + // to args.nick rsl = irc_cmd_nick (session, args.nick); info("Requested nick is taken, try %s\n", args.nick, args.nick); } @@ -1065,6 +1297,8 @@ void manage_event (irc_session_t *session, const char *event, const char *origin info("Cannot join channel: bad password."); } else if (atoi(event) == LIBIRC_RFC_ERR_INVITEONLYCHAN) { info("Cannot join channel: channel is invite-only."); + } else if (atoi(event) == ERR_BADCHANNAME) { + info("Could not join channel."); } if (count == 2) { @@ -1081,10 +1315,13 @@ void manage_event (irc_session_t *session, const char *event, const char *origin void event_nick (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + debug("[] <%s> /nick %s\n", origin, params[0]); + saw_user(origin); + rename_user(&fifos, origin, params[0]); if (args.event_to == COMMAND) - printf("[] <%s> /nick %s\n", origin, params[0]); + printf("[] <%s> /nick %s\n", output_nick(origin), params[0]); if (args.event_to == MESSAGE) - printf("[] -!- %s is now known as %s\n", origin, params[0]); + printf("[] -!- %s is now known as %s\n", output_nick(origin), params[0]); } // Handle a join event @@ -1093,24 +1330,25 @@ void event_join (irc_session_t * session, const char *event, const char *origin, { // TODO check if it is us! manage_event(session, event, origin, params, count); - irc_cmd_user_mode (session, "+i"); + // TODO make it an option? irc_cmd_user_mode (session, "+i"); + debug("[%s] <%s> /join %s\n", params[0], origin, params[0]); + saw_user(params[0]); if (args.event_to == COMMAND) - printf("[%s] <%s> /join %s\n", params[0], origin, params[0]); + printf("[%s] <%s> /join %s\n", params[0], output_nick(origin), params[0]); if (args.event_to == MESSAGE) - printf("[%s] -!- %s has joined %s\n", params[0], origin, params[0]); - - debug("Joined!"); + printf("[%s] -!- %s has joined %s\n", params[0], output_nick(origin), params[0]); } // Handle a part event void event_part (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); if (args.event_to == COMMAND) - printf("[%s] <%s> /part %s\n", params[0], origin, params[1]); + printf("[%s] <%s> /part %s\n", params[0], output_nick(origin), params[1]); if (args.event_to == MESSAGE) - printf("[%s] -!- %s has left %s (%s)\n", params[0], origin, params[0], + printf("[%s] -!- %s has left %s (%s)\n", params[0], output_nick(origin), params[0], params[1]); } @@ -1118,10 +1356,11 @@ void event_part (irc_session_t *session, const char *event, const char *origin, void event_topic (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); if (args.event_to == COMMAND) - printf("[%s] <%s> /topic %s\n", params[0], origin, params[1]); + printf("[%s] <%s> /topic %s\n", params[0], output_nick(origin), params[1]); if (args.event_to == MESSAGE) - printf("[%s] -!- %s changed the topic of %s to %s\n", params[0], origin, + printf("[%s] -!- %s changed the topic of %s to %s\n", params[0], output_nick(origin), params[0], params[1]); } @@ -1129,39 +1368,44 @@ void event_topic (irc_session_t *session, const char *event, const char *origin, void event_quit (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); if (args.event_to == COMMAND) - printf("[] <%s> /quit %s\n", origin, params[0]); + printf("[] <%s> /quit %s\n", output_nick(origin), params[0]); if (args.event_to == MESSAGE) - printf("[] -!- %s has quit (%s)\n", origin, params[1]); + printf("[] -!- %s has quit (%s)\n", output_nick(origin), params[1]); } // Handle an action void event_ctcp_action (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); //TODO where is the channel?! if (args.event_to == COMMAND) - printf("[%s] <%s> /me %s\n", params[0], origin, params[1]); + printf("[%s] <%s> /me %s\n", params[0], output_nick(origin), params[1]); if (args.event_to == MESSAGE) - printf("[%s] *** %s %s\n", params[0], origin, params[1]); + printf("[%s] *** %s %s\n", params[0], output_nick(origin), params[1]); } // Handle a kick event void event_kick (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); + saw_user(params[1]); // TODO check for optional params if (args.event_to == COMMAND) - printf("[%s] <%s> /kick %s %s\n", params[0], origin, params[1], params[2]); + printf("[%s] <%s> /kick %s %s\n", params[0], output_nick(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], output_nick(params[1]), + params[0], output_nick(origin), params[2]); } // Handle a connect event void event_connect (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); irc_ctx_t * ctx = (irc_ctx_t *) irc_get_ctx (session); int i; @@ -1181,6 +1425,7 @@ void event_connect (irc_session_t *session, const char *event, const char *origi void event_channel (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); int i = 0, ok = 1, ok2; const char* pruned; @@ -1206,7 +1451,7 @@ void event_channel (irc_session_t *session, const char *event, else /* not robust, and useless since we use STRIPNICKS */ //while(origin[i++] != '!') putchar(origin[i-1]); - printf("%s", origin); + printf("%s", output_nick(origin)); printf("> "); if (params[1][0] == '/' && args.event_to == COMMAND) printf("/ "); // escape slashes @@ -1235,7 +1480,13 @@ void event_channel (irc_session_t *session, const char *event, 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); + // if UNIQUE, store the uid, else store the surface + if (args.track == UNIQUE) { + strncpy(args.last_nick_in, origin?output_nick(origin):"someone", + MAX_LEN-1); + } else { + strncpy(args.last_nick_in, origin?origin:"someone", MAX_LEN-1); + } fflush(stdout); } } @@ -1244,6 +1495,7 @@ void event_channel (irc_session_t *session, const char *event, void event_privmsg (irc_session_t *session, const char *event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); manage_event(session, event, origin, params, count); /* TODO sure? */ @@ -1254,6 +1506,7 @@ void event_privmsg (irc_session_t *session, const char *event, void event_numeric (irc_session_t *session, unsigned int event, const char *origin, const char **params, unsigned int count) { + saw_user(origin); char buf[24]; sprintf (buf, "%d", event); @@ -1521,6 +1774,7 @@ int main (int argc, char **argv) // initialize the fifo init_fifo_set(&fifos); + saw_user(args.nick); //return test_fifo_set();