X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;ds=sidebyside;f=src%2FIRCClient.c;fp=src%2FIRCClient.c;h=7720602acac787f1658762dea9b2b116cb702017;hb=9d6d43ef738ec4ea700d1ab62bae892d69bede8a;hp=eee86b1f8162d78e6f4fc81cc0416dfed58b17ef;hpb=2d411320c5f1fbfff3862f5fa8c1f1e6b7b6f9d0;p=TransparentIRC.git diff --git a/src/IRCClient.c b/src/IRCClient.c index eee86b1..7720602 100644 --- a/src/IRCClient.c +++ b/src/IRCClient.c @@ -59,6 +59,14 @@ void ircclient_close(struct IRCClient *client) { 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); } @@ -101,6 +109,160 @@ static struct IRCUser ircclient_parse_user(char *from) { 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]; @@ -125,6 +287,10 @@ static void ircclient_recv(struct IRCClient *client, char *line) { 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") || @@ -149,24 +315,33 @@ static void ircclient_recv(struct IRCClient *client, char *line) { } } 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 @@ -175,15 +350,18 @@ static void ircclient_recv(struct IRCClient *client, char *line) { 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 @@ -192,8 +370,10 @@ static void ircclient_recv(struct IRCClient *client, char *line) { 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")) { @@ -202,6 +382,66 @@ static void ircclient_recv(struct IRCClient *client, char *line) { 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) @@ -240,12 +480,28 @@ void ircclient_recover_session(struct UserSession *session) { 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); + } } }