source: irc.c @ a6b00fc

Last change on this file since a6b00fc was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 25.7 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* The IRC-based UI (for now the only one)                              */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include "bitlbee.h"
27#include "ipc.h"
28#include "dcc.h"
29#include "lib/ssl_client.h"
30
31GSList *irc_connection_list;
32GSList *irc_plugins;
33
34static gboolean irc_userping(gpointer _irc, gint fd, b_input_condition cond);
35static char *set_eval_charset(set_t *set, char *value);
36static char *set_eval_password(set_t *set, char *value);
37static char *set_eval_bw_compat(set_t *set, char *value);
38static char *set_eval_utf8_nicks(set_t *set, char *value);
39
40irc_t *irc_new(int fd)
41{
42        irc_t *irc;
43        struct sockaddr_storage sock;
44        socklen_t socklen = sizeof(sock);
45        char *host = NULL, *myhost = NULL;
46        irc_user_t *iu;
47        GSList *l;
48        set_t *s;
49        bee_t *b;
50
51        irc = g_new0(irc_t, 1);
52
53        irc->fd = fd;
54        sock_make_nonblocking(irc->fd);
55
56        irc->r_watch_source_id = b_input_add(irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc);
57
58        irc->status = USTATUS_OFFLINE;
59        irc->last_pong = gettime();
60
61        irc->nick_user_hash = g_hash_table_new(g_str_hash, g_str_equal);
62        irc->watches = g_hash_table_new(g_str_hash, g_str_equal);
63
64        irc->iconv = (GIConv) - 1;
65        irc->oconv = (GIConv) - 1;
66
67        if (global.conf->hostname) {
68                myhost = g_strdup(global.conf->hostname);
69        } else if (getsockname(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) {
70                char buf[NI_MAXHOST + 1];
71
72                if (getnameinfo((struct sockaddr *) &sock, socklen, buf,
73                                NI_MAXHOST, NULL, 0, 0) == 0) {
74                        myhost = g_strdup(ipv6_unwrap(buf));
75                }
76        }
77
78        if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) {
79                char buf[NI_MAXHOST + 1];
80
81                if (getnameinfo((struct sockaddr *) &sock, socklen, buf,
82                                NI_MAXHOST, NULL, 0, 0) == 0) {
83                        host = g_strdup(ipv6_unwrap(buf));
84                }
85        }
86
87        if (host == NULL) {
88                host = g_strdup("localhost.localdomain");
89        }
90        if (myhost == NULL) {
91                myhost = g_strdup("localhost.localdomain");
92        }
93
94        if (global.conf->ping_interval > 0 && global.conf->ping_timeout > 0) {
95                irc->ping_source_id = b_timeout_add(global.conf->ping_interval * 1000, irc_userping, irc);
96        }
97
98        irc_connection_list = g_slist_append(irc_connection_list, irc);
99
100        b = irc->b = bee_new();
101        b->ui_data = irc;
102        b->ui = &irc_ui_funcs;
103
104        s = set_add(&b->set, "allow_takeover", "true", set_eval_bool, irc);
105        s = set_add(&b->set, "away_devoice", "true", set_eval_bw_compat, irc);
106        s->flags |= SET_HIDDEN;
107        s = set_add(&b->set, "away_reply_timeout", "3600", set_eval_int, irc);
108        s = set_add(&b->set, "charset", "utf-8", set_eval_charset, irc);
109        s = set_add(&b->set, "default_target", "root", NULL, irc);
110        s = set_add(&b->set, "display_namechanges", "false", set_eval_bool, irc);
111        s = set_add(&b->set, "display_timestamps", "true", set_eval_bool, irc);
112        s = set_add(&b->set, "handle_unknown", "add_channel", NULL, irc);
113        s = set_add(&b->set, "last_version", "0", NULL, irc);
114        s->flags |= SET_HIDDEN;
115        s = set_add(&b->set, "lcnicks", "true", set_eval_bool, irc);
116        s = set_add(&b->set, "nick_format", "%-@nick", NULL, irc);
117        s = set_add(&b->set, "offline_user_quits", "true", set_eval_bool, irc);
118        s = set_add(&b->set, "ops", "both", set_eval_irc_channel_ops, irc);
119        s = set_add(&b->set, "paste_buffer", "false", set_eval_bool, irc);
120        s->old_key = g_strdup("buddy_sendbuffer");
121        s = set_add(&b->set, "paste_buffer_delay", "200", set_eval_int, irc);
122        s->old_key = g_strdup("buddy_sendbuffer_delay");
123        s = set_add(&b->set, "password", NULL, set_eval_password, irc);
124        s->flags |= SET_NULL_OK | SET_PASSWORD;
125        s = set_add(&b->set, "private", "true", set_eval_bool, irc);
126        s = set_add(&b->set, "query_order", "lifo", NULL, irc);
127        s = set_add(&b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc);
128        s->flags |= SET_HIDDEN;
129        s = set_add(&b->set, "show_offline", "false", set_eval_bw_compat, irc);
130        s->flags |= SET_HIDDEN;
131        s = set_add(&b->set, "simulate_netsplit", "true", set_eval_bool, irc);
132        s = set_add(&b->set, "timezone", "local", set_eval_timezone, irc);
133        s = set_add(&b->set, "to_char", ": ", set_eval_to_char, irc);
134        s = set_add(&b->set, "typing_notice", "false", set_eval_bool, irc);
135        s = set_add(&b->set, "utf8_nicks", "false", set_eval_utf8_nicks, irc);
136
137        irc->root = iu = irc_user_new(irc, ROOT_NICK);
138        iu->host = g_strdup(myhost);
139        iu->fullname = g_strdup(ROOT_FN);
140        iu->f = &irc_user_root_funcs;
141
142        iu = irc_user_new(irc, NS_NICK);
143        iu->host = g_strdup(myhost);
144        iu->fullname = g_strdup(ROOT_FN);
145        iu->f = &irc_user_root_funcs;
146
147        irc->user = g_new0(irc_user_t, 1);
148        irc->user->host = g_strdup(host);
149
150        conf_loaddefaults(irc);
151
152        /* Evaluator sets the iconv/oconv structures. */
153        set_eval_charset(set_find(&b->set, "charset"), set_getstr(&b->set, "charset"));
154
155        irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on");
156        if (isatty(irc->fd)) {
157                irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host,
158                          "If you read this, you most likely accidentally "
159                          "started BitlBee in inetd mode on the command line. "
160                          "You probably want to run it in (Fork)Daemon mode. "
161                          "See doc/README for more information.");
162        }
163
164        g_free(myhost);
165        g_free(host);
166
167        /* libpurple doesn't like fork()s after initializing itself, so this
168           is the right moment to initialize it. */
169#ifdef WITH_PURPLE
170        nogaim_init();
171#endif
172
173        /* SSL library initialization also should be done after the fork, to
174           avoid shared CSPRNG state. This is required by NSS, which refuses to
175           work if a fork is detected */
176        ssl_init();
177
178        for (l = irc_plugins; l; l = l->next) {
179                irc_plugin_t *p = l->data;
180                if (p->irc_new) {
181                        p->irc_new(irc);
182                }
183        }
184
185        return irc;
186}
187
188/* immed=1 makes this function pretty much equal to irc_free(), except that
189   this one will "log". In case the connection is already broken and we
190   shouldn't try to write to it. */
191void irc_abort(irc_t *irc, int immed, char *format, ...)
192{
193        char *reason = NULL;
194
195        if (format != NULL) {
196                va_list params;
197
198                va_start(params, format);
199                reason = g_strdup_vprintf(format, params);
200                va_end(params);
201        }
202
203        if (reason) {
204                irc_write(irc, "ERROR :Closing link: %s", reason);
205        }
206
207        ipc_to_master_str("OPERMSG :Client exiting: %s@%s [%s]\r\n",
208                          irc->user->nick ? irc->user->nick : "(NONE)",
209                          irc->user->host, reason ? : "");
210
211        g_free(reason);
212
213        irc_flush(irc);
214        if (immed) {
215                irc_free(irc);
216        } else {
217                b_event_remove(irc->ping_source_id);
218                irc->ping_source_id = b_timeout_add(1, (b_event_handler) irc_free, irc);
219        }
220}
221
222static gboolean irc_free_hashkey(gpointer key, gpointer value, gpointer data);
223
224void irc_free(irc_t * irc)
225{
226        GSList *l;
227
228        irc->status |= USTATUS_SHUTDOWN;
229
230        log_message(LOGLVL_INFO, "Destroying connection with fd %d", irc->fd);
231
232        if (irc->status & USTATUS_IDENTIFIED && set_getbool(&irc->b->set, "save_on_quit")) {
233                if (storage_save(irc, NULL, TRUE) != STORAGE_OK) {
234                        log_message(LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick);
235                }
236        }
237
238        for (l = irc_plugins; l; l = l->next) {
239                irc_plugin_t *p = l->data;
240                if (p->irc_free) {
241                        p->irc_free(irc);
242                }
243        }
244
245        irc_connection_list = g_slist_remove(irc_connection_list, irc);
246
247        while (irc->queries != NULL) {
248                query_del(irc, irc->queries);
249        }
250
251        /* This is a little bit messy: bee_free() frees all b->users which
252           calls us back to free the corresponding irc->users. So do this
253           before we clear the remaining ones ourselves. */
254        bee_free(irc->b);
255
256        while (irc->users) {
257                irc_user_free(irc, (irc_user_t *) irc->users->data);
258        }
259
260        while (irc->channels) {
261                irc_channel_free(irc->channels->data);
262        }
263
264        if (irc->ping_source_id > 0) {
265                b_event_remove(irc->ping_source_id);
266        }
267        if (irc->r_watch_source_id > 0) {
268                b_event_remove(irc->r_watch_source_id);
269        }
270        if (irc->w_watch_source_id > 0) {
271                b_event_remove(irc->w_watch_source_id);
272        }
273
274        closesocket(irc->fd);
275        irc->fd = -1;
276
277        g_hash_table_foreach_remove(irc->nick_user_hash, irc_free_hashkey, NULL);
278        g_hash_table_destroy(irc->nick_user_hash);
279
280        g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
281        g_hash_table_destroy(irc->watches);
282
283        if (irc->iconv != (GIConv) - 1) {
284                g_iconv_close(irc->iconv);
285        }
286        if (irc->oconv != (GIConv) - 1) {
287                g_iconv_close(irc->oconv);
288        }
289
290        g_free(irc->sendbuffer);
291        g_free(irc->readbuffer);
292        g_free(irc->password);
293
294        g_free(irc);
295
296        if (global.conf->runmode == RUNMODE_INETD ||
297            global.conf->runmode == RUNMODE_FORKDAEMON ||
298            (global.conf->runmode == RUNMODE_DAEMON &&
299             global.listen_socket == -1 &&
300             irc_connection_list == NULL)) {
301                b_main_quit();
302        }
303}
304
305static gboolean irc_free_hashkey(gpointer key, gpointer value, gpointer data)
306{
307        g_free(key);
308
309        return(TRUE);
310}
311
312/* USE WITH CAUTION!
313   Sets pass without checking */
314void irc_setpass(irc_t *irc, const char *pass)
315{
316        g_free(irc->password);
317
318        if (pass) {
319                irc->password = g_strdup(pass);
320        } else {
321                irc->password = NULL;
322        }
323}
324
325static char *set_eval_password(set_t *set, char *value)
326{
327        irc_t *irc = set->data;
328
329        if (irc->status & USTATUS_IDENTIFIED && value) {
330                irc_setpass(irc, value);
331                return NULL;
332        } else {
333                return SET_INVALID;
334        }
335}
336
337static char **irc_splitlines(char *buffer);
338
339void irc_process(irc_t *irc)
340{
341        char **lines, *temp, **cmd;
342        int i;
343
344        if (irc->readbuffer != NULL) {
345                lines = irc_splitlines(irc->readbuffer);
346
347                for (i = 0; *lines[i] != '\0'; i++) {
348                        char *conv = NULL;
349
350                        /* [WvG] If the last line isn't empty, it's an incomplete line and we
351                           should wait for the rest to come in before processing it. */
352                        if (lines[i + 1] == NULL) {
353                                temp = g_strdup(lines[i]);
354                                g_free(irc->readbuffer);
355                                irc->readbuffer = temp;
356                                i++;
357                                break;
358                        }
359
360                        if (irc->iconv != (GIConv) - 1) {
361                                gsize bytes_read, bytes_written;
362
363                                conv = g_convert_with_iconv(lines[i], -1, irc->iconv,
364                                                            &bytes_read, &bytes_written, NULL);
365
366                                if (conv == NULL || bytes_read != strlen(lines[i])) {
367                                        /* GLib can do strange things if things are not in the expected charset,
368                                           so let's be a little bit paranoid here: */
369                                        if (irc->status & USTATUS_LOGGED_IN) {
370                                                irc_rootmsg(irc, "Error: Charset mismatch detected. The charset "
371                                                            "setting is currently set to %s, so please make "
372                                                            "sure your IRC client will send and accept text in "
373                                                            "that charset, or tell BitlBee which charset to "
374                                                            "expect by changing the charset setting. See "
375                                                            "`help set charset' for more information. Your "
376                                                            "message was ignored.",
377                                                            set_getstr(&irc->b->set, "charset"));
378
379                                                g_free(conv);
380                                                conv = NULL;
381                                        } else {
382                                                irc_write(irc, ":%s NOTICE AUTH :%s", irc->root->host,
383                                                          "Warning: invalid characters received at login time.");
384
385                                                conv = g_strdup(lines[i]);
386                                                for (temp = conv; *temp; temp++) {
387                                                        if (*temp & 0x80) {
388                                                                *temp = '?';
389                                                        }
390                                                }
391                                        }
392                                }
393                                lines[i] = conv;
394                        }
395
396                        if (lines[i] && (cmd = irc_parse_line(lines[i]))) {
397                                irc_exec(irc, cmd);
398                                g_free(cmd);
399                        }
400
401                        g_free(conv);
402
403                        /* Shouldn't really happen, but just in case... */
404                        if (!g_slist_find(irc_connection_list, irc)) {
405                                g_free(lines);
406                                return;
407                        }
408                }
409
410                if (lines[i] != NULL) {
411                        g_free(irc->readbuffer);
412                        irc->readbuffer = NULL;
413                }
414
415                g_free(lines);
416        }
417}
418
419/* Splits a long string into separate lines. The array is NULL-terminated
420   and, unless the string contains an incomplete line at the end, ends with
421   an empty string. Could use g_strsplit() but this one does it in-place.
422   (So yes, it's destructive.) */
423static char **irc_splitlines(char *buffer)
424{
425        int i, j, n = 3;
426        char **lines;
427
428        /* Allocate n+1 elements. */
429        lines = g_new(char *, n + 1);
430
431        lines[0] = buffer;
432
433        /* Split the buffer in several strings, and accept any kind of line endings,
434         * knowing that ERC on Windows may send something interesting like \r\r\n,
435         * and surely there must be clients that think just \n is enough... */
436        for (i = 0, j = 0; buffer[i] != '\0'; i++) {
437                if (buffer[i] == '\r' || buffer[i] == '\n') {
438                        while (buffer[i] == '\r' || buffer[i] == '\n') {
439                                buffer[i++] = '\0';
440                        }
441
442                        lines[++j] = buffer + i;
443
444                        if (j >= n) {
445                                n *= 2;
446                                lines = g_renew(char *, lines, n + 1);
447                        }
448
449                        if (buffer[i] == '\0') {
450                                break;
451                        }
452                }
453        }
454
455        /* NULL terminate our list. */
456        lines[++j] = NULL;
457
458        return lines;
459}
460
461/* Split an IRC-style line into little parts/arguments. */
462char **irc_parse_line(char *line)
463{
464        int i, j;
465        char **cmd;
466
467        /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */
468        if (line[0] == ':') {
469                for (i = 0; line[i] && line[i] != ' '; i++) {
470                        ;
471                }
472                line = line + i;
473        }
474        for (i = 0; line[i] == ' '; i++) {
475                ;
476        }
477        line = line + i;
478
479        /* If we're already at the end of the line, return. If not, we're going to need at least one element. */
480        if (line[0] == '\0') {
481                return NULL;
482        }
483
484        /* Count the number of char **cmd elements we're going to need. */
485        j = 1;
486        for (i = 0; line[i] != '\0'; i++) {
487                if (line[i] == ' ') {
488                        j++;
489
490                        if (line[i + 1] == ':') {
491                                break;
492                        }
493                }
494        }
495
496        /* Allocate the space we need. */
497        cmd = g_new(char *, j + 1);
498        cmd[j] = NULL;
499
500        /* Do the actual line splitting, format is:
501         * Input: "PRIVMSG #bitlbee :foo bar"
502         * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL
503         */
504
505        cmd[0] = line;
506        for (i = 0, j = 0; line[i] != '\0'; i++) {
507                if (line[i] == ' ') {
508                        line[i] = '\0';
509                        cmd[++j] = line + i + 1;
510
511                        if (line[i + 1] == ':') {
512                                cmd[j]++;
513                                break;
514                        }
515                }
516        }
517
518        return cmd;
519}
520
521/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
522char *irc_build_line(char **cmd)
523{
524        int i, len;
525        char *s;
526
527        if (cmd[0] == NULL) {
528                return NULL;
529        }
530
531        len = 1;
532        for (i = 0; cmd[i]; i++) {
533                len += strlen(cmd[i]) + 1;
534        }
535
536        if (strchr(cmd[i - 1], ' ') != NULL) {
537                len++;
538        }
539
540        s = g_new0(char, len + 1);
541        for (i = 0; cmd[i]; i++) {
542                if (cmd[i + 1] == NULL && strchr(cmd[i], ' ') != NULL) {
543                        strcat(s, ":");
544                }
545
546                strcat(s, cmd[i]);
547
548                if (cmd[i + 1]) {
549                        strcat(s, " ");
550                }
551        }
552        strcat(s, "\r\n");
553
554        return s;
555}
556
557void irc_write(irc_t *irc, char *format, ...)
558{
559        va_list params;
560
561        va_start(params, format);
562        irc_vawrite(irc, format, params);
563        va_end(params);
564
565        return;
566}
567
568void irc_write_all(int now, char *format, ...)
569{
570        va_list params;
571        GSList *temp;
572
573        va_start(params, format);
574
575        temp = irc_connection_list;
576        while (temp != NULL) {
577                irc_t *irc = temp->data;
578
579                if (now) {
580                        g_free(irc->sendbuffer);
581                        irc->sendbuffer = g_strdup("\r\n");
582                }
583                irc_vawrite(temp->data, format, params);
584                if (now) {
585                        bitlbee_io_current_client_write(irc, irc->fd, B_EV_IO_WRITE);
586                }
587                temp = temp->next;
588        }
589
590        va_end(params);
591        return;
592}
593
594void irc_vawrite(irc_t *irc, char *format, va_list params)
595{
596        int size;
597        char line[IRC_MAX_LINE + 1];
598
599        /* Don't try to write anything new anymore when shutting down. */
600        if (irc->status & USTATUS_SHUTDOWN) {
601                return;
602        }
603
604        memset(line, 0, sizeof(line));
605        g_vsnprintf(line, IRC_MAX_LINE - 2, format, params);
606        strip_newlines(line);
607
608        if (irc->oconv != (GIConv) - 1) {
609                gsize bytes_read, bytes_written;
610                char *conv;
611
612                conv = g_convert_with_iconv(line, -1, irc->oconv,
613                                            &bytes_read, &bytes_written, NULL);
614
615                if (bytes_read == strlen(line)) {
616                        strncpy(line, conv, IRC_MAX_LINE - 2);
617                }
618
619                g_free(conv);
620        }
621        g_strlcat(line, "\r\n", IRC_MAX_LINE + 1);
622
623        if (irc->sendbuffer != NULL) {
624                size = strlen(irc->sendbuffer) + strlen(line);
625                irc->sendbuffer = g_renew(char, irc->sendbuffer, size + 1);
626                strcpy((irc->sendbuffer + strlen(irc->sendbuffer)), line);
627        } else {
628                irc->sendbuffer = g_strdup(line);
629        }
630
631        if (irc->w_watch_source_id == 0) {
632                /* If the buffer is empty we can probably write, so call the write event handler
633                   immediately. If it returns TRUE, it should be called again, so add the event to
634                   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
635                   in the event queue. */
636                /* Really can't be done as long as the code doesn't do error checking very well:
637                if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
638
639                /* So just always do it via the event handler. */
640                irc->w_watch_source_id = b_input_add(irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc);
641        }
642
643        return;
644}
645
646/* Flush sendbuffer if you can. If it fails, fail silently and let some
647   I/O event handler clean up. */
648void irc_flush(irc_t *irc)
649{
650        ssize_t n;
651        size_t len;
652
653        if (irc->sendbuffer == NULL) {
654                return;
655        }
656
657        len = strlen(irc->sendbuffer);
658        if ((n = send(irc->fd, irc->sendbuffer, len, 0)) == len) {
659                g_free(irc->sendbuffer);
660                irc->sendbuffer = NULL;
661
662                b_event_remove(irc->w_watch_source_id);
663                irc->w_watch_source_id = 0;
664        } else if (n > 0) {
665                char *s = g_strdup(irc->sendbuffer + n);
666                g_free(irc->sendbuffer);
667                irc->sendbuffer = s;
668        }
669        /* Otherwise something went wrong and we don't currently care
670           what the error was. We may or may not succeed later, we
671           were just trying to flush the buffer immediately. */
672}
673
674/* Meant for takeover functionality. Transfer an IRC connection to a different
675   socket. */
676void irc_switch_fd(irc_t *irc, int fd)
677{
678        irc_write(irc, "ERROR :Transferring session to a new connection");
679        irc_flush(irc);   /* Write it now or forget about it forever. */
680
681        if (irc->sendbuffer) {
682                b_event_remove(irc->w_watch_source_id);
683                irc->w_watch_source_id = 0;
684                g_free(irc->sendbuffer);
685                irc->sendbuffer = NULL;
686        }
687
688        b_event_remove(irc->r_watch_source_id);
689        closesocket(irc->fd);
690        irc->fd = fd;
691        irc->r_watch_source_id = b_input_add(irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc);
692}
693
694void irc_sync(irc_t *irc)
695{
696        GSList *l;
697
698        irc_write(irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick,
699                  irc->user->user, irc->user->host, irc->user->nick,
700                  irc->umode);
701
702        for (l = irc->channels; l; l = l->next) {
703                irc_channel_t *ic = l->data;
704                if (ic->flags & IRC_CHANNEL_JOINED) {
705                        irc_send_join(ic, irc->user);
706                }
707        }
708
709        /* We may be waiting for a PONG from the previous client connection. */
710        irc->pinging = FALSE;
711}
712
713void irc_desync(irc_t *irc)
714{
715        GSList *l;
716
717        for (l = irc->channels; l; l = l->next) {
718                irc_channel_del_user(l->data, irc->user, IRC_CDU_KICK,
719                                     "Switching to old session");
720        }
721
722        irc_write(irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
723                  irc->user->user, irc->user->host, irc->user->nick,
724                  irc->umode);
725}
726
727int irc_check_login(irc_t *irc)
728{
729        if (irc->user->user && irc->user->nick) {
730                if (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED)) {
731                        irc_send_num(irc, 464, ":This server is password-protected.");
732                        return 0;
733                } else {
734                        irc_channel_t *ic;
735                        irc_user_t *iu = irc->user;
736
737                        irc->user = irc_user_new(irc, iu->nick);
738                        irc->user->user = iu->user;
739                        irc->user->host = iu->host;
740                        irc->user->fullname = iu->fullname;
741                        irc->user->f = &irc_user_self_funcs;
742                        g_free(iu->nick);
743                        g_free(iu);
744
745                        if (global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON) {
746                                ipc_to_master_str("CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick,
747                                                  irc->user->fullname);
748                        }
749
750                        irc->status |= USTATUS_LOGGED_IN;
751
752                        irc_send_login(irc);
753
754                        irc->umode[0] = '\0';
755                        irc_umode_set(irc, "+" UMODE, TRUE);
756
757                        ic = irc->default_channel = irc_channel_new(irc, ROOT_CHAN);
758                        irc_channel_set_topic(ic, CONTROL_TOPIC, irc->root);
759                        set_setstr(&ic->set, "auto_join", "true");
760                        irc_channel_auto_joins(irc, NULL);
761
762                        irc->root->last_channel = irc->default_channel;
763
764                        irc_rootmsg(irc,
765                                    "Welcome to the BitlBee gateway!\n\n"
766                                    "If you've never used BitlBee before, please do read the help "
767                                    "information using the \x02help\x02 command. Lots of FAQs are "
768                                    "answered there.\n"
769                                    "If you already have an account on this server, just use the "
770                                    "\x02identify\x02 command to identify yourself.");
771
772                        /* This is for bug #209 (use PASS to identify to NickServ). */
773                        if (irc->password != NULL) {
774                                char *send_cmd[] = { "identify", g_strdup(irc->password), NULL };
775
776                                irc_setpass(irc, NULL);
777                                root_command(irc, send_cmd);
778                                g_free(send_cmd[1]);
779                        }
780
781                        return 1;
782                }
783        } else {
784                /* More information needed. */
785                return 0;
786        }
787}
788
789/* TODO: This is a mess, but this function is a bit too complicated to be
790   converted to something more generic. */
791void irc_umode_set(irc_t *irc, const char *s, gboolean allow_priv)
792{
793        /* allow_priv: Set to 0 if s contains user input, 1 if you want
794           to set a "privileged" mode (+o, +R, etc). */
795        char m[128], st = 1;
796        const char *t;
797        int i;
798        char changes[512], st2 = 2;
799        char badflag = 0;
800
801        memset(m, 0, sizeof(m));
802
803        /* Keep track of which modes are enabled in this array. */
804        for (t = irc->umode; *t; t++) {
805                if (*t < sizeof(m)) {
806                        m[(int) *t] = 1;
807                }
808        }
809
810        i = 0;
811        for (t = s; *t && i < sizeof(changes) - 3; t++) {
812                if (*t == '+' || *t == '-') {
813                        st = *t == '+';
814                } else if ((st == 0 && (!strchr(UMODES_KEEP, *t) || allow_priv)) ||
815                           (st == 1 && strchr(UMODES, *t)) ||
816                           (st == 1 && allow_priv && strchr(UMODES_PRIV, *t))) {
817                        if (m[(int) *t] != st) {
818                                /* If we're actually making a change, remember this
819                                   for the response. */
820                                if (st != st2) {
821                                        st2 = st, changes[i++] = st ? '+' : '-';
822                                }
823                                changes[i++] = *t;
824                        }
825                        m[(int) *t] = st;
826                } else {
827                        badflag = 1;
828                }
829        }
830        changes[i] = '\0';
831
832        /* Convert the m array back into an umode string. */
833        memset(irc->umode, 0, sizeof(irc->umode));
834        for (i = 'A'; i <= 'z' && strlen(irc->umode) < (sizeof(irc->umode) - 1); i++) {
835                if (m[i]) {
836                        irc->umode[strlen(irc->umode)] = i;
837                }
838        }
839
840        if (badflag) {
841                irc_send_num(irc, 501, ":Unknown MODE flag");
842        }
843        if (*changes) {
844                irc_write(irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
845                          irc->user->user, irc->user->host, irc->user->nick,
846                          changes);
847        }
848}
849
850/* Returns 0 if everything seems to be okay, a number >0 when there was a
851   timeout. The number returned is the number of seconds we received no
852   pongs from the user. When not connected yet, we don't ping but drop the
853   connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */
854static gboolean irc_userping(gpointer _irc, gint fd, b_input_condition cond)
855{
856        double now = gettime();
857        irc_t *irc = _irc;
858        int fail = 0;
859
860        if (!(irc->status & USTATUS_LOGGED_IN)) {
861                if (now > (irc->last_pong + IRC_LOGIN_TIMEOUT)) {
862                        fail = now - irc->last_pong;
863                }
864        } else {
865                if (now > (irc->last_pong + global.conf->ping_timeout)) {
866                        fail = now - irc->last_pong;
867                } else {
868                        irc_write(irc, "PING :%s", IRC_PING_STRING);
869                }
870        }
871
872        if (fail > 0) {
873                irc_abort(irc, 0, "Ping Timeout: %d seconds", fail);
874                return FALSE;
875        }
876
877        return TRUE;
878}
879
880static char *set_eval_charset(set_t *set, char *value)
881{
882        irc_t *irc = (irc_t *) set->data;
883        char *test;
884        gsize test_bytes = 0;
885        GIConv ic, oc;
886
887        if (g_strcasecmp(value, "none") == 0) {
888                value = g_strdup("utf-8");
889        }
890
891        if ((oc = g_iconv_open(value, "utf-8")) == (GIConv) - 1) {
892                return NULL;
893        }
894
895        /* Do a test iconv to see if the user picked an IRC-compatible
896           charset (for example utf-16 goes *horribly* wrong). */
897        if ((test = g_convert_with_iconv(" ", 1, oc, NULL, &test_bytes, NULL)) == NULL ||
898            test_bytes > 1) {
899                g_free(test);
900                g_iconv_close(oc);
901                irc_rootmsg(irc, "Unsupported character set: The IRC protocol "
902                            "only supports 8-bit character sets.");
903                return NULL;
904        }
905        g_free(test);
906
907        if ((ic = g_iconv_open("utf-8", value)) == (GIConv) - 1) {
908                g_iconv_close(oc);
909                return NULL;
910        }
911
912        if (irc->iconv != (GIConv) - 1) {
913                g_iconv_close(irc->iconv);
914        }
915        if (irc->oconv != (GIConv) - 1) {
916                g_iconv_close(irc->oconv);
917        }
918
919        irc->iconv = ic;
920        irc->oconv = oc;
921
922        return value;
923}
924
925/* Mostly meant for upgrades. If one of these is set to the non-default,
926   set show_users of all channels to something with the same effect. */
927static char *set_eval_bw_compat(set_t *set, char *value)
928{
929        irc_t *irc = set->data;
930        char *val;
931        GSList *l;
932
933        irc_rootmsg(irc, "Setting `%s' is obsolete, use the `show_users' "
934                    "channel setting instead.", set->key);
935
936        if (strcmp(set->key, "away_devoice") == 0 && !bool2int(value)) {
937                val = "online,special%,away";
938        } else if (strcmp(set->key, "show_offline") == 0 && bool2int(value)) {
939                val = "online@,special%,away+,offline";
940        } else {
941                val = "online+,special%,away";
942        }
943
944        for (l = irc->channels; l; l = l->next) {
945                irc_channel_t *ic = l->data;
946                /* No need to check channel type, if the setting doesn't exist it
947                   will just be ignored. */
948                set_setstr(&ic->set, "show_users", val);
949        }
950
951        return SET_INVALID;
952}
953
954static char *set_eval_utf8_nicks(set_t *set, char *value)
955{
956        irc_t *irc = set->data;
957        gboolean val = bool2int(value);
958
959        /* Do *NOT* unset this flag in the middle of a session. There will
960           be UTF-8 nicks around already so if we suddenly disable support
961           for them, various functions might behave strangely. */
962        if (val) {
963                irc->status |= IRC_UTF8_NICKS;
964        } else if (irc->status & IRC_UTF8_NICKS) {
965                irc_rootmsg(irc, "You need to reconnect to BitlBee for this "
966                            "change to take effect.");
967        }
968
969        return set_eval_bool(set, value);
970}
971
972void register_irc_plugin(const struct irc_plugin *p)
973{
974        irc_plugins = g_slist_prepend(irc_plugins, (gpointer) p);
975}
Note: See TracBrowser for help on using the repository browser.