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