1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <string.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22
23 #include "hexchat.h"
24 #include "hexchatc.h"
25 #include "modes.h"
26 #include "server.h"
27 #include "text.h"
28 #include "fe.h"
29 #include "util.h"
30 #include "inbound.h"
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
34
35 #include <glib/gprintf.h>
36
37 typedef struct
38 {
39 server *serv;
40 char *op;
41 char *deop;
42 char *voice;
43 char *devoice;
44 } mode_run;
45
46 static int is_prefix_char (server * serv, char c);
47 static void record_chan_mode (session *sess, char sign, char mode, char *arg);
48 static char *mode_cat (char *str, char *addition);
49 static void handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
50 char *chan, char *arg, int quiet, int is_324,
51 const message_tags_data *tags_data);
52 static int mode_has_arg (server *serv, char sign, char mode);
53 static void mode_print_grouped (session *sess, char *nick, mode_run *mr,
54 const message_tags_data *tags_data);
55 static int mode_chanmode_type (server * serv, char mode);
56
57
58 /* word[] - list of nicks.
59 wpos - index into word[]. Where nicks really start.
60 end - index into word[]. Last entry plus one.
61 sign - a char, e.g. '+' or '-'
62 mode - a mode, e.g. 'o' or 'v' */
63 void
send_channel_modes(session * sess,char * tbuf,char * word[],int wpos,int end,char sign,char mode,int modes_per_line)64 send_channel_modes (session *sess, char *tbuf, char *word[], int wpos,
65 int end, char sign, char mode, int modes_per_line)
66 {
67 int usable_modes, orig_len, len, wlen, i, max;
68 server *serv = sess->server;
69
70 /* sanity check. IRC RFC says three per line but some servers may support less. */
71 if (serv->modes_per_line < 1)
72 serv->modes_per_line = 3;
73 if (modes_per_line < 1)
74 modes_per_line = serv->modes_per_line;
75
76 /* RFC max, minus length of "MODE %s " and "\r\n" and 1 +/- sign */
77 /* 512 - 6 - 2 - 1 - strlen(chan) */
78 max = 503 - strlen (sess->channel);
79
80 while (wpos < end)
81 {
82 tbuf[0] = '\0';
83 orig_len = len = 0;
84
85 /* we'll need this many modechars too */
86 len += modes_per_line;
87
88 /* how many can we fit? */
89 for (i = 0; i < modes_per_line; i++)
90 {
91 /* no more nicks left? */
92 if (wpos + i >= end)
93 break;
94 wlen = strlen (word[wpos + i]) + 1;
95 if (wlen + len > max)
96 break;
97 len += wlen; /* length of our whole string so far */
98 }
99 if (i < 1)
100 return;
101 usable_modes = i; /* this is how many we'll send on this line */
102
103 /* add the +/-modemodemodemode */
104 len = orig_len;
105 tbuf[len] = sign;
106 len++;
107 for (i = 0; i < usable_modes; i++)
108 {
109 tbuf[len] = mode;
110 len++;
111 }
112 tbuf[len] = 0; /* null terminate for the strcat() to work */
113
114 /* add all the nicknames */
115 for (i = 0; i < usable_modes; i++)
116 {
117 strcat (tbuf, " ");
118 strcat (tbuf, word[wpos + i]);
119 }
120 serv->p_mode (serv, sess->channel, tbuf);
121
122 wpos += usable_modes;
123 }
124 }
125
126 /* does 'chan' have a valid prefix? e.g. # or & */
127
128 int
is_channel(server * serv,char * chan)129 is_channel (server * serv, char *chan)
130 {
131 if (strchr (serv->chantypes, chan[0]))
132 return 1;
133 return 0;
134 }
135
136 /* is the given char a valid nick mode char? e.g. @ or + */
137
138 static int
is_prefix_char(server * serv,char c)139 is_prefix_char (server * serv, char c)
140 {
141 int pos = 0;
142 char *np = serv->nick_prefixes;
143
144 while (np[0])
145 {
146 if (np[0] == c)
147 return pos;
148 pos++;
149 np++;
150 }
151
152 if (serv->bad_prefix)
153 {
154 if (strchr (serv->bad_nick_prefixes, c))
155 /* valid prefix char, but mode unknown */
156 return -2;
157 }
158
159 return -1;
160 }
161
162 /* returns '@' for ops etc... */
163
164 char
get_nick_prefix(server * serv,unsigned int access)165 get_nick_prefix (server * serv, unsigned int access)
166 {
167 int pos;
168 char c;
169
170 for (pos = 0; pos < USERACCESS_SIZE; pos++)
171 {
172 c = serv->nick_prefixes[pos];
173 if (c == 0)
174 break;
175 if (access & (1 << pos))
176 return c;
177 }
178
179 return 0;
180 }
181
182 /* returns the access bitfield for a nickname. E.g.
183 @nick would return 000010 in binary
184 %nick would return 000100 in binary
185 +nick would return 001000 in binary */
186
187 unsigned int
nick_access(server * serv,char * nick,int * modechars)188 nick_access (server * serv, char *nick, int *modechars)
189 {
190 int i;
191 unsigned int access = 0;
192 char *orig = nick;
193
194 while (*nick)
195 {
196 i = is_prefix_char (serv, *nick);
197 if (i == -1)
198 break;
199
200 /* -2 == valid prefix char, but mode unknown */
201 if (i != -2)
202 access |= (1 << i);
203
204 nick++;
205 }
206
207 *modechars = nick - orig;
208
209 return access;
210 }
211
212 /* returns the access number for a particular mode. e.g.
213 mode 'a' returns 0
214 mode 'o' returns 1
215 mode 'h' returns 2
216 mode 'v' returns 3
217 Also puts the nick-prefix-char in 'prefix' */
218
219 int
mode_access(server * serv,char mode,char * prefix)220 mode_access (server * serv, char mode, char *prefix)
221 {
222 int pos = 0;
223
224 while (serv->nick_modes[pos])
225 {
226 if (serv->nick_modes[pos] == mode)
227 {
228 *prefix = serv->nick_prefixes[pos];
229 return pos;
230 }
231 pos++;
232 }
233
234 *prefix = 0;
235
236 return -1;
237 }
238
239 static void
record_chan_mode(session * sess,char sign,char mode,char * arg)240 record_chan_mode (session *sess, char sign, char mode, char *arg)
241 {
242 /* Somebody needed to acutally update sess->current_modes, needed to
243 play nice with bouncers, and less mode calls. Also keeps modes up
244 to date for scripts */
245 server *serv = sess->server;
246 GString *current = g_string_new(sess->current_modes);
247 gint mode_pos = -1;
248 gchar *current_char = current->str;
249 gint modes_length;
250 gint argument_num = 0;
251 gint argument_offset = 0;
252 gint argument_length = 0;
253 int i = 0;
254 gchar *arguments_start;
255
256 /* find out if the mode currently exists */
257 arguments_start = g_strstr_len(current->str , -1, " ");
258 if (arguments_start) {
259 modes_length = arguments_start - current->str;
260 }
261 else {
262 modes_length = current->len;
263 /* set this to the end of the modes */
264 arguments_start = current->str + current->len;
265 }
266
267 while (mode_pos == -1 && i < modes_length)
268 {
269 if (*current_char == mode)
270 {
271 mode_pos = i;
272 }
273 else
274 {
275 i++;
276 current_char++;
277 }
278 }
279
280 /* if the mode currently exists and has an arg, need to know where
281 * (including leading space) */
282 if (mode_pos != -1 && mode_has_arg(serv, '+', mode))
283 {
284 current_char = current->str;
285
286 i = 0;
287 while (i <= mode_pos)
288 {
289 if (mode_has_arg(serv, '+', *current_char))
290 argument_num++;
291 current_char++;
292 i++;
293 }
294
295 /* check through arguments for where to start */
296 current_char = arguments_start;
297 i = 0;
298 while (i < argument_num && *current_char != '\0')
299 {
300 if (*current_char == ' ')
301 i++;
302 if (i != argument_num)
303 current_char++;
304 }
305 argument_offset = current_char - current->str;
306
307 /* how long the existing argument is for this key
308 * important for malloc and strncpy */
309 if (i == argument_num)
310 {
311 argument_length++;
312 current_char++;
313 while (*current_char != '\0' && *current_char != ' ')
314 {
315 argument_length++;
316 current_char++;
317 }
318 }
319 }
320
321 /* two cases, adding and removing a mode, handled differently */
322 if (sign == '+')
323 {
324 if (mode_pos != -1)
325 {
326 /* if it already exists, only need to do something (change)
327 * if there should be a param */
328 if (mode_has_arg(serv, sign, mode))
329 {
330 /* leave the old space there */
331 current = g_string_erase(current, argument_offset+1, argument_length-1);
332 current = g_string_insert(current, argument_offset+1, arg);
333
334 g_free(sess->current_modes);
335 sess->current_modes = g_string_free(current, FALSE);
336 }
337 }
338 /* mode wasn't there before */
339 else
340 {
341 /* insert the new mode character */
342 current = g_string_insert_c(current, modes_length, mode);
343
344 /* add the argument, with space if there is one */
345 if (mode_has_arg(serv, sign, mode))
346 {
347 current = g_string_append_c(current, ' ');
348 current = g_string_append(current, arg);
349 }
350
351 g_free(sess->current_modes);
352 sess->current_modes = g_string_free(current, FALSE);
353 }
354 }
355 else if (sign == '-' && mode_pos != -1)
356 {
357 /* remove the argument first if it has one*/
358 if (mode_has_arg(serv, '+', mode))
359 current = g_string_erase(current, argument_offset, argument_length);
360
361 /* remove the mode character */
362 current = g_string_erase(current, mode_pos, 1);
363
364 g_free(sess->current_modes);
365 sess->current_modes = g_string_free(current, FALSE);
366 }
367 }
368
369 static char *
mode_cat(char * str,char * addition)370 mode_cat (char *str, char *addition)
371 {
372 int len;
373
374 if (str)
375 {
376 len = strlen (str) + strlen (addition) + 2;
377 str = g_realloc (str, len);
378 strcat (str, " ");
379 strcat (str, addition);
380 }
381 else
382 {
383 str = g_strdup (addition);
384 }
385
386 return str;
387 }
388
389 /* handle one mode, e.g.
390 handle_single_mode (mr,'+','b',"elite","#warez","banneduser",) */
391
392 static void
handle_single_mode(mode_run * mr,char sign,char mode,char * nick,char * chan,char * arg,int quiet,int is_324,const message_tags_data * tags_data)393 handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
394 char *chan, char *arg, int quiet, int is_324,
395 const message_tags_data *tags_data)
396 {
397 session *sess;
398 server *serv = mr->serv;
399 char outbuf[4];
400 char *cm = serv->chanmodes;
401 gboolean supportsq = FALSE;
402
403 outbuf[0] = sign;
404 outbuf[1] = 0;
405 outbuf[2] = mode;
406 outbuf[3] = 0;
407
408 sess = find_channel (serv, chan);
409 if (!sess || !is_channel (serv, chan))
410 {
411 /* got modes for a chan we're not in! probably nickmode +isw etc */
412 sess = serv->front_session;
413 goto genmode;
414 }
415
416 /* is this a nick mode? */
417 if (strchr (serv->nick_modes, mode))
418 {
419 /* update the user in the userlist */
420 userlist_update_mode (sess, /*nickname */ arg, mode, sign);
421 } else
422 {
423 if (!is_324 && !sess->ignore_mode && mode_chanmode_type(serv, mode) >= 1)
424 record_chan_mode (sess, sign, mode, arg);
425 }
426
427 /* Is q a chanmode on this server? */
428 if (cm)
429 while (*cm)
430 {
431 if (*cm == ',')
432 break;
433 if (*cm == 'q')
434 supportsq = TRUE;
435 cm++;
436 }
437
438 switch (sign)
439 {
440 case '+':
441 switch (mode)
442 {
443 case 'k':
444 safe_strcpy (sess->channelkey, arg, sizeof (sess->channelkey));
445 fe_update_channel_key (sess);
446 fe_update_mode_buttons (sess, mode, sign);
447 if (!quiet)
448 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANSETKEY, sess, nick, arg, NULL,
449 NULL, 0, tags_data->timestamp);
450 return;
451 case 'l':
452 sess->limit = atoi (arg);
453 fe_update_channel_limit (sess);
454 fe_update_mode_buttons (sess, mode, sign);
455 if (!quiet)
456 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANSETLIMIT, sess, nick, arg, NULL,
457 NULL, 0, tags_data->timestamp);
458 return;
459 case 'o':
460 if (!quiet)
461 mr->op = mode_cat (mr->op, arg);
462 return;
463 case 'h':
464 if (!quiet)
465 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANHOP, sess, nick, arg, NULL, NULL,
466 0, tags_data->timestamp);
467 return;
468 case 'v':
469 if (!quiet)
470 mr->voice = mode_cat (mr->voice, arg);
471 return;
472 case 'b':
473 if (!quiet)
474 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANBAN, sess, nick, arg, NULL, NULL,
475 0, tags_data->timestamp);
476 return;
477 case 'e':
478 if (!quiet)
479 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANEXEMPT, sess, nick, arg, NULL,
480 NULL, 0, tags_data->timestamp);
481 return;
482 case 'I':
483 if (!quiet)
484 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANINVITE, sess, nick, arg, NULL, NULL,
485 0, tags_data->timestamp);
486 return;
487 case 'q':
488 if (!supportsq)
489 break; /* +q is owner on this server */
490 if (!quiet)
491 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANQUIET, sess, nick, arg, NULL, NULL, 0,
492 tags_data->timestamp);
493 return;
494 }
495 break;
496 case '-':
497 switch (mode)
498 {
499 case 'k':
500 sess->channelkey[0] = 0;
501 fe_update_channel_key (sess);
502 fe_update_mode_buttons (sess, mode, sign);
503 if (!quiet)
504 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMKEY, sess, nick, NULL, NULL,
505 NULL, 0, tags_data->timestamp);
506 return;
507 case 'l':
508 sess->limit = 0;
509 fe_update_channel_limit (sess);
510 fe_update_mode_buttons (sess, mode, sign);
511 if (!quiet)
512 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMLIMIT, sess, nick, NULL, NULL,
513 NULL, 0, tags_data->timestamp);
514 return;
515 case 'o':
516 if (!quiet)
517 mr->deop = mode_cat (mr->deop, arg);
518 return;
519 case 'h':
520 if (!quiet)
521 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEHOP, sess, nick, arg, NULL,
522 NULL, 0, tags_data->timestamp);
523 return;
524 case 'v':
525 if (!quiet)
526 mr->devoice = mode_cat (mr->devoice, arg);
527 return;
528 case 'b':
529 if (!quiet)
530 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANUNBAN, sess, nick, arg, NULL, NULL,
531 0, tags_data->timestamp);
532 return;
533 case 'e':
534 if (!quiet)
535 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMEXEMPT, sess, nick, arg, NULL,
536 NULL, 0, tags_data->timestamp);
537 return;
538 case 'I':
539 if (!quiet)
540 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMINVITE, sess, nick, arg, NULL,
541 NULL, 0, tags_data->timestamp);
542 return;
543 case 'q':
544 if (!supportsq)
545 break; /* -q is owner on this server */
546 if (!quiet)
547 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANUNQUIET, sess, nick, arg, NULL,
548 NULL, 0, tags_data->timestamp);
549 return;
550 }
551 }
552
553 fe_update_mode_buttons (sess, mode, sign);
554
555 genmode:
556 /* Received umode +e. If we're waiting to send JOIN then send now! */
557 if (mode == 'e' && sign == '+' && !serv->p_cmp (chan, serv->nick))
558 inbound_identified (serv);
559
560 if (!quiet)
561 {
562 if (*arg)
563 {
564 char *buf = g_strdup_printf ("%s %s", chan, arg);
565 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
566 outbuf + 2, buf, 0, tags_data->timestamp);
567 g_free (buf);
568 }
569 else
570 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
571 outbuf + 2, chan, 0, tags_data->timestamp);
572 }
573 }
574
575 /* does this mode have an arg? like +b +l +o */
576
577 static int
mode_has_arg(server * serv,char sign,char mode)578 mode_has_arg (server * serv, char sign, char mode)
579 {
580 int type;
581
582 /* if it's a nickmode, it must have an arg */
583 if (strchr (serv->nick_modes, mode))
584 return 1;
585
586 type = mode_chanmode_type (serv, mode);
587 switch (type)
588 {
589 case 0: /* type A */
590 case 1: /* type B */
591 return 1;
592 case 2: /* type C */
593 if (sign == '+')
594 return 1;
595 case 3: /* type D */
596 return 0;
597 default:
598 return 0;
599 }
600
601 }
602
603 /* what type of chanmode is it? -1 for not in chanmode */
604 static int
mode_chanmode_type(server * serv,char mode)605 mode_chanmode_type (server * serv, char mode)
606 {
607 /* see what numeric 005 CHANMODES=xxx said */
608 char *cm = serv->chanmodes;
609 int type = 0;
610 int found = 0;
611
612 while (*cm && !found)
613 {
614 if (*cm == ',')
615 {
616 type++;
617 } else if (*cm == mode)
618 {
619 found = 1;
620 }
621 cm++;
622 }
623 if (found)
624 return type;
625 /* not found? -1 */
626 else
627 return -1;
628 }
629
630 static void
mode_print_grouped(session * sess,char * nick,mode_run * mr,const message_tags_data * tags_data)631 mode_print_grouped (session *sess, char *nick, mode_run *mr,
632 const message_tags_data *tags_data)
633 {
634 /* print all the grouped Op/Deops */
635 if (mr->op)
636 {
637 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0,
638 tags_data->timestamp);
639 g_free(mr->op);
640 mr->op = NULL;
641 }
642
643 if (mr->deop)
644 {
645 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL,
646 0, tags_data->timestamp);
647 g_free(mr->deop);
648 mr->deop = NULL;
649 }
650
651 if (mr->voice)
652 {
653 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL,
654 0, tags_data->timestamp);
655 g_free(mr->voice);
656 mr->voice = NULL;
657 }
658
659 if (mr->devoice)
660 {
661 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL,
662 NULL, 0, tags_data->timestamp);
663 g_free(mr->devoice);
664 mr->devoice = NULL;
665 }
666 }
667
668
669 /* handle a MODE or numeric 324 from server */
670
671 void
handle_mode(server * serv,char * word[],char * word_eol[],char * nick,int numeric_324,const message_tags_data * tags_data)672 handle_mode (server * serv, char *word[], char *word_eol[],
673 char *nick, int numeric_324, const message_tags_data *tags_data)
674 {
675 session *sess;
676 char *chan;
677 char *modes;
678 char *argstr;
679 char sign;
680 int len;
681 size_t arg;
682 size_t i, num_args;
683 int num_modes;
684 size_t offset = 3;
685 int all_modes_have_args = FALSE;
686 int using_front_tab = FALSE;
687 mode_run mr;
688
689 mr.serv = serv;
690 mr.op = mr.deop = mr.voice = mr.devoice = NULL;
691
692 /* numeric 324 has everything 1 word later (as opposed to MODE) */
693 if (numeric_324)
694 offset++;
695
696 chan = word[offset];
697 modes = word[offset + 1];
698 if (*modes == ':')
699 modes++;
700
701 if (*modes == 0)
702 return; /* beyondirc's blank modes */
703
704 sess = find_channel (serv, chan);
705 if (!sess)
706 {
707 sess = serv->front_session;
708 using_front_tab = TRUE;
709 }
710 /* remove trailing space */
711 len = strlen (word_eol[offset]) - 1;
712 if (word_eol[offset][len] == ' ')
713 word_eol[offset][len] = 0;
714
715 if (prefs.hex_irc_raw_modes && !numeric_324)
716 EMIT_SIGNAL_TIMESTAMP (XP_TE_RAWMODES, sess, nick, word_eol[offset], 0, 0, 0,
717 tags_data->timestamp);
718
719 if (numeric_324 && !using_front_tab)
720 {
721 g_free (sess->current_modes);
722 sess->current_modes = g_strdup (word_eol[offset+1]);
723 }
724
725 sign = *modes;
726 modes++;
727 arg = 1;
728
729 /* count the number of arguments (e.g. after the -o+v) */
730 num_args = 0;
731 i = 1;
732 while ((i + offset + 1) < PDIWORDS)
733 {
734 i++;
735 if (!(*word[i + offset]))
736 break;
737 num_args++;
738 if (word[i + offset][0] == ':')
739 break;
740 }
741
742 /* count the number of modes (without the -/+ chars */
743 num_modes = 0;
744 i = 0;
745 while (i < strlen (modes))
746 {
747 if (modes[i] != '+' && modes[i] != '-')
748 num_modes++;
749 i++;
750 }
751
752 if (num_args == num_modes)
753 all_modes_have_args = TRUE;
754
755 while (*modes)
756 {
757 switch (*modes)
758 {
759 case '-':
760 case '+':
761 /* print all the grouped Op/Deops */
762 mode_print_grouped (sess, nick, &mr, tags_data);
763 sign = *modes;
764 break;
765 default:
766 argstr = "";
767 if ((all_modes_have_args || mode_has_arg (serv, sign, *modes)) && arg < (num_args + 1))
768 {
769 arg++;
770 argstr = STRIP_COLON(word, word_eol, arg+offset);
771 }
772 handle_single_mode (&mr, sign, *modes, nick, chan,
773 argstr, numeric_324 || prefs.hex_irc_raw_modes,
774 numeric_324, tags_data);
775 }
776
777 modes++;
778 }
779
780 /* update the title at the end, now that the mode update is internal now */
781 if (!using_front_tab)
782 fe_set_title (sess);
783
784 /* print all the grouped Op/Deops */
785 mode_print_grouped (sess, nick, &mr, tags_data);
786 }
787
788 static char
hex_to_chr(char chr)789 hex_to_chr(char chr)
790 {
791 return g_ascii_isdigit (chr) ? chr - '0' : g_ascii_tolower (chr) - 'a' + 10;
792 }
793
794 static void
parse_005_token(const char * token,char ** name,char ** value,gboolean * adding)795 parse_005_token (const char *token, char **name, char **value, gboolean *adding)
796 {
797 char *toksplit, *valuecurr;
798 size_t idx;
799
800 if (token[0] == '-')
801 {
802 *adding = FALSE;
803 token++;
804 } else
805 {
806 *adding = TRUE;
807 }
808
809 toksplit = strchr (token, '=');
810 if (toksplit && *toksplit++)
811 {
812 /* The token has a value; parse any escape codes. */
813 *name = g_strndup (token, toksplit - token - 1);
814 *value = g_malloc (strlen (toksplit) + 1);
815 valuecurr = *value;
816
817 while (*toksplit)
818 {
819 if (toksplit[0] == '\\')
820 {
821 /** If it's a malformed escape then just skip it. */
822 if (toksplit[1] == 'x' && g_ascii_isxdigit (toksplit[2]) && g_ascii_isxdigit (toksplit[3]))
823 *valuecurr++ = hex_to_chr (toksplit[2]) << 4 | hex_to_chr (toksplit[3]);
824
825 for (idx = 0; idx < 4; ++idx)
826 {
827 /* We need to do this to avoid jumping past the end of the array. */
828 if (*toksplit)
829 toksplit++;
830 }
831 } else
832 {
833 /** Non-escape characters can be copied as is. */
834 *valuecurr++ = *toksplit++;
835 }
836 }
837 *valuecurr++ = 0;
838 } else
839 {
840 /* The token has no value; store a dummy value instead. */
841 *name = g_strdup (token);
842 *value = g_strdup ("");
843 }
844 }
845
846 /* handle the 005 numeric */
847
848 void
inbound_005(server * serv,char * word[],const message_tags_data * tags_data)849 inbound_005 (server * serv, char *word[], const message_tags_data *tags_data)
850 {
851 int w;
852 char *pre;
853 char *tokname, *tokvalue;
854 gboolean tokadding;
855
856 w = 4; /* start at the 4th word */
857 while (w < PDIWORDS && *word[w])
858 {
859 if (word[w][0] == ':')
860 break; // :are supported by this server
861
862 parse_005_token(word[w], &tokname, &tokvalue, &tokadding);
863 if (g_strcmp0 (tokname, "MODES") == 0)
864 {
865 serv->modes_per_line = atoi (tokvalue);
866 } else if (g_strcmp0 (tokname, "CHANTYPES") == 0)
867 {
868 g_free (serv->chantypes);
869 serv->chantypes = g_strdup (tokvalue);
870 } else if (g_strcmp0 (tokname, "CHANMODES") == 0)
871 {
872 g_free (serv->chanmodes);
873 serv->chanmodes = g_strdup (tokvalue);
874 } else if (g_strcmp0 (tokname, "PREFIX") == 0)
875 {
876 pre = strchr (tokvalue, ')');
877 if (pre)
878 {
879 pre[0] = 0; /* NULL out the ')' */
880 g_free (serv->nick_prefixes);
881 g_free (serv->nick_modes);
882 serv->nick_prefixes = g_strdup (pre + 1);
883 serv->nick_modes = g_strdup (tokvalue + 1);
884 } else
885 {
886 /* bad! some ircds don't give us the modes. */
887 /* in this case, we use it only to strip /NAMES */
888 serv->bad_prefix = TRUE;
889 g_free (serv->bad_nick_prefixes);
890 serv->bad_nick_prefixes = g_strdup (tokvalue);
891 }
892 } else if (g_strcmp0 (tokname, "WATCH") == 0)
893 {
894 serv->supports_watch = tokadding;
895 } else if (g_strcmp0 (tokname, "MONITOR") == 0)
896 {
897 serv->supports_monitor = tokadding;
898 } else if (g_strcmp0 (tokname, "NETWORK") == 0)
899 {
900 if (serv->server_session->type == SESS_SERVER && strlen (tokvalue))
901 {
902 safe_strcpy (serv->server_session->channel, tokvalue, CHANLEN);
903 fe_set_channel (serv->server_session);
904 }
905
906 } else if (g_strcmp0 (tokname, "CASEMAPPING") == 0)
907 {
908 if (g_strcmp0 (tokvalue, "ascii") == 0)
909 serv->p_cmp = (void *)g_ascii_strcasecmp;
910 } else if (g_strcmp0 (tokname, "CHARSET") == 0)
911 {
912 if (g_ascii_strcasecmp (tokvalue, "UTF-8") == 0)
913 {
914 server_set_encoding (serv, "UTF-8");
915 }
916 } else if (g_strcmp0 (tokname, "UTF8ONLY") == 0)
917 {
918 server_set_encoding (serv, "UTF-8");
919 } else if (g_strcmp0 (tokname, "NAMESX") == 0)
920 {
921 /* 12345678901234567 */
922 tcp_send_len (serv, "PROTOCTL NAMESX\r\n", 17);
923 } else if (g_strcmp0 (tokname, "WHOX") == 0)
924 {
925 serv->have_whox = tokadding;
926 } else if (g_strcmp0 (tokname, "EXCEPTS") == 0)
927 {
928 serv->have_except = tokadding;
929 } else if (g_strcmp0 (tokname, "INVEX") == 0)
930 {
931 /* supports mode letter +I, default channel invite */
932 serv->have_invite = tokadding;
933 } else if (g_strcmp0 (tokname, "ELIST") == 0)
934 {
935 /* supports LIST >< min/max user counts? */
936 if (strchr (tokvalue, 'U') || strchr (tokvalue, 'u'))
937 serv->use_listargs = TRUE;
938 }
939
940 g_free (tokname);
941 g_free (tokvalue);
942 w++;
943 }
944 }
945