free(recover_line->line);
free(recover_line);
}
+ if(client->network_prefixes)
+ free(client->network_prefixes);
+ if(client->network_prefixes_char)
+ free(client->network_prefixes_char);
+ if(client->network_chanmodes)
+ free(client->network_chanmodes);
+ if(client->network_chantypes)
+ free(client->network_chantypes);
free(client);
}
return user;
}
+static void ircclient_userlist_clear(struct IRCChannel *channel) {
+ struct IRCChannelMember *member, *next_member;
+ for(member = channel->userlist; member; member = next_member) {
+ next_member = member->next;
+ free(member->nick);
+ free(member);
+ }
+ channel->userlist = NULL;
+}
+
+static struct IRCChannelMember *ircclient_userlist_add(struct IRCChannel *channel, char *nick) {
+ struct IRCChannelMember *member;
+ for(member = channel->userlist; member; member = member->next) {
+ if(!stricmp(member->nick, nick)) return member; //prevent duplicates
+ }
+ member = calloc(1, sizeof(*member));
+ member->nick = strdup(nick);
+ member->next = channel->userlist;
+ if(channel->userlist)
+ channel->userlist->prev = member;
+ channel->userlist = member;
+ return member;
+}
+
+static void ircclient_userlist_del(struct IRCChannel *channel, char *nick) {
+ struct IRCChannelMember *member;
+ for(member = channel->userlist; member; member = member->next) {
+ if(!stricmp(member->nick, nick)) break;
+ }
+ if(!member) return;
+ if(member->prev)
+ member->prev->next = member->next;
+ else
+ channel->userlist = member->next;
+ if(member->next)
+ member->next->prev = member->prev;
+ free(member->nick);
+ free(member);
+}
+
+static void ircclient_raw005_parse(struct IRCClient *client, char **argv, int argc) {
+ int i;
+ char *name;
+ char *value;
+ for(i = 0; i < argc; i++) {
+ name = argv[i];
+ value = strchr(argv[i], '=');
+ if(value) {
+ *value = '\0';
+ value++;
+ }
+ if(!stricmp(name, "PREFIX")) {
+ //parse prefixes
+ if(*value != '(') continue;
+ value++;
+ char *prefixes_char = value;
+ value = strchr(value, ')');
+ if(!value) continue;
+ *value = '\0';
+ value++;
+ if(strlen(prefixes_char) == strlen(value)) {
+ client->network_prefixes = strdup(value);
+ client->network_prefixes_char = strdup(prefixes_char);
+ }
+ }
+ }
+}
+
+static void ircclient_chanmode_parse(struct IRCClient *client, struct IRCChannel *channel, char *mode, char **argv, int argc) {
+ int add = 1;
+ int arg = 0;
+ int i, j;
+ char *carg;
+ struct IRCChannelMember *member;
+ while(*mode) {
+ if(*mode == '+')
+ add = 1;
+ else if(*mode == '-')
+ add = 0;
+ else {
+ char *prefix_chars = client->network_prefixes_char ? client->network_prefixes_char : "ov";
+ i = 0;
+ while(prefix_chars[i]) {
+ if(prefix_chars[i] == *mode) {
+ carg = argv[arg++];
+ member = ircclient_userlist_add(channel, carg);
+ if(add)
+ member->modes |= (1 << i);
+ else
+ member->modes &= ~(1 << i);
+ goto ircclient_chanmode_parse_next;
+ }
+ i++;
+ }
+ char *chanmodes = client->network_chanmodes ? client->network_chanmodes : "b,k,l,";
+ i = 0;
+ j = 0;
+ while(chanmodes[i]) {
+ if(chanmodes[i] == ',') {
+ j++;
+ if(j == 2 && !add) break;
+ if(j == 3) break;
+ }
+ if(chanmodes[i] == *mode) {
+ arg++;
+ goto ircclient_chanmode_parse_next;
+ }
+ }
+ //default mode without parameter...
+ }
+ ircclient_chanmode_parse_next:
+ mode++;
+ }
+}
+
+static int ircclient_is_channel_prefix(struct IRCClient *client, char prefix) {
+ if(!client->network_chantypes) {
+ return (prefix == '#'); //default
+ }
+ int i = 0;
+ while(client->network_chantypes[i]) {
+ if(prefix == client->network_chantypes[i])
+ return 1;
+ i++;
+ }
+ return 0;
+}
+
+static int ircclient_is_chanuser_prefix(struct IRCClient *client, char prefix, int *prefix_id) {
+ char *prefixes = client->network_prefixes ? client->network_prefixes : "@+";
+ int i;
+ for(i = 0; prefixes[i]; i++) {
+ if(prefixes[i] == prefix) {
+ *prefix_id = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int ircclient_build_chanuser_prefix(struct IRCClient *client, struct IRCChannelMember *member, char *buffer, int highest_only) {
+ char *prefixes = client->network_prefixes ? client->network_prefixes : "@+";
+ int i;
+ int j = 0;
+ for(i = 0; prefixes[i]; i++) {
+ if(member->modes & (1 << i)) {
+ buffer[j++] = prefixes[i];
+ if(highest_only) break;
+ }
+ }
+ buffer[j] = '\0';
+ return j;
+}
+
static void ircclient_recv(struct IRCClient *client, char *line) {
struct UserSession *session = client->session;
char *argv[MAXNUMPARAMS];
session->nick = strdup(argv[2]);
client->fully_connected = 1;
}
+ if(!stricmp(argv[1], "005")) {
+ //parse 005 raw
+ ircclient_raw005_parse(client, argv+3, argc-3);
+ }
if(!stricmp(argv[1], "001") ||
!stricmp(argv[1], "002") ||
!stricmp(argv[1], "003") ||
}
} else if(!stricmp(argv[1], "JOIN")) {
struct IRCUser from = ircclient_parse_user(argv[0]);
+ struct IRCChannel *channel;
if(!stricmp(from.nick, session->nick)) {
- struct IRCChannel *lchannel = NULL, *channel = malloc(sizeof(*channel));
+ struct IRCChannel *lchannel = NULL;
+ channel = calloc(1, sizeof(*channel));
if(client->channel)
for(lchannel = client->channel; lchannel->next; lchannel = lchannel->next) {}
channel->name = strdup(argv[2]);
- channel->next = NULL;
channel->prev = lchannel;
if(lchannel)
lchannel->next = channel;
else
client->channel = channel;
+ } else {
+ for(channel = client->channel; channel; channel = channel->next) {
+ if(!stricmp(channel->name, argv[2])) {
+ ircclient_userlist_add(channel, from.nick);
+ break;
+ }
+ }
}
} else if(!stricmp(argv[1], "PART")) {
struct IRCUser from = ircclient_parse_user(argv[0]);
- if(!stricmp(from.nick, session->nick)) {
- struct IRCChannel *channel;
- for(channel = client->channel; channel; channel = channel->next) {
- if(!stricmp(channel->name, argv[2])) {
+ struct IRCChannel *channel;
+ for(channel = client->channel; channel; channel = channel->next) {
+ if(!stricmp(channel->name, argv[2])) {
+ if(!stricmp(from.nick, session->nick)) {
+ ircclient_userlist_clear(channel);
if(channel->prev)
channel->prev->next = channel->next;
else
channel->next->prev = channel->prev;
free(channel->name);
free(channel);
- break;
+ } else {
+ ircclient_userlist_del(channel, from.nick);
}
+ break;
}
}
} else if(!stricmp(argv[1], "KICK")) {
- if(!stricmp(argv[3], session->nick)) {
- struct IRCChannel *channel;
- for(channel = client->channel; channel; channel = channel->next) {
- if(!stricmp(channel->name, argv[2])) {
+ struct IRCChannel *channel;
+ for(channel = client->channel; channel; channel = channel->next) {
+ if(!stricmp(channel->name, argv[2])) {
+ if(!stricmp(argv[3], session->nick)) {
+ ircclient_userlist_clear(channel);
if(channel->prev)
channel->prev->next = channel->next;
else
channel->next->prev = channel->prev;
free(channel->name);
free(channel);
- break;
+ } else {
+ ircclient_userlist_del(channel, argv[3]);
}
+ break;
}
}
} else if(!stricmp(argv[1], "NICK")) {
free(session->nick);
session->nick = strdup(argv[2]);
}
+ struct IRCChannel *channel;
+ struct IRCChannelMember *member;
+ for(channel = client->channel; channel; channel = channel->next) {
+ for(member = channel->userlist; member; member = member->next) {
+ if(!stricmp(member->nick, from.nick)) {
+ free(member->nick);
+ member->nick = strdup(argv[2]);
+ break;
+ }
+ }
+ }
+ } else if(!stricmp(argv[1], "MODE")) {
+ if(!stricmp(argv[2], session->nick)) {
+ //user mode
+ } else if(ircclient_is_channel_prefix(client, argv[2][0])) {
+ struct IRCChannel *channel;
+ struct IRCChannelMember *member;
+ for(channel = client->channel; channel; channel = channel->next) {
+ for(member = channel->userlist; member; member = member->next) {
+ ircclient_chanmode_parse(client, channel, argv[3], argv+4, argc-4);
+ }
+ }
+ }
+ } else if(!stricmp(argv[1], "353")) {
+ struct IRCChannel *channel;
+ struct IRCChannelMember *member;
+ for(channel = client->channel; channel; channel = channel->next) {
+ if(!stricmp(channel->name, argv[4])) {
+ if(!channel->synchronizing_userlist) {
+ channel->synchronizing_userlist = 1;
+ ircclient_userlist_clear(channel);
+ }
+ char *a = argv[5];
+ char *b, *c;
+ do {
+ b = strchr(a, ' ');
+ if(b) *b = '\0';
+ if((c = strchr(a, '!'))) {
+ *c = '\0';
+ }
+ int prefix_id;
+ int member_modes = 0;
+ while(ircclient_is_chanuser_prefix(client, *a, &prefix_id)) {
+ member_modes |= (1 << prefix_id);
+ a++;
+ }
+ member = ircclient_userlist_add(channel, a);
+ member->modes = member_modes;
+ if(b) a = b+1;
+ } while(b);
+ break;
+ }
+ }
+ } else if(!stricmp(argv[1], "366")) {
+ struct IRCChannel *channel;
+ for(channel = client->channel; channel; channel = channel->next) {
+ if(!stricmp(channel->name, argv[3])) {
+ channel->synchronizing_userlist = 0;
+ }
+ }
}
}
if(pass_to_client)
usersession_client_raw(session, recover_line->line);
}
struct IRCChannel *channel;
+ struct IRCChannelMember *member;
char raw[LINELEN];
+ int prefixlen = strlen((client->network_prefixes ? client->network_prefixes : "@+"));
+ char prefixbuf[prefixlen+1];
for(channel = client->channel; channel; channel = channel->next) {
sprintf(raw, ":%s!%s@TransparentIRC.session.recover JOIN %s", client->session->nick, client->session->username, channel->name);
usersession_client_raw(session, raw);
-
- sprintf(raw, "NAMES %s", channel->name);
- ircclient_send(client, raw);
+ //replay /NAMES
+ int headerlen = sprintf(raw, ":TransparentIRC.session.recover 353 %s = %s :", client->session->nick, channel->name);
+ int first_element = 1;
+ for(member = channel->userlist; member; member = member->next) {
+ if(headerlen + strlen(member->nick) + prefixlen + 1 >= LINELEN-2) {
+ usersession_client_raw(session, raw);
+ headerlen = sprintf(raw, ":TransparentIRC.session.recover 353 %s = %s :", client->session->nick, channel->name);
+ first_element = 1;
+ }
+ ircclient_build_chanuser_prefix(client, member, prefixbuf, 1);
+ headerlen += sprintf(raw+headerlen, "%s%s%s", (first_element ? "" : " "), prefixbuf, member->nick);
+ first_element = 0;
+ }
+ if(!first_element) {
+ usersession_client_raw(session, raw);
+ }
}
}