2 ctrlproxy: A modular IRC proxy
3 (c) 2005 Jelmer Vernooij <jelmer@nl.linux.org>
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.
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.
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.
20 #include "internals.h"
22 #define DEFAULT_PREFIX "(ov)@+"
23 #define DEFAULT_CHANTYPES "#&"
25 void network_info_init(struct network_info *info)
27 memset(info, 0, sizeof(struct network_info));
28 info->prefix = g_strdup(DEFAULT_PREFIX);
29 info->chantypes = g_strdup(DEFAULT_CHANTYPES);
32 void free_network_info(struct network_info *info)
35 g_free(info->chantypes);
36 g_free(info->charset);
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);
45 g_free(info->statusmsg);
48 char *network_info_string(struct network_info *info)
51 GList *fs = NULL, *gl;
54 if (info->name != NULL)
55 fs = g_list_append(fs, g_strdup_printf("NETWORK=%s", info->name));
57 switch (info->casemapping) {
60 casemap = g_strdup("CASEMAPPING=rfc1459");
62 case CASEMAP_STRICT_RFC1459:
63 casemap = g_strdup("CASEMAPPING=strict-rfc1459");
66 casemap = g_strdup("CASEMAPPING=ascii");
71 fs = g_list_append(fs, casemap);
73 if (info->forced_nick_changes)
74 fs = g_list_append(fs, g_strdup("FNC"));
77 fs = g_list_append(fs, "MAP");
79 if (info->charset != NULL)
80 fs = g_list_append(fs, g_strdup_printf("CHARSET=%s", info->charset));
82 if (info->nicklen != 0)
83 fs = g_list_append(fs, g_strdup_printf("NICKLEN=%d", info->nicklen));
85 if (info->userlen != 0)
86 fs = g_list_append(fs, g_strdup_printf("USERLEN=%d", info->userlen));
89 fs = g_list_append(fs, g_strdup_printf("WATCH=%d", info->watch));
92 fs = g_list_append(fs, "VBANLIST");
94 if (info->operoverride)
95 fs = g_list_append(fs, "OVERRIDE");
97 if (info->hostlen != 0)
98 fs = g_list_append(fs, g_strdup_printf("HOSTLEN=%d", info->hostlen));
100 if (info->channellen != 0)
101 fs = g_list_append(fs, g_strdup_printf("CHANNELLEN=%d", info->channellen));
103 if (info->awaylen != 0)
104 fs = g_list_append(fs, g_strdup_printf("AWAYLEN=%d", info->awaylen));
106 if (info->kicklen != 0)
107 fs = g_list_append(fs, g_strdup_printf("KICKLEN=%d", info->kicklen));
109 if (info->topiclen != 0)
110 fs = g_list_append(fs, g_strdup_printf("TOPICLEN=%d", info->topiclen));
112 if (info->maxchannels != 0)
113 fs = g_list_append(fs, g_strdup_printf("MAXCHANNELS=%d", info->maxchannels));
115 if (info->maxtargets != 0)
116 fs = g_list_append(fs, g_strdup_printf("MAXTARGETS=%d", info->maxtargets));
118 if (info->maxbans != 0)
119 fs = g_list_append(fs, g_strdup_printf("MAXBANS=%d", info->maxbans));
121 if (info->maxmodes != 0)
122 fs = g_list_append(fs, g_strdup_printf("MODES=%d", info->maxmodes));
124 if (info->maxpara != 0)
125 fs = g_list_append(fs, g_strdup_printf("MAXPARA=%d", info->maxpara));
128 fs = g_list_append(fs, g_strdup("WALLCHOPS"));
130 if (info->wallvoices)
131 fs = g_list_append(fs, g_strdup("WALLVOICES"));
134 fs = g_list_append(fs, g_strdup("RFC2812"));
137 fs = g_list_append(fs, g_strdup("PENALTY"));
140 fs = g_list_append(fs, g_strdup("REMOVE"));
143 fs = g_list_append(fs, g_strdup("SAFELIST"));
146 fs = g_list_append(fs, g_strdup("USERIP"));
149 fs = g_list_append(fs, g_strdup("CAPAB"));
152 fs = g_list_append(fs, g_strdup("CPRIVMSG"));
155 fs = g_list_append(fs, g_strdup("CNOTICE"));
158 fs = g_list_append(fs, g_strdup("KNOCK"));
161 fs = g_list_append(fs, g_strdup("VCHANNELS"));
164 fs = g_list_append(fs, g_strdup("WHOX"));
167 fs = g_list_append(fs, g_strdup("CALLERID"));
170 fs = g_list_append(fs, g_strdup("ACCEPT"));
172 if (info->keylen != 0)
173 fs = g_list_append(fs, g_strdup_printf("KEYLEN=%d", info->keylen));
176 if (info->silence_limit != 0)
177 fs = g_list_append(fs,
178 g_strdup_printf("SILENCE=%d", info->silence_limit));
180 fs = g_list_append(fs, g_strdup("SILENCE"));
183 if (info->chantypes != NULL)
184 fs = g_list_append(fs, g_strdup_printf("CHANTYPES=%s", info->chantypes));
186 if (info->chanmodes != NULL) {
187 char *tmp = g_strjoinv(",", info->chanmodes);
188 fs = g_list_append(fs, g_strdup_printf("CHANMODES=%s", tmp));
192 if (info->chanlimit != NULL)
193 fs = g_list_append(fs, g_strdup_printf("CHANLIMIT=%s", info->chanlimit));
196 fs = g_list_append(fs, "NAMESX");
198 if (info->securelist)
199 fs = g_list_append(fs, "SECURELIST");
201 if (info->excepts_mode != '\0')
202 fs = g_list_append(fs, g_strdup_printf("EXCEPTS=%c", info->excepts_mode));
204 if (info->statusmsg != NULL)
205 fs = g_list_append(fs, g_strdup_printf("STATUSMSG=%s", info->statusmsg));
207 if (info->invex_mode != '\0')
208 fs = g_list_append(fs, g_strdup_printf("INVEX=%c", info->invex_mode));
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) {
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));
230 if (info->deaf_mode != '\0')
231 fs = g_list_append(fs, g_strdup_printf("DEAF=%c", info->deaf_mode));
233 if (info->maxlist != NULL)
234 fs = g_list_append(fs, g_strdup_printf("MAXLIST=%s", info->maxlist));
236 if (info->idchan != NULL)
237 fs = g_list_append(fs, g_strdup_printf("IDCHAN=%s", info->idchan));
239 if (info->prefix != NULL)
240 fs = g_list_append(fs, g_strdup_printf("PREFIX=%s", info->prefix));
242 ret = list_make_string(fs);
244 for (gl = fs; gl; gl = gl->next)
252 void network_info_parse(struct network_info *info, const char *parameter)
257 sep = strchr(parameter, '=');
260 key = g_strdup(parameter);
263 key = g_strndup(parameter, sep - parameter);
264 val = g_strdup(sep+1);
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;
275 info->casemapping = CASEMAP_UNKNOWN;
276 log_global(LOG_WARNING, "Unknown CASEMAPPING value '%s'", val);
278 } else if (!g_strcasecmp(key, "NETWORK")) {
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")) {
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")) {
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")) {
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")) {
335 } else if (!g_strcasecmp(key, "CAPAB")) {
337 } else if (!g_strcasecmp(key, "VCHANNELS")) {
338 info->vchannels = TRUE;
339 } else if (!g_strcasecmp(key, "WHOX")) {
341 } else if (!g_strcasecmp(key, "CALLERID")) {
342 info->callerid = TRUE;
343 } else if (!g_strcasecmp(key, "ACCEPT")) {
345 } else if (!g_strcasecmp(key, "KEYLEN")) {
346 info->keylen = atoi(val);
347 } else if (!g_strcasecmp(key, "SILENCE")) {
349 info->silence_limit = atoi(val);
350 info->silence = TRUE;
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")) {
365 info->excepts_mode = 'e';
366 else if (strlen(val) > 1)
367 log_global(LOG_WARNING, "Invalid length excepts value: %s", val);
369 info->excepts_mode = val[0];
370 } else if (!g_strcasecmp(key, "INVEX")) {
372 info->invex_mode = 'I';
373 else if (strlen(val) > 1)
374 log_global(LOG_WARNING, "Invalid length invex value: %s", val);
376 info->invex_mode = val[0];
377 } else if (!g_strcasecmp(key, "ELIST")) {
379 for (i = 0; val[i]; 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;
387 log_global(LOG_WARNING, "Unknown ELIST parameter '%c'", val[i]);
391 } else if (!g_strcasecmp(key, "DEAF")) {
393 info->deaf_mode = 'D';
394 else if (strlen(val) > 1)
395 log_global(LOG_WARNING, "Invalid length deaf value: %s", val);
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")) {
415 } else if (!g_strcasecmp(key, "SECURELIST")) {
416 info->securelist = TRUE;
418 log_global(LOG_WARNING, "Unknown 005 parameter `%s'", key);
424 void handle_005(struct network_state *s, struct line *l)
430 g_assert(l->argc >= 1);
432 for (i = 3; i < l->argc-1; i++)
433 network_info_parse(&s->info, l->args[i]);
436 int irccmp(const struct network_info *n, const char *a, const char *b)
438 switch(n != NULL?n->casemapping:CASEMAP_UNKNOWN) {
440 case CASEMAP_UNKNOWN:
441 case CASEMAP_RFC1459:
442 return str_rfc1459cmp(a,b);
444 return str_asciicmp(a,b);
445 case CASEMAP_STRICT_RFC1459:
446 return str_strictrfc1459cmp(a,b);
452 gboolean is_channelname(const char *name, const struct network_info *n)
455 g_assert(n->chantypes != NULL);
457 g_assert(name != NULL);
459 if (strchr(n->chantypes, name[0]))
465 gboolean is_prefix(char p, const struct network_info *n)
467 const char *pref_end;
470 g_assert(n->prefix != NULL);
472 pref_end = strchr(n->prefix, ')');
473 if (!pref_end)pref_end = n->prefix; else pref_end++;
475 if(strchr(pref_end, p)) return TRUE;
479 const char *get_charset(const struct network_info *n)
485 char get_prefix_by_mode(char mode, const struct network_info *n)
492 g_assert(n->prefix != NULL);
496 pref_end = strchr(prefix, ')');
497 if(prefix[0] != '(' || !pref_end) {
498 log_global(LOG_WARNING, "Malformed PREFIX data `%s'", prefix);
504 for(i = 0; pref_end[i]; i++) {
505 if(prefix[i] == mode) return pref_end[i];
510 const static char *default_chanmodes[] = {
511 "beI", "k" , "l" , "imnpsta"
514 int network_chanmode_type(char m, struct network_info *n)
517 for (i = 0; i < 4; i++) {
518 if (strchr((n && n->chanmodes)?n->chanmodes[i]:default_chanmodes[i], m)) {