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();