source: irc.c @ c39cd8e

Last change on this file since c39cd8e was c39cd8e, checked in by Wilmer van der Gaast <wilmer@…>, at 2017-04-02T18:50:49Z

Add reverse_lookup() function which does a more proper (verified) DNS
reverse lookup. (Code copied from OpenSSH instead of redoing it
poorly.)

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