1 /*
2  * net.c: network routines
3  * Copyright (C) 2002-2004 Saulius Menkevicius
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * $Id: net.c,v 1.36 2005/01/06 10:16:09 bobas Exp $
20  */
21 
22 #include <glib.h>
23 #include <errno.h>
24 #include <string.h>
25 
26 #include "libqcproto/qcproto.h"
27 
28 #include "main.h"
29 #include "net.h"
30 #include "prefs.h"
31 #include "sess.h"
32 #include "user.h"
33 #include "gui.h"
34 
35 #define NET_DEFAULT_CHARSET "ISO-8859-1"
36 
37 /** static vars
38   *************************/
39 static qcs_link net;
40 static enum net_type_enum net_type;
41 static guint net_event_source;
42 static GString * old_nickname;
43 
44 #define LOG_NET		N_("Network")
45 
46 #define ERRSTR_ENCODING	N_("Charset encoding/decoding error; \
47 please check \"Network charset\" under the \"Network\" on the Preferences dialog")
48 
49 /** local routines
50   *************************/
51 static gboolean net_connect();
52 static void net_disconnect();
53 
54 static enum user_mode_enum
net_local_mode(int qcs_umode)55 net_local_mode(int qcs_umode)
56 {
57 	enum user_mode_enum m;
58 
59 	switch(qcs_umode) {
60 	case QCS_UMODE_NORMAL:	m = UMODE_NORMAL;	break;
61 	case QCS_UMODE_OFFLINE:	m = UMODE_OFFLINE;	break;
62 	case QCS_UMODE_DND:	m = UMODE_DND;		break;
63 	case QCS_UMODE_AWAY:	m = UMODE_AWAY;		break;
64 	case QCS_UMODE_INVALID:	m = UMODE_NULL;		break;
65 	}
66 	return m;
67 }
68 
69 static int
net_qcs_mode(enum user_mode_enum local_mode)70 net_qcs_mode(enum user_mode_enum local_mode)
71 {
72 	int m;
73 
74 	switch(local_mode) {
75 	case UMODE_NORMAL:
76 	case UMODE_INVISIBLE:
77 		m = QCS_UMODE_NORMAL;
78 		break;
79 	case UMODE_DND:		m = QCS_UMODE_DND;	break;
80 	case UMODE_AWAY:	m = QCS_UMODE_AWAY;	break;
81 	case UMODE_OFFLINE:	m = QCS_UMODE_OFFLINE;	break;
82 	default:		m = QCS_UMODE_INVALID;	break;
83 	}
84 	return m;
85 }
86 
87 static void
delete_converted_msg(qcs_msg * m)88 delete_converted_msg(qcs_msg * m)
89 {
90 	if(m->src) g_free(m->src);
91 	if(m->dst) g_free(m->dst);
92 	if(m->text) g_free(m->text);
93 	if(m->supp) g_free(m->supp);
94 	if(m->chan) g_free(m->chan);
95 	g_free(m);
96 }
97 
98 static qcs_msg *
convert_msg_charset(const qcs_msg * m,const gchar * to,const gchar * from)99 convert_msg_charset(const qcs_msg * m, const gchar * to, const gchar * from)
100 {
101 	qcs_msg * t = g_new0(qcs_msg, 1);
102 
103 	t->msg = m->msg;
104 	t->umode = m->umode;
105 	t->uactive = m->uactive;
106 	t->src_ip = m->src_ip;
107 	t->src = !m->src ? NULL: g_convert_with_fallback(m->src, -1, to, from, "?", NULL, NULL, NULL);
108 	t->dst = !m->dst ? NULL: g_convert_with_fallback(m->dst, -1, to, from, "?", NULL, NULL, NULL);
109 	t->text = !m->text ? NULL: g_convert_with_fallback(m->text, -1, to, from, "?", NULL, NULL, NULL);
110 	t->supp = !m->supp ? NULL: g_convert_with_fallback(m->supp, -1, to, from, "?", NULL, NULL, NULL);
111 	t->chan = !m->chan ? NULL: g_convert_with_fallback(m->chan, -1, to, from, "?", NULL, NULL, NULL);
112 
113 	/* if any of the conversions have failed.. */
114 	if((m->src && !t->src) || (m->dst && !t->dst) || (m->text && !t->text)
115 	    || (m->supp && !t->supp) || (m->chan && !t->chan)) {
116 		delete_converted_msg(t);
117 		return NULL;
118 	}
119 
120 	return t;
121 }
122 
123 static GList *
decode_chanlist(const char * chanstring)124 decode_chanlist(const char * chanstring)
125 {
126 	const char * p, * next;
127 	char * name;
128 	GList * chanlist = NULL;
129 	int len;
130 
131 	g_assert(chanstring);
132 
133 	len = strlen(chanstring);
134 
135 	/* check that the chanstring is valid */
136 	if (len<3 || chanstring[0]!='#' || chanstring[len-1]!='#') {
137 		return NULL;
138 	}
139 
140 	/* parse the chanstring */
141 	p = chanstring + 1;
142 	do {
143 		/* extract name */
144 		next = strchr(p, '#');
145 		g_assert(next);
146 
147 		/* make sure that the channel name is valid */
148 		len = next - p;
149 		if (len>0 && len<=MAX_CHAN_LENGTH) {
150 			/* malloc a copy */
151 			name = g_malloc(len+1);
152 			memcpy(name, p, len);
153 			name[len] = '\0';
154 
155 			/* insert this into list */
156 			chanlist = g_list_append(chanlist, name);
157 		}
158 
159 		/* seek for next channel */
160 		p = next + 1;
161 	} while (*p);
162 
163 	return chanlist;
164 }
165 
166 static void
free_chanlist(GList * chanlist)167 free_chanlist(GList * chanlist)
168 {
169 	while(chanlist) {
170 		g_free(chanlist->data);
171 		chanlist = g_list_delete_link(chanlist, chanlist);
172 	}
173 }
174 
175 static void
net_errno(int errno_value)176 net_errno(int errno_value)
177 {
178 	const gchar * error_str = _(strerror(errno_value));
179 
180 	log_ferror(_(LOG_NET), g_strdup_printf("ERROR (%s)", error_str));
181 	raise_event(EVENT_NET_ERROR, (gpointer)error_str, errno_value);
182 }
183 
184 /*
185  * net_send:
186  *	sends a message to network
187  */
188 static void
net_send(const qcs_msg * m)189 net_send(const qcs_msg * m)
190 {
191 	g_assert(m);
192 
193 	if(net) {
194 		qcs_msg * cmsg = convert_msg_charset(m, prefs_str(PREFS_NET_ENCODING), "UTF-8");
195 		if(cmsg) {
196 			if(!qcs_send(net, cmsg)) {
197 				net_errno(errno);
198 			}
199 			delete_converted_msg(cmsg);
200 		} else {
201 			log_error(_(LOG_NET), _(ERRSTR_ENCODING));
202 		}
203 	}
204 }
205 
206 /**
207  * reply_on_net_join:
208  *	sends topic info & our existence to new user
209  */
210 static void
net_reply_on_net_join(const char * nick)211 net_reply_on_net_join(const char * nick)
212 {
213 	qcs_msg * m;
214 
215 	g_assert(nick && strlen(nick));
216 
217 	m = qcs_newmsg();
218 
219 	/* send topic reply
220 	 *	XXX: don't know what to
221 	 *	reply to vypresschat clients
222 	 */
223 
224 	/*
225 	m->msg = QCS_MSG_TOPIC_REPLY;
226 	qcs_msgset(m, QCS_DST, nick);
227 	qcs_msgset(m, QCS_TEXT, "");
228 	nett_send(m);
229 	*/
230 
231 	/* send refresh reply */
232 	m->msg = QCS_MSG_REFRESH_ACK;
233 	m->umode = net_qcs_mode(my_mode());
234 	m->uactive = gui_is_active();
235 	qcs_msgset(m, QCS_SRC, my_nickname());
236 	qcs_msgset(m, QCS_DST, nick);
237 
238 	net_send(m);
239 
240 	qcs_deletemsg(m);
241 }
242 
243 /** net_reply_to_info:
244   *	replies to info requests
245   */
246 static void
net_reply_to_info_request(const char * src)247 net_reply_to_info_request(const char * src)
248 {
249 
250 	g_assert(src);
251 
252 	/* do the logging */
253 	if(prefs_bool(PREFS_NET_VERBOSE))
254 		log_fevent(_(LOG_NET), g_strdup_printf(_("host info request from %s"),src));
255 
256 	/* reply, if configured to */
257 	if(prefs_bool(PREFS_NET_REPLY_INFO_REQ) && my_mode()!=UMODE_INVISIBLE) {
258 		gchar * chanlist;
259 		qcs_msg * m = qcs_newmsg();
260 
261 		m->msg = QCS_MSG_INFO_REPLY;
262 		qcs_msgset(m, QCS_SRC, my_nickname());
263 		qcs_msgset(m, QCS_DST, src);
264 		qcs_msgset(m, QCS_TEXT, my_nickname());
265 		qcs_msgset(m, QCS_SUPP, my_motd());
266 
267 		qcs_msgset(m, QCS_CHAN, chanlist = sess_make_channel_list());
268 		g_free(chanlist);
269 
270 		net_send(m);
271 		qcs_deletemsg(m);
272 
273 		if(prefs_bool(PREFS_NET_VERBOSE))
274 			log_event(_(LOG_NET), _("..replied to info request"));
275 	}
276 }
277 
278 /* net_reply_to_chanlist_request:
279  *	sends reply to the QCS_MSG_CHANLIST_REQUEST message
280  */
281 static void
net_reply_to_chanlist_request(gpointer user)282 net_reply_to_chanlist_request(gpointer user)
283 {
284 	if(prefs_bool(PREFS_NET_REPLY_INFO_REQ) && my_mode()!=UMODE_INVISIBLE) {
285 		gchar * chanlist;
286 		qcs_msg * m = qcs_newmsg();
287 		m->msg = QCS_MSG_CHANLIST_REPLY;
288 		qcs_msgset(m, QCS_DST, user_name_of(user));
289 		qcs_msgset(m, QCS_CHAN, chanlist = sess_make_channel_list());
290 		g_free(chanlist);
291 
292 		net_send(m);
293 		qcs_deletemsg(m);
294 	}
295 }
296 
297 static void
net_send_refresh_ack(const gchar * name)298 net_send_refresh_ack(const gchar * name)
299 {
300 	g_assert(name && g_utf8_strlen(name, -1));
301 
302 	if(my_mode() != UMODE_INVISIBLE) {
303 		qcs_msg * m = qcs_newmsg();
304 		m->msg = QCS_MSG_REFRESH_ACK;
305 		m->umode = net_qcs_mode(my_mode());
306 		m->uactive = gui_is_active();
307 		qcs_msgset(m, QCS_SRC, my_nickname());
308 		qcs_msgset(m, QCS_DST, name);
309 
310 		net_send(m);
311 		qcs_deletemsg(m);
312 	}
313 }
314 
315 static void
net_beep_sent_from(gpointer user)316 net_beep_sent_from(gpointer user)
317 {
318 	/* send beep ack (if not invisible) */
319 	if(my_mode() != UMODE_INVISIBLE) {
320 		qcs_msg * m = qcs_newmsg();
321 		m->msg = QCS_MSG_BEEP_ACK;
322 		qcs_msgset(m, QCS_SRC, my_nickname());
323 		qcs_msgset(m, QCS_DST, user_name_of(user));
324 		net_send(m);
325 		qcs_deletemsg(m);
326 	}
327 
328 	/* inform the world about beep message from 'user' */
329 	raise_event(EVENT_NET_MSG_BEEP_SEND, user, 0);
330 }
331 
332 static void
net_message_from(const gchar * nickname,const gchar * text,gboolean mass_message)333 net_message_from(
334 	const gchar * nickname, const gchar * text,
335 	gboolean mass_message)
336 {
337 	qcs_msg * m;
338 	gpointer event_v[3];
339 	gpointer user;
340 
341 	g_assert(nickname && text);
342 
343 	/* check if we can receive the message in this mode
344 	 */
345 	if(!IS_MESSAGEABLE_MODE(my_mode())) {
346 		log_fevent(_(LOG_NET), g_strdup_printf(
347 			mass_message
348 				? _("%s has sent a mass message however you are in %s mode")
349 				: _("%s has sent you a message however you are in %s mode"),
350 			nickname, user_mode_name(my_mode())
351 			)
352 		);
353 		return;
354 	}
355 
356 	/* check if this message is a mass message, and if we block them
357 	 */
358 	if(mass_message && prefs_bool(PREFS_NET_IGNORE_MASS_MSG)) {
359 		log_fevent(_(LOG_NET), g_strdup_printf(
360 			_("%s has sent a mass message however you have blocked them"),
361 			nickname)
362 		);
363 		return;
364 	}
365 
366 	if((user = user_by_name(nickname))!=0) {
367 		event_v[0] = user;
368 		event_v[1] = (gpointer)text;
369 		event_v[2] = (gpointer)mass_message;
370 		raise_event(EVENT_NET_MSG_MESSAGE, event_v, 0);
371 
372 		/* send confirmation that we've received his/her message */
373 		m = qcs_newmsg();
374 		m->msg = QCS_MSG_MESSAGE_ACK;
375 		m->umode = net_qcs_mode(my_mode());
376 		qcs_msgset(m, QCS_SRC, my_nickname());
377 		qcs_msgset(m, QCS_DST, user_name_of(user));
378 		qcs_msgset(m, QCS_TEXT, my_motd());
379 		net_send(m);
380 		qcs_deletemsg(m);
381 	}
382 }
383 
384 /**
385  * net_handle_netmsg:
386  *	Parses a message from the network.
387  *	Here we assume that m->src contains NULL if m->msg
388  *	has nothing to do with m->src
389  */
390 static void
net_handle_netmsg(const qcs_msg * raw_msg)391 net_handle_netmsg(const qcs_msg * raw_msg)
392 {
393 	gpointer event_v[5];
394 	qcs_msg * m;
395 
396 	g_assert(raw_msg);
397 
398 	m = convert_msg_charset(raw_msg, "UTF-8", prefs_str(PREFS_NET_ENCODING));
399 	if(!m) {
400 		/* conversion failed */
401 		log_error(_(LOG_NET), _(ERRSTR_ENCODING));
402 		return;
403 	}
404 
405 	/* check if the msg is invalid or one from ourselves */
406 	if( m->msg==QCS_MSG_INVALID
407 		|| (m->dst && nickname_cmp(m->dst, my_nickname()))
408 		|| (m->src && !nickname_cmp(m->src, my_nickname())) )
409 	{
410 		goto delete_and_exit;
411 	}
412 
413 	if(m->src && m->msg!=QCS_MSG_CHANNEL_LEAVE
414 		&& m->msg!=QCS_MSG_RENAME
415 		&& m->msg!=QCS_MSG_REFRESH_REQUEST
416 		&& m->msg!=QCS_MSG_PRIVATE_CLOSE)
417 	{
418 		/* check if the user is not ignored */
419 		if(prefs_list_contains(PREFS_NET_IGNORED_USERS, m->src))
420 			goto delete_and_exit;
421 
422 		if(!user_exists(m->src)) {
423 			event_v[0] = m->src;
424 			event_v[1] = GINT_TO_POINTER((m->msg==QCS_MSG_CHANNEL_JOIN
425 				|| m->msg==QCS_MSG_MODE_CHANGE
426 				|| m->msg==QCS_MSG_REFRESH_ACK)
427 					? net_local_mode(m->umode) : UMODE_NORMAL);
428 			event_v[2] = GINT_TO_POINTER(m->msg==QCS_MSG_REFRESH_ACK);
429 
430 			/* new user found on the network */
431 			raise_event(EVENT_NET_NEW_USER, event_v, 0);
432 		}
433 	}
434 
435 	switch(m->msg) {
436 	case QCS_MSG_TOPIC_REPLY:
437 	case QCS_MSG_TOPIC_CHANGE:
438 		if(net_type==NET_TYPE_QCHAT) {
439 			/* topic is the same for all channels in QChat */
440 			raise_event(EVENT_NET_MSG_TOPIC_CHANGE, m->text, 0);
441 		} else {
442 			/* topic is different for each channel in VyChat*/
443 			event_v[0] = m->chan;
444 			event_v[1] = m->text;
445 			raise_event(EVENT_NET_MSG_TOPIC_CHANGE_4, event_v, 0);
446 		}
447 		break;
448 
449 	case QCS_MSG_REFRESH_REQUEST:
450 		net_send_refresh_ack(m->src);
451 		break;
452 
453 	case QCS_MSG_REFRESH_ACK:
454 		event_v[0] = user_by_name(m->src);
455 		event_v[1] = GINT_TO_POINTER(m->uactive);
456 		raise_event(
457 			EVENT_NET_MSG_REFRESH_ACK,
458 			event_v, net_local_mode(m->umode));
459 		break;
460 
461 	case QCS_MSG_INFO_REQUEST:
462 		net_reply_to_info_request(m->src);
463 		break;
464 
465 	case QCS_MSG_MESSAGE_MASS:
466 	case QCS_MSG_MESSAGE_SEND:
467 		net_message_from(m->src, m->text, m->msg==QCS_MSG_MESSAGE_MASS);
468 		break;
469 
470 	case QCS_MSG_MESSAGE_ACK:
471 		event_v[0] = user_by_name(m->src);
472 		event_v[1] = m->text;
473 		if(event_v[0])
474 			raise_event(
475 				EVENT_NET_MSG_MESSAGE_ACK,
476 				event_v, net_local_mode(m->umode));
477 		break;
478 
479 	case QCS_MSG_RENAME:
480 		event_v[0] = user_by_name(m->src);
481 		event_v[1] = m->text;
482 		if(event_v[0])
483 			raise_event(EVENT_NET_MSG_RENAME, event_v, 0);
484 		break;
485 
486 	case QCS_MSG_MODE_CHANGE:
487 		raise_event(
488 			EVENT_NET_MSG_MODE_CHANGE,
489 			user_by_name(m->src), net_local_mode(m->umode));
490 		break;
491 
492 	case QCS_MSG_ACTIVE_CHANGE:
493 		raise_event(
494 			EVENT_NET_MSG_ACTIVE_CHANGE,
495 			user_by_name(m->src), m->uactive);
496 		break;
497 
498 	case QCS_MSG_CHANNEL_JOIN:
499 		if(!channel_cmp(m->chan, "Main")) {
500 			/* reply topic & our presence */
501 			net_reply_on_net_join(m->src);
502 		}
503 		event_v[0] = user_by_name(m->src);
504 		event_v[1] = m->chan;
505 		raise_event(EVENT_NET_MSG_CHANNEL_JOIN, event_v, 0);
506 		break;
507 
508 	case QCS_MSG_CHANNEL_LEAVE:
509 		event_v[0] = user_by_name(m->src);
510 		event_v[1] = m->chan;
511 		if(event_v[0])
512 			raise_event(EVENT_NET_MSG_CHANNEL_LEAVE, event_v, 0);
513 		break;
514 
515 	case QCS_MSG_CHANNEL_BROADCAST:
516 	case QCS_MSG_CHANNEL_ME:
517 		event_v[0] = user_by_name(m->src);
518 		event_v[1] = m->chan;
519 		event_v[2] = m->text;
520 		raise_event(
521 			EVENT_NET_MSG_CHANNEL_TEXT,
522 			event_v, m->msg==QCS_MSG_CHANNEL_ME);
523 		break;
524 
525 	case QCS_MSG_PRIVATE_OPEN:
526 		raise_event(EVENT_NET_MSG_PRIVATE_OPEN, user_by_name(m->src), 0);
527 		break;
528 
529 	case QCS_MSG_PRIVATE_CLOSE:
530 		event_v[0] = user_by_name(m->src);
531 		if(event_v[0])
532 			raise_event(EVENT_NET_MSG_PRIVATE_CLOSE, event_v[0], 0);
533 		break;
534 
535 	case QCS_MSG_PRIVATE_TEXT:
536 	case QCS_MSG_PRIVATE_ME:
537 		event_v[0] = user_by_name(m->src);
538 		event_v[1] = m->text;
539 		raise_event(EVENT_NET_MSG_PRIVATE_TEXT,
540 			event_v, m->msg==QCS_MSG_PRIVATE_ME);
541 		break;
542 
543 	case QCS_MSG_INFO_REPLY:
544 		event_v[0] = user_by_name(m->src);
545 		event_v[2] = (gpointer)decode_chanlist(m->chan);
546 		if(event_v[2]) {
547 			event_v[1] = m->text;
548 			event_v[3] = m->supp;
549 			event_v[4] = GINT_TO_POINTER(m->src_ip);
550 			raise_event(EVENT_NET_MSG_INFO_REPLY, event_v, 0);
551 			free_chanlist((GList*)event_v[2]);
552 		}
553 		break;
554 
555 	case QCS_MSG_BEEP_SEND:
556 		net_beep_sent_from(user_by_name(m->src));
557 		break;
558 
559 	case QCS_MSG_BEEP_ACK:
560 		raise_event(EVENT_NET_MSG_BEEP_ACK, user_by_name(m->src), 0);
561 		break;
562 
563 	case QCS_MSG_CHANLIST_REQUEST:
564 		net_reply_to_chanlist_request(user_by_name(m->src));
565 		break;
566 
567 	case QCS_MSG_CHANLIST_REPLY:
568 		event_v[0] = (gpointer)decode_chanlist(m->chan);
569 		if (event_v[0]!=NULL) {
570 			raise_event(EVENT_NET_MSG_CHANNEL_LIST, event_v[0], 0);
571 			free_chanlist( (GList*)event_v[0] );
572 		}
573 		break;
574 	default:
575 		break;	/* NOTHING */
576 	}
577 
578 delete_and_exit:
579 	delete_converted_msg(m);
580 }
581 
582 static gboolean
net_io_func(GIOChannel * s,GIOCondition c,gpointer data)583 net_io_func(
584 	GIOChannel * s,
585 	GIOCondition c,
586 	gpointer data)
587 {
588 	qcs_msg * m;
589 
590 	switch(c) {
591 	case G_IO_IN:
592 		m = qcs_newmsg();
593 		qcs_recv(net, m);
594 		net_handle_netmsg(m);
595 		qcs_deletemsg(m);
596 		break;
597 	case G_IO_ERR:
598 	case G_IO_HUP:
599 	case G_IO_NVAL:
600 		net_errno(0);
601 		break;
602 	default:
603 		break; /* nothing else */
604 	}
605 
606 	return TRUE;	/* leave handler untouched */
607 }
608 
609 static void
net_handle_my_text(const char * text,int me_text)610 net_handle_my_text(
611 	const char * text,
612 	int me_text)		/* for "/me <text>" expressions */
613 {
614 	sess_id s;
615 	enum session_type type;
616 	qcs_msg * m;
617 
618 	/* get current session & check that it's channel or private */
619 	s = sess_current();
620 	g_assert(s);
621 	type = sess_type(s);
622 	if(type!=SESSTYPE_CHANNEL && type!=SESSTYPE_PRIVATE)
623 		return;
624 
625 	/* make proto message */
626 	g_assert(text);
627 
628 	m = qcs_newmsg();
629 	qcs_msgset(m, QCS_SRC, my_nickname());
630 	qcs_msgset(m, QCS_TEXT, text);
631 
632 	/* set msg type & destination */
633 	if(type==SESSTYPE_CHANNEL) {
634 		m->msg = me_text
635 			? QCS_MSG_CHANNEL_ME : QCS_MSG_CHANNEL_BROADCAST;
636 		qcs_msgset(m, QCS_CHAN, sess_name(s));
637 	} else {
638 		m->msg = me_text
639 			? QCS_MSG_PRIVATE_ME : QCS_MSG_PRIVATE_TEXT;
640 		qcs_msgset(m, QCS_DST, sess_name(s));
641 	}
642 
643 	/* send the msg */
644 	net_send(m);
645 
646 	qcs_deletemsg(m);
647 }
648 
649 static void
net_handle_session_open_close(sess_id s,int open)650 net_handle_session_open_close(
651 	sess_id s,
652 	int open)	/* open(1) or close(0) */
653 {
654 	qcs_msg * m;
655 
656 	/* don't notify members of the channel if
657 	 * the `PREFS_CHANNEL_GREET' config option is not set,
658 	 * or we are "invisible"
659 	 */
660  	if(sess_type(s)==SESSTYPE_CHANNEL
661 			&& (!prefs_bool(PREFS_NET_CHANNEL_NOTIFY) || my_mode()==UMODE_INVISIBLE)
662 		) return;
663 
664 	m = qcs_newmsg();
665 
666 	switch(sess_type(s)) {
667 	case SESSTYPE_PRIVATE:
668 		m->msg = open ? QCS_MSG_PRIVATE_OPEN: QCS_MSG_PRIVATE_CLOSE;
669 		qcs_msgset(m, QCS_SRC, my_nickname());
670 		qcs_msgset(m, QCS_DST, sess_name(s));
671 		break;
672 	case SESSTYPE_CHANNEL:
673 		m->msg = open ? QCS_MSG_CHANNEL_JOIN: QCS_MSG_CHANNEL_LEAVE;
674 		m->umode = net_qcs_mode(my_mode());
675 		qcs_msgset(m, QCS_SRC, my_nickname());
676 		qcs_msgset(m, QCS_CHAN, sess_name(s));
677 		break;
678 	default:
679 		/* ignore status & etc open/close */
680 		break;
681 	}
682 	if(m->msg!=QCS_MSG_INVALID) {
683 		net_send(m);
684 	}
685 
686 	qcs_deletemsg(m);
687 }
688 
689 static void
handle_channel_topic_enter(sess_id session,const char * new_topic)690 handle_channel_topic_enter(
691 	sess_id session,
692 	const char * new_topic)
693 {
694 	qcs_msg * m;
695 
696 	g_assert(session && new_topic);
697 
698 	/* send message */
699 	m = qcs_newmsg();
700 	m->msg = QCS_MSG_TOPIC_CHANGE;
701 	qcs_msgset(m, QCS_CHAN, net_type==NET_TYPE_VYPRESS ? sess_name(session): "Main");
702 	qcs_msgset(m, QCS_TEXT, new_topic);
703 	net_send(m);
704 	qcs_deletemsg(m);
705 
706 	/* update topics for all the channels, if we are on qchat network */
707 	if(net_type==NET_TYPE_QCHAT) {
708 		raise_event(EVENT_NET_MSG_TOPIC_CHANGE, (void*)new_topic, 0);
709 	}
710 }
711 
712 static void
net_send_message_to(gpointer dst_user,const char * text)713 net_send_message_to(gpointer dst_user, const char * text)
714 {
715 	qcs_msg * m;
716 
717 	g_assert(dst_user && text);
718 
719 	m = qcs_newmsg();
720 	m->msg = QCS_MSG_MESSAGE_SEND;
721 	qcs_msgset(m, QCS_SRC, my_nickname());
722 	qcs_msgset(m, QCS_DST, user_name_of(dst_user));
723 	qcs_msgset(m, QCS_TEXT, text);
724 
725 	net_send(m);
726 	qcs_deletemsg(m);
727 }
728 
729 /** net_send_chan_req
730   *	supp func to send USER_CHANLIST_REQ
731   */
732 static void
net_send_chan_req()733 net_send_chan_req()
734 {
735 	qcs_msg * m;
736 
737 	m = qcs_newmsg();
738 	m->msg = QCS_MSG_CHANLIST_REQUEST;
739 	qcs_msgset(m, QCS_SRC, my_nickname());
740 	net_send(m);
741 	qcs_deletemsg(m);
742 }
743 
744 /** net_send_info_req:
745   *	supp funct to send USER_INFO_REQ
746   */
747 static void
net_send_info_req(gpointer dst_user)748 net_send_info_req(gpointer dst_user)
749 {
750 	qcs_msg * m;
751 
752 	g_assert(dst_user);
753 
754 	m = qcs_newmsg();
755 	m->msg = QCS_MSG_INFO_REQUEST;
756 	qcs_msgset(m, QCS_SRC, my_nickname());
757 	qcs_msgset(m, QCS_DST, user_name_of(dst_user));
758 
759 	net_send(m);
760 	qcs_deletemsg(m);
761 }
762 
763 /* net_send_beep:
764  *	sends BEEP to the specified user
765  */
766 static void
net_send_beep(gpointer dst_user)767 net_send_beep(gpointer dst_user)
768 {
769 	qcs_msg * m = qcs_newmsg();
770 	m->msg = QCS_MSG_BEEP_SEND;
771 	qcs_msgset(m, QCS_SRC, my_nickname());
772 	qcs_msgset(m, QCS_DST, user_name_of(dst_user));
773 
774 	net_send(m);
775 	qcs_deletemsg(m);
776 }
777 
778 
779 /** net_send_refresh_req:
780   *	supp func to send REFRESH_REQ
781   */
782 static void
net_send_refresh_req()783 net_send_refresh_req()
784 {
785 	qcs_msg * m = qcs_newmsg();
786 	m->msg = QCS_MSG_REFRESH_REQUEST;
787 	qcs_msgset(m, QCS_SRC, my_nickname());
788 
789 	net_send(m);
790 	qcs_deletemsg(m);
791 }
792 
793 /** net_send_active_change
794  *	notify network if our client gui is not active
795  *	(minimized, not-on-top, hidden, etc)
796  */
797 static void
net_send_active_change(gboolean is_active)798 net_send_active_change(gboolean is_active)
799 {
800 	qcs_msg * m = qcs_newmsg();
801 	m->msg = QCS_MSG_ACTIVE_CHANGE;
802 	m->uactive = is_active;
803 	qcs_msgset(m, QCS_SRC, my_nickname());
804 
805 	net_send(m);
806 	qcs_deletemsg(m);
807 }
808 
809 static gboolean
net_prefs_type_validator(const gchar * prefs_name,gpointer user_data)810 net_prefs_type_validator(const gchar * prefs_name, gpointer user_data)
811 {
812 	return prefs_int(prefs_name) < NET_TYPE_NUM;
813 }
814 
815 static gboolean
net_prefs_port_validator(const gchar * prefs_name,gpointer user_data)816 net_prefs_port_validator(const gchar * prefs_name, gpointer user_data)
817 {
818 	return prefs_int(prefs_name) <= G_MAXUINT16;
819 }
820 
821 static gboolean
net_prefs_encoding_validator(const gchar * prefs_name,gpointer user_data)822 net_prefs_encoding_validator(const gchar * prefs_name, gpointer user_data)
823 {
824 	gchar * enc_test;
825 	const gchar * new_encoding = prefs_str(prefs_name);
826 
827 	enc_test = g_convert("a", -1, "UTF-8", new_encoding, NULL, NULL, NULL);
828 	if(!enc_test) {
829 		log_ferror(NULL, g_strdup_printf(
830 			_("Charset encoding \"%s\" is invalid or not supported on this system"),
831 			new_encoding));
832 		return FALSE;
833 	}
834 	g_free(enc_test);
835 	return TRUE;
836 }
837 
838 static void
net_prefs_main_nickname_changed_cb(const gchar * prefs_name)839 net_prefs_main_nickname_changed_cb(const gchar * prefs_name)
840 {
841 	qcs_msg * m;
842 
843 	if(my_mode()!=UMODE_INVISIBLE) {
844 		/* we've been started & online: send MSG_RENAME */
845 		m = qcs_newmsg();
846 		m->msg = QCS_MSG_RENAME;
847 		qcs_msgset(m, QCS_SRC, old_nickname->str);
848 		qcs_msgset(m, QCS_TEXT, my_nickname());
849 		net_send(m);
850 		qcs_deletemsg(m);
851 	}
852 
853 	/* preserve old nickname */
854 	g_string_assign(old_nickname, my_nickname());
855 }
856 
857 static void
net_prefs_main_mode_changed_cb(const gchar * prefs_name)858 net_prefs_main_mode_changed_cb(const gchar * prefs_name)
859 {
860 	qcs_msg * m;
861 
862 	/* send MSG_MODE_CHANGE to network */
863 	if(my_mode()!=UMODE_INVISIBLE) {
864 		m = qcs_newmsg();
865 		m->msg = QCS_MSG_MODE_CHANGE;
866 		m->umode = net_qcs_mode(my_mode());
867 		qcs_msgset(m, QCS_SRC, my_nickname());
868 		net_send(m);
869 		qcs_deletemsg(m);
870 	}
871 }
872 
873 static void
net_prefs_net_settings_changed_cb(const gchar * prefs_name)874 net_prefs_net_settings_changed_cb(const gchar * prefs_name)
875 {
876 	if(app_status()==APP_RUNNING) {
877 		gboolean do_reconnect;
878 
879 		if(!strcmp(prefs_name, PREFS_NET_TYPE))
880 			do_reconnect = 1;
881 		else if(!strcmp(prefs_name, PREFS_NET_PORT))
882 			do_reconnect = 1;
883 		else if(!strcmp(prefs_name, PREFS_NET_USE_MULTICAST))
884 			do_reconnect = prefs_int(PREFS_NET_TYPE)==NET_TYPE_VYPRESS;
885 		else if(!strcmp(prefs_name, PREFS_NET_MULTICAST_ADDR))
886 			do_reconnect =
887 				prefs_int(PREFS_NET_TYPE)==NET_TYPE_VYPRESS
888 				&& prefs_bool(PREFS_NET_USE_MULTICAST);
889 		else if(!strcmp(prefs_name, PREFS_NET_BROADCAST_MASK))
890 			do_reconnect =
891 				prefs_int(PREFS_NET_TYPE)!=NET_TYPE_VYPRESS
892 				|| !prefs_bool(PREFS_NET_USE_MULTICAST);
893 		else
894 			do_reconnect = 0;
895 
896 		if(do_reconnect) {
897 			net_disconnect();
898 			net_connect();
899 		}
900 	}
901 }
902 
903 static void
net_prefs_net_is_configured_changed_cb(const gchar * prefs_name)904 net_prefs_net_is_configured_changed_cb(const gchar * prefs_name)
905 {
906 	if(prefs_bool(PREFS_NET_IS_CONFIGURED)
907 			&& !net_connected()
908 			&& app_status()==APP_RUNNING)
909 		net_connect();
910 }
911 
912 static void
net_register_prefs()913 net_register_prefs()
914 {
915 	prefs_register(PREFS_NET_TYPE,		PREFS_TYPE_UINT,
916 		_("Network type"), net_prefs_type_validator, NULL);
917 	prefs_register(PREFS_NET_PORT,		PREFS_TYPE_UINT,
918 		_("Network port"), net_prefs_port_validator, NULL);
919 	prefs_register(PREFS_NET_IS_CONFIGURED,	PREFS_TYPE_BOOL,
920 		_("Network is configured"), NULL, NULL);
921 	prefs_register(PREFS_NET_BROADCAST_MASK, PREFS_TYPE_UINT,
922 		_("Network broadcast mask"), NULL, NULL);
923 	prefs_register(PREFS_NET_USE_MULTICAST, PREFS_TYPE_BOOL,
924 		_("Use multicast networking"), NULL, NULL);
925 	prefs_register(PREFS_NET_MULTICAST_ADDR, PREFS_TYPE_UINT,
926 		_("Network multicast address"), NULL, NULL);
927 	prefs_register(PREFS_NET_VERBOSE,	PREFS_TYPE_BOOL,
928 		_("Verbosely report network events"), NULL, NULL);
929 	prefs_register(PREFS_NET_CHANNEL_NOTIFY,PREFS_TYPE_BOOL,
930 		_("Notify channel users on join/leave"), NULL, NULL);
931 	prefs_register(PREFS_NET_IGNORE_MASS_MSG,PREFS_TYPE_BOOL,
932 		_("Ignore mass messages"), NULL, NULL);
933 	prefs_register(PREFS_NET_ENCODING,	PREFS_TYPE_STR,
934 		_("Network charset encoding"), net_prefs_encoding_validator, NULL);
935 	prefs_register(PREFS_NET_REPLY_INFO_REQ,PREFS_TYPE_BOOL,
936 		_("Reply to user information requests"), NULL, NULL);
937 	prefs_register(PREFS_NET_MOTD,		PREFS_TYPE_STR,
938 		_("User information text"), NULL, NULL);
939 	prefs_register(PREFS_NET_MOTD_WHEN_AWAY,PREFS_TYPE_STR,
940 		_("User information text when away"), NULL, NULL);
941 	prefs_register(PREFS_NET_IGNORED_USERS, PREFS_TYPE_LIST,
942 		_("Ignored users"), NULL, NULL);
943 
944 	prefs_add_notifier(PREFS_MAIN_NICKNAME, (GHookFunc)net_prefs_main_nickname_changed_cb);
945 	prefs_add_notifier(PREFS_MAIN_MODE, (GHookFunc)net_prefs_main_mode_changed_cb);
946 
947 	prefs_add_notifier(PREFS_NET_PORT, (GHookFunc)net_prefs_net_settings_changed_cb);
948 	prefs_add_notifier(PREFS_NET_TYPE, (GHookFunc)net_prefs_net_settings_changed_cb);
949 	prefs_add_notifier(PREFS_NET_BROADCAST_MASK, (GHookFunc)net_prefs_net_settings_changed_cb);
950 	prefs_add_notifier(PREFS_NET_MULTICAST_ADDR, (GHookFunc)net_prefs_net_settings_changed_cb);
951 	prefs_add_notifier(PREFS_NET_USE_MULTICAST, (GHookFunc)net_prefs_net_settings_changed_cb);
952 
953 	prefs_add_notifier(PREFS_NET_IS_CONFIGURED, (GHookFunc)net_prefs_net_is_configured_changed_cb);
954 }
955 
956 static void
net_preset_prefs()957 net_preset_prefs()
958 {
959 	/* set default values */
960 	prefs_set(PREFS_NET_CHANNEL_NOTIFY, TRUE);
961 	prefs_set(PREFS_NET_ENCODING, NET_DEFAULT_CHARSET);
962 	prefs_set(PREFS_NET_REPLY_INFO_REQ, TRUE);
963 	prefs_set(PREFS_NET_MOTD_WHEN_AWAY, _("User is away"));
964 	prefs_set(PREFS_NET_BROADCAST_MASK, 0xffffffff);	/* 255.255.255.255 */
965 	prefs_set(PREFS_NET_MULTICAST_ADDR, 0xe3000002);	/* 227.0.0.2 */
966 }
967 
968 static void
net_event_cb(enum app_event_enum e,gpointer p,int i)969 net_event_cb(enum app_event_enum e, gpointer p, int i)
970 {
971 	switch(e) {
972 	case EVENT_MAIN_INIT:
973 		net = NULL;
974 		net_event_source = 0;
975 		old_nickname = g_string_new(NULL);
976 		break;
977 
978 	case EVENT_MAIN_REGISTER_PREFS:
979 		net_register_prefs();
980 		break;
981 
982 	case EVENT_MAIN_PRESET_PREFS:
983 		net_preset_prefs();
984 		break;
985 
986 	case EVENT_MAIN_START:
987 		/* gui_netselect_dlg should popup it's dialog
988 		 * if the network is not configured
989 		 */
990 		if(prefs_bool(PREFS_NET_IS_CONFIGURED))
991 			net_connect();
992 		break;
993 
994 	case EVENT_MAIN_PRECLOSE:
995 		net_disconnect();
996 		break;
997 
998 	case EVENT_MAIN_CLOSE:
999 		g_string_free(old_nickname, TRUE);
1000 		old_nickname = NULL;
1001 		break;
1002 
1003 	case EVENT_SESSION_OPENED:
1004 	case EVENT_SESSION_CLOSED:
1005 		net_handle_session_open_close(
1006 			(sess_id)p,
1007 			e==EVENT_SESSION_OPENED);
1008 		break;
1009 
1010 	case EVENT_SESSION_TOPIC_ENTER:
1011 		if(sess_type(EVENT_V(p, 0))==SESSTYPE_CHANNEL)
1012 			handle_channel_topic_enter(EVENT_V(p,0), EVENT_V(p,1));
1013 		break;
1014 
1015 	case EVENT_CMDPROC_SESSION_TEXT:
1016 		net_handle_my_text((const char *)p, i);
1017 		break;
1018 	case EVENT_MESSAGE_SEND:
1019 		net_send_message_to(EVENT_V(p,0), (const char *)EVENT_V(p,1));
1020 		break;
1021 	case EVENT_USER_NET_UPDATE_REQ:
1022 		net_send_refresh_req();
1023 		break;
1024 	case EVENT_IFACE_USER_INFO_REQ:
1025 		net_send_info_req(p);
1026 		break;
1027 	case EVENT_IFACE_USER_BEEP_REQ:
1028 		net_send_beep(p);
1029 		break;
1030 	case EVENT_IFACE_REQUEST_NET_CHANNELS:
1031 		net_send_chan_req();
1032 		break;
1033 	case EVENT_IFACE_ACTIVE_CHANGE:
1034 		net_send_active_change((gboolean)i);
1035 		break;
1036 	default:
1037 		break;	/* nothing else */
1038 	}
1039 }
1040 
1041 static gboolean
net_connect()1042 net_connect()
1043 {
1044 	GIOChannel * channel;
1045 	int rx_socket;
1046 	enum net_type_enum type = prefs_int(PREFS_NET_TYPE);
1047 	unsigned short port = prefs_int(PREFS_NET_PORT);
1048 
1049 	g_assert(net==NULL);
1050 
1051 	/* check network type num */
1052 	if(type>=NET_TYPE_NUM) {
1053 		log_error(_(LOG_NET), ("invalid network type specified: falling back to QuickChat"));
1054 		type = NET_TYPE_QCHAT;
1055 	}
1056 
1057 	/* notify everyone that we're about to connect to the network */
1058 	raise_event(EVENT_NET_PRECONNECT, 0, 0);
1059 
1060 	net_type = type;
1061 
1062 	/* do the real connecting */
1063 	if(type==QCS_PROTO_VYPRESS) {
1064 		int use_multicast = prefs_bool(PREFS_NET_USE_MULTICAST);
1065 		net = qcs_open(
1066 			QCS_PROTO_VYPRESS,
1067 			use_multicast ? QCS_PROTO_OPT_MULTICAST: 0,
1068 			use_multicast
1069 				? prefs_int(PREFS_NET_MULTICAST_ADDR)
1070 				: prefs_int(PREFS_NET_BROADCAST_MASK),
1071 			port);
1072 	} else {
1073 		net = qcs_open(QCS_PROTO_QCHAT, 0, prefs_int(PREFS_NET_BROADCAST_MASK), port);
1074 	}
1075 
1076 	/* check for errors */
1077 	if(!net) {
1078 		log_ferror(_(LOG_NET), g_strdup_printf(
1079 			_("failed to connect to %s network on port %hd: %s"),
1080 				net_name_of_type(type), port, strerror(errno)));
1081 		log_error(_(LOG_NET),
1082 			_("check if there are any programs running on the same port"
1083 				" and your firewall settings"));
1084 		return FALSE;
1085 	}
1086 
1087 	/* install glib event handler on socket */
1088 	qcs_rxsocket(net, &rx_socket);
1089 	channel = g_io_channel_unix_new(rx_socket);
1090 	net_event_source = g_io_add_watch(
1091 			channel,
1092 			G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
1093 			net_io_func, 0);
1094 	g_io_channel_unref(channel);
1095 
1096 	log_fevent(_(LOG_NET), g_strdup_printf(
1097 		_("connected to %s network on port %hd"), net_name_of_type(type), port));
1098 
1099 	/* notify that we've connected */
1100 	raise_event(EVENT_NET_CONNECTED, 0,0);
1101 
1102 	return TRUE;
1103 }
1104 
1105 static void
net_disconnect()1106 net_disconnect()
1107 {
1108 	if(net) {
1109 		/* notify everyone of our evil intentions */
1110 		raise_event(EVENT_NET_PREDISCONNECT, 0,0);
1111 
1112 		/* remove io_chan */
1113 		g_source_remove(net_event_source);
1114 		net_event_source = 0;
1115 
1116 		/* close link */
1117 		qcs_close(net);
1118 		net = NULL;
1119 
1120 		raise_event(EVENT_NET_DISCONNECTED, 0,0);
1121 	}
1122 }
1123 
1124 /** exported routines
1125   *************************/
net_register()1126 void net_register()
1127 {
1128 	register_event_cb(
1129 		net_event_cb,
1130 		EVENT_MAIN | EVENT_CMDPROC | EVENT_IFACE
1131 		| EVENT_SESSION | EVENT_MESSAGE | EVENT_USER);
1132 }
1133 
net_connected()1134 gboolean net_connected()
1135 {
1136 	return net!=NULL;
1137 }
1138 
1139 const gchar *
net_name_of_type(enum net_type_enum t)1140 net_name_of_type(enum net_type_enum t)
1141 {
1142 	switch(t) {
1143 	case NET_TYPE_QCHAT:	return "quickChat";
1144 	case NET_TYPE_VYPRESS:	return "Vypress Chat";
1145 	default:
1146 		break;	/* nothing else */
1147 	}
1148 	return "(invalid network type)";
1149 }
1150