source: irc.c @ df291a6

Last change on this file since df291a6 was 90254d0, checked in by dequis <dx@…>, at 2016-12-26T00:18:55Z

Add nick_lowercase and nick_underscores settings

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