source: irc.c @ 2b6cb87

Last change on this file since 2b6cb87 was b097945, checked in by Wilmer van der Gaast <github@…>, at 2017-04-06T20:25:08Z

Move canohost functions (diff licence) to separate file.

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