1 /*
2 * $Id: irc.c,v 1.156 2005/04/21 06:58:50 nohar Exp $
3 *
4 * This file is part of the bip project
5 * Copyright (C) 2004 2005 Arnaud Cornet and Loïc Gomez
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 * See the file "COPYING" for the exact licensing terms.
12 */
13
14 #include "config.h"
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdio.h>
18 #include "util.h"
19 #include "irc.h"
20 #include "bip.h"
21 #include "log.h"
22 #include "connection.h"
23 #include "md5.h"
24
25 #define S_CONN_DELAY (10)
26
27 extern int sighup;
28 extern bip_t *_bip;
29
30 static int irc_join(struct link_server *server, struct line *line);
31 static int irc_part(struct link_server *server, struct line *line);
32 static int irc_mode(struct link_server *server, struct line *line);
33 static int irc_mode_channel(struct link_server *s, struct channel *channel,
34 struct line *line, const char* mode, int add, int cur_arg);
35 static int irc_kick(struct link_server *server, struct line *line);
36 static int irc_privmsg(struct link_server *server, struct line *line);
37 static int irc_notice(struct link_server *server, struct line *line);
38 static int irc_quit(struct link_server *server, struct line *line);
39 static int irc_nick(struct link_server *server, struct line *line);
40 static int irc_generic_quit(struct link_server *server, struct line *line);
41 static int irc_topic(struct link_server *server, struct line *line);
42 static int irc_332(struct link_server *server, struct line *line);
43 static int irc_333(struct link_server *server, struct line *line);
44 static int irc_353(struct link_server *server, struct line *line);
45 static int irc_366(struct link_server *server, struct line *line);
46 static int irc_367(struct link_server *server, struct line *l);
47 static int irc_368(struct link_server *server, struct line *l);
48 void irc_server_shutdown(struct link_server *s);
49 static int origin_is_me(struct line *l, struct link_server *server);
50 static void ls_set_nick(struct link_server *ircs, char *nick);
51 static void server_set_chanmodes(struct link_server *l, const char *chanmodes);
52 static void server_set_prefix(struct link_server *l, const char *prefix);
53 static void server_init_modes(struct link_server *s);
54 static int bip_get_index(const char* str, char car);
55 static int bip_fls(int v);
56
57 void oidentd_dump(bip_t *bip);
58
59 void irc_client_free(struct link_client *cli);
60 extern int conf_log_sync_interval;
61 extern int conf_reconn_timer;
62
63 void write_user_list(connection_t *c, char *dest);
64
65 static void irc_copy_cli(struct link_client *src, struct link_client *dest,
66 struct line *line);
67 static void irc_cli_make_join(struct link_client *ic);
68 static void server_setup_reconnect_timer(struct link *link);
69 int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line);
70
71 #define LAGOUT_TIME 480
72 #define LAGCHECK_TIME (90)
73 #define RECONN_TIMER_MAX (600)
74 #define LOGGING_TIMEOUT (360)
75 #define CONN_INTERVAL 60
76 #define CONNECT_TIMEOUT 60
77
channel_new(const char * name)78 struct channel *channel_new(const char *name)
79 {
80 struct channel *chan;
81 chan = bip_calloc(sizeof(struct channel), 1);
82 chan->name = bip_strdup(name);
83 hash_init(&chan->ovmasks, HASH_NOCASE);
84 return chan;
85 }
86
nick_from_ircmask(const char * mask)87 char *nick_from_ircmask(const char *mask)
88 {
89 const char *nick = mask;
90 char *ret;
91 size_t len;
92
93 assert(mask);
94
95 while (*nick && *nick != '!')
96 nick++;
97 if (!*nick)
98 return bip_strdup(mask);
99 len = nick - mask;
100 ret = bip_malloc(len + 1);
101 memcpy(ret, mask, len);
102 ret[len] = 0;
103 return ret;
104 }
105
106 #define NAMESIZE 256
107
channel_name_list(struct link_server * server,struct channel * c)108 list_t *channel_name_list(struct link_server *server, struct channel *c)
109 {
110 list_t *ret;
111 hash_iterator_t hi;
112 size_t len = 0;
113 char *str = bip_malloc(NAMESIZE + 1);
114
115 ret = list_new(NULL);
116 *str = 0;
117 for (hash_it_init(&c->ovmasks, &hi); hash_it_key(&hi);
118 hash_it_next(&hi)){
119 const char *nick = hash_it_key(&hi);
120 long int ovmask = (long int)hash_it_item(&hi);
121
122 assert(strlen(nick) + 2 < NAMESIZE);
123
124 if (len + strlen(nick) + 2 + (ovmask ? 1 : 0) >= NAMESIZE) {
125 list_add_last(ret, str);
126 str = bip_malloc(NAMESIZE + 1);
127 *str = 0;
128 len = 0;
129 }
130 if (len != 0) {
131 strcat(str, " ");
132 len++;
133 }
134
135 // prepend symbol corresponding to the usermode
136 int msb;
137 if ((msb = bip_fls(ovmask))) {
138 str[len] = server->prefixes[msb - 1];
139 str[++len] = 0;
140 }
141
142 strcat(str, nick);
143 len += strlen(nick);
144 assert(len < NAMESIZE);
145 }
146 list_add_last(ret, str);
147 return ret;
148 }
149
link_name(struct link_any * l)150 char *link_name(struct link_any *l)
151 {
152 if (LINK(l))
153 return LINK(l)->name ? LINK(l)->name : "(null)";
154 return "*connecting*";
155 }
156
irc_001(struct link_server * server,struct line * line)157 static int irc_001(struct link_server *server, struct line *line)
158 {
159 (void)line;
160
161 if (LINK(server)->s_state == IRCS_WAS_CONNECTED)
162 LINK(server)->s_state = IRCS_RECONNECTING;
163 else
164 LINK(server)->s_state = IRCS_CONNECTING;
165
166 /* change nick on client */
167 int i;
168 for (i = 0; i < LINK(server)->l_clientc; i++) {
169 struct link_client *c = LINK(server)->l_clientv[i];
170 WRITE_LINE1(CONN(c), LINK(server)->cli_nick, "NICK",
171 server->nick);
172 }
173 return OK_COPY;
174 }
175
irc_start_lagtest(struct link_server * l)176 void irc_start_lagtest(struct link_server *l)
177 {
178 l->laginit_ts = time(NULL);
179 write_line_fast(CONN(l), "PING :" S_PING "\r\n");
180 }
181
182 /*
183 * returns 0 if we ping timeout
184 */
irc_compute_lag(struct link_server * is)185 void irc_compute_lag(struct link_server *is)
186 {
187 assert(is->laginit_ts != -1);
188 is->lag = time(NULL) - is->laginit_ts;
189 }
190
irc_lags_out(struct link_server * is)191 int irc_lags_out(struct link_server *is)
192 {
193 if (is->lag > LAGOUT_TIME) {
194 mylog(LOG_ERROR, "[%s] Lags out! closing", LINK(is)->name);
195 return 1;
196 } else {
197 mylog(LOG_DEBUG, "[%s] lag : %d\n", LINK(is)->name, is->lag);
198 return 0;
199 }
200 }
201
irc_lag_init(struct link_server * is)202 void irc_lag_init(struct link_server *is)
203 {
204 is->lagtest_timeout = LAGCHECK_TIME;
205 is->laginit_ts = -1;
206 }
207
irc_server_join(struct link_server * s)208 static void irc_server_join(struct link_server *s)
209 {
210 list_iterator_t it;
211 for (list_it_init(&LINK(s)->chan_infos_order, &it); list_it_item(&it);
212 list_it_next(&it)) {
213 struct chan_info *ci = list_it_item(&it);
214 if (!ci->key)
215 WRITE_LINE1(CONN(s), NULL, "JOIN", ci->name);
216 else
217 WRITE_LINE2(CONN(s), NULL, "JOIN", ci->name, ci->key);
218 }
219 }
220
irc_server_connected(struct link_server * server)221 static void irc_server_connected(struct link_server *server)
222 {
223 int i;
224
225 LINK(server)->s_state = IRCS_CONNECTED;
226 LINK(server)->s_conn_attempt = 0;
227
228 mylog(LOG_INFO, "[%s] Connected for user %s",
229 LINK(server)->name, LINK(server)->user->name);
230
231 irc_server_join(server);
232 log_connected(LINK(server)->log);
233
234 if (LINK(server)->cli_nick) {
235 /* we change nick on client */
236 for (i = 0; i < LINK(server)->l_clientc; i++) {
237 struct link_client *ic = LINK(server)->l_clientv[i];
238 WRITE_LINE1(CONN(ic), LINK(server)->cli_nick, "NICK",
239 server->nick);
240 }
241 free(LINK(server)->cli_nick);
242 LINK(server)->cli_nick = NULL;
243 }
244
245 /* basic helper for nickserv and co */
246 list_iterator_t itocs;
247 for (list_it_init(&LINK(server)->on_connect_send, &itocs);
248 list_it_item(&itocs); list_it_next(&itocs)) {
249 ssize_t len = strlen(list_it_item(&itocs)) + 2;
250 char *str = bip_malloc(len + 1);
251 sprintf(str, "%s\r\n", (char *)list_it_item(&itocs));
252 write_line(CONN(server), str);
253 free(str);
254 }
255
256 if (LINK(server)->l_clientc == 0) {
257 if (LINK(server)->away_nick)
258 WRITE_LINE1(CONN(server), NULL, "NICK",
259 LINK(server)->away_nick);
260 if (LINK(server)->no_client_away_msg)
261 WRITE_LINE1(CONN(server), NULL, "AWAY",
262 LINK(server)->no_client_away_msg);
263 }
264 }
265
266 /*
267 * Given the way irc nets disrespect the rfc, we completely forget
268 * about this damn ircmask...
269 :irc.iiens.net 352 pwet * ~a je.suis.t1r.net irc.iiens.net pwet H :0 d
270 -> nohar!~nohar@haruka.t1r.net
271 */
irc_352(struct link_server * server,struct line * line)272 static int irc_352(struct link_server *server, struct line *line)
273 {
274 (void)server;
275 if (!irc_line_includes(line, 6))
276 return ERR_PROTOCOL;
277
278 #if 0
279 if (irc_line_elem_case_equals(line, 6, server->nick)) {
280 const char *nick = server->nick;
281 const char *iname = irc_line_elem(line, 3);
282 const char *ihost = irc_line_elem(line, 4);
283 char *ircmask = bip_malloc(strlen(nick) + strlen(iname) +
284 strlen(ihost) + 3);
285 strcpy(ircmask, nick);
286 strcat(ircmask, "!");
287 strcat(ircmask, iname);
288 strcat(ircmask, "@");
289 strcat(ircmask, ihost);
290 if (server->ircmask)
291 free(server->ircmask);
292 server->ircmask = ircmask;
293 }
294 #endif
295
296 #if 0
297 if (!origin_is_me(line, server)) {
298 struct channel *channel;
299 struct nick *nick;
300
301 channel = hash_get(&server->channels, irc_line_elem(line, 2));
302 if (!channel)
303 return OK_COPY_WHO;
304
305 nick = hash_get(&channel->nicks, irc_line_elem(line, 6));
306 if (!nick)
307 return OK_COPY_WHO;
308 }
309
310 #endif
311 return OK_COPY_WHO;
312 }
313
irc_315(struct link_server * server,struct line * l)314 static int irc_315(struct link_server *server, struct line *l)
315 {
316 (void)l;
317 struct link *link = LINK(server);
318 if (link->who_client) {
319 if (link->who_client->who_count == 0) {
320 mylog(LOG_DEBUG, "Spurious irc_315");
321 return OK_COPY_WHO;
322 }
323 link->who_client->whoc_tstamp = time(NULL);
324 if (link->who_client->who_count > 0) {
325 --link->who_client->who_count;
326 mylog(LOG_DEBUG,
327 "RPL_ENDOFWHO: "
328 "Decrementing who count for %p: %d",
329 link->who_client, link->who_client->who_count);
330 }
331 }
332
333 return OK_COPY_WHO;
334 }
335
rotate_who_client(struct link * link)336 void rotate_who_client(struct link *link)
337 {
338 int i;
339 mylog(LOG_DEBUG, "rotate_who_client %p", link->who_client);
340 /* find a client with non-null who_count */
341 link->who_client = NULL;
342 for (i = 0; i < link->l_clientc; i++) {
343 struct link_client *ic = link->l_clientv[i];
344 if (!list_is_empty(&ic->who_queue)) {
345 char *l;
346 while ((l = list_remove_first(&ic->who_queue))) {
347 write_line(CONN(link->l_server), l);
348 free(l);
349 }
350 link->who_client = ic;
351 break;
352 }
353 }
354 }
355
irc_dispatch_server(bip_t * bip,struct link_server * server,struct line * line)356 int irc_dispatch_server(bip_t *bip, struct link_server *server,
357 struct line *line)
358 {
359 int ret = OK_COPY;
360 /* shut gcc up */
361 (void)bip;
362
363 if (!irc_line_includes(line, 0))
364 return ERR_PROTOCOL;
365
366 if (irc_line_elem_equals(line, 0, "PING")) {
367 if (!irc_line_includes(line, 1))
368 return ERR_PROTOCOL;
369 struct line *resp = irc_line_new();
370 char *resps;
371 irc_line_append(resp, "PONG");
372 irc_line_append(resp, irc_line_elem(line, 1));
373 resp->colon = 1; /* it seems some ircds want it */
374 resps = irc_line_to_string(resp);
375 write_line_fast(CONN(server), resps);
376 irc_line_free(resp);
377 free(resps);
378 ret = OK_FORGET;
379 } else if (irc_line_elem_equals(line, 0, "PONG")) {
380 /* not all server reply with PONG <servername> <our string>
381 * so we blindly assume the PONG is ours. */
382 if (irc_line_count(line) == 2 || irc_line_count(line) == 3) {
383 if (server->laginit_ts != -1) {
384 irc_compute_lag(server);
385 irc_lag_init(server);
386 }
387 ret = OK_FORGET;
388 }
389 } else if (irc_line_elem_equals(line, 0, "433")) {
390 if (LINK(server)->s_state != IRCS_CONNECTED) {
391 size_t nicklen = strlen(server->nick);
392 char *newnick = bip_malloc(nicklen + 2);
393
394 strcpy(newnick, server->nick);
395 if (strlen(server->nick) < 9) {
396 strcat(newnick, "`");
397 } else {
398 if (newnick[7] != '`') {
399 if (newnick[8] != '`') {
400 newnick[8] = '`';
401 } else {
402 newnick[7] = '`';
403 }
404 } else {
405 newnick[8] = rand() *
406 ('z' - 'a') / RAND_MAX + 'a';
407 }
408 newnick[9] = 0;
409 }
410 ls_set_nick(server, newnick);
411
412 WRITE_LINE1(CONN(server), NULL, "NICK", server->nick);
413 ret = OK_FORGET;
414 }
415 } else if (LINK(server)->s_state == IRCS_RECONNECTING) {
416 ret = OK_FORGET;
417 if (irc_line_elem_equals(line, 0, "376")) /* end of motd */
418 irc_server_connected(server);
419 else if (irc_line_elem_equals(line, 0, "422")) /* no motd */
420 irc_server_connected(server);
421
422 } else if (LINK(server)->s_state == IRCS_CONNECTING) {
423 ret = OK_FORGET;
424 if (irc_line_elem_equals(line, 0, "005")) {
425 int i;
426 for (i = irc_line_count(line) - 1; i > 0; i--) {
427 if (LINK(server)->ignore_server_capab &&
428 irc_line_elem_equals(line, i, "CAPAB"))
429 irc_line_drop(line, i);
430 else if (!strncmp(irc_line_elem(line, i), "CHANMODES=", 10))
431 server_set_chanmodes(server, irc_line_elem(line, i) + 10);
432 else if (!strncmp(irc_line_elem(line, i), "PREFIX=(", 8))
433 server_set_prefix(server, irc_line_elem(line, i) + 7);
434 }
435 }
436 if (irc_line_elem_equals(line, 0, "NOTICE")) {
437 } else if (irc_line_elem_equals(line, 0, "376")) {
438 /* end of motd */
439 irc_server_connected(server);
440 list_add_last(&LINK(server)->init_strings,
441 irc_line_dup(line));
442 } else if (irc_line_elem_equals(line, 0, "422")) { /* no motd */
443 irc_server_connected(server);
444 list_add_last(&LINK(server)->init_strings,
445 irc_line_dup(line));
446 } else {
447 list_add_last(&LINK(server)->init_strings,
448 irc_line_dup(line));
449 }
450 } else if (irc_line_elem_equals(line, 0, "001")) {
451 ret = irc_001(server, line);
452 if (LINK(server)->s_state == IRCS_CONNECTING) {
453 if (!list_is_empty(&LINK(server)->init_strings))
454 return ERR_PROTOCOL;
455 /* update the irc mask */
456 list_add_last(&LINK(server)->init_strings,
457 irc_line_dup(line));
458 }
459 } else if (irc_line_elem_equals(line, 0, "JOIN")) {
460 ret = irc_join(server, line);
461 } else if (irc_line_elem_equals(line, 0, "332")) {
462 ret = irc_332(server, line);
463 } else if (irc_line_elem_equals(line, 0, "333")) {
464 ret = irc_333(server, line);
465 } else if (irc_line_elem_equals(line, 0, "352")) {
466 ret = irc_352(server, line);
467 } else if (irc_line_elem_equals(line, 0, "315")) {
468 ret = irc_315(server, line);
469 } else if (irc_line_elem_equals(line, 0, "353")) {
470 ret = irc_353(server, line);
471 } else if (irc_line_elem_equals(line, 0, "366")) {
472 ret = irc_366(server, line);
473 } else if (irc_line_elem_equals(line, 0, "367")) {
474 ret = irc_367(server, line);
475 } else if (irc_line_elem_equals(line, 0, "368")) {
476 ret = irc_368(server, line);
477 } else if (irc_line_elem_equals(line, 0, "PART")) {
478 ret = irc_part(server, line);
479 } else if (irc_line_elem_equals(line, 0, "MODE")) {
480 ret = irc_mode(server, line);
481 } else if (irc_line_elem_equals(line, 0, "TOPIC")) {
482 ret = irc_topic(server, line);
483 } else if (irc_line_elem_equals(line, 0, "KICK")) {
484 ret = irc_kick(server, line);
485 } else if (irc_line_elem_equals(line, 0, "PRIVMSG")) {
486 ret = irc_privmsg(server, line);
487 } else if (irc_line_elem_equals(line, 0, "NOTICE")) {
488 ret = irc_notice(server, line);
489 } else if (irc_line_elem_equals(line, 0, "QUIT")) {
490 ret = irc_quit(server, line);
491 } else if (irc_line_elem_equals(line, 0, "NICK")) {
492 ret = irc_nick(server, line);
493 }
494
495 if (ret == OK_COPY) {
496 int i;
497 for (i = 0; i < LINK(server)->l_clientc; i++) {
498 if (TYPE(LINK(server)->l_clientv[i]) ==
499 IRC_TYPE_CLIENT) {
500 char *s = irc_line_to_string(line);
501 write_line(CONN(LINK(server)->l_clientv[i]), s);
502 free(s);
503 }
504 }
505 }
506 if (ret == OK_COPY_WHO && LINK(server)->who_client) {
507 char *s;
508
509 s = irc_line_to_string(line);
510 write_line(CONN(LINK(server)->who_client), s);
511 free(s);
512 }
513 if (LINK(server)->who_client &&
514 LINK(server)->who_client->who_count == 0) {
515 mylog(LOG_DEBUG, "OK_COPY_WHO: who_count for %p is nul",
516 LINK(server)->who_client);
517 rotate_who_client(LINK(server));
518 }
519 return ret;
520 }
521
522 /* send join and related stuff to client */
irc_send_join(struct link_client * ic,struct channel * chan)523 static void irc_send_join(struct link_client *ic, struct channel *chan)
524 {
525 struct bipuser *user;
526 char *ircmask;
527
528 user = LINK(ic)->user;
529 assert(user);
530
531 /* user ircmask here for rbot */
532 ircmask = bip_malloc(strlen(LINK(ic)->l_server->nick) +
533 strlen(BIP_FAKEMASK) + 1);
534 strcpy(ircmask, LINK(ic)->l_server->nick);
535 strcat(ircmask, BIP_FAKEMASK);
536 WRITE_LINE1(CONN(ic), ircmask, "JOIN", chan->name);
537 free(ircmask);
538
539 if (chan->topic)
540 WRITE_LINE3(CONN(ic), P_SERV, "332", LINK(ic)->l_server->nick,
541 chan->name, chan->topic);
542 if (chan->creator && chan->create_ts)
543 WRITE_LINE4(CONN(ic), P_SERV, "333", LINK(ic)->l_server->nick,
544 chan->name, chan->creator, chan->create_ts);
545
546 list_t *name_list = channel_name_list(LINK(ic)->l_server, chan);
547 char *s;
548 while ((s = list_remove_first(name_list))) {
549 char tmptype[2];
550 tmptype[0] = chan->type;
551 tmptype[1] = 0;
552 WRITE_LINE4(CONN(ic), P_SERV, "353", LINK(ic)->l_server->nick,
553 tmptype, chan->name, s);
554 free(s);
555 }
556 list_free(name_list);
557
558 WRITE_LINE3(CONN(ic), P_SERV, "366", LINK(ic)->l_server->nick,
559 chan->name, "End of /NAMES list.");
560 }
561
write_init_string(connection_t * c,struct line * line,char * nick)562 static void write_init_string(connection_t *c, struct line *line, char *nick)
563 {
564 char *l;
565
566 l = irc_line_to_string_to(line, nick);
567 write_line(c, l);
568 free(l);
569 }
570
bind_to_link(struct link * l,struct link_client * ic)571 static void bind_to_link(struct link *l, struct link_client *ic)
572 {
573 int i = l->l_clientc;
574
575 LINK(ic) = l;
576 l->l_clientc++;
577 l->l_clientv = bip_realloc(l->l_clientv, l->l_clientc *
578 sizeof(struct link_client *));
579 l->l_clientv[i] = ic;
580 }
581
unbind_from_link(struct link_client * ic)582 void unbind_from_link(struct link_client *ic)
583 {
584 struct link *l = LINK(ic);
585 int i;
586
587 for (i = 0; i < l->l_clientc; i++)
588 if (l->l_clientv[i] == ic)
589 break;
590
591 assert(i != l->l_clientc);
592
593 if (l->who_client == ic) {
594 mylog(LOG_DEBUG, "unbind_from_link: %p: %d", l->who_client,
595 ic->who_count);
596 l->who_client = NULL;
597 }
598
599 for (i = i + 1; i < l->l_clientc; i++)
600 l->l_clientv[i - 1] = l->l_clientv[i];
601
602 l->l_clientc--;
603 l->l_clientv = bip_realloc(l->l_clientv, l->l_clientc *
604 sizeof(struct link_client *));
605 if (l->l_clientc == 0) { /* bip_realloc was equiv to free() */
606 l->l_clientv = NULL;
607 return;
608 }
609 }
610
irc_cli_bip(bip_t * bip,struct link_client * ic,struct line * line)611 int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line)
612 {
613 return adm_bip(bip, ic, line, 0);
614 }
615
616 #define PASS_SEP ':'
617
get_str_elem(char * str,int num)618 static char *get_str_elem(char *str, int num)
619 {
620 char *ret;
621 char *c;
622 char *cur = str;
623 int index = 0;
624
625 while ((c = strchr(cur, PASS_SEP))) {
626 if (index < num) {
627 index++;
628 cur = c + 1;
629 continue;
630 }
631 if (c - cur < 1)
632 return NULL;
633 ret = bip_malloc(c - cur + 1);
634 memcpy(ret, cur, c - cur);
635 ret[c - cur] = 0;
636 return ret;
637 }
638 if (index == num) {
639 c = str + strlen(str);
640 if (c - cur < 1)
641 return NULL;
642 ret = bip_malloc(c - cur + 1);
643 memcpy(ret, cur, c - cur);
644 ret[c - cur] = 0;
645 return ret;
646 }
647 return NULL;
648 }
649
irc_cli_make_join(struct link_client * ic)650 static void irc_cli_make_join(struct link_client *ic)
651 {
652 if (LINK(ic)->l_server) {
653 /* join channels, step one, those in conf, in order */
654 list_iterator_t li;
655 for (list_it_init(&LINK(ic)->chan_infos_order, &li);
656 list_it_item(&li); list_it_next(&li)) {
657 struct chan_info *ci = (struct chan_info *)
658 list_it_item(&li);
659 struct channel *chan;
660 if ((chan = hash_get(&LINK(ic)->l_server->channels,
661 ci->name)))
662 irc_send_join(ic, chan);
663 }
664
665 /* step two, those not in conf */
666 hash_iterator_t hi;
667 for (hash_it_init(&LINK(ic)->l_server->channels, &hi);
668 hash_it_item(&hi); hash_it_next(&hi)) {
669 struct channel *chan = (struct channel *)
670 hash_it_item(&hi);
671 if (!hash_get(&LINK(ic)->chan_infos, chan->name))
672 irc_send_join(ic, chan);
673 }
674 }
675 }
676
irc_cli_backlog(struct link_client * ic,int hours)677 void irc_cli_backlog(struct link_client *ic, int hours)
678 {
679 struct bipuser *user;
680
681 user = LINK(ic)->user;
682 assert(user);
683 assert(LINK(ic)->l_server);
684
685 if (!user->backlog) {
686 mylog(LOG_DEBUG, "Backlog disabled for %s, not backlogging",
687 user->name);
688 return;
689 }
690
691 if (hours != 0) {
692 /* have some limit */
693 if (hours > 24 * 366)
694 hours = 24 * 366;
695 }
696
697 list_t *backlogl;
698 char *bl;
699 list_t *bllines;
700
701 backlogl = log_backlogs(LINK(ic)->log);
702 while ((bl = list_remove_first(backlogl))) {
703 bllines = backlog_lines(LINK(ic)->log, bl,
704 LINK(ic)->l_server->nick, hours);
705 if (bllines) {
706 if (!list_is_empty(bllines)) {
707 mylog(LOG_INFO, "[%s] backlogging: %s",
708 LINK(ic)->name, bl);
709 write_lines(CONN(ic), bllines);
710 }
711 list_free(bllines);
712 }
713 free(bl);
714 }
715 list_free(backlogl);
716 }
717
irc_cli_startup(bip_t * bip,struct link_client * ic,struct line * line)718 static int irc_cli_startup(bip_t *bip, struct link_client *ic,
719 struct line *line)
720 {
721 char *init_nick;
722 char *user, *pass, *connname;
723 (void)line;
724
725 assert(ic->init_pass);
726
727 user = get_str_elem(ic->init_pass, 0);
728 if (!user)
729 return ERR_AUTH;
730 pass = get_str_elem(ic->init_pass, 1);
731 if (!pass) {
732 free(user);
733 return ERR_AUTH;
734 }
735 connname = get_str_elem(ic->init_pass, 2);
736 if (!connname) {
737 free(pass);
738 free(user);
739 return ERR_AUTH;
740 }
741
742 list_iterator_t it;
743 for (list_it_init(&bip->link_list, &it); list_it_item(&it);
744 list_it_next(&it)) {
745 struct link *l = list_it_item(&it);
746 if (strcmp(user, l->user->name) == 0 &&
747 strcmp(connname, l->name) == 0) {
748 if (chash_cmp(pass, l->user->password,
749 l->user->seed) == 0) {
750 bind_to_link(l, ic);
751 break;
752 }
753 }
754 }
755
756 if (!LINK(ic))
757 mylog(LOG_ERROR, "[%s] Invalid credentials (user: %s)",
758 connname, user);
759 free(user);
760 free(connname);
761 free(pass);
762
763 free(ic->init_pass);
764 ic->init_pass = NULL;
765 init_nick = ic->init_nick;
766 ic->init_nick = NULL;
767
768 if (!LINK(ic)) {
769 free(init_nick);
770 return ERR_AUTH;
771 }
772
773 #ifdef HAVE_LIBSSL
774 if (LINK(ic)->s_state != IRCS_CONNECTED) {
775 /* Check if we have an untrusted certificate from the server */
776 if (ssl_check_trust(ic)) {
777 free(init_nick);
778 return OK_FORGET;
779 }
780 }
781 #endif
782
783 if (LINK(ic)->s_state == IRCS_NONE) {
784 /* drop it if corresponding server hasn't connected at all. */
785 write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
786 ":ERROR Proxy not yet connected, try again "
787 "later\r\n");
788 unbind_from_link(ic);
789 free(init_nick);
790 return OK_CLOSE;
791 }
792
793 list_remove(&bip->connecting_client_list, ic);
794 TYPE(ic) = IRC_TYPE_CLIENT;
795
796 for (list_it_init(&LINK(ic)->init_strings, &it);
797 list_it_item(&it); list_it_next(&it))
798 write_init_string(CONN(ic), list_it_item(&it), init_nick);
799
800 /* we change nick on server */
801 if (LINK(ic)->l_server) {
802 struct link_server *server = LINK(ic)->l_server;
803 WRITE_LINE1(CONN(ic), init_nick, "NICK", server->nick);
804
805 if (!LINK(ic)->ignore_first_nick)
806 WRITE_LINE1(CONN(server), NULL, "NICK", init_nick);
807 else if (LINK(ic)->away_nick &&
808 strcmp(LINK(ic)->away_nick, server->nick) == 0)
809 WRITE_LINE1(CONN(server), NULL, "NICK",
810 LINK(server)->connect_nick);
811
812 /* change away status */
813 if (server && LINK(ic)->no_client_away_msg)
814 WRITE_LINE0(CONN(server), NULL, "AWAY");
815 }
816
817 if (!LINK(ic)->l_server) {
818 free(init_nick);
819 return OK_FORGET;
820 }
821
822 irc_cli_make_join(ic);
823 irc_cli_backlog(ic, 0);
824
825 log_client_connected(LINK(ic)->log);
826 free(init_nick);
827
828 return OK_FORGET;
829 }
830
irc_cli_nick(bip_t * bip,struct link_client * ic,struct line * line)831 static int irc_cli_nick(bip_t *bip, struct link_client *ic, struct line *line)
832 {
833 if (irc_line_count(line) != 2)
834 return ERR_PROTOCOL;
835
836 if ((ic->state & IRCC_READY) == IRCC_READY)
837 return OK_COPY;
838
839 ic->state |= IRCC_NICK;
840 if (ic->init_nick)
841 free(ic->init_nick);
842 ic->init_nick = bip_strdup(irc_line_elem(line, 1));
843
844 if ((ic->state & IRCC_READY) == IRCC_READY)
845 return irc_cli_startup(bip, ic, line);
846
847 if ((ic->state & IRCC_PASS) != IRCC_PASS)
848 WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", ic->init_nick,
849 "You should type /QUOTE PASS your_username:"
850 "your_password:your_connection_name");
851
852 return OK_FORGET;
853 }
854
irc_cli_user(bip_t * bip,struct link_client * ic,struct line * line)855 static int irc_cli_user(bip_t *bip, struct link_client *ic, struct line *line)
856 {
857 if (irc_line_count(line) != 5)
858 return ERR_PROTOCOL;
859
860 if ((ic->state & IRCC_READY) == IRCC_READY)
861 return ERR_PROTOCOL;
862
863 ic->state |= IRCC_USER;
864 if ((ic->state & IRCC_READY) == IRCC_READY)
865 return irc_cli_startup(bip, ic, line);
866 return OK_FORGET;
867 }
868
irc_cli_pass(bip_t * bip,struct link_client * ic,struct line * line)869 static int irc_cli_pass(bip_t *bip, struct link_client *ic, struct line *line)
870 {
871 if (irc_line_count(line) != 2)
872 return ERR_PROTOCOL;
873
874 if ((ic->state & IRCC_READY) == IRCC_READY)
875 return ERR_PROTOCOL;
876
877 ic->state |= IRCC_PASS;
878 if (ic->init_pass)
879 free(ic->init_pass);
880 ic->init_pass = bip_strdup(irc_line_elem(line, 1));
881 if ((ic->state & IRCC_READY) == IRCC_READY)
882 return irc_cli_startup(bip, ic, line);
883 return OK_FORGET;
884 }
885
irc_cli_quit(struct link_client * ic,struct line * line)886 static int irc_cli_quit(struct link_client *ic, struct line *line)
887 {
888 (void)ic;
889 (void)line;
890 return OK_CLOSE;
891 }
892
irc_cli_privmsg(bip_t * bip,struct link_client * ic,struct line * line)893 static int irc_cli_privmsg(bip_t *bip, struct link_client *ic,
894 struct line *line)
895 {
896 if (!irc_line_includes(line, 2))
897 return OK_FORGET;
898
899 if (irc_line_elem_equals(line, 1, "-bip"))
900 return adm_bip(bip, ic, line, 1);
901 else
902 log_cli_privmsg(LINK(ic)->log, LINK(ic)->l_server->nick,
903 irc_line_elem(line, 1), irc_line_elem(line, 2));
904
905 if (LINK(ic)->user->blreset_on_talk) {
906 if (LINK(ic)->user->blreset_connection)
907 log_reset_all(LINK(ic)->log);
908 else
909 log_reset_store(LINK(ic)->log, irc_line_elem(line, 1));
910 }
911 return OK_COPY_CLI;
912 }
913
irc_cli_notice(struct link_client * ic,struct line * line)914 static int irc_cli_notice(struct link_client *ic, struct line *line)
915 {
916 if (!irc_line_includes(line, 2))
917 return OK_FORGET;
918 log_cli_notice(LINK(ic)->log, LINK(ic)->l_server->nick,
919 irc_line_elem(line, 1), irc_line_elem(line, 2));
920 if (LINK(ic)->user->blreset_on_talk) {
921 if (LINK(ic)->user->blreset_connection)
922 log_reset_all(LINK(ic)->log);
923 else
924 log_reset_store(LINK(ic)->log, irc_line_elem(line, 1));
925 }
926 return OK_COPY_CLI;
927 }
928
irc_cli_who(struct link_client * ic,struct line * line)929 static int irc_cli_who(struct link_client *ic, struct line *line)
930 {
931 struct link *l = LINK(ic);
932
933 ++ic->who_count;
934 if (ic->who_count == 1)
935 ic->whoc_tstamp = time(NULL);
936 mylog(LOG_DEBUG, "cli_who: Incrementing who count for %p: %d",
937 ic, ic->who_count);
938
939 if (l->who_client && l->who_client != ic) {
940 list_add_first(&ic->who_queue, irc_line_to_string(line));
941 return OK_FORGET;
942 }
943
944 if (!l->who_client)
945 l->who_client = ic;
946
947 return OK_COPY;
948 }
949
irc_cli_mode(struct link_client * ic,struct line * line)950 static int irc_cli_mode(struct link_client *ic, struct line *line)
951 {
952 struct link *l = LINK(ic);
953
954 if (irc_line_count(line) != 3)
955 return OK_COPY;
956
957 /* This is a wild guess and that sucks. */
958 if (!irc_line_elem_equals(line, 0, "MODE") ||
959 strchr(irc_line_elem(line, 2), 'b') == NULL)
960 return OK_COPY;
961
962 ++ic->who_count;
963 if (ic->who_count == 1)
964 ic->whoc_tstamp = time(NULL);
965 mylog(LOG_DEBUG, "cli_mode: Incrementing who count for %p: %d",
966 l->who_client, ic->who_count);
967
968 if (l->who_client && l->who_client != ic) {
969 list_add_first(&ic->who_queue, irc_line_to_string(line));
970 return OK_FORGET;
971 }
972
973 if (!l->who_client)
974 l->who_client = ic;
975
976 return OK_COPY;
977 }
978
979
irc_notify_disconnection(struct link_server * is)980 static void irc_notify_disconnection(struct link_server *is)
981 {
982 int i;
983 LINK(is)->cli_nick = bip_strdup(is->nick);
984
985 for (i = 0; i < LINK(is)->l_clientc; i++) {
986 struct link_client *ic = LINK(is)->l_clientv[i];
987 hash_iterator_t hi;
988 for (hash_it_init(&is->channels, &hi); hash_it_item(&hi);
989 hash_it_next(&hi)) {
990 struct channel *c = (struct channel *)hash_it_item(&hi);
991 WRITE_LINE3(CONN(ic), P_IRCMASK, "KICK",
992 c->name, is->nick,
993 "Server disconnected, reconnecting");
994 }
995 bip_notify(ic, "Server disconnected, reconnecting");
996 }
997 }
998
irc_add_channel_info(struct link_server * ircs,const char * chan,const char * key)999 void irc_add_channel_info(struct link_server *ircs, const char *chan,
1000 const char *key)
1001 {
1002 struct chan_info *ci;
1003 if (!ischannel(*chan))
1004 return;
1005
1006 ci = hash_get(&LINK(ircs)->chan_infos, chan);
1007 if (!ci) {
1008 struct chan_info *ci;
1009 ci = chan_info_new();
1010 ci->name = bip_strdup(chan);
1011 ci->key = key ? bip_strdup(key) : NULL;
1012 ci->backlog = 1;
1013 hash_insert(&LINK(ircs)->chan_infos, chan, ci);
1014 list_add_last(&LINK(ircs)->chan_infos_order, ci);
1015 } else {
1016 if (ci->key) {
1017 free(ci->key);
1018 ci->key = NULL;
1019 }
1020 ci->key = key ? bip_strdup(key) : NULL;
1021 }
1022 }
1023
irc_cli_join(struct link_client * irc,struct line * line)1024 static int irc_cli_join(struct link_client *irc, struct line *line)
1025 {
1026 if (irc_line_count(line) != 2 && irc_line_count(line) != 3)
1027 return ERR_PROTOCOL;
1028
1029 const char *s, *e, *ks, *ke = NULL;
1030 s = irc_line_elem(line, 1);
1031 if (irc_line_count(line) == 3)
1032 ks = irc_line_elem(line, 2);
1033 else
1034 ks = NULL;
1035
1036 while ((e = strchr(s, ','))) {
1037 size_t len = e - s;
1038 char *p = bip_malloc(len + 1);
1039 size_t klen;
1040 char *kp = NULL;
1041
1042 memcpy(p, s, len);
1043 p[len] = 0;
1044 if (ks) {
1045 if (strlen(ks)) {
1046 ke = strchr(ks, ',');
1047 if (!ke)
1048 ke = ks + strlen(ks);
1049 klen = ke - ks;
1050 kp = bip_malloc(klen + 1);
1051 memcpy(kp, ks, klen);
1052 kp[klen] = 0;
1053 if (*ke == 0)
1054 ks = NULL;
1055 } else {
1056 kp = NULL;
1057 ks = NULL;
1058 }
1059 }
1060
1061 irc_add_channel_info(LINK(irc)->l_server, p, kp);
1062 free(p);
1063 if (kp) {
1064 free(kp);
1065 if (ks)
1066 ks = ke + 1;
1067 }
1068 s = e + 1;
1069 }
1070
1071 irc_add_channel_info(LINK(irc)->l_server, s, ks);
1072 return OK_COPY;
1073 }
1074
irc_cli_part(struct link_client * irc,struct line * line)1075 static int irc_cli_part(struct link_client *irc, struct line *line)
1076 {
1077 struct chan_info *ci;
1078 char *cname;
1079
1080 if (irc_line_count(line) != 2 && irc_line_count(line) != 3)
1081 return ERR_PROTOCOL;
1082
1083 cname = (char *)irc_line_elem(line, 1);
1084
1085 if ((ci = hash_remove_if_exists(&LINK(irc)->chan_infos,
1086 cname)) != NULL) {
1087 list_remove(&LINK(irc)->chan_infos_order, ci);
1088 free(ci->name);
1089 if (ci->key)
1090 free(ci->key);
1091 free(ci);
1092 }
1093 return OK_COPY;
1094 }
1095
1096 #ifdef HAVE_LIBSSL
irc_dispatch_trust_client(struct link_client * ic,struct line * line)1097 static int irc_dispatch_trust_client(struct link_client *ic, struct line *line)
1098 {
1099 int r = OK_COPY;
1100 if (!irc_line_includes(line, 1))
1101 return ERR_PROTOCOL;
1102
1103 if (strcasecmp(irc_line_elem(line, 0), "BIP") == 0 &&
1104 strcasecmp(irc_line_elem(line, 1), "TRUST") == 0)
1105 r = adm_trust(ic, line);
1106
1107 return r;
1108 }
1109 #endif
1110
irc_dispatch_client(bip_t * bip,struct link_client * ic,struct line * line)1111 static int irc_dispatch_client(bip_t *bip, struct link_client *ic,
1112 struct line *line)
1113 {
1114 int r = OK_COPY;
1115 if (irc_line_count(line) == 0)
1116 return ERR_PROTOCOL;
1117
1118 if (irc_line_elem_equals(line, 0, "PING")) {
1119 if (!irc_line_includes(line, 1))
1120 return ERR_PROTOCOL;
1121 WRITE_LINE1(CONN(ic), link_name((struct link_any *)ic), "PONG",
1122 irc_line_elem(line, 1));
1123 r = OK_FORGET;
1124 } else if (LINK(ic)->s_state != IRCS_CONNECTED) {
1125 write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
1126 ":ERROR Proxy not connected, please wait "
1127 "before sending commands\r\n");
1128 r = OK_FORGET;
1129 } else if (strcasecmp(irc_line_elem(line, 0), "BIP") == 0) {
1130 r = irc_cli_bip(bip, ic, line);
1131 } else if (irc_line_elem_equals(line, 0, "JOIN")) {
1132 r = irc_cli_join(ic, line);
1133 } else if (irc_line_elem_equals(line, 0, "PART")) {
1134 r = irc_cli_part(ic, line);
1135 } else if (irc_line_elem_equals(line, 0, "NICK")) {
1136 r = irc_cli_nick(bip, ic, line);
1137 } else if (irc_line_elem_equals(line, 0, "QUIT")) {
1138 r = irc_cli_quit(ic, line);
1139 } else if (irc_line_elem_equals(line, 0, "PRIVMSG")) {
1140 r = irc_cli_privmsg(bip, ic, line);
1141 } else if (irc_line_elem_equals(line, 0, "NOTICE")) {
1142 r = irc_cli_notice(ic, line);
1143 } else if (irc_line_elem_equals(line, 0, "WHO")) {
1144 r = irc_cli_who(ic, line);
1145 } else if (irc_line_elem_equals(line, 0, "MODE")) {
1146 r = irc_cli_mode(ic, line);
1147 }
1148
1149 if (r == OK_COPY || r == OK_COPY_CLI) {
1150 char *str = irc_line_to_string(line);
1151 if (LINK(ic)->s_state == IRCS_CONNECTED &&
1152 LINK(ic)->l_server->nick)
1153 write_line(CONN(LINK(ic)->l_server), str);
1154 else if (LINK(ic)->l_server->nick)
1155 WRITE_LINE2(CONN(ic), P_IRCMASK,
1156 (LINK(ic)->user->bip_use_notice ?
1157 "NOTICE" : "PRIVMSG"),
1158 LINK(ic)->l_server->nick,
1159 ":Not connected please try again "
1160 "later...\r\n");
1161
1162 free(str);
1163 if (r == OK_COPY_CLI) {
1164 int i;
1165 struct link_server *s = LINK(ic)->l_server;
1166
1167 for (i = 0; i < LINK(s)->l_clientc; i++)
1168 irc_copy_cli(ic, LINK(s)->l_clientv[i], line);
1169 }
1170 }
1171 return r;
1172 }
1173
irc_copy_cli(struct link_client * src,struct link_client * dest,struct line * line)1174 static void irc_copy_cli(struct link_client *src, struct link_client *dest,
1175 struct line *line)
1176 {
1177 char *str;
1178
1179 if (src == dest)
1180 return;
1181
1182 if (!irc_line_includes(line, 1) ||
1183 !irc_line_elem_equals(line, 0, "PRIVMSG")) {
1184 str = irc_line_to_string(line);
1185 write_line(CONN(dest), str);
1186 free(str);
1187 return;
1188 }
1189
1190 if (ischannel(*irc_line_elem(line, 1)) || LINK(src) != LINK(dest)) {
1191 assert(!line->origin);
1192 line->origin = LINK(src)->l_server->nick;
1193 str = irc_line_to_string(line);
1194 line->origin = NULL;
1195 write_line(CONN(dest), str);
1196 free(str);
1197 return;
1198 }
1199
1200 /* LINK(src) == LINK(dest) */
1201 size_t len = strlen(irc_line_elem(line, 2)) + 5;
1202 char *tmp;
1203
1204 if (len == 0)
1205 return;
1206
1207 tmp = bip_malloc(len);
1208
1209 snprintf(tmp, len, " -> %s", irc_line_elem(line, 2));
1210 tmp[len - 1] = 0;
1211
1212 struct line *retline = irc_line_new();
1213
1214 retline->origin = bip_strdup(irc_line_elem(line, 1));
1215 irc_line_append(retline, irc_line_elem(line, 0));
1216 irc_line_append(retline, LINK(src)->l_server->nick);
1217 irc_line_append(retline, tmp);
1218 free(tmp);
1219 str = irc_line_to_string(retline);
1220 irc_line_free(retline);
1221 #if 0
1222 /* tricky: */
1223 irc_line_elem(line, 1) = LINK(src)->l_server->nick;
1224
1225 oldelem = irc_line_elem(line, 2);
1226 irc_line_elem(line, 2) = tmp;
1227 str = irc_line_to_string(line);
1228 /* end of trick: */
1229 irc_line_elem(line, 1) = line->origin;
1230 irc_line_elem(line, 2) = oldelem;
1231 line->origin = NULL;
1232 #endif
1233 write_line(CONN(dest), str);
1234 free(str);
1235 return;
1236 }
1237
irc_dispatch_loging_client(bip_t * bip,struct link_client * ic,struct line * line)1238 static int irc_dispatch_loging_client(bip_t *bip, struct link_client *ic,
1239 struct line *line)
1240 {
1241 if (irc_line_count(line) == 0)
1242 return ERR_PROTOCOL;
1243
1244 if (irc_line_elem_equals(line, 0, "NICK")) {
1245 return irc_cli_nick(bip, ic, line);
1246 } else if (irc_line_elem_equals(line, 0, "USER")) {
1247 return irc_cli_user(bip, ic, line);
1248 } else if (irc_line_elem_equals(line, 0, "PASS")) {
1249 return irc_cli_pass(bip, ic, line);
1250 }
1251 return OK_FORGET;
1252 }
1253
irc_dispatch(bip_t * bip,struct link_any * l,struct line * line)1254 int irc_dispatch(bip_t *bip, struct link_any *l, struct line *line)
1255 {
1256 switch (TYPE(l)) {
1257 case IRC_TYPE_SERVER:
1258 return irc_dispatch_server(bip, (struct link_server*)l, line);
1259 break;
1260 case IRC_TYPE_CLIENT:
1261 return irc_dispatch_client(bip, (struct link_client*)l, line);
1262 break;
1263 case IRC_TYPE_LOGING_CLIENT:
1264 return irc_dispatch_loging_client(bip, (struct link_client*)l,
1265 line);
1266 break;
1267 #ifdef HAVE_LIBSSL
1268 case IRC_TYPE_TRUST_CLIENT:
1269 return irc_dispatch_trust_client((struct link_client*)l, line);
1270 break;
1271 #endif
1272 default:
1273 fatal("gnéééééé");
1274 }
1275 return ERR_PROTOCOL; /* never reached */
1276 }
1277
origin_is_me(struct line * l,struct link_server * server)1278 static int origin_is_me(struct line *l, struct link_server *server)
1279 {
1280 char *nick;
1281
1282 if (!l->origin)
1283 return 0;
1284 nick = nick_from_ircmask(l->origin);
1285 if (strcasecmp(nick, server->nick) == 0) {
1286 free(nick);
1287 return 1;
1288 }
1289 free(nick);
1290 return 0;
1291 }
1292
irc_join(struct link_server * server,struct line * line)1293 static int irc_join(struct link_server *server, struct line *line)
1294 {
1295 char *s_nick;
1296 const char *s_chan;
1297 struct channel *channel;
1298
1299 if (irc_line_count(line) != 2 && irc_line_count(line) != 3)
1300 return ERR_PROTOCOL;
1301
1302 s_chan = irc_line_elem(line, 1);
1303 log_join(LINK(server)->log, line->origin, s_chan);
1304
1305 channel = hash_get(&server->channels, s_chan);
1306 if (origin_is_me(line, server)) {
1307 if (!channel) {
1308 channel = channel_new(s_chan);
1309 hash_insert(&server->channels, s_chan, channel);
1310 }
1311 return OK_COPY;
1312 }
1313 /* if we're not on channel and !origin_is_me, we should not get any
1314 * JOIN */
1315 if (!channel)
1316 return ERR_PROTOCOL;
1317 if (!line->origin)
1318 return ERR_PROTOCOL;
1319
1320 s_nick = nick_from_ircmask(line->origin);
1321 hash_insert(&channel->ovmasks, s_nick, 0);
1322 free(s_nick);
1323 return OK_COPY;
1324 }
1325
irc_332(struct link_server * server,struct line * line)1326 static int irc_332(struct link_server *server, struct line *line)
1327 {
1328 struct channel *channel;
1329 if (irc_line_count(line) != 4)
1330 return ERR_PROTOCOL;
1331
1332 channel = hash_get(&server->channels, irc_line_elem(line, 2));
1333 /* we can get topic reply for chans we're not on */
1334 if (!channel)
1335 return OK_COPY;
1336
1337 if (channel->topic)
1338 free(channel->topic);
1339 channel->topic = bip_strdup(irc_line_elem(line, 3));
1340
1341 log_init_topic(LINK(server)->log, channel->name, channel->topic);
1342 return OK_COPY;
1343 }
1344
irc_333(struct link_server * server,struct line * line)1345 static int irc_333(struct link_server *server, struct line *line)
1346 {
1347 struct channel *channel;
1348 if (!irc_line_includes(line, 2))
1349 return ERR_PROTOCOL;
1350
1351 channel = hash_get(&server->channels, irc_line_elem(line, 2));
1352 /* we can get topic info reply for chans we're not on */
1353 if (!channel)
1354 return OK_COPY;
1355 if (channel->creator)
1356 free(channel->creator);
1357 if (channel->create_ts)
1358 free(channel->create_ts);
1359 if (irc_line_count(line) == 5) {
1360 channel->creator = bip_strdup(irc_line_elem(line, 3));
1361 channel->create_ts = bip_strdup(irc_line_elem(line, 4));
1362 } else {
1363 channel->creator = bip_strdup("");
1364 channel->create_ts = bip_strdup("0");
1365 }
1366 log_init_topic_time(LINK(server)->log, channel->name, channel->creator,
1367 channel->create_ts);
1368 return OK_COPY;
1369 }
1370
irc_353(struct link_server * server,struct line * line)1371 static int irc_353(struct link_server *server, struct line *line)
1372 {
1373 struct channel *channel;
1374 const char *names, *eon;
1375 size_t len;
1376 char *nick;
1377
1378 if (irc_line_count(line) != 5)
1379 return ERR_PROTOCOL;
1380
1381 channel = hash_get(&server->channels, irc_line_elem(line, 3));
1382 /* we can get names reply for chans we're not on */
1383 if (!channel)
1384 return OK_COPY;
1385
1386 if (!channel->running_names) {
1387 channel->running_names = 1;
1388 hash_clean(&channel->ovmasks);
1389 }
1390
1391 /* TODO check that type is one of "=" / "*" / "@" */
1392 channel->type = irc_line_elem(line, 2)[0];
1393
1394 names = irc_line_elem(line, 4);
1395
1396 int index;
1397 while (*names) {
1398 long int ovmask = 0;
1399 /* some ircds (e.g. unreal) may display several flags for the
1400 same nick */
1401 while ((index = bip_get_index(server->prefixes, *names))) {
1402 ovmask |= 1 << index;
1403 names++;
1404 }
1405 eon = names;
1406 while (*eon && *eon != ' ')
1407 eon++;
1408
1409 len = eon - names;
1410 nick = bip_malloc(len + 1);
1411 memcpy(nick, names, len);
1412 nick[len] = 0;
1413
1414 /* we just ignore names for nicks that are crazy long */
1415 if (len + 2 < NAMESIZE)
1416 hash_insert(&channel->ovmasks, nick, (void *)ovmask);
1417 free(nick);
1418
1419 while (*eon && *eon == ' ')
1420 eon++;
1421 names = eon;
1422 }
1423 return OK_COPY;
1424 }
1425
irc_366(struct link_server * server,struct line * line)1426 static int irc_366(struct link_server *server, struct line *line)
1427 {
1428 struct channel *channel;
1429
1430 if (irc_line_count(line) != 4)
1431 return ERR_PROTOCOL;
1432
1433 channel = hash_get(&server->channels, irc_line_elem(line, 2));
1434 if (channel && channel->running_names)
1435 channel->running_names = 0;
1436 return OK_COPY;
1437 }
1438
irc_367(struct link_server * server,struct line * l)1439 static int irc_367(struct link_server *server, struct line *l)
1440 {
1441 (void)server;
1442 (void)l;
1443 return OK_COPY_WHO;
1444 }
1445
1446 /* same as irc_315 */
irc_368(struct link_server * server,struct line * l)1447 static int irc_368(struct link_server *server, struct line *l)
1448 {
1449 (void)l;
1450 struct link *link = LINK(server);
1451 if (link->who_client) {
1452 if (link->who_client->who_count == 0) {
1453 mylog(LOG_DEBUG, "Spurious irc_368");
1454 return OK_COPY_WHO;
1455 }
1456 link->who_client->whoc_tstamp = time(NULL);
1457
1458 if (link->who_client->who_count > 0) {
1459 --link->who_client->who_count;
1460 mylog(LOG_DEBUG,
1461 "RPL_ENDOFBANLIST: "
1462 "Decrementing who count for %p: %d",
1463 link->who_client, link->who_client->who_count);
1464 }
1465 }
1466
1467 return OK_COPY_WHO;
1468 }
1469
channel_free(struct channel * c)1470 static void channel_free(struct channel *c)
1471 {
1472 if (c->name)
1473 free(c->name);
1474 if (c->mode)
1475 free(c->mode);
1476 if (c->key)
1477 free(c->key);
1478 if (c->topic)
1479 free(c->topic);
1480 if (c->creator)
1481 free(c->creator);
1482 if (c->create_ts)
1483 free(c->create_ts);
1484
1485 hash_clean(&c->ovmasks);
1486 free(c);
1487 }
1488
irc_part(struct link_server * server,struct line * line)1489 static int irc_part(struct link_server *server, struct line *line)
1490 {
1491 char *s_nick;
1492 const char *s_chan;
1493 struct channel *channel;
1494
1495 if (irc_line_count(line) != 2 && irc_line_count(line) != 3)
1496 return ERR_PROTOCOL;
1497
1498 s_chan = irc_line_elem(line, 1);
1499
1500 channel = hash_get(&server->channels, s_chan);
1501 /* we can't get part message for chans we're not on */
1502 if (!channel)
1503 return ERR_PROTOCOL;
1504
1505 if (origin_is_me(line, server)) {
1506 log_part(LINK(server)->log, line->origin, s_chan,
1507 irc_line_count(line) == 3 ? irc_line_elem(line, 2) :
1508 NULL);
1509 log_reset_store(LINK(server)->log, s_chan);
1510 log_drop(LINK(server)->log, s_chan);
1511
1512 hash_remove(&server->channels, s_chan);
1513 channel_free(channel);
1514 return OK_COPY;
1515 }
1516
1517 if (!line->origin)
1518 return ERR_PROTOCOL;
1519 s_nick = nick_from_ircmask(line->origin);
1520 if (!hash_includes(&channel->ovmasks, s_nick)) {
1521 free(s_nick);
1522 return ERR_PROTOCOL;
1523 }
1524 hash_remove(&channel->ovmasks, s_nick);
1525 free(s_nick);
1526
1527 log_part(LINK(server)->log, line->origin, s_chan,
1528 irc_line_count(line) == 3 ?
1529 irc_line_elem(line, 2) : NULL);
1530
1531 return OK_COPY;
1532 }
1533
mode_add_letter_uniq(struct link_server * s,char c)1534 static void mode_add_letter_uniq(struct link_server *s, char c)
1535 {
1536 int i;
1537 for (i = 0; i < s->user_mode_len; i++) {
1538 if (s->user_mode[i] == c)
1539 return;
1540 }
1541 s->user_mode = bip_realloc(s->user_mode, s->user_mode_len + 1);
1542 s->user_mode[s->user_mode_len++] = c;
1543 }
1544
mode_remove_letter(struct link_server * s,char c)1545 static void mode_remove_letter(struct link_server *s, char c)
1546 {
1547 int i;
1548 for (i = 0; i < s->user_mode_len; i++) {
1549 if (s->user_mode[i] == c) {
1550 for (; i < s->user_mode_len - 1; i++)
1551 s->user_mode[i] = s->user_mode[i + 1];
1552 s->user_mode_len--;
1553 s->user_mode = bip_realloc(s->user_mode,
1554 s->user_mode_len);
1555 return;
1556 }
1557 }
1558 }
1559
irc_user_mode(struct link_server * server,struct line * line)1560 static void irc_user_mode(struct link_server *server, struct line *line)
1561 {
1562 const char *mode;
1563 int add = 1;
1564
1565 for (mode = irc_line_elem(line, 2); *mode; mode++) {
1566 if (*mode == '-')
1567 add = 0;
1568 else if (*mode == '+')
1569 add = 1;
1570 else {
1571 if (add) {
1572 mode_add_letter_uniq(server, *mode);
1573 } else {
1574 mode_remove_letter(server, *mode);
1575 }
1576 }
1577 }
1578 }
1579
irc_mode(struct link_server * server,struct line * line)1580 static int irc_mode(struct link_server *server, struct line *line)
1581 {
1582 struct channel *channel;
1583 const char *mode;
1584 int add = 1;
1585 unsigned cur_arg = 0;
1586 array_t *mode_args = NULL;
1587 int ret;
1588
1589 if (!irc_line_includes(line, 2))
1590 return ERR_PROTOCOL;
1591
1592 /* nick mode change */
1593 if (irc_line_elem_equals(line, 1, server->nick)) {
1594 if (irc_line_includes(line, 3))
1595 mode_args = array_extract(&line->words, 3, -1);
1596 log_mode(LINK(server)->log, line->origin,
1597 irc_line_elem(line, 1), irc_line_elem(line, 2),
1598 mode_args);
1599 if (mode_args)
1600 array_free(mode_args);
1601 irc_user_mode(server, line);
1602 return OK_COPY;
1603 }
1604
1605 if (!ischannel(irc_line_elem(line, 1)[0]))
1606 return ERR_PROTOCOL;
1607
1608 /* channel mode change */
1609 channel = hash_get(&server->channels, irc_line_elem(line, 1));
1610 /* we can't get mode message for chans we're not on */
1611 if (!channel)
1612 return ERR_PROTOCOL;
1613
1614 mode_args = NULL;
1615 if (irc_line_includes(line, 3))
1616 mode_args = array_extract(&line->words, 3, -1);
1617 log_mode(LINK(server)->log, line->origin, irc_line_elem(line, 1),
1618 irc_line_elem(line, 2), mode_args);
1619 if (mode_args)
1620 array_free(mode_args);
1621
1622 /*
1623 * MODE -a+b.. #channel args
1624 * ^ ^
1625 * mode cur_arg
1626 */
1627 for (mode = irc_line_elem(line, 2); *mode; mode++) {
1628 if (*mode == '-')
1629 add = 0;
1630 else if (*mode == '+')
1631 add = 1;
1632 else {
1633 int i = 0;
1634 char *str = 0;
1635
1636 // Check if mode is known: first user modes then
1637 // server modes
1638 if (!(str = strchr(server->usermodes, *mode))) {
1639 array_each(&server->chanmodes, i, str) {
1640 if ((str = strchr(str, *mode)))
1641 break;
1642 }
1643 }
1644
1645 if (str) {
1646 // Usermodes, types A & B always take a parameter
1647 // Type C take a parameter only when set
1648 if (i <= 1 || (i == 2 && add)) {
1649 if (!irc_line_includes(line, cur_arg + 3)) {
1650 return ERR_PROTOCOL;
1651 } else {
1652 ret = irc_mode_channel(server, channel, line, mode,
1653 add, cur_arg);
1654 cur_arg++;
1655 }
1656 } else {
1657 ret = irc_mode_channel(server, channel, line, mode, add,
1658 cur_arg);
1659 }
1660 }
1661 }
1662 if (ret == ERR_PROTOCOL)
1663 return ret;
1664 }
1665 return OK_COPY;
1666 }
1667
irc_mode_channel(struct link_server * s,struct channel * channel,struct line * line,const char * mode,int add,int cur_arg)1668 static int irc_mode_channel(struct link_server *s, struct channel *channel,
1669 struct line *line, const char* mode, int add, int cur_arg)
1670 {
1671 const char *nick;
1672 long int ovmask;
1673 int index;
1674
1675 if (*mode == 'k') {
1676 if (add) {
1677 channel->key = bip_strdup(
1678 irc_line_elem(line, cur_arg + 3));
1679 } else {
1680 if (channel->key) {
1681 free(channel->key);
1682 channel->key = NULL;
1683 }
1684 }
1685 } else if ((index = bip_get_index(s->usermodes, *mode))) {
1686 nick = irc_line_elem(line, cur_arg + 3);
1687
1688 if (!hash_includes(&channel->ovmasks, nick))
1689 return ERR_PROTOCOL;
1690 ovmask = (long int)hash_remove(&channel->ovmasks, nick);
1691
1692 if (add)
1693 ovmask |= 1 << index;
1694 else
1695 ovmask &= ~(1 << index);
1696 hash_insert(&channel->ovmasks, nick, (void *)ovmask);
1697 }
1698 return OK_COPY;
1699 }
1700
irc_timestamp(void)1701 static char *irc_timestamp(void)
1702 {
1703 char *ts = bip_malloc(21);
1704 snprintf(ts, 20, "%ld", (long int)time(NULL));
1705 return ts;
1706 }
1707
irc_topic(struct link_server * server,struct line * line)1708 static int irc_topic(struct link_server *server, struct line *line)
1709 {
1710 struct channel *channel;
1711 const char *topic;
1712
1713 if (irc_line_count(line) != 3)
1714 return ERR_PROTOCOL;
1715
1716 channel = hash_get(&server->channels, irc_line_elem(line, 1));
1717 /* we can't get topic message for chans we're not on */
1718 if (!channel)
1719 return ERR_PROTOCOL;
1720
1721 if (channel->topic)
1722 free(channel->topic);
1723 topic = irc_line_elem(line, 2);
1724 if (*topic == ':')
1725 topic++;
1726 channel->topic = bip_strdup(topic);
1727
1728 /*
1729 * :arion.oftc.net 333 bip`luser #bipqSDFQE3
1730 * nohar!~nohar@borne28.noc.nerim.net 1107338095
1731 */
1732
1733 if (channel->creator)
1734 free(channel->creator);
1735 channel->creator = bip_strmaydup(line->origin);
1736 if (channel->create_ts)
1737 free(channel->create_ts);
1738 channel->create_ts = irc_timestamp();
1739
1740 log_topic(LINK(server)->log, line->origin, irc_line_elem(line, 1),
1741 topic);
1742 return OK_COPY;
1743 }
1744
irc_kick(struct link_server * server,struct line * line)1745 static int irc_kick(struct link_server *server, struct line *line)
1746 {
1747 struct channel *channel;
1748
1749 if (irc_line_count(line) != 3 && irc_line_count(line) != 4)
1750 return ERR_PROTOCOL;
1751
1752 channel = hash_get(&server->channels, irc_line_elem(line, 1));
1753 /* we can't get kick message for chans we're not on */
1754 if (!channel)
1755 return ERR_PROTOCOL;
1756
1757 if (!hash_includes(&channel->ovmasks, irc_line_elem(line, 2)))
1758 return ERR_PROTOCOL;
1759
1760 if (strcasecmp(irc_line_elem(line, 2), server->nick) == 0) {
1761 /* we get kicked !! */
1762 log_kick(LINK(server)->log, line->origin, channel->name,
1763 irc_line_elem(line, 2),
1764 irc_line_count(line) == 4 ?
1765 irc_line_elem(line, 3) : NULL);
1766 log_reset_store(LINK(server)->log, channel->name);
1767 log_drop(LINK(server)->log, channel->name);
1768
1769 if (LINK(server)->autojoin_on_kick) {
1770 if (!channel->key)
1771 WRITE_LINE1(CONN(server), NULL, "JOIN",
1772 channel->name);
1773 else
1774 WRITE_LINE2(CONN(server), NULL, "JOIN",
1775 channel->name, channel->key);
1776 }
1777
1778 hash_remove(&server->channels, channel->name);
1779 channel_free(channel);
1780 return OK_COPY;
1781 }
1782
1783 hash_remove(&channel->ovmasks, irc_line_elem(line, 2));
1784 log_kick(LINK(server)->log, line->origin, irc_line_elem(line, 1),
1785 irc_line_elem(line, 2),
1786 irc_line_count(line) == 4 ? irc_line_elem(line, 3) : NULL);
1787
1788
1789 return OK_COPY;
1790 }
1791
irc_privmsg_check_ctcp(struct link_server * server,struct line * line)1792 static void irc_privmsg_check_ctcp(struct link_server *server,
1793 struct line *line)
1794 {
1795 if (irc_line_count(line) != 3)
1796 return;
1797
1798 if (!line->origin)
1799 return;
1800
1801 char *nick;
1802 nick = nick_from_ircmask(line->origin);
1803 if (irc_line_elem_equals(line, 2, "\001VERSION\001")) {
1804 WRITE_LINE2(CONN(server), NULL, "NOTICE", nick,
1805 "\001VERSION bip-" PACKAGE_VERSION "\001");
1806 }
1807 free(nick);
1808 }
1809
irc_privmsg(struct link_server * server,struct line * line)1810 static int irc_privmsg(struct link_server *server, struct line *line)
1811 {
1812 if (!irc_line_includes(line, 2))
1813 return ERR_PROTOCOL;
1814 if (LINK(server)->s_state == IRCS_CONNECTED)
1815 log_privmsg(LINK(server)->log, line->origin,
1816 irc_line_elem(line, 1), irc_line_elem(line, 2));
1817 irc_privmsg_check_ctcp(server, line);
1818 return OK_COPY;
1819 }
1820
irc_notice(struct link_server * server,struct line * line)1821 static int irc_notice(struct link_server *server, struct line *line)
1822 {
1823 if (!irc_line_includes(line, 2))
1824 return ERR_PROTOCOL;
1825 if (LINK(server)->s_state == IRCS_CONNECTED)
1826 log_notice(LINK(server)->log, line->origin,
1827 irc_line_elem(line, 1), irc_line_elem(line, 2));
1828 return OK_COPY;
1829 }
1830
irc_quit(struct link_server * server,struct line * line)1831 static int irc_quit(struct link_server *server, struct line *line)
1832 {
1833 return irc_generic_quit(server, line);
1834 }
1835
irc_nick(struct link_server * server,struct line * line)1836 static int irc_nick(struct link_server *server, struct line *line)
1837 {
1838 struct channel *channel;
1839 hash_iterator_t hi;
1840 char *org_nick;
1841 const char *dst_nick;
1842
1843 if (irc_line_count(line) != 2)
1844 return ERR_PROTOCOL;
1845
1846 if (!line->origin)
1847 return ERR_PROTOCOL;
1848
1849 org_nick = nick_from_ircmask(line->origin);
1850 dst_nick = irc_line_elem(line, 1);
1851
1852 for (hash_it_init(&server->channels, &hi); hash_it_item(&hi);
1853 hash_it_next(&hi)) {
1854 channel = hash_it_item(&hi);
1855 if (!hash_includes(&channel->ovmasks, org_nick))
1856 continue;
1857 hash_rename_key(&channel->ovmasks, org_nick, dst_nick);
1858 log_nick(LINK(server)->log, org_nick, channel->name, dst_nick);
1859 }
1860
1861 if (origin_is_me(line, server)) {
1862 free(server->nick);
1863 server->nick = bip_strdup(dst_nick);
1864 if (LINK(server)->follow_nick &&
1865 (LINK(server)->away_nick == NULL ||
1866 strcmp(server->nick, LINK(server)->away_nick))
1867 != 0) {
1868 free(LINK(server)->connect_nick);
1869 LINK(server)->connect_nick = bip_strdup(server->nick);
1870 }
1871 }
1872
1873 free(org_nick);
1874 return OK_COPY;
1875 }
1876
irc_generic_quit(struct link_server * server,struct line * line)1877 static int irc_generic_quit(struct link_server *server, struct line *line)
1878 {
1879 struct channel *channel;
1880 hash_iterator_t hi;
1881 char *s_nick;
1882
1883 if (irc_line_count(line) != 2 && irc_line_count(line) != 1)
1884 return ERR_PROTOCOL;
1885
1886 if (!line->origin)
1887 return ERR_PROTOCOL;
1888 s_nick = nick_from_ircmask(line->origin);
1889 for (hash_it_init(&server->channels, &hi); hash_it_item(&hi);
1890 hash_it_next(&hi)) {
1891 channel = hash_it_item(&hi);
1892 if (!hash_includes(&channel->ovmasks, s_nick))
1893 continue;
1894 hash_remove(&channel->ovmasks, s_nick);
1895 log_quit(LINK(server)->log, line->origin, channel->name,
1896 irc_line_includes(line, 1) ?
1897 irc_line_elem(line, 1) : NULL);
1898 }
1899 free(s_nick);
1900 return OK_COPY;
1901 }
1902
ls_set_nick(struct link_server * ircs,char * nick)1903 static void ls_set_nick(struct link_server *ircs, char *nick)
1904 {
1905 if (ircs->nick)
1906 free(ircs->nick);
1907 ircs->nick = nick;
1908 #if 0
1909 if (ircs->ircmask) {
1910 char *eom = strchr(ircs->ircmask, '!');
1911 if (!eom) {
1912 free(ircs->ircmask);
1913 goto fake;
1914 }
1915 eom = bip_strdup(eom);
1916 free(ircs->ircmask);
1917 ircs->ircmask = bip_malloc(strlen(nick) + strlen(eom) + 1);
1918 strcpy(ircs->ircmask, nick);
1919 strcat(ircs->ircmask, eom);
1920 free(eom);
1921 return;
1922 }
1923 fake:
1924 ircs->ircmask = bip_malloc(strlen(nick) + strlen(BIP_FAKEMASK) + 1);
1925 strcpy(ircs->ircmask, nick);
1926 strcat(ircs->ircmask, BIP_FAKEMASK);
1927 #endif
1928 }
1929
irc_server_startup(struct link_server * ircs)1930 static void irc_server_startup(struct link_server *ircs)
1931 {
1932 char *nick;
1933 char *username, *realname;
1934
1935 /* lower the token number as freenode hates fast login */
1936 CONN(ircs)->token = 1;
1937
1938 if (LINK(ircs)->s_password)
1939 WRITE_LINE1(CONN(ircs), NULL, "PASS", LINK(ircs)->s_password);
1940
1941 username = LINK(ircs)->username;
1942 if (!username)
1943 username = LINK(ircs)->user->default_username;
1944 realname = LINK(ircs)->realname;
1945 if (!realname)
1946 realname = LINK(ircs)->user->default_realname;
1947 WRITE_LINE4(CONN(ircs), NULL, "USER", username, "0", "*", realname);
1948
1949 nick = ircs->nick;
1950 if (LINK(ircs)->away_nick && LINK(ircs)->l_clientc == 0) {
1951 if (nick)
1952 free(nick);
1953 nick = bip_strdup(LINK(ircs)->away_nick);
1954 }
1955 if ((!LINK(ircs)->follow_nick && !LINK(ircs)->away_nick)
1956 || nick == NULL) {
1957 if (nick)
1958 free(nick);
1959 if (!LINK(ircs)->connect_nick)
1960 LINK(ircs)->connect_nick =
1961 bip_strdup(LINK(ircs)->user->default_nick);
1962 nick = bip_strdup(LINK(ircs)->connect_nick);
1963 }
1964
1965 ls_set_nick(ircs, nick);
1966 WRITE_LINE1(CONN(ircs), NULL, "NICK", ircs->nick);
1967 }
1968
server_next(struct link * l)1969 static void server_next(struct link *l)
1970 {
1971 l->cur_server++;
1972 if (l->cur_server >= l->network->serverc)
1973 l->cur_server = 0;
1974 }
1975
irc_accept_new(connection_t * conn)1976 static struct link_client *irc_accept_new(connection_t *conn)
1977 {
1978 struct link_client *ircc;
1979 connection_t *newconn;
1980
1981 newconn = accept_new(conn);
1982 if (!newconn)
1983 return NULL;
1984
1985 ircc = bip_calloc(sizeof(struct link_client), 1);
1986 CONN(ircc) = newconn;
1987 TYPE(ircc) = IRC_TYPE_LOGING_CLIENT;
1988 CONN(ircc)->user_data = ircc;
1989 return ircc;
1990 }
1991
server_cleanup(struct link_server * server)1992 void server_cleanup(struct link_server *server)
1993 {
1994 if (server->nick) {
1995 free(server->nick);
1996 server->nick = NULL;
1997 }
1998 if (LINK(server)->s_state == IRCS_CONNECTED) {
1999 LINK(server)->s_state = IRCS_WAS_CONNECTED;
2000 } else {
2001 struct line *s;
2002 LINK(server)->s_state = IRCS_NONE;
2003 while ((s = list_remove_first(&LINK(server)->init_strings)))
2004 irc_line_free(s);
2005 }
2006
2007 hash_iterator_t hi;
2008 for (hash_it_init(&server->channels, &hi); hash_it_item(&hi);
2009 hash_it_next(&hi))
2010 channel_free(hash_it_item(&hi));
2011 hash_clean(&server->channels);
2012
2013 if (CONN(server)) {
2014 connection_free(CONN(server));
2015 CONN(server) = NULL;
2016 }
2017 irc_lag_init(server);
2018 }
2019
irc_client_close(struct link_client * ic)2020 void irc_client_close(struct link_client *ic)
2021 {
2022 if (TYPE(ic) == IRC_TYPE_CLIENT) {
2023 struct link_server *is = LINK(ic)->l_server;
2024 log_client_disconnected(LINK(ic)->log);
2025 unbind_from_link(ic);
2026 if (LINK(ic)->l_clientc == 0) {
2027 if (is && LINK(ic)->away_nick)
2028 WRITE_LINE1(CONN(is), NULL, "NICK",
2029 LINK(ic)->away_nick);
2030 if (is && LINK(ic)->no_client_away_msg)
2031 WRITE_LINE1(CONN(is), NULL, "AWAY",
2032 LINK(ic)->no_client_away_msg);
2033 log_client_none_connected(LINK(ic)->log);
2034 }
2035 irc_client_free(ic);
2036 } else if (TYPE(ic) == IRC_TYPE_TRUST_CLIENT) {
2037 unbind_from_link(ic);
2038 irc_client_free(ic);
2039 } else if (TYPE(ic) == IRC_TYPE_LOGING_CLIENT) {
2040 irc_client_free(ic);
2041 }
2042 }
2043
server_setup_reconnect_timer(struct link * link)2044 static void server_setup_reconnect_timer(struct link *link)
2045 {
2046 int timer = 0;
2047
2048 if (link->last_connection_attempt &&
2049 time(NULL) - link->last_connection_attempt
2050 < CONN_INTERVAL) {
2051 timer = conf_reconn_timer * (link->s_conn_attempt);
2052 if (timer > RECONN_TIMER_MAX)
2053 timer = RECONN_TIMER_MAX;
2054 }
2055 mylog(LOG_ERROR, "[%s] reconnecting in %d seconds", link->name,
2056 timer);
2057 link->recon_timer = timer;
2058 }
2059
irc_close(struct link_any * l)2060 static void irc_close(struct link_any *l)
2061 {
2062 if (CONN(l)) {
2063 connection_free(CONN(l));
2064 CONN(l) = NULL;
2065 }
2066 if (TYPE(l) == IRC_TYPE_SERVER) {
2067 /* TODO: free link_server as a whole */
2068 struct link_server *is = (struct link_server *)l;
2069
2070 if (LINK(is)->s_state == IRCS_CONNECTED)
2071 irc_notify_disconnection(is);
2072 irc_server_shutdown(is);
2073 log_disconnected(LINK(is)->log);
2074
2075 server_next(LINK(is));
2076 server_cleanup(is);
2077 server_setup_reconnect_timer(LINK(is));
2078
2079 LINK(is)->l_server = NULL;
2080 irc_server_free((struct link_server *)is);
2081 } else {
2082 irc_client_close((struct link_client *)l);
2083 }
2084 }
2085
irc_client_new(void)2086 struct link_client *irc_client_new(void)
2087 {
2088 struct link_client *c;
2089
2090 c = bip_calloc(sizeof(struct link_client), 1);
2091 list_init(&c->who_queue, list_ptr_cmp);
2092
2093 return c;
2094 }
2095
irc_server_new(struct link * link,connection_t * conn)2096 struct link_server *irc_server_new(struct link *link, connection_t *conn)
2097 {
2098 struct link_server *s;
2099
2100 s = bip_calloc(sizeof(struct link_server), 1);
2101
2102 TYPE(s) = IRC_TYPE_SERVER;
2103 hash_init(&s->channels, HASH_NOCASE);
2104
2105 link->l_server = s;
2106 LINK(s) = link;
2107 CONN(s) = conn;
2108
2109 irc_lag_init(s);
2110 array_init(&s->chanmodes);
2111 s->prefixes = NULL;
2112 s->usermodes = NULL;
2113 server_init_modes(s);
2114
2115 return s;
2116 }
2117
server_init_modes(struct link_server * s)2118 static void server_init_modes(struct link_server *s)
2119 {
2120 // Default values used if CHANMODES is not specified by the server
2121 array_push(&s->chanmodes, bip_strdup("beHIq"));
2122 array_push(&s->chanmodes, bip_strdup("k"));
2123 array_push(&s->chanmodes, bip_strdup("fjl"));
2124 array_push(&s->chanmodes, bip_strdup("fjl"));
2125
2126 // Default values used if prefix is not specified by the server
2127 s->prefixes = bip_realloc(s->prefixes, sizeof(*s->prefixes) * 3);
2128 s->usermodes = bip_realloc(s->usermodes, sizeof(s->usermodes) * 3);
2129 strcpy(s->prefixes, "@+");
2130 strcpy(s->usermodes, "ov");
2131 }
2132
irc_server_free(struct link_server * s)2133 void irc_server_free(struct link_server *s)
2134 {
2135 if (CONN(s))
2136 connection_free(CONN(s));
2137 if (s->nick)
2138 free(s->nick);
2139 if (s->user_mode)
2140 free(s->user_mode);
2141
2142 int i;
2143 char *ptr;
2144 array_each(&s->chanmodes, i, ptr)
2145 free(ptr);
2146
2147 MAYFREE(s->prefixes);
2148 MAYFREE(s->usermodes);
2149
2150 hash_iterator_t hi;
2151 for (hash_it_init(&s->channels, &hi); hash_it_item(&hi);
2152 hash_it_next(&hi)) {
2153 struct channel *chan = hash_it_item(&hi);
2154 channel_free(chan);
2155 }
2156 hash_clean(&s->channels);
2157
2158 free(s);
2159 }
2160
irc_server_connect(struct link * link)2161 connection_t *irc_server_connect(struct link *link)
2162 {
2163 struct link_server *ls;
2164 connection_t *conn;
2165
2166 link->s_conn_attempt++;
2167
2168 mylog(LOG_INFO, "[%s] Connecting user '%s' using server "
2169 "%s:%d", link->name, link->user->name,
2170 link->network->serverv[link->cur_server].host,
2171 link->network->serverv[link->cur_server].port);
2172 conn = connection_new(link->network->serverv[link->cur_server].host,
2173 link->network->serverv[link->cur_server].port,
2174 link->vhost, link->bind_port,
2175 #ifdef HAVE_LIBSSL
2176 link->network->ssl,
2177 link->network->ciphers,
2178 link->ssl_check_mode,
2179 link->user->ssl_check_store,
2180 link->user->ssl_client_certfile,
2181 #else
2182 0, NULL, 0, NULL, NULL,
2183 #endif
2184 CONNECT_TIMEOUT);
2185 assert(conn);
2186 if (conn->handle == -1) {
2187 mylog(LOG_INFO, "[%s] Cannot connect.", link->name);
2188 connection_free(conn);
2189 server_next(link);
2190 return NULL;
2191 }
2192
2193 ls = irc_server_new(link, conn);
2194 conn->user_data = ls;
2195
2196 list_add_last(&_bip->conn_list, conn);
2197 oidentd_dump(_bip);
2198 irc_server_startup(ls);
2199 return conn;
2200 }
2201
irc_server_lag_compute(struct link * l)2202 int irc_server_lag_compute(struct link *l)
2203 {
2204 struct link_server *server = l->l_server;
2205
2206 if (LINK(server)->s_state == IRCS_CONNECTED) {
2207 if (server->laginit_ts != -1) {
2208 irc_compute_lag(server);
2209 if (!irc_lags_out(server))
2210 return 0;
2211 return 1;
2212 } else {
2213 server->lagtest_timeout--;
2214 if (server->lagtest_timeout == 0)
2215 irc_start_lagtest(server);
2216 }
2217 }
2218 return 0;
2219 }
2220
irc_server_shutdown(struct link_server * s)2221 void irc_server_shutdown(struct link_server *s)
2222 {
2223 int i;
2224 char *cur;
2225 array_each(&s->chanmodes, i, cur)
2226 free(cur);
2227 array_deinit(&s->chanmodes);
2228
2229 server_init_modes(s);
2230
2231 if (!s->nick)
2232 return;
2233 if (LINK(s)->prev_nick)
2234 free(LINK(s)->prev_nick);
2235 LINK(s)->prev_nick = bip_strdup(s->nick);
2236 }
2237
2238 #define BIP_OIDENTD_START "## AUTOGENERATED BY BIP. DO NOT EDIT ##\n"
2239 #define BIP_OIDENTD_END "## END OF AUTOGENERATED STUFF ##\n"
2240 #define BIP_OIDENTD_END_LENGTH strlen(BIP_OIDENTD_END)
2241
oidentd_dump(bip_t * bip)2242 void oidentd_dump(bip_t *bip)
2243 {
2244 mylog(LOG_ERROR, "%d %s", bip->write_oidentd, bip->oidentdpath);
2245 if (!bip->write_oidentd || bip->oidentdpath == NULL) {
2246 return;
2247 }
2248 list_iterator_t it;
2249 FILE *f;
2250 char *bipstart = NULL, *bipend = NULL;
2251 struct stat stats;
2252 char tag_written = 0;
2253
2254 if (stat(bip->oidentdpath, &stats) == -1) {
2255 if (errno == ENOENT && (f = fopen(bip->oidentdpath, "w+"))) {
2256 fchmod(fileno(f), 0644);
2257 } else {
2258 mylog(LOG_WARN, "Can't open/create %s",
2259 bip->oidentdpath);
2260 return;
2261 }
2262 } else {
2263 /* strip previously autogenerated content */
2264 char *content;
2265 f = fopen(bip->oidentdpath, "r+");
2266
2267 if (!f) {
2268 mylog(LOG_WARN, "Can't open/create %s",
2269 bip->oidentdpath);
2270 return;
2271 }
2272
2273 content = (char *)bip_malloc(stats.st_size + 1);
2274
2275 if (fread(content, 1, stats.st_size, f) !=
2276 (size_t)stats.st_size) {
2277 mylog(LOG_WARN, "Can't read %s fully",
2278 bip->oidentdpath);
2279 free(content);
2280 goto clean_oidentd;
2281 }
2282
2283 /* Set terminating zero for strstr */
2284 content[stats.st_size] = '\0';
2285
2286 bipstart = strstr(content, BIP_OIDENTD_START);
2287 if (bipstart != NULL) {
2288 /* We have some config left, rewrite the file
2289 * completely */
2290 fseek(f, SEEK_SET, 0);
2291 if (ftruncate(fileno(f), 0) == -1) {
2292 mylog(LOG_DEBUG, "Can't reset %s size",
2293 bip->oidentdpath);
2294 free(content);
2295 goto clean_oidentd;
2296 }
2297
2298 bipend = strstr(bipstart, BIP_OIDENTD_END);
2299
2300 /* data preceeding the tag */
2301 fwrite(content, 1, bipstart - content, f);
2302
2303 /* data following the tag, if any */
2304 if (bipend != NULL)
2305 fwrite(bipend + BIP_OIDENTD_END_LENGTH, 1,
2306 stats.st_size -
2307 (bipend - content) -
2308 BIP_OIDENTD_END_LENGTH, f);
2309 else
2310 mylog(LOG_WARN, "No %s mark found in %s",
2311 BIP_OIDENTD_END,
2312 bip->oidentdpath);
2313 } else {
2314 /* No previous conf */
2315 if (stats.st_size != 0 &&
2316 content[stats.st_size - 1] != '\n')
2317 fprintf(f, "\n");
2318 }
2319 free(content);
2320 }
2321
2322 for (list_it_init(&bip->conn_list, &it); list_it_item(&it);
2323 list_it_next(&it)) {
2324 connection_t *c = list_it_item(&it);
2325 struct link_any *la = c->user_data;
2326 if (la && TYPE(la) == IRC_TYPE_SERVER && (
2327 c->connected == CONN_OK ||
2328 c->connected == CONN_NEED_SSLIZE ||
2329 c->connected == CONN_UNTRUSTED)) {
2330 struct link_server *ls;
2331 struct link *l;
2332
2333 if (!tag_written) {
2334 fprintf(f, BIP_OIDENTD_START);
2335 tag_written = 1;
2336 }
2337
2338 ls = (struct link_server*)la;
2339 l = LINK(ls);
2340
2341 fprintf(f, "to %s fport %d from %s lport %d {\n",
2342 c->remoteip, c->remoteport, c->localip,
2343 c->localport);
2344 fprintf(f, "\treply \"%s\"\n", l->username);
2345 fprintf(f, "}\n");
2346 }
2347 }
2348 if (tag_written)
2349 fprintf(f, BIP_OIDENTD_END);
2350
2351 clean_oidentd:
2352 fclose(f);
2353 }
2354
timeout_clean_who_counts(list_t * conns)2355 void timeout_clean_who_counts(list_t *conns)
2356 {
2357 list_iterator_t it;
2358 for (list_it_init(conns, &it); list_it_item(&it); list_it_next(&it)) {
2359 struct link *l = list_it_item(&it);
2360 struct link_client *client = l->who_client;
2361
2362 if (client && client->whoc_tstamp) {
2363 time_t now;
2364 now = time(NULL);
2365 if (now - client->whoc_tstamp > 10) {
2366 mylog(LOG_DEBUG, "Yawn, "
2367 "forgetting one who reply");
2368 if (client->who_count > 0)
2369 --client->who_count;
2370 client->whoc_tstamp = time(NULL);
2371 if (client->who_count == 0)
2372 rotate_who_client(l);
2373 }
2374 }
2375 }
2376 }
2377
bip_init(bip_t * bip)2378 void bip_init(bip_t *bip)
2379 {
2380 memset(bip, 0, sizeof(bip_t));
2381 list_init(&bip->link_list, list_ptr_cmp);
2382 list_init(&bip->conn_list, list_ptr_cmp);
2383 list_init(&bip->connecting_client_list, list_ptr_cmp);
2384
2385 hash_init(&bip->users, HASH_NOCASE);
2386 hash_init(&bip->networks, HASH_NOCASE);
2387 }
2388
2389 /* Called each second. */
bip_tick(bip_t * bip)2390 void bip_tick(bip_t *bip)
2391 {
2392 static int logflush_timer = 0;
2393 struct link *link;
2394 list_iterator_t li;
2395
2396 /* log flushs */
2397 if (logflush_timer-- <= 0) {
2398 logflush_timer = conf_log_sync_interval;
2399 log_flush_all();
2400 }
2401
2402 /* handle tick for links: detect lags or start a reconnection */
2403 for (list_it_init(&bip->link_list, &li); (link = list_it_item(&li));
2404 list_it_next(&li)) {
2405 if (link->l_server) {
2406 if (irc_server_lag_compute(link)) {
2407 log_ping_timeout(link->log);
2408 list_remove(&bip->conn_list,
2409 CONN(link->l_server));
2410 irc_close((struct link_any *) link->l_server);
2411 }
2412 } else {
2413 if (link->recon_timer == 0) {
2414 connection_t *conn;
2415 link->last_connection_attempt = time(NULL);
2416 conn = irc_server_connect(link);
2417 if (!conn)
2418 server_setup_reconnect_timer(link);
2419 } else {
2420 link->recon_timer--;
2421 }
2422 }
2423 }
2424
2425 /* drop lagging connecting client */
2426 for (list_it_init(&bip->connecting_client_list, &li); list_it_item(&li);
2427 list_it_next(&li)) {
2428 struct link_client *ic = list_it_item(&li);
2429 ic->logging_timer++;
2430 if (ic->logging_timer > LOGGING_TIMEOUT) {
2431 if (CONN(ic))
2432 list_remove(&bip->conn_list, CONN(ic));
2433 irc_close((struct link_any *)ic);
2434 list_it_remove(&li);
2435 }
2436 }
2437
2438 /*
2439 * Cleanup lagging or dangling who_count buffers
2440 */
2441 timeout_clean_who_counts(&bip->link_list);
2442 }
2443
bip_on_event(bip_t * bip,connection_t * conn)2444 void bip_on_event(bip_t *bip, connection_t *conn)
2445 {
2446 struct link_any *lc = (struct link_any *)conn->user_data;
2447
2448 if (conn == bip->listener) {
2449 struct link_client *n = irc_accept_new(conn);
2450 if (n) {
2451 list_add_last(&bip->conn_list, CONN(n));
2452 list_add_last(&bip->connecting_client_list, n);
2453 }
2454 return;
2455 }
2456
2457 /* reached only if socket is not listening */
2458 int err;
2459 list_t *linel = read_lines(conn, &err);
2460 if (err) {
2461 if (TYPE(lc) == IRC_TYPE_SERVER) {
2462 mylog(LOG_ERROR, "[%s] read_lines error, closing...",
2463 link_name(lc));
2464 irc_server_shutdown(LINK(lc)->l_server);
2465 } else {
2466 mylog(LOG_ERROR, "client read_lines error, closing...");
2467 }
2468 goto prot_err;
2469 }
2470 if (!linel)
2471 return;
2472
2473 char *line_s;
2474 while ((line_s = list_remove_first(linel))) {
2475 struct line *line;
2476 mylog(LOG_DEBUG, "\"%s\"", line_s);
2477 if (*line_s == 0) { /* irssi does that.*/
2478 free(line_s);
2479 continue;
2480 }
2481
2482 line = irc_line_new_from_string(line_s);
2483 if (!line) {
2484 mylog(LOG_ERROR, "[%s] Error in protocol, closing...",
2485 link_name(lc));
2486 free(line_s);
2487 goto prot_err_lines;
2488 }
2489 int r;
2490 r = irc_dispatch(bip, lc, line);
2491 irc_line_free(line);
2492 free(line_s);
2493 if (r == ERR_PROTOCOL) {
2494 mylog(LOG_ERROR, "[%s] Error in protocol, closing...",
2495 link_name(lc));
2496 goto prot_err_lines;
2497 }
2498 if (r == ERR_AUTH)
2499 goto prot_err_lines;
2500 /* XXX: not real error */
2501 if (r == OK_CLOSE)
2502 goto prot_err_lines;
2503
2504 }
2505 list_free(linel);
2506 return;
2507 prot_err_lines:
2508 while ((line_s = list_remove_first(linel)))
2509 free(line_s);
2510 prot_err:
2511 list_remove(&bip->conn_list, conn);
2512 if (linel)
2513 list_free(linel);
2514 if (lc) {
2515 if (TYPE(lc) == IRC_TYPE_LOGING_CLIENT || TYPE(lc) ==
2516 IRC_TYPE_TRUST_CLIENT)
2517 list_remove(&bip->connecting_client_list, lc);
2518 irc_close(lc);
2519 }
2520 }
2521
2522 /*
2523 * The main loop
2524 * inc is the incoming connection, clientl list a list of client struct that
2525 * represent the accepcted credentials
2526 */
irc_main(bip_t * bip)2527 void irc_main(bip_t *bip)
2528 {
2529 int timeleft = 1000;
2530
2531 if (bip->reloading_client) {
2532 char *l;
2533
2534 while ((l = list_remove_first(&bip->errors)))
2535 bip_notify(bip->reloading_client, "%s", l);
2536 bip->reloading_client = NULL;
2537 }
2538
2539 /*
2540 * If the list is empty, we are starting. Otherwise we are reloading,
2541 * and conn_list is kept accross reloads.
2542 */
2543 if (list_is_empty(&bip->conn_list))
2544 list_add_first(&bip->conn_list, bip->listener);
2545
2546 while (!sighup) {
2547 connection_t *conn;
2548
2549 if (timeleft == 0) {
2550 /*
2551 * Compute timeouts for next reconnections and lagouts
2552 */
2553
2554 timeleft = 1000;
2555 bip_tick(bip);
2556 }
2557
2558 int nc;
2559 /* Da main loop */
2560 list_t *ready = wait_event(&bip->conn_list, &timeleft, &nc);
2561 if (nc)
2562 oidentd_dump(bip);
2563 while ((conn = list_remove_first(ready)))
2564 bip_on_event(bip, conn);
2565 list_free(ready);
2566 }
2567 while (list_remove_first(&bip->connecting_client_list))
2568 ;
2569 return;
2570 }
2571
irc_client_free(struct link_client * cli)2572 void irc_client_free(struct link_client *cli)
2573 {
2574 if (CONN(cli))
2575 connection_free(CONN(cli));
2576 if (cli->init_pass)
2577 free(cli->init_pass);
2578 if (cli->init_nick)
2579 free(cli->init_nick);
2580 free(cli);
2581 }
2582
irc_link_new()2583 struct link *irc_link_new()
2584 {
2585 struct link *link;
2586 link = bip_calloc(sizeof(struct link), 1);
2587
2588 link->l_server = NULL;
2589 hash_init(&link->chan_infos, HASH_NOCASE);
2590 list_init(&link->chan_infos_order, list_ptr_cmp);
2591 list_init(&link->on_connect_send, list_ptr_cmp);
2592 link->autojoin_on_kick = 1;
2593 link->ignore_server_capab = 1;
2594 return link;
2595 }
2596
link_kill(bip_t * bip,struct link * link)2597 void link_kill(bip_t *bip, struct link *link)
2598 {
2599 /* in case in never got connected */
2600 if (link->l_server) {
2601 list_remove(&bip->conn_list, CONN(link->l_server));
2602 server_cleanup(link->l_server);
2603 irc_server_free(link->l_server);
2604 }
2605 while (link->l_clientc) {
2606 struct link_client *lc = link->l_clientv[0];
2607 if (lc == bip->reloading_client)
2608 bip->reloading_client = NULL;
2609 list_remove(&bip->conn_list, CONN(lc));
2610 unbind_from_link(lc);
2611 irc_client_free(lc);
2612 }
2613
2614 hash_remove(&link->user->connections, link->name);
2615 free(link->name);
2616 log_free(link->log);
2617 MAYFREE(link->prev_nick);
2618 MAYFREE(link->cli_nick);
2619
2620 void *p;
2621 while ((p = list_remove_first(&link->init_strings)))
2622 free(p);
2623 while ((p = list_remove_first(&link->on_connect_send)))
2624 free(p);
2625 MAYFREE(link->no_client_away_msg);
2626 MAYFREE(link->away_nick);
2627 hash_clean(&link->chan_infos);
2628
2629 struct chan_infos *ci;
2630 while ((ci = list_remove_first(&link->chan_infos_order)))
2631 free(ci);
2632
2633 list_remove(&bip->link_list, link);
2634
2635 MAYFREE(link->username);
2636 MAYFREE(link->realname);
2637 MAYFREE(link->s_password);
2638 MAYFREE(link->connect_nick);
2639 MAYFREE(link->vhost);
2640 #ifdef HAVE_LIBSSL
2641 sk_X509_free(link->untrusted_certs);
2642 #endif
2643 free(link);
2644 }
2645
server_set_chanmodes(struct link_server * l,const char * modes)2646 static void server_set_chanmodes(struct link_server *l, const char *modes)
2647 {
2648 int i;
2649 char *cur;
2650 char *dup;
2651
2652 mylog(LOG_DEBUG, "[%s] Set chanmodes", LINK(l)->name);
2653
2654 array_each(&l->chanmodes, i, cur)
2655 free(cur);
2656 array_deinit(&l->chanmodes);
2657
2658 // handle four categories, ignore all others
2659 for (i = 0; i < 4; i++) {
2660 cur = strchr(modes, ',');
2661 if (cur || modes) {
2662 size_t len;
2663 if (cur)
2664 len = cur - modes;
2665 else
2666 len = strlen(modes); // last piece
2667 dup = bip_malloc(len + 1);
2668 memcpy(dup, modes, len);
2669 dup[len] = 0;
2670 modes = cur + 1;
2671 } else {
2672 // emptry string
2673 dup = bip_calloc(1, sizeof(char));
2674 }
2675 mylog(LOG_DEBUGVERB, "[%s] Modes: '%s'", LINK(l)->name, dup);
2676 array_push(&l->chanmodes, dup);
2677 }
2678 }
2679
2680
server_set_prefix(struct link_server * s,const char * modes)2681 static void server_set_prefix(struct link_server *s, const char *modes)
2682 {
2683 char * end_mode;
2684 unsigned int len;
2685
2686 mylog(LOG_DEBUG, "[%s] Set user modes", LINK(s)->name);
2687
2688 // PREFIX=(mode)prefix
2689
2690 end_mode = strchr(modes + 1, ')'); // skip '('
2691 if (*modes != '(' || !end_mode) {
2692 mylog(LOG_WARN, "[%s] Unable to parse PREFIX parameter", LINK(s)->name);
2693 return;
2694 }
2695
2696 len = end_mode - modes - 1; // len of mode without '('
2697 if (len * 2 + 2 != strlen(modes)) {
2698 mylog(LOG_WARN, "[%s] Unable to parse PREFIX parameter", LINK(s)->name);
2699 return;
2700 }
2701
2702 s->prefixes = bip_realloc(s->prefixes, sizeof(*s->prefixes) * (len + 1));
2703 s->usermodes = bip_realloc(s->usermodes, sizeof(s->usermodes) * (len + 1));
2704
2705 memcpy(s->usermodes, modes + 1, len);
2706 s->usermodes[len] = 0;
2707 memcpy(s->prefixes, end_mode + 1, len);
2708 s->prefixes[len] = 0;
2709
2710 mylog(LOG_DEBUGVERB, "[%s] user prefix: '%s'", LINK(s)->name, s->prefixes);
2711 mylog(LOG_DEBUGVERB, "[%s] user modes: '%s'", LINK(s)->name, s->usermodes);
2712 }
2713
2714 // Return the position (*1 based*) of car in str, else -1
bip_get_index(const char * str,char car)2715 static int bip_get_index(const char* str, char car)
2716 {
2717 char *cur;
2718 if ((cur = strchr(str, car)))
2719 return cur - str + 1;
2720 else
2721 return 0;
2722 }
2723
bip_fls(int v)2724 static int bip_fls(int v)
2725 {
2726 unsigned int r = 0;
2727 while (v >>= 1)
2728 r++;
2729
2730 return r;
2731 }
2732