source: irc.c @ 19ba56e

Last change on this file since 19ba56e was 5bb4477, checked in by dequis <dx@…>, at 2018-03-26T03:34:39Z

Improve the wording of the message when starting with inetd accidentally

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