Don't use charsets by default (#117).
[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 #define DEFAULT_CHARSET         "iso8859-15"
25
26 void network_info_init(struct network_info *info)
27 {
28         memset(info, 0, sizeof(struct network_info));
29         info->prefix = g_strdup(DEFAULT_PREFIX);
30         info->chantypes = g_strdup(DEFAULT_CHANTYPES);
31         info->charset = g_strdup(DEFAULT_CHARSET);
32 }
33
34 void free_network_info(struct network_info *info)
35 {
36         g_free(info->prefix);
37         g_free(info->chantypes);
38         g_free(info->charset);
39         g_free(info->name);
40         g_free(info->server);
41         g_free(info->supported_user_modes);
42         g_free(info->supported_channel_modes);
43         g_strfreev(info->chanmodes);
44         g_free(info->chanlimit);
45         g_free(info->maxlist);
46         g_free(info->idchan);
47         g_free(info->statusmsg);
48 }
49
50 char *network_info_string(struct network_info *info)
51 {
52         char *ret = NULL;
53         GList *fs = NULL, *gl;
54         char *casemap = NULL;
55
56         if (info->name != NULL)
57                 fs = g_list_append(fs, g_strdup_printf("NETWORK=%s", info->name));
58
59         switch (info->casemapping) {
60         default:
61         case CASEMAP_RFC1459:
62                 casemap = g_strdup("CASEMAPPING=rfc1459");
63                 break;
64         case CASEMAP_STRICT_RFC1459:
65                 casemap = g_strdup("CASEMAPPING=strict-rfc1459");
66                 break;
67         case CASEMAP_ASCII:
68                 casemap = g_strdup("CASEMAPPING=ascii");
69                 break;
70         }
71
72         if (casemap != NULL)
73                 fs = g_list_append(fs, casemap);
74
75         if (info->forced_nick_changes)
76                 fs = g_list_append(fs, g_strdup("FNC"));
77
78         if (info->charset != NULL)
79                 fs = g_list_append(fs, g_strdup_printf("CHARSET=%s", info->charset));
80
81         if (info->nicklen != 0)
82                 fs = g_list_append(fs, g_strdup_printf("NICKLEN=%d", info->nicklen));
83
84         if (info->userlen != 0)
85                 fs = g_list_append(fs, g_strdup_printf("USERLEN=%d", info->userlen));
86
87         if (info->hostlen != 0)
88                 fs = g_list_append(fs, g_strdup_printf("HOSTLEN=%d", info->hostlen));
89
90         if (info->channellen != 0)
91                 fs = g_list_append(fs, g_strdup_printf("CHANNELLEN=%d", info->channellen));
92
93         if (info->awaylen != 0)
94                 fs = g_list_append(fs, g_strdup_printf("AWAYLEN=%d", info->awaylen));
95
96         if (info->kicklen != 0)
97                 fs = g_list_append(fs, g_strdup_printf("KICKLEN=%d", info->kicklen));
98
99         if (info->topiclen != 0)
100                 fs = g_list_append(fs, g_strdup_printf("TOPICLEN=%d", info->topiclen));
101
102         if (info->maxchannels != 0)
103                 fs = g_list_append(fs, g_strdup_printf("MAXCHANNELS=%d", info->maxchannels));
104
105         if (info->maxtargets != 0)
106                 fs = g_list_append(fs, g_strdup_printf("MAXTARGETS=%d", info->maxtargets));
107
108         if (info->maxbans != 0)
109                 fs = g_list_append(fs, g_strdup_printf("MAXBANS=%d", info->maxbans));
110
111         if (info->maxmodes != 0)
112                 fs = g_list_append(fs, g_strdup_printf("MODES=%d", info->maxmodes));
113
114         if (info->wallchops)
115                 fs = g_list_append(fs, g_strdup("WALLCHOPS"));
116
117         if (info->wallvoices)
118                 fs = g_list_append(fs, g_strdup("WALLVOICES"));
119
120         if (info->rfc2812)
121                 fs = g_list_append(fs, g_strdup("RFC2812"));
122
123         if (info->penalty)
124                 fs = g_list_append(fs, g_strdup("PENALTY"));
125
126         if (info->safelist)
127                 fs = g_list_append(fs, g_strdup("SAFELIST"));
128         
129         if (info->userip)
130                 fs = g_list_append(fs, g_strdup("USERIP"));
131
132         if (info->capab)
133                 fs = g_list_append(fs, g_strdup("CAPAB"));
134
135         if (info->cprivmsg)
136                 fs = g_list_append(fs, g_strdup("CPRIVMSG"));
137
138         if (info->cnotice)
139                 fs = g_list_append(fs, g_strdup("CNOTICE"));
140
141         if (info->knock)
142                 fs = g_list_append(fs, g_strdup("KNOCK"));
143
144         if (info->vchannels)
145                 fs = g_list_append(fs, g_strdup("VCHANNELS"));
146
147         if (info->whox)
148                 fs = g_list_append(fs, g_strdup("WHOX"));
149
150         if (info->callerid)
151                 fs = g_list_append(fs, g_strdup("CALLERID"));
152
153         if (info->accept)
154                 fs = g_list_append(fs, g_strdup("ACCEPT"));
155
156         if (info->keylen != 0)
157                 fs = g_list_append(fs, g_strdup_printf("KEYLEN=%d", info->keylen));
158
159         if (info->silence != 0)
160                 fs = g_list_append(fs, g_strdup_printf("SILENCE=%d", info->silence));
161
162         if (info->chantypes != NULL)
163                 fs = g_list_append(fs, g_strdup_printf("CHANTYPES=%s", info->chantypes));
164
165         if (info->chanmodes != NULL) {
166                 char *tmp = g_strjoinv(",", info->chanmodes);
167                 fs = g_list_append(fs, g_strdup_printf("CHANMODES=%s", tmp));
168                 g_free(tmp);
169         }
170
171         if (info->chanlimit != NULL) {
172                 fs = g_list_append(fs, g_strdup_printf("CHANLIMIT=%s", info->chanlimit));
173         }
174
175         if (info->excepts_mode != '\0')
176                 fs = g_list_append(fs, g_strdup_printf("EXCEPTS=%c", info->excepts_mode));
177
178         if (info->statusmsg != NULL)
179                 fs = g_list_append(fs, g_strdup_printf("STATUSMSG=%s", info->statusmsg));
180
181         if (info->invex_mode != '\0')
182                 fs = g_list_append(fs, g_strdup_printf("INVEX=%c", info->invex_mode));
183
184         if (info->elist_mask_search ||
185                 info->elist_inverse_mask_search ||
186                 info->elist_usercount_search ||
187                 info->elist_creation_time_search ||
188                 info->elist_topic_search) {
189                 char elist[100];
190                 strcpy(elist, "");
191                 if (info->elist_mask_search)
192                         strncat(elist, "M", sizeof(elist));
193                 if (info->elist_inverse_mask_search)
194                         strncat(elist, "N", sizeof(elist));
195                 if (info->elist_usercount_search)
196                         strncat(elist, "U", sizeof(elist));
197                 if (info->elist_creation_time_search)
198                         strncat(elist, "C", sizeof(elist));
199                 if (info->elist_topic_search)
200                         strncat(elist, "T", sizeof(elist));
201                 fs = g_list_append(fs, g_strdup_printf("ELIST=%s", elist));
202         }
203         
204         if (info->deaf_mode != '\0')
205                 fs = g_list_append(fs, g_strdup_printf("DEAF=%c", info->deaf_mode));
206
207         if (info->maxlist != NULL)
208                 fs = g_list_append(fs, g_strdup_printf("MAXLIST=%s", info->maxlist));
209
210         if (info->idchan != NULL)
211                 fs = g_list_append(fs, g_strdup_printf("IDCHAN=%s", info->idchan));
212
213         if (info->prefix != NULL) 
214                 fs = g_list_append(fs, g_strdup_printf("PREFIX=%s", info->prefix));
215
216         ret = list_make_string(fs);
217
218         for (gl = fs; gl; gl = gl->next)
219                 g_free(gl->data);
220
221         g_list_free(fs);
222
223         return ret;
224 }
225
226 void network_info_parse(struct network_info *info, const char *parameter)
227 {
228         char *sep;
229         char *key, *val;
230
231         sep = strchr(parameter, '=');
232
233         if (!sep) { 
234                 key = g_strdup(parameter);
235                 val = NULL; 
236         } else {
237                 key = g_strndup(parameter, sep - parameter);
238                 val = g_strdup(sep+1);
239         }
240         
241         if(!g_strcasecmp(key, "CASEMAPPING")) {
242                 if(!g_strcasecmp(val, "rfc1459")) {
243                         info->casemapping = CASEMAP_RFC1459;
244                 } else if(!g_strcasecmp(val, "strict-rfc1459")) {
245                         info->casemapping = CASEMAP_STRICT_RFC1459;
246                 } else if(!g_strcasecmp(val, "ascii")) {
247                         info->casemapping = CASEMAP_ASCII;
248                 } else {
249                         info->casemapping = CASEMAP_UNKNOWN;
250                         log_global(LOG_WARNING, "Unknown CASEMAPPING value '%s'", val);
251                 }
252         } else if (!g_strcasecmp(key, "NETWORK")) {
253                 g_free(info->name);
254                 info->name = g_strdup(val);
255         } else if (!g_strcasecmp(key, "NICKLEN") || !g_strcasecmp(key, "MAXNICKLEN")) {
256                 info->nicklen = atoi(val);
257         } else if (!g_strcasecmp(key, "USERLEN")) {
258                 info->userlen = atoi(val);
259         } else if (!g_strcasecmp(key, "HOSTLEN")) {
260                 info->hostlen = atoi(val);
261         } else if (!g_strcasecmp(key, "CHANNELLEN") || !g_strcasecmp(key, "MAXCHANNELLEN")) {
262                 info->channellen = atoi(val);
263         } else if (!g_strcasecmp(key, "AWAYLEN")) {
264                 info->awaylen = atoi(val);
265         } else if (!g_strcasecmp(key, "KICKLEN")) {
266                 info->kicklen = atoi(val);
267         } else if (!g_strcasecmp(key, "TOPICLEN")) {
268                 info->topiclen = atoi(val);
269         } else if (!g_strcasecmp(key, "MAXCHANNELS")) {
270                 info->maxchannels = atoi(val);
271         } else if (!g_strcasecmp(key, "MAXTARGETS")) {
272                 info->maxtargets = atoi(val);
273         } else if (!g_strcasecmp(key, "MAXBANS")) {
274                 info->maxbans = atoi(val);
275         } else if (!g_strcasecmp(key, "MODES")) {
276                 info->maxmodes = atoi(val);
277         } else if (!g_strcasecmp(key, "WALLCHOPS")) {
278                 info->wallchops = TRUE;
279         } else if (!g_strcasecmp(key, "WALLVOICES")) {
280                 info->wallvoices = TRUE;
281         } else if (!g_strcasecmp(key, "RFC2812")) {
282                 info->rfc2812 = TRUE;
283         } else if (!g_strcasecmp(key, "PENALTY")) {
284                 info->penalty = TRUE;
285         } else if (!g_strcasecmp(key, "FNC")) {
286                 info->forced_nick_changes = TRUE;
287         } else if (!g_strcasecmp(key, "SAFELIST")) {
288                 info->safelist = TRUE;
289         } else if (!g_strcasecmp(key, "USERIP")) {
290                 info->userip = TRUE;
291         } else if (!g_strcasecmp(key, "CPRIVMSG")) {
292                 info->cprivmsg = TRUE;
293         } else if (!g_strcasecmp(key, "CNOTICE")) {
294                 info->cnotice = TRUE;
295         } else if (!g_strcasecmp(key, "KNOCK")) {
296                 info->knock = TRUE;
297         } else if (!g_strcasecmp(key, "CAPAB")) {
298                 info->capab = TRUE;
299         } else if (!g_strcasecmp(key, "VCHANNELS")) {
300                 info->vchannels = TRUE;
301         } else if (!g_strcasecmp(key, "WHOX")) {
302                 info->whox = TRUE;
303         } else if (!g_strcasecmp(key, "CALLERID")) {
304                 info->callerid = TRUE;
305         } else if (!g_strcasecmp(key, "ACCEPT")) {
306                 info->accept = TRUE;
307         } else if (!g_strcasecmp(key, "KEYLEN")) {
308                 info->keylen = atoi(val);
309         } else if (!g_strcasecmp(key, "SILENCE")) {
310                 info->silence = atoi(val);
311         } else if (!g_strcasecmp(key, "CHANTYPES")) {
312                 g_free(info->chantypes);
313                 info->chantypes = g_strdup(val);
314         } else if (!g_strcasecmp(key, "CHANMODES")) {
315                 g_strfreev(info->chanmodes);
316                 info->chanmodes = g_strsplit(val, ",", 4);
317                 /* FIXME: Make sure info->chanmodes length is exactly 4 */
318         } else if (!g_strcasecmp(key, "CHANLIMIT")) {
319                 g_free(info->chanlimit);
320                 info->chanlimit = g_strdup(val);
321         } else if (!g_strcasecmp(key, "EXCEPTS")) {
322                 if (val == NULL) 
323                         info->excepts_mode = 'e';
324                 else if (strlen(val) > 1)
325                         log_global(LOG_WARNING, "Invalid length excepts value: %s", val);
326                 else
327                         info->excepts_mode = val[0];
328         } else if (!g_strcasecmp(key, "INVEX")) {
329                 if (val == NULL) 
330                         info->invex_mode = 'I';
331                 else if (strlen(val) > 1)
332                         log_global(LOG_WARNING, "Invalid length invex value: %s", val);
333                 else
334                         info->invex_mode = val[0];
335         } else if (!g_strcasecmp(key, "ELIST")) {
336                 int i;
337                 for (i = 0; val[i]; i++) {
338                         switch (val[i]) {
339                         case 'M': info->elist_mask_search = TRUE; break;
340                         case 'N': info->elist_inverse_mask_search = TRUE; break;
341                         case 'T': info->elist_topic_search = TRUE; break;
342                         case 'U': info->elist_usercount_search = TRUE; break;
343                         case 'C': info->elist_creation_time_search = TRUE; break;
344                         default:
345                                   log_global(LOG_WARNING, "Unknown ELIST parameter '%c'", val[i]);
346                                   break;
347                         }
348                 }
349         } else if (!g_strcasecmp(key, "DEAF")) {
350                 if (val == NULL) 
351                         info->deaf_mode = 'D';
352                 else if (strlen(val) > 1)
353                         log_global(LOG_WARNING, "Invalid length deaf value: %s", val);
354                 else
355                         info->deaf_mode = val[0];
356         } else if (!g_strcasecmp(key, "MAXLIST")) {
357                 g_free(info->maxlist);
358                 info->maxlist = g_strdup(val);
359         } else if (!g_strcasecmp(key, "IDCHAN")) {
360                 g_free(info->idchan);
361                 info->idchan = g_strdup(val);
362         } else if (!g_strcasecmp(key, "STATUSMSG")) {
363                 g_free(info->statusmsg);
364                 info->statusmsg = g_strdup(val);
365         } else if (!g_strcasecmp(key, "PREFIX")) {
366                 g_free(info->prefix);
367                 info->prefix = g_strdup(val);
368         } else if (!g_strcasecmp(key, "CHARSET")) {
369                 g_free(info->charset);
370                 info->charset = g_strdup(val);
371         } else {
372                 log_global(LOG_WARNING, "Unknown 005 parameter `%s'", key);
373         }
374         g_free(key);
375         g_free(val);
376 }
377
378 void handle_005(struct network_state *s, struct line *l)
379 {
380         int i;
381         g_assert(s);
382         g_assert(l);
383
384         g_assert(l->argc >= 1);
385
386         for (i = 3; i < l->argc-1; i++) 
387                 network_info_parse(&s->info, l->args[i]);
388 }
389
390 int irccmp(const struct network_info *n, const char *a, const char *b)
391 {
392         switch(n != NULL?n->casemapping:CASEMAP_UNKNOWN) {
393         default:
394         case CASEMAP_UNKNOWN:
395         case CASEMAP_RFC1459:
396                 return str_rfc1459cmp(a,b);
397         case CASEMAP_ASCII:
398                 return str_asciicmp(a,b);
399         case CASEMAP_STRICT_RFC1459:
400                 return str_strictrfc1459cmp(a,b);
401         }
402
403         return 0;
404 }
405
406 gboolean is_channelname(const char *name, const struct network_info *n)
407 {
408         g_assert(n != NULL);
409         g_assert(n->chantypes != NULL);
410
411         g_assert(name != NULL);
412         
413         if (strchr(n->chantypes, name[0])) 
414                 return TRUE;
415
416         return FALSE;
417 }
418
419 gboolean is_prefix(char p, const struct network_info *n)
420 {
421         const char *pref_end;
422         
423         g_assert(n != NULL);
424         g_assert(n->prefix != NULL);
425
426         pref_end = strchr(n->prefix, ')');
427         if (!pref_end)pref_end = n->prefix; else pref_end++;
428
429         if(strchr(pref_end, p)) return TRUE;
430         return FALSE;
431 }
432
433 const char *get_charset(const struct network_info *n)
434 {
435         g_assert(n != NULL);
436         return n->charset;
437 }
438
439 char get_prefix_by_mode(char mode, const struct network_info *n)
440 {
441         int i;
442         char *pref_end;
443         const char *prefix;
444         
445         g_assert(n != NULL);
446         g_assert(n->prefix != NULL);
447
448         prefix = n->prefix;
449         
450         pref_end = strchr(prefix, ')');
451         if(prefix[0] != '(' || !pref_end) {
452                 log_global(LOG_WARNING, "Malformed PREFIX data `%s'", prefix);
453                 return ' ';
454         }
455         pref_end++;
456         prefix++;
457
458         for(i = 0; pref_end[i]; i++) {
459                 if(prefix[i] == mode) return pref_end[i];
460         }
461         return ' ';
462 }
463
464 const static char *default_chanmodes[] = { 
465         "beI", "k" , "l" , "imnpsta" 
466 };
467
468 int network_chanmode_type(char m, struct network_info *n)
469 {
470         int i;
471         for (i = 0; i < 4; i++) {
472                 if (strchr((n && n->chanmodes)?n->chanmodes[i]:default_chanmodes[i], m)) {
473                         return i;
474                 }
475         }
476
477         return 3;
478 }