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