Release 3.0.3.
[jelmer/ctrlproxy.git] / src / isupport.c
1 /*
2         ctrlproxy: A modular IRC proxy
3         (c) 2005 Jelmer Vernooij <jelmer@nl.linux.org>
4
5         This program is free software; you can redistribute it and/or modify
6         it under the terms of the GNU General Public License as published by
7         the Free Software Foundation; either version 2 of the License, or
8         (at your option) any later version.
9
10         This program is distributed in the hope that it will be useful,
11         but WITHOUT ANY WARRANTY; without even the implied warranty of
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13         GNU General Public License for more details.
14
15         You should have received a copy of the GNU General Public License
16         along with this program; if not, write to the Free Software
17         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "internals.h"
21
22 #define DEFAULT_PREFIX          "(ov)@+"
23 #define DEFAULT_CHANTYPES       "#&"
24
25 void network_info_init(struct network_info *info)
26 {
27         memset(info, 0, sizeof(struct network_info));
28         info->prefix = g_strdup(DEFAULT_PREFIX);
29         info->chantypes = g_strdup(DEFAULT_CHANTYPES);
30 }
31
32 void free_network_info(struct network_info *info)
33 {
34         g_free(info->prefix);
35         g_free(info->chantypes);
36         g_free(info->charset);
37         g_free(info->name);
38         g_free(info->server);
39         g_free(info->supported_user_modes);
40         g_free(info->supported_channel_modes);
41         g_strfreev(info->chanmodes);
42         g_free(info->chanlimit);
43         g_free(info->maxlist);
44         g_free(info->idchan);
45         g_free(info->statusmsg);
46 }
47
48 char *network_info_string(struct network_info *info)
49 {
50         char *ret = NULL;
51         GList *fs = NULL, *gl;
52         char *casemap = NULL;
53
54         if (info->name != NULL)
55                 fs = g_list_append(fs, g_strdup_printf("NETWORK=%s", info->name));
56
57         switch (info->casemapping) {
58         default:
59         case CASEMAP_RFC1459:
60                 casemap = g_strdup("CASEMAPPING=rfc1459");
61                 break;
62         case CASEMAP_STRICT_RFC1459:
63                 casemap = g_strdup("CASEMAPPING=strict-rfc1459");
64                 break;
65         case CASEMAP_ASCII:
66                 casemap = g_strdup("CASEMAPPING=ascii");
67                 break;
68         }
69
70         if (casemap != NULL)
71                 fs = g_list_append(fs, casemap);
72
73         if (info->forced_nick_changes)
74                 fs = g_list_append(fs, g_strdup("FNC"));
75
76         if (info->map)
77                 fs = g_list_append(fs, "MAP");
78
79         if (info->charset != NULL)
80                 fs = g_list_append(fs, g_strdup_printf("CHARSET=%s", info->charset));
81
82         if (info->nicklen != 0)
83                 fs = g_list_append(fs, g_strdup_printf("NICKLEN=%d", info->nicklen));
84
85         if (info->userlen != 0)
86                 fs = g_list_append(fs, g_strdup_printf("USERLEN=%d", info->userlen));
87
88         if (info->watch != 0)
89                 fs = g_list_append(fs, g_strdup_printf("WATCH=%d", info->watch));
90
91         if (info->vbanlist)
92                 fs = g_list_append(fs, "VBANLIST");
93
94         if (info->operoverride)
95                 fs = g_list_append(fs, "OVERRIDE");
96
97         if (info->hostlen != 0)
98                 fs = g_list_append(fs, g_strdup_printf("HOSTLEN=%d", info->hostlen));
99
100         if (info->channellen != 0)
101                 fs = g_list_append(fs, g_strdup_printf("CHANNELLEN=%d", info->channellen));
102
103         if (info->awaylen != 0)
104                 fs = g_list_append(fs, g_strdup_printf("AWAYLEN=%d", info->awaylen));
105
106         if (info->kicklen != 0)
107                 fs = g_list_append(fs, g_strdup_printf("KICKLEN=%d", info->kicklen));
108
109         if (info->topiclen != 0)
110                 fs = g_list_append(fs, g_strdup_printf("TOPICLEN=%d", info->topiclen));
111
112         if (info->maxchannels != 0)
113                 fs = g_list_append(fs, g_strdup_printf("MAXCHANNELS=%d", info->maxchannels));
114
115         if (info->maxtargets != 0)
116                 fs = g_list_append(fs, g_strdup_printf("MAXTARGETS=%d", info->maxtargets));
117
118         if (info->maxbans != 0)
119                 fs = g_list_append(fs, g_strdup_printf("MAXBANS=%d", info->maxbans));
120
121         if (info->maxmodes != 0)
122                 fs = g_list_append(fs, g_strdup_printf("MODES=%d", info->maxmodes));
123
124         if (info->maxpara != 0)
125                 fs = g_list_append(fs, g_strdup_printf("MAXPARA=%d", info->maxpara));
126
127         if (info->wallchops)
128                 fs = g_list_append(fs, g_strdup("WALLCHOPS"));
129
130         if (info->wallvoices)
131                 fs = g_list_append(fs, g_strdup("WALLVOICES"));
132
133         if (info->rfc2812)
134                 fs = g_list_append(fs, g_strdup("RFC2812"));
135
136         if (info->penalty)
137                 fs = g_list_append(fs, g_strdup("PENALTY"));
138
139         if (info->remove)
140                 fs = g_list_append(fs, g_strdup("REMOVE"));
141
142         if (info->safelist)
143                 fs = g_list_append(fs, g_strdup("SAFELIST"));
144         
145         if (info->userip)
146                 fs = g_list_append(fs, g_strdup("USERIP"));
147
148         if (info->capab)
149                 fs = g_list_append(fs, g_strdup("CAPAB"));
150
151         if (info->cprivmsg)
152                 fs = g_list_append(fs, g_strdup("CPRIVMSG"));
153
154         if (info->cnotice)
155                 fs = g_list_append(fs, g_strdup("CNOTICE"));
156
157         if (info->knock)
158                 fs = g_list_append(fs, g_strdup("KNOCK"));
159
160         if (info->vchannels)
161                 fs = g_list_append(fs, g_strdup("VCHANNELS"));
162
163         if (info->whox)
164                 fs = g_list_append(fs, g_strdup("WHOX"));
165
166         if (info->callerid)
167                 fs = g_list_append(fs, g_strdup("CALLERID"));
168
169         if (info->accept)
170                 fs = g_list_append(fs, g_strdup("ACCEPT"));
171
172         if (info->keylen != 0)
173                 fs = g_list_append(fs, g_strdup_printf("KEYLEN=%d", info->keylen));
174
175         if (info->silence) {
176                 if (info->silence_limit != 0)
177                         fs = g_list_append(fs, 
178                                         g_strdup_printf("SILENCE=%d", info->silence_limit));
179                 else 
180                         fs = g_list_append(fs, g_strdup("SILENCE"));
181         }
182
183         if (info->chantypes != NULL)
184                 fs = g_list_append(fs, g_strdup_printf("CHANTYPES=%s", info->chantypes));
185
186         if (info->chanmodes != NULL) {
187                 char *tmp = g_strjoinv(",", info->chanmodes);
188                 fs = g_list_append(fs, g_strdup_printf("CHANMODES=%s", tmp));
189                 g_free(tmp);
190         }
191
192         if (info->chanlimit != NULL)
193                 fs = g_list_append(fs, g_strdup_printf("CHANLIMIT=%s", info->chanlimit));
194
195         if (info->namesx) 
196                 fs = g_list_append(fs, "NAMESX");
197
198         if (info->securelist)
199                 fs = g_list_append(fs, "SECURELIST");
200
201         if (info->excepts_mode != '\0')
202                 fs = g_list_append(fs, g_strdup_printf("EXCEPTS=%c", info->excepts_mode));
203
204         if (info->statusmsg != NULL)
205                 fs = g_list_append(fs, g_strdup_printf("STATUSMSG=%s", info->statusmsg));
206
207         if (info->invex_mode != '\0')
208                 fs = g_list_append(fs, g_strdup_printf("INVEX=%c", info->invex_mode));
209
210         if (info->elist_mask_search ||
211                 info->elist_inverse_mask_search ||
212                 info->elist_usercount_search ||
213                 info->elist_creation_time_search ||
214                 info->elist_topic_search) {
215                 char elist[100];
216                 strcpy(elist, "");
217                 if (info->elist_mask_search)
218                         strncat(elist, "M", sizeof(elist));
219                 if (info->elist_inverse_mask_search)
220                         strncat(elist, "N", sizeof(elist));
221                 if (info->elist_usercount_search)
222                         strncat(elist, "U", sizeof(elist));
223                 if (info->elist_creation_time_search)
224                         strncat(elist, "C", sizeof(elist));
225                 if (info->elist_topic_search)
226                         strncat(elist, "T", sizeof(elist));
227                 fs = g_list_append(fs, g_strdup_printf("ELIST=%s", elist));
228         }
229         
230         if (info->deaf_mode != '\0')
231                 fs = g_list_append(fs, g_strdup_printf("DEAF=%c", info->deaf_mode));
232
233         if (info->maxlist != NULL)
234                 fs = g_list_append(fs, g_strdup_printf("MAXLIST=%s", info->maxlist));
235
236         if (info->idchan != NULL)
237                 fs = g_list_append(fs, g_strdup_printf("IDCHAN=%s", info->idchan));
238
239         if (info->prefix != NULL) 
240                 fs = g_list_append(fs, g_strdup_printf("PREFIX=%s", info->prefix));
241
242         ret = list_make_string(fs);
243
244         for (gl = fs; gl; gl = gl->next)
245                 g_free(gl->data);
246
247         g_list_free(fs);
248
249         return ret;
250 }
251
252 void network_info_parse(struct network_info *info, const char *parameter)
253 {
254         char *sep;
255         char *key, *val;
256
257         sep = strchr(parameter, '=');
258
259         if (!sep) { 
260                 key = g_strdup(parameter);
261                 val = NULL; 
262         } else {
263                 key = g_strndup(parameter, sep - parameter);
264                 val = g_strdup(sep+1);
265         }
266         
267         if(!g_strcasecmp(key, "CASEMAPPING")) {
268                 if(!g_strcasecmp(val, "rfc1459")) {
269                         info->casemapping = CASEMAP_RFC1459;
270                 } else if(!g_strcasecmp(val, "strict-rfc1459")) {
271                         info->casemapping = CASEMAP_STRICT_RFC1459;
272                 } else if(!g_strcasecmp(val, "ascii")) {
273                         info->casemapping = CASEMAP_ASCII;
274                 } else {
275                         info->casemapping = CASEMAP_UNKNOWN;
276                         log_global(LOG_WARNING, "Unknown CASEMAPPING value '%s'", val);
277                 }
278         } else if (!g_strcasecmp(key, "NETWORK")) {
279                 g_free(info->name);
280                 info->name = g_strdup(val);
281         } else if (!g_strcasecmp(key, "NICKLEN") || !g_strcasecmp(key, "MAXNICKLEN")) {
282                 info->nicklen = atoi(val);
283         } else if (!g_strcasecmp(key, "USERLEN")) {
284                 info->userlen = atoi(val);
285         } else if (!g_strcasecmp(key, "HOSTLEN")) {
286                 info->hostlen = atoi(val);
287         } else if (!g_strcasecmp(key, "OVERRIDE")) {
288                 info->operoverride = TRUE;
289         } else if (!g_strcasecmp(key, "CHANNELLEN") || !g_strcasecmp(key, "MAXCHANNELLEN")) {
290                 info->channellen = atoi(val);
291         } else if (!g_strcasecmp(key, "AWAYLEN")) {
292                 info->awaylen = atoi(val);
293         } else if (!g_strcasecmp(key, "KICKLEN")) {
294                 info->kicklen = atoi(val);
295         } else if (!g_strcasecmp(key, "TOPICLEN")) {
296                 info->topiclen = atoi(val);
297         } else if (!g_strcasecmp(key, "WATCH")) {
298                 info->watch = atoi(val);
299         } else if (!g_strcasecmp(key, "VBANLIST")) {
300                 info->vbanlist = TRUE;
301         } else if (!g_strcasecmp(key, "MAXPARA")) {
302                 info->maxpara = atoi(val);
303         } else if (!g_strcasecmp(key, "MAXCHANNELS")) {
304                 info->maxchannels = atoi(val);
305         } else if (!g_strcasecmp(key, "MAXTARGETS")) {
306                 info->maxtargets = atoi(val);
307         } else if (!g_strcasecmp(key, "REMOVE")) {
308                 info->remove = TRUE;
309         } else if (!g_strcasecmp(key, "MAXBANS")) {
310                 info->maxbans = atoi(val);
311         } else if (!g_strcasecmp(key, "MODES")) {
312                 info->maxmodes = atoi(val);
313         } else if (!g_strcasecmp(key, "WALLCHOPS")) {
314                 info->wallchops = TRUE;
315         } else if (!g_strcasecmp(key, "MAP")) {
316                 info->map = TRUE;
317         } else if (!g_strcasecmp(key, "WALLVOICES")) {
318                 info->wallvoices = TRUE;
319         } else if (!g_strcasecmp(key, "RFC2812")) {
320                 info->rfc2812 = TRUE;
321         } else if (!g_strcasecmp(key, "PENALTY")) {
322                 info->penalty = TRUE;
323         } else if (!g_strcasecmp(key, "FNC")) {
324                 info->forced_nick_changes = TRUE;
325         } else if (!g_strcasecmp(key, "SAFELIST")) {
326                 info->safelist = TRUE;
327         } else if (!g_strcasecmp(key, "USERIP")) {
328                 info->userip = TRUE;
329         } else if (!g_strcasecmp(key, "CPRIVMSG")) {
330                 info->cprivmsg = TRUE;
331         } else if (!g_strcasecmp(key, "CNOTICE")) {
332                 info->cnotice = TRUE;
333         } else if (!g_strcasecmp(key, "KNOCK")) {
334                 info->knock = TRUE;
335         } else if (!g_strcasecmp(key, "CAPAB")) {
336                 info->capab = TRUE;
337         } else if (!g_strcasecmp(key, "VCHANNELS")) {
338                 info->vchannels = TRUE;
339         } else if (!g_strcasecmp(key, "WHOX")) {
340                 info->whox = TRUE;
341         } else if (!g_strcasecmp(key, "CALLERID")) {
342                 info->callerid = TRUE;
343         } else if (!g_strcasecmp(key, "ACCEPT")) {
344                 info->accept = TRUE;
345         } else if (!g_strcasecmp(key, "KEYLEN")) {
346                 info->keylen = atoi(val);
347         } else if (!g_strcasecmp(key, "SILENCE")) {
348                 if (val) {
349                         info->silence_limit = atoi(val);
350                         info->silence = TRUE;
351                 } else
352                         info->silence = TRUE;
353         } else if (!g_strcasecmp(key, "CHANTYPES")) {
354                 g_free(info->chantypes);
355                 info->chantypes = g_strdup(val);
356         } else if (!g_strcasecmp(key, "CHANMODES")) {
357                 g_strfreev(info->chanmodes);
358                 info->chanmodes = g_strsplit(val, ",", 4);
359                 /* FIXME: Make sure info->chanmodes length is exactly 4 */
360         } else if (!g_strcasecmp(key, "CHANLIMIT")) {
361                 g_free(info->chanlimit);
362                 info->chanlimit = g_strdup(val);
363         } else if (!g_strcasecmp(key, "EXCEPTS")) {
364                 if (val == NULL) 
365                         info->excepts_mode = 'e';
366                 else if (strlen(val) > 1)
367                         log_global(LOG_WARNING, "Invalid length excepts value: %s", val);
368                 else
369                         info->excepts_mode = val[0];
370         } else if (!g_strcasecmp(key, "INVEX")) {
371                 if (val == NULL) 
372                         info->invex_mode = 'I';
373                 else if (strlen(val) > 1)
374                         log_global(LOG_WARNING, "Invalid length invex value: %s", val);
375                 else
376                         info->invex_mode = val[0];
377         } else if (!g_strcasecmp(key, "ELIST")) {
378                 int i;
379                 for (i = 0; val[i]; i++) {
380                         switch (val[i]) {
381                         case 'M': info->elist_mask_search = TRUE; break;
382                         case 'N': info->elist_inverse_mask_search = TRUE; break;
383                         case 'T': info->elist_topic_search = TRUE; break;
384                         case 'U': info->elist_usercount_search = TRUE; break;
385                         case 'C': info->elist_creation_time_search = TRUE; break;
386                         default:
387                                   log_global(LOG_WARNING, "Unknown ELIST parameter '%c'", val[i]);
388                                   break;
389                         }
390                 }
391         } else if (!g_strcasecmp(key, "DEAF")) {
392                 if (val == NULL) 
393                         info->deaf_mode = 'D';
394                 else if (strlen(val) > 1)
395                         log_global(LOG_WARNING, "Invalid length deaf value: %s", val);
396                 else
397                         info->deaf_mode = val[0];
398         } else if (!g_strcasecmp(key, "MAXLIST")) {
399                 g_free(info->maxlist);
400                 info->maxlist = g_strdup(val);
401         } else if (!g_strcasecmp(key, "IDCHAN")) {
402                 g_free(info->idchan);
403                 info->idchan = g_strdup(val);
404         } else if (!g_strcasecmp(key, "STATUSMSG")) {
405                 g_free(info->statusmsg);
406                 info->statusmsg = g_strdup(val);
407         } else if (!g_strcasecmp(key, "PREFIX")) {
408                 g_free(info->prefix);
409                 info->prefix = g_strdup(val);
410         } else if (!g_strcasecmp(key, "CHARSET")) {
411                 g_free(info->charset);
412                 info->charset = g_strdup(val);
413         } else if (!g_strcasecmp(key, "NAMESX")) {
414                 info->namesx = TRUE;
415         } else if (!g_strcasecmp(key, "SECURELIST")) {
416                 info->securelist = TRUE;
417         } else {
418                 log_global(LOG_WARNING, "Unknown 005 parameter `%s'", key);
419         }
420         g_free(key);
421         g_free(val);
422 }
423
424 void handle_005(struct network_state *s, struct line *l)
425 {
426         int i;
427         g_assert(s);
428         g_assert(l);
429
430         g_assert(l->argc >= 1);
431
432         for (i = 3; i < l->argc-1; i++) 
433                 network_info_parse(&s->info, l->args[i]);
434 }
435
436 int irccmp(const struct network_info *n, const char *a, const char *b)
437 {
438         switch(n != NULL?n->casemapping:CASEMAP_UNKNOWN) {
439         default:
440         case CASEMAP_UNKNOWN:
441         case CASEMAP_RFC1459:
442                 return str_rfc1459cmp(a,b);
443         case CASEMAP_ASCII:
444                 return str_asciicmp(a,b);
445         case CASEMAP_STRICT_RFC1459:
446                 return str_strictrfc1459cmp(a,b);
447         }
448
449         return 0;
450 }
451
452 gboolean is_channelname(const char *name, const struct network_info *n)
453 {
454         g_assert(n != NULL);
455         g_assert(n->chantypes != NULL);
456
457         g_assert(name != NULL);
458         
459         if (strchr(n->chantypes, name[0])) 
460                 return TRUE;
461
462         return FALSE;
463 }
464
465 gboolean is_prefix(char p, const struct network_info *n)
466 {
467         const char *pref_end;
468         
469         g_assert(n != NULL);
470         g_assert(n->prefix != NULL);
471
472         pref_end = strchr(n->prefix, ')');
473         if (!pref_end)pref_end = n->prefix; else pref_end++;
474
475         if(strchr(pref_end, p)) return TRUE;
476         return FALSE;
477 }
478
479 const char *get_charset(const struct network_info *n)
480 {
481         g_assert(n != NULL);
482         return n->charset;
483 }
484
485 char get_prefix_by_mode(char mode, const struct network_info *n)
486 {
487         int i;
488         char *pref_end;
489         const char *prefix;
490         
491         g_assert(n != NULL);
492         g_assert(n->prefix != NULL);
493
494         prefix = n->prefix;
495         
496         pref_end = strchr(prefix, ')');
497         if(prefix[0] != '(' || !pref_end) {
498                 log_global(LOG_WARNING, "Malformed PREFIX data `%s'", prefix);
499                 return ' ';
500         }
501         pref_end++;
502         prefix++;
503
504         for(i = 0; pref_end[i]; i++) {
505                 if(prefix[i] == mode) return pref_end[i];
506         }
507         return ' ';
508 }
509
510 const static char *default_chanmodes[] = { 
511         "beI", "k" , "l" , "imnpsta" 
512 };
513
514 int network_chanmode_type(char m, struct network_info *n)
515 {
516         int i;
517         for (i = 0; i < 4; i++) {
518                 if (strchr((n && n->chanmodes)?n->chanmodes[i]:default_chanmodes[i], m)) {
519                         return i;
520                 }
521         }
522
523         return 3;
524 }