~ubuntu-branches/ubuntu/trusty/bip/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/armel.patch/src/irc.c

  • Committer: Bazaar Package Importer
  • Author(s): Pierre-Louis Bonicoli
  • Date: 2010-09-22 11:15:15 UTC
  • Revision ID: james.westby@ubuntu.com-20100922111515-e8z0kt0nki6eb96m
Tags: 0.8.6-2
* New maintainer (with Nohar's blessing).
* Add armel.patch: fix build errors on armel (Closes: #597262). 

Show diffs side-by-side

added added

removed removed

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