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