1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <string.h>
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <time.h>
25
26 #ifdef WIN32
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31
32 #define WANTARPA
33 #define WANTDNS
34 #include "inet.h"
35
36 #include "hexchat.h"
37 #include "util.h"
38 #include "ignore.h"
39 #include "fe.h"
40 #include "modes.h"
41 #include "notify.h"
42 #include "outbound.h"
43 #include "inbound.h"
44 #include "server.h"
45 #include "servlist.h"
46 #include "text.h"
47 #include "ctcp.h"
48 #include "hexchatc.h"
49 #include "chanopt.h"
50
51
52 void
clear_channel(session * sess)53 clear_channel (session *sess)
54 {
55 if (sess->channel[0])
56 strcpy (sess->waitchannel, sess->channel);
57 sess->channel[0] = 0;
58 sess->doing_who = FALSE;
59 sess->done_away_check = FALSE;
60
61 log_close (sess);
62
63 if (sess->current_modes)
64 {
65 g_free (sess->current_modes);
66 sess->current_modes = NULL;
67 }
68
69 if (sess->mode_timeout_tag)
70 {
71 fe_timeout_remove (sess->mode_timeout_tag);
72 sess->mode_timeout_tag = 0;
73 }
74
75 fe_clear_channel (sess);
76 userlist_clear (sess);
77 fe_set_nonchannel (sess, FALSE);
78 fe_set_title (sess);
79 }
80
81 void
set_topic(session * sess,char * topic,char * stripped_topic)82 set_topic (session *sess, char *topic, char *stripped_topic)
83 {
84 /* The topic of dialogs are the users hostname which is logged is new */
85 if (sess->type == SESS_DIALOG && (!sess->topic || strcmp(sess->topic, stripped_topic))
86 && sess->logfd != -1)
87 {
88 char tbuf[1024];
89 g_snprintf (tbuf, sizeof (tbuf), "[%s has address %s]\n", sess->channel, stripped_topic);
90 write (sess->logfd, tbuf, strlen (tbuf));
91 }
92
93 g_free (sess->topic);
94 sess->topic = g_strdup (stripped_topic);
95 fe_set_topic (sess, topic, stripped_topic);
96 }
97
98 static session *
find_session_from_nick(char * nick,server * serv)99 find_session_from_nick (char *nick, server *serv)
100 {
101 session *sess;
102 GSList *list = sess_list;
103
104 sess = find_dialog (serv, nick);
105 if (sess)
106 return sess;
107
108 if (serv->front_session)
109 {
110 // If we are here for ChanServ, then it is usually a reply for the user
111 if (!g_ascii_strcasecmp(nick, "ChanServ") || userlist_find (serv->front_session, nick))
112 return serv->front_session;
113 }
114
115 if (current_sess && current_sess->server == serv)
116 {
117 if (userlist_find (current_sess, nick))
118 return current_sess;
119 }
120
121 while (list)
122 {
123 sess = list->data;
124 if (sess->server == serv)
125 {
126 if (userlist_find (sess, nick))
127 return sess;
128 }
129 list = list->next;
130 }
131 return NULL;
132 }
133
134 static session *
inbound_open_dialog(server * serv,char * from,const message_tags_data * tags_data)135 inbound_open_dialog (server *serv, char *from,
136 const message_tags_data *tags_data)
137 {
138 session *sess;
139
140 sess = new_ircwindow (serv, from, SESS_DIALOG, 0);
141 /* for playing sounds */
142 EMIT_SIGNAL_TIMESTAMP (XP_TE_OPENDIALOG, sess, NULL, NULL, NULL, NULL, 0,
143 tags_data->timestamp);
144
145 return sess;
146 }
147
148 static void
inbound_make_idtext(server * serv,char * idtext,int max,int id)149 inbound_make_idtext (server *serv, char *idtext, int max, int id)
150 {
151 idtext[0] = 0;
152 if (serv->have_idmsg || serv->have_accnotify)
153 {
154 if (id)
155 {
156 safe_strcpy (idtext, prefs.hex_irc_id_ytext, max);
157 } else
158 {
159 safe_strcpy (idtext, prefs.hex_irc_id_ntext, max);
160 }
161 /* convert codes like %C,%U to the proper ones */
162 check_special_chars (idtext, TRUE);
163 }
164 }
165
166 void
inbound_privmsg(server * serv,char * from,char * ip,char * text,int id,const message_tags_data * tags_data)167 inbound_privmsg (server *serv, char *from, char *ip, char *text, int id,
168 const message_tags_data *tags_data)
169 {
170 session *sess;
171 struct User *user;
172 char idtext[64];
173 gboolean nodiag = FALSE;
174
175 sess = find_dialog (serv, from);
176
177 if (sess || prefs.hex_gui_autoopen_dialog)
178 {
179 /*0=ctcp 1=priv will set hex_gui_autoopen_dialog=0 here is flud detected */
180 if (!sess)
181 {
182 if (flood_check (from, ip, serv, current_sess, 1))
183 /* Create a dialog session */
184 sess = inbound_open_dialog (serv, from, tags_data);
185 else
186 sess = serv->server_session;
187 if (!sess)
188 return; /* ?? */
189 }
190
191 if (ip && ip[0])
192 set_topic (sess, ip, ip);
193 inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, tags_data->identified, tags_data);
194 return;
195 }
196
197 sess = find_session_from_nick (from, serv);
198 if (!sess)
199 {
200 sess = serv->front_session;
201 nodiag = TRUE; /* We don't want it to look like a normal message in front sess */
202 }
203
204 user = userlist_find (sess, from);
205 if (user)
206 {
207 user->lasttalk = time (0);
208 if (user->account)
209 id = TRUE;
210 }
211
212 inbound_make_idtext (serv, idtext, sizeof (idtext), id);
213
214 if (sess->type == SESS_DIALOG && !nodiag)
215 EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
216 tags_data->timestamp);
217 else
218 EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, 0,
219 tags_data->timestamp);
220 }
221
222 /* used for Alerts section. Masks can be separated by commas and spaces. */
223
224 gboolean
alert_match_word(char * word,char * masks)225 alert_match_word (char *word, char *masks)
226 {
227 char *p = masks;
228 char endchar;
229 int res;
230
231 if (masks[0] == 0)
232 return FALSE;
233
234 while (1)
235 {
236 /* if it's a 0, space or comma, the word has ended. */
237 if (*p == 0 || *p == ' ' || *p == ',')
238 {
239 endchar = *p;
240 *p = 0;
241 res = match (g_strchug (masks), word);
242 *p = endchar;
243
244 if (res)
245 return TRUE; /* yes, matched! */
246
247 masks = p + 1;
248 if (*p == 0)
249 return FALSE;
250 }
251 p++;
252 }
253 }
254
255 gboolean
alert_match_text(char * text,char * masks)256 alert_match_text (char *text, char *masks)
257 {
258 unsigned char *p = text;
259 unsigned char endchar;
260 int res;
261
262 if (masks[0] == 0)
263 return FALSE;
264
265 while (1)
266 {
267 if (*p >= '0' && *p <= '9')
268 {
269 p++;
270 continue;
271 }
272
273 /* if it's RFC1459 <special>, it can be inside a word */
274 switch (*p)
275 {
276 case '-': case '[': case ']': case '\\':
277 case '`': case '^': case '{': case '}':
278 case '_': case '|':
279 p++;
280 continue;
281 }
282
283 /* if it's a 0, space or comma, the word has ended. */
284 if (*p == 0 || *p == ' ' || *p == ',' ||
285 /* if it's anything BUT a letter, the word has ended. */
286 (!g_unichar_isalpha (g_utf8_get_char (p))))
287 {
288 endchar = *p;
289 *p = 0;
290 res = alert_match_word (text, masks);
291 *p = endchar;
292
293 if (res)
294 return TRUE; /* yes, matched! */
295
296 text = p + g_utf8_skip [p[0]];
297 if (*p == 0)
298 return FALSE;
299 }
300
301 p += g_utf8_skip [p[0]];
302 }
303 }
304
305 static int
is_hilight(char * from,char * text,session * sess,server * serv)306 is_hilight (char *from, char *text, session *sess, server *serv)
307 {
308 if (alert_match_word (from, prefs.hex_irc_no_hilight))
309 return 0;
310
311 text = strip_color (text, -1, STRIP_ALL);
312
313 if (alert_match_text (text, serv->nick) ||
314 alert_match_text (text, prefs.hex_irc_extra_hilight) ||
315 alert_match_word (from, prefs.hex_irc_nick_hilight))
316 {
317 g_free (text);
318 if (sess != current_tab)
319 {
320 sess->tab_state |= TAB_STATE_NEW_HILIGHT;
321 lastact_update (sess);
322 }
323 return 1;
324 }
325
326 g_free (text);
327 return 0;
328 }
329
330 void
inbound_action(session * sess,char * chan,char * from,char * ip,char * text,int fromme,int id,const message_tags_data * tags_data)331 inbound_action (session *sess, char *chan, char *from, char *ip, char *text,
332 int fromme, int id, const message_tags_data *tags_data)
333 {
334 session *def = sess;
335 server *serv = sess->server;
336 struct User *user;
337 char nickchar[2] = "\000";
338 char idtext[64];
339 int privaction = FALSE;
340
341 if (!fromme)
342 {
343 if (is_channel (serv, chan))
344 {
345 sess = find_channel (serv, chan);
346 } else
347 {
348 /* it's a private action! */
349 privaction = TRUE;
350 /* find a dialog tab for it */
351 sess = find_dialog (serv, from);
352 /* if non found, open a new one */
353 if (!sess && prefs.hex_gui_autoopen_dialog)
354 {
355 /* but only if it wouldn't flood */
356 if (flood_check (from, ip, serv, current_sess, 1))
357 sess = inbound_open_dialog (serv, from, tags_data);
358 else
359 sess = serv->server_session;
360 }
361 if (!sess)
362 {
363 sess = find_session_from_nick (from, serv);
364 /* still not good? */
365 if (!sess)
366 sess = serv->front_session;
367 }
368 }
369 }
370
371 if (!sess)
372 sess = def;
373
374 if (sess != current_tab)
375 {
376 if (fromme)
377 sess->tab_state |= TAB_STATE_NEW_DATA;
378 else
379 sess->tab_state |= TAB_STATE_NEW_MSG;
380 lastact_update (sess);
381 }
382
383 user = userlist_find (sess, from);
384 if (user)
385 {
386 nickchar[0] = user->prefix[0];
387 user->lasttalk = time (0);
388 if (user->account)
389 id = TRUE;
390 if (user->me)
391 fromme = TRUE;
392 }
393
394 inbound_make_idtext (serv, idtext, sizeof (idtext), id);
395
396 if (!fromme && !privaction)
397 {
398 if (is_hilight (from, text, sess, serv))
399 {
400 EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANACTION, sess, from, text, nickchar,
401 idtext, 0, tags_data->timestamp);
402 return;
403 }
404 }
405
406 if (fromme)
407 EMIT_SIGNAL_TIMESTAMP (XP_TE_UACTION, sess, from, text, nickchar, idtext,
408 0, tags_data->timestamp);
409 else if (!privaction)
410 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANACTION, sess, from, text, nickchar,
411 idtext, 0, tags_data->timestamp);
412 else if (sess->type == SESS_DIALOG)
413 EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL,
414 0, tags_data->timestamp);
415 else
416 EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, 0,
417 tags_data->timestamp);
418 }
419
420 void
inbound_chanmsg(server * serv,session * sess,char * chan,char * from,char * text,char fromme,int id,const message_tags_data * tags_data)421 inbound_chanmsg (server *serv, session *sess, char *chan, char *from,
422 char *text, char fromme, int id,
423 const message_tags_data *tags_data)
424 {
425 struct User *user;
426 int hilight = FALSE;
427 char nickchar[2] = "\000";
428 char idtext[64];
429
430 if (!sess)
431 {
432 if (chan)
433 {
434 sess = find_channel (serv, chan);
435 if (!sess && !is_channel (serv, chan))
436 sess = find_dialog (serv, chan);
437 } else
438 {
439 sess = find_dialog (serv, from);
440 }
441 if (!sess)
442 return;
443 }
444
445 if (sess != current_tab)
446 {
447 sess->tab_state |= TAB_STATE_NEW_MSG;
448 lastact_update (sess);
449 }
450
451 user = userlist_find (sess, from);
452 if (user)
453 {
454 if (user->account)
455 id = TRUE;
456 nickchar[0] = user->prefix[0];
457 user->lasttalk = time (0);
458 if (user->me)
459 fromme = TRUE;
460 }
461
462 if (fromme)
463 {
464 if (prefs.hex_away_auto_unmark && serv->is_away && !tags_data->timestamp)
465 sess->server->p_set_back (sess->server);
466 EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL,
467 0, tags_data->timestamp);
468 return;
469 }
470
471 inbound_make_idtext (serv, idtext, sizeof (idtext), id);
472
473 if (is_hilight (from, text, sess, serv))
474 hilight = TRUE;
475
476 if (sess->type == SESS_DIALOG)
477 EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
478 tags_data->timestamp);
479 else if (hilight)
480 EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext,
481 0, tags_data->timestamp);
482 else
483 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMSG, sess, from, text, nickchar, idtext,
484 0, tags_data->timestamp);
485 }
486
487 void
inbound_newnick(server * serv,char * nick,char * newnick,int quiet,const message_tags_data * tags_data)488 inbound_newnick (server *serv, char *nick, char *newnick, int quiet,
489 const message_tags_data *tags_data)
490 {
491 int me = FALSE;
492 session *sess;
493 GSList *list = sess_list;
494
495 if (!serv->p_cmp (nick, serv->nick))
496 {
497 me = TRUE;
498 safe_strcpy (serv->nick, newnick, NICKLEN);
499 }
500
501 while (list)
502 {
503 sess = list->data;
504 if (sess->server == serv)
505 {
506 if (userlist_change (sess, nick, newnick) || (me && sess->type == SESS_SERVER))
507 {
508 if (!quiet)
509 {
510 if (me)
511 EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANGENICK, sess, nick,
512 newnick, NULL, NULL, 0,
513 tags_data->timestamp);
514 else
515 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANGENICK, sess, nick,
516 newnick, NULL, NULL, 0, tags_data->timestamp);
517 }
518 }
519 if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
520 {
521 safe_strcpy (sess->channel, newnick, CHANLEN);
522 fe_set_channel (sess);
523 }
524 fe_set_title (sess);
525 }
526 list = list->next;
527 }
528
529 dcc_change_nick (serv, nick, newnick);
530
531 if (me)
532 fe_set_nick (serv, newnick);
533 }
534
535 /* find a "<none>" tab */
536 static session *
find_unused_session(server * serv)537 find_unused_session (server *serv)
538 {
539 session *sess;
540 GSList *list = sess_list;
541 while (list)
542 {
543 sess = (session *) list->data;
544 if (sess->type == SESS_CHANNEL && sess->channel[0] == 0 &&
545 sess->server == serv)
546 {
547 if (sess->waitchannel[0] == 0)
548 return sess;
549 }
550 list = list->next;
551 }
552 return NULL;
553 }
554
555 static session *
find_session_from_waitchannel(char * chan,struct server * serv)556 find_session_from_waitchannel (char *chan, struct server *serv)
557 {
558 session *sess;
559 GSList *list = sess_list;
560 while (list)
561 {
562 sess = (session *) list->data;
563 if (sess->server == serv && sess->channel[0] == 0 && sess->type == SESS_CHANNEL)
564 {
565 if (!serv->p_cmp (chan, sess->waitchannel))
566 return sess;
567 }
568 list = list->next;
569 }
570 return NULL;
571 }
572
573 void
inbound_ujoin(server * serv,char * chan,char * nick,char * ip,const message_tags_data * tags_data)574 inbound_ujoin (server *serv, char *chan, char *nick, char *ip,
575 const message_tags_data *tags_data)
576 {
577 session *sess;
578 int found_unused = FALSE;
579
580 /* already joined? probably a bnc */
581 sess = find_channel (serv, chan);
582 if (!sess)
583 {
584 /* see if a window is waiting to join this channel */
585 sess = find_session_from_waitchannel (chan, serv);
586 if (!sess)
587 {
588 /* find a "<none>" tab and use that */
589 sess = find_unused_session (serv);
590 found_unused = sess != NULL;
591 if (!sess)
592 /* last resort, open a new tab/window */
593 sess = new_ircwindow (serv, chan, SESS_CHANNEL, 1);
594 }
595 }
596
597 safe_strcpy (sess->channel, chan, CHANLEN);
598 if (found_unused)
599 {
600 chanopt_load (sess);
601 scrollback_load (sess);
602 if (sess->scrollwritten && sess->scrollback_replay_marklast)
603 sess->scrollback_replay_marklast (sess);
604 }
605
606 fe_set_channel (sess);
607 fe_set_title (sess);
608 fe_set_nonchannel (sess, TRUE);
609 userlist_clear (sess);
610
611 log_open_or_close (sess);
612
613 sess->waitchannel[0] = 0;
614 sess->ignore_date = TRUE;
615 sess->ignore_mode = TRUE;
616 sess->ignore_names = TRUE;
617 sess->end_of_names = FALSE;
618
619 /* sends a MODE */
620 serv->p_join_info (sess->server, chan);
621
622 EMIT_SIGNAL_TIMESTAMP (XP_TE_UJOIN, sess, nick, chan, ip, NULL, 0,
623 tags_data->timestamp);
624
625 if (prefs.hex_irc_who_join)
626 {
627 /* sends WHO #channel */
628 serv->p_user_list (sess->server, chan);
629 sess->doing_who = TRUE;
630 }
631 }
632
633 void
inbound_ukick(server * serv,char * chan,char * kicker,char * reason,const message_tags_data * tags_data)634 inbound_ukick (server *serv, char *chan, char *kicker, char *reason,
635 const message_tags_data *tags_data)
636 {
637 session *sess = find_channel (serv, chan);
638 if (sess)
639 {
640 EMIT_SIGNAL_TIMESTAMP (XP_TE_UKICK, sess, serv->nick, chan, kicker,
641 reason, 0, tags_data->timestamp);
642 clear_channel (sess);
643 if (prefs.hex_irc_auto_rejoin)
644 {
645 serv->p_join (serv, chan, sess->channelkey);
646 safe_strcpy (sess->waitchannel, chan, CHANLEN);
647 }
648 }
649 }
650
651 void
inbound_upart(server * serv,char * chan,char * ip,char * reason,const message_tags_data * tags_data)652 inbound_upart (server *serv, char *chan, char *ip, char *reason,
653 const message_tags_data *tags_data)
654 {
655 session *sess = find_channel (serv, chan);
656 if (sess)
657 {
658 if (*reason)
659 EMIT_SIGNAL_TIMESTAMP (XP_TE_UPARTREASON, sess, serv->nick, ip, chan,
660 reason, 0, tags_data->timestamp);
661 else
662 EMIT_SIGNAL_TIMESTAMP (XP_TE_UPART, sess, serv->nick, ip, chan, NULL,
663 0, tags_data->timestamp);
664 clear_channel (sess);
665 }
666 }
667
668 void
inbound_nameslist(server * serv,char * chan,char * names,const message_tags_data * tags_data)669 inbound_nameslist (server *serv, char *chan, char *names,
670 const message_tags_data *tags_data)
671 {
672 session *sess;
673 char **name_list;
674 char *host, *nopre_name;
675 char name[NICKLEN];
676 int i;
677 size_t offset;
678
679 sess = find_channel (serv, chan);
680 if (!sess)
681 {
682 EMIT_SIGNAL_TIMESTAMP (XP_TE_USERSONCHAN, serv->server_session, chan,
683 names, NULL, NULL, 0, tags_data->timestamp);
684 return;
685 }
686 if (!sess->ignore_names)
687 EMIT_SIGNAL_TIMESTAMP (XP_TE_USERSONCHAN, sess, chan, names, NULL, NULL,
688 0, tags_data->timestamp);
689
690 if (sess->end_of_names)
691 {
692 sess->end_of_names = FALSE;
693 userlist_clear (sess);
694 }
695
696 name_list = g_strsplit (names, " ", -1);
697 for (i = 0; name_list[i]; i++)
698 {
699 host = NULL;
700 offset = sizeof(name);
701
702 if (name_list[i][0] == 0)
703 continue;
704
705 if (serv->have_uhnames)
706 {
707 offset = 0;
708 nopre_name = name_list[i];
709
710 /* Ignore prefixes so '!' won't cause issues */
711 while (strchr (serv->nick_prefixes, *nopre_name) != NULL)
712 {
713 nopre_name++;
714 offset++;
715 }
716
717 offset += strcspn (nopre_name, "!");
718 if (offset++ < strlen (name_list[i]))
719 host = name_list[i] + offset;
720 }
721
722 g_strlcpy (name, name_list[i], MIN(offset, sizeof(name)));
723
724 userlist_add (sess, name, host, NULL, NULL, tags_data);
725 }
726 g_strfreev (name_list);
727 }
728
729 void
inbound_topic(server * serv,char * chan,char * topic_text,const message_tags_data * tags_data)730 inbound_topic (server *serv, char *chan, char *topic_text,
731 const message_tags_data *tags_data)
732 {
733 session *sess = find_channel (serv, chan);
734 char *stripped_topic;
735
736 if (sess)
737 {
738 stripped_topic = strip_color (topic_text, -1, STRIP_ALL);
739 set_topic (sess, topic_text, stripped_topic);
740 g_free (stripped_topic);
741 } else
742 sess = serv->server_session;
743
744 EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPIC, sess, chan, topic_text, NULL, NULL, 0,
745 tags_data->timestamp);
746 }
747
748 void
inbound_topicnew(server * serv,char * nick,char * chan,char * topic,const message_tags_data * tags_data)749 inbound_topicnew (server *serv, char *nick, char *chan, char *topic,
750 const message_tags_data *tags_data)
751 {
752 session *sess;
753 char *stripped_topic;
754
755 sess = find_channel (serv, chan);
756 if (sess)
757 {
758 EMIT_SIGNAL_TIMESTAMP (XP_TE_NEWTOPIC, sess, nick, topic, chan, NULL, 0,
759 tags_data->timestamp);
760 stripped_topic = strip_color (topic, -1, STRIP_ALL);
761 set_topic (sess, topic, stripped_topic);
762 g_free (stripped_topic);
763 }
764 }
765
766 void
inbound_join(server * serv,char * chan,char * user,char * ip,char * account,char * realname,const message_tags_data * tags_data)767 inbound_join (server *serv, char *chan, char *user, char *ip, char *account,
768 char *realname, const message_tags_data *tags_data)
769 {
770 session *sess = find_channel (serv, chan);
771 if (sess)
772 {
773 EMIT_SIGNAL_TIMESTAMP (XP_TE_JOIN, sess, user, chan, ip, account, 0,
774 tags_data->timestamp);
775 userlist_add (sess, user, ip, account, realname, tags_data);
776 }
777 }
778
779 void
inbound_kick(server * serv,char * chan,char * user,char * kicker,char * reason,const message_tags_data * tags_data)780 inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason,
781 const message_tags_data *tags_data)
782 {
783 session *sess = find_channel (serv, chan);
784 if (sess)
785 {
786 EMIT_SIGNAL_TIMESTAMP (XP_TE_KICK, sess, kicker, user, chan, reason, 0,
787 tags_data->timestamp);
788 userlist_remove (sess, user);
789 }
790 }
791
792 void
inbound_part(server * serv,char * chan,char * user,char * ip,char * reason,const message_tags_data * tags_data)793 inbound_part (server *serv, char *chan, char *user, char *ip, char *reason,
794 const message_tags_data *tags_data)
795 {
796 session *sess = find_channel (serv, chan);
797 if (sess)
798 {
799 if (*reason)
800 EMIT_SIGNAL_TIMESTAMP (XP_TE_PARTREASON, sess, user, ip, chan, reason,
801 0, tags_data->timestamp);
802 else
803 EMIT_SIGNAL_TIMESTAMP (XP_TE_PART, sess, user, ip, chan, NULL, 0,
804 tags_data->timestamp);
805 userlist_remove (sess, user);
806 }
807 }
808
809 void
inbound_topictime(server * serv,char * chan,char * nick,time_t stamp,const message_tags_data * tags_data)810 inbound_topictime (server *serv, char *chan, char *nick, time_t stamp,
811 const message_tags_data *tags_data)
812 {
813 char *tim = ctime (&stamp);
814 session *sess = find_channel (serv, chan);
815
816 if (!sess)
817 sess = serv->server_session;
818
819 if (tim != NULL)
820 tim[24] = 0; /* get rid of the \n */
821
822 EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPICDATE, sess, chan, nick, tim, NULL, 0,
823 tags_data->timestamp);
824 }
825
826 void
inbound_quit(server * serv,char * nick,char * ip,char * reason,const message_tags_data * tags_data)827 inbound_quit (server *serv, char *nick, char *ip, char *reason,
828 const message_tags_data *tags_data)
829 {
830 GSList *list = sess_list;
831 session *sess;
832 struct User *user;
833 int was_on_front_session = FALSE;
834
835 while (list)
836 {
837 sess = (session *) list->data;
838 if (sess->server == serv)
839 {
840 if (sess == current_sess)
841 was_on_front_session = TRUE;
842 if ((user = userlist_find (sess, nick)))
843 {
844 EMIT_SIGNAL_TIMESTAMP (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0,
845 tags_data->timestamp);
846 userlist_remove_user (sess, user);
847 } else if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
848 {
849 EMIT_SIGNAL_TIMESTAMP (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0,
850 tags_data->timestamp);
851 }
852 }
853 list = list->next;
854 }
855
856 notify_set_offline (serv, nick, was_on_front_session, tags_data);
857 }
858
859 void
inbound_account(server * serv,char * nick,char * account,const message_tags_data * tags_data)860 inbound_account (server *serv, char *nick, char *account,
861 const message_tags_data *tags_data)
862 {
863 session *sess = NULL;
864 GSList *list;
865
866 list = sess_list;
867 while (list)
868 {
869 sess = list->data;
870 if (sess->server == serv)
871 userlist_set_account (sess, nick, account);
872 list = list->next;
873 }
874 }
875
876 void
inbound_ping_reply(session * sess,char * timestring,char * from,const message_tags_data * tags_data)877 inbound_ping_reply (session *sess, char *timestring, char *from,
878 const message_tags_data *tags_data)
879 {
880 unsigned long tim, nowtim, dif;
881 int lag = 0;
882 char outbuf[64];
883
884 if (strncmp (timestring, "LAG", 3) == 0)
885 {
886 timestring += 3;
887 lag = 1;
888 }
889
890 tim = strtoul (timestring, NULL, 10);
891 nowtim = make_ping_time ();
892 dif = nowtim - tim;
893
894 sess->server->ping_recv = time (0);
895
896 if (lag)
897 {
898 sess->server->lag_sent = 0;
899 sess->server->lag = dif;
900 fe_set_lag (sess->server, dif);
901 return;
902 }
903
904 if (atol (timestring) == 0)
905 {
906 if (sess->server->lag_sent)
907 sess->server->lag_sent = 0;
908 else
909 EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, "?", NULL, NULL, 0,
910 tags_data->timestamp);
911 } else
912 {
913 g_snprintf (outbuf, sizeof (outbuf), "%ld.%03ld", dif / 1000, dif % 1000);
914 EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, outbuf, NULL, NULL, 0,
915 tags_data->timestamp);
916 }
917 }
918
919 static session *
find_session_from_type(int type,server * serv)920 find_session_from_type (int type, server *serv)
921 {
922 session *sess;
923 GSList *list = sess_list;
924 while (list)
925 {
926 sess = list->data;
927 if (sess->type == type && serv == sess->server)
928 return sess;
929 list = list->next;
930 }
931 return NULL;
932 }
933
934 void
inbound_notice(server * serv,char * to,char * nick,char * msg,char * ip,int id,const message_tags_data * tags_data)935 inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id,
936 const message_tags_data *tags_data)
937 {
938 char *ptr = to;
939 session *sess = 0;
940 int server_notice = FALSE;
941
942 if (is_channel (serv, ptr))
943 sess = find_channel (serv, ptr);
944
945 /* /notice [mode-prefix]#channel should end up in that channel */
946 if (!sess && ptr[0] && strchr(serv->nick_prefixes, ptr[0]) != NULL)
947 {
948 ptr++;
949 sess = find_channel (serv, ptr);
950 }
951
952 if (strcmp (nick, ip) == 0)
953 server_notice = TRUE;
954
955 if (!sess)
956 {
957 ptr = 0;
958 if (prefs.hex_irc_notice_pos == 0)
959 {
960 /* paranoia check */
961 if (msg[0] == '[' && (!serv->have_idmsg || id))
962 {
963 /* guess where chanserv meant to post this -sigh- */
964 if (!g_ascii_strcasecmp (nick, "ChanServ") && !find_dialog (serv, nick))
965 {
966 char *dest = g_strdup (msg + 1);
967 char *end = strchr (dest, ']');
968 if (end)
969 {
970 *end = 0;
971 sess = find_channel (serv, dest);
972 }
973 g_free (dest);
974 }
975 }
976 if (!sess)
977 sess = find_session_from_nick (nick, serv);
978 } else if (prefs.hex_irc_notice_pos == 1)
979 {
980 int stype = server_notice ? SESS_SNOTICES : SESS_NOTICES;
981 sess = find_session_from_type (stype, serv);
982 if (!sess)
983 {
984 if (stype == SESS_NOTICES)
985 sess = new_ircwindow (serv, "(notices)", SESS_NOTICES, 0);
986 else
987 sess = new_ircwindow (serv, "(snotices)", SESS_SNOTICES, 0);
988 fe_set_channel (sess);
989 fe_set_title (sess);
990 fe_set_nonchannel (sess, FALSE);
991 userlist_clear (sess);
992 log_open_or_close (sess);
993 }
994 /* Avoid redundancy with some Undernet notices */
995 if (!strncmp (msg, "*** Notice -- ", 14))
996 msg += 14;
997 } else
998 {
999 sess = serv->front_session;
1000 }
1001
1002 if (!sess)
1003 {
1004 if (server_notice)
1005 sess = serv->server_session;
1006 else
1007 sess = serv->front_session;
1008 }
1009 }
1010
1011 if (msg[0] == '\001')
1012 {
1013 size_t len;
1014
1015 msg++;
1016 if (!strncmp (msg, "PING", 4))
1017 {
1018 inbound_ping_reply (sess, msg + 5, nick, tags_data);
1019 return;
1020 }
1021
1022 len = strlen(msg);
1023 if (msg[len - 1] == '\001')
1024 msg[len - 1] = '\000';
1025 }
1026
1027 if (server_notice)
1028 EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVNOTICE, sess, msg, nick, NULL, NULL, 0,
1029 tags_data->timestamp);
1030 else if (ptr)
1031 EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANNOTICE, sess, nick, to, msg, NULL, 0,
1032 tags_data->timestamp);
1033 else
1034 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTICE, sess, nick, msg, NULL, NULL, 0,
1035 tags_data->timestamp);
1036 }
1037
1038 void
inbound_away(server * serv,char * nick,char * msg,const message_tags_data * tags_data)1039 inbound_away (server *serv, char *nick, char *msg,
1040 const message_tags_data *tags_data)
1041 {
1042 struct away_msg *away = server_away_find_message (serv, nick);
1043 session *sess = NULL;
1044 GSList *list;
1045
1046 if (away && !strcmp (msg, away->message)) /* Seen the msg before? */
1047 {
1048 if (prefs.hex_away_show_once && !serv->inside_whois)
1049 return;
1050 } else
1051 {
1052 server_away_save_message (serv, nick, msg);
1053 }
1054
1055 if (prefs.hex_irc_whois_front)
1056 sess = serv->front_session;
1057 else
1058 {
1059 if (!serv->inside_whois)
1060 sess = find_session_from_nick (nick, serv);
1061 if (!sess)
1062 sess = serv->server_session;
1063 }
1064
1065 /* possibly hide the output */
1066 if (!serv->inside_whois || !serv->skip_next_whois)
1067 EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS5, sess, nick, msg, NULL, NULL, 0,
1068 tags_data->timestamp);
1069
1070 list = sess_list;
1071 while (list)
1072 {
1073 sess = list->data;
1074 if (sess->server == serv)
1075 userlist_set_away (sess, nick, TRUE);
1076 list = list->next;
1077 }
1078 }
1079
1080 void
inbound_away_notify(server * serv,char * nick,char * reason,const message_tags_data * tags_data)1081 inbound_away_notify (server *serv, char *nick, char *reason,
1082 const message_tags_data *tags_data)
1083 {
1084 session *sess = NULL;
1085 GSList *list;
1086
1087 list = sess_list;
1088 while (list)
1089 {
1090 sess = list->data;
1091 if (sess->server == serv)
1092 {
1093 userlist_set_away (sess, nick, reason ? TRUE : FALSE);
1094 if (sess == serv->front_session && notify_is_in_list (serv, nick))
1095 {
1096 if (reason)
1097 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYAWAY, sess, nick, reason, NULL,
1098 NULL, 0, tags_data->timestamp);
1099 else
1100 EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYBACK, sess, nick, NULL, NULL,
1101 NULL, 0, tags_data->timestamp);
1102 }
1103 }
1104 list = list->next;
1105 }
1106 }
1107
1108 int
inbound_nameslist_end(server * serv,char * chan,const message_tags_data * tags_data)1109 inbound_nameslist_end (server *serv, char *chan,
1110 const message_tags_data *tags_data)
1111 {
1112 session *sess;
1113 GSList *list;
1114
1115 if (!strcmp (chan, "*"))
1116 {
1117 list = sess_list;
1118 while (list)
1119 {
1120 sess = list->data;
1121 if (sess->server == serv)
1122 {
1123 sess->end_of_names = TRUE;
1124 sess->ignore_names = FALSE;
1125 fe_userlist_numbers (sess);
1126 }
1127 list = list->next;
1128 }
1129 return TRUE;
1130 }
1131 sess = find_channel (serv, chan);
1132 if (sess)
1133 {
1134 sess->end_of_names = TRUE;
1135 sess->ignore_names = FALSE;
1136 fe_userlist_numbers (sess);
1137 return TRUE;
1138 }
1139 return FALSE;
1140 }
1141
1142 static gboolean
check_autojoin_channels(server * serv)1143 check_autojoin_channels (server *serv)
1144 {
1145 int i = 0;
1146 session *sess;
1147 GSList *list = sess_list;
1148 GSList *sess_channels = NULL; /* joined channels that are not in the favorites list */
1149 favchannel *fav;
1150
1151 /* shouldn't really happen, the io tag is destroyed in server.c */
1152 if (!is_server (serv))
1153 {
1154 return FALSE;
1155 }
1156
1157 /* If there's a session (i.e. this is a reconnect), autojoin to everything that was open previously. */
1158 while (list)
1159 {
1160 sess = list->data;
1161
1162 if (sess->server == serv)
1163 {
1164 if (sess->willjoinchannel[0] != 0)
1165 {
1166 strcpy (sess->waitchannel, sess->willjoinchannel);
1167 sess->willjoinchannel[0] = 0;
1168
1169 fav = servlist_favchan_find (serv->network, sess->waitchannel, NULL); /* Is this channel in our favorites? */
1170
1171 /* session->channelkey is initially unset for channels joined from the favorites. You have to fill them up manually from favorites settings. */
1172 if (fav)
1173 {
1174 /* session->channelkey is set if there was a key change during the session. In that case, use the session key, not the one from favorites. */
1175 if (fav->key && !strlen (sess->channelkey))
1176 {
1177 safe_strcpy (sess->channelkey, fav->key, sizeof (sess->channelkey));
1178 }
1179 }
1180
1181 /* for easier checks, ensure that favchannel->key is just NULL when session->channelkey is empty i.e. '' */
1182 if (strlen (sess->channelkey))
1183 {
1184 sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, sess->channelkey);
1185 }
1186 else
1187 {
1188 sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, NULL);
1189 }
1190 i++;
1191 }
1192 }
1193
1194 list = list->next;
1195 }
1196
1197 if (sess_channels)
1198 {
1199 serv->p_join_list (serv, sess_channels);
1200 g_slist_free_full (sess_channels, (GDestroyNotify) servlist_favchan_free);
1201 }
1202 else
1203 {
1204 /* If there's no session, just autojoin to favorites. */
1205 if (serv->favlist)
1206 {
1207 serv->p_join_list (serv, serv->favlist);
1208 i++;
1209
1210 /* FIXME this is not going to work and is not needed either. server_free() does the job already. */
1211 /* g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); */
1212 }
1213 }
1214
1215 serv->joindelay_tag = 0;
1216 fe_server_event (serv, FE_SE_LOGGEDIN, i);
1217 return FALSE;
1218 }
1219
1220 void
inbound_next_nick(session * sess,char * nick,int error,const message_tags_data * tags_data)1221 inbound_next_nick (session *sess, char *nick, int error,
1222 const message_tags_data *tags_data)
1223 {
1224 char *newnick;
1225 server *serv = sess->server;
1226 ircnet *net;
1227
1228 serv->nickcount++;
1229
1230 switch (serv->nickcount)
1231 {
1232 case 2:
1233 newnick = prefs.hex_irc_nick2;
1234 net = serv->network;
1235 /* use network specific "Second choice"? */
1236 if (net && !(net->flags & FLAG_USE_GLOBAL) && net->nick2)
1237 {
1238 newnick = net->nick2;
1239 }
1240 serv->p_change_nick (serv, newnick);
1241 if (error)
1242 {
1243 EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKERROR, sess, nick, newnick, NULL, NULL,
1244 0, tags_data->timestamp);
1245 }
1246 else
1247 {
1248 EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL,
1249 0, tags_data->timestamp);
1250 }
1251 break;
1252
1253 case 3:
1254 serv->p_change_nick (serv, prefs.hex_irc_nick3);
1255 if (error)
1256 {
1257 EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKERROR, sess, nick, prefs.hex_irc_nick3,
1258 NULL, NULL, 0, tags_data->timestamp);
1259 }
1260 else
1261 {
1262 EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3,
1263 NULL, NULL, 0, tags_data->timestamp);
1264 }
1265 break;
1266
1267 default:
1268 EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKFAIL, sess, NULL, NULL, NULL, NULL, 0,
1269 tags_data->timestamp);
1270 }
1271 }
1272
1273
1274 static void
dns_addr_callback(GObject * obj,GAsyncResult * result,gpointer user_data)1275 dns_addr_callback (GObject *obj, GAsyncResult *result, gpointer user_data)
1276 {
1277 GResolver *resolver = G_RESOLVER(obj);
1278 session *sess = (session*)user_data;
1279 gchar *addr;
1280
1281 g_return_if_fail (is_session(sess));
1282
1283 addr = g_resolver_lookup_by_address_finish (resolver, result, NULL);
1284 if (addr)
1285 PrintTextf (sess, _("Resolved to %s"), addr);
1286 else
1287 PrintText (sess, _("Not found"));
1288 }
1289
1290 static void
dns_name_callback(GObject * obj,GAsyncResult * result,gpointer user_data)1291 dns_name_callback (GObject *obj, GAsyncResult *result, gpointer user_data)
1292 {
1293 GResolver *resolver = G_RESOLVER(obj);
1294 session *sess = (session*)user_data;
1295 GList* addrs;
1296 gchar* addr;
1297 GList* list;
1298
1299 g_return_if_fail (is_session (sess));
1300
1301 addrs = g_resolver_lookup_by_name_finish (resolver, result, NULL);
1302 if (addrs)
1303 {
1304 PrintText (sess, _("Resolved to:"));
1305
1306 for (list = g_list_first (addrs); list; list = g_list_next (list))
1307 {
1308 addr = g_inet_address_to_string (list->data);
1309 PrintTextf (sess, " %s", addr);
1310 }
1311
1312 g_resolver_free_addresses (addrs);
1313 }
1314 else
1315 PrintText (sess, _("Not found"));
1316 }
1317
1318 void
do_dns(session * sess,char * nick,char * host,const message_tags_data * tags_data)1319 do_dns (session *sess, char *nick, char *host,
1320 const message_tags_data *tags_data)
1321 {
1322 GResolver *res = g_resolver_get_default ();
1323 GInetAddress *addr;
1324 char *po;
1325
1326 po = strrchr (host, '@');
1327 if (po)
1328 host = po + 1;
1329
1330 if (nick)
1331 EMIT_SIGNAL_TIMESTAMP (XP_TE_RESOLVINGUSER, sess, nick, host, NULL, NULL, 0,
1332 tags_data->timestamp);
1333
1334 PrintTextf (sess, _("Looking up %s..."), host);
1335
1336 addr = g_inet_address_new_from_string (host);
1337 if (addr)
1338 g_resolver_lookup_by_address_async (res, addr, NULL, dns_addr_callback, sess);
1339 else
1340 g_resolver_lookup_by_name_async (res, host, NULL, dns_name_callback, sess);
1341 }
1342
1343 static void
set_default_modes(server * serv)1344 set_default_modes (server *serv)
1345 {
1346 char modes[8];
1347
1348 modes[0] = '+';
1349 modes[1] = '\0';
1350
1351 if (prefs.hex_irc_wallops)
1352 strcat (modes, "w");
1353 if (prefs.hex_irc_servernotice)
1354 strcat (modes, "s");
1355 if (prefs.hex_irc_invisible)
1356 strcat (modes, "i");
1357 if (prefs.hex_irc_hidehost)
1358 strcat (modes, "x");
1359
1360 if (modes[1] != '\0')
1361 {
1362 serv->p_mode (serv, serv->nick, modes);
1363 }
1364 }
1365
1366 void
inbound_login_start(session * sess,char * nick,char * servname,const message_tags_data * tags_data)1367 inbound_login_start (session *sess, char *nick, char *servname,
1368 const message_tags_data *tags_data)
1369 {
1370 inbound_newnick (sess->server, sess->server->nick, nick, TRUE, tags_data);
1371 server_set_name (sess->server, servname);
1372 if (sess->type == SESS_SERVER)
1373 log_open_or_close (sess);
1374 /* reset our away status */
1375 if (sess->server->reconnect_away)
1376 {
1377 handle_command (sess->server->server_session, "away", FALSE);
1378 sess->server->reconnect_away = FALSE;
1379 }
1380 }
1381
1382 static void
inbound_set_all_away_status(server * serv,char * nick,unsigned int status)1383 inbound_set_all_away_status (server *serv, char *nick, unsigned int status)
1384 {
1385 GSList *list;
1386 session *sess;
1387
1388 list = sess_list;
1389 while (list)
1390 {
1391 sess = list->data;
1392 if (sess->server == serv)
1393 userlist_set_away (sess, nick, status);
1394 list = list->next;
1395 }
1396 }
1397
1398 void
inbound_uaway(server * serv,const message_tags_data * tags_data)1399 inbound_uaway (server *serv, const message_tags_data *tags_data)
1400 {
1401 serv->is_away = TRUE;
1402 serv->away_time = time (NULL);
1403 fe_set_away (serv);
1404
1405 inbound_set_all_away_status (serv, serv->nick, 1);
1406 }
1407
1408 void
inbound_uback(server * serv,const message_tags_data * tags_data)1409 inbound_uback (server *serv, const message_tags_data *tags_data)
1410 {
1411 serv->is_away = FALSE;
1412 serv->reconnect_away = FALSE;
1413 fe_set_away (serv);
1414
1415 inbound_set_all_away_status (serv, serv->nick, 0);
1416 }
1417
1418 void
inbound_foundip(session * sess,char * ip,const message_tags_data * tags_data)1419 inbound_foundip (session *sess, char *ip, const message_tags_data *tags_data)
1420 {
1421 struct hostent *HostAddr;
1422
1423 HostAddr = gethostbyname (ip);
1424 if (HostAddr)
1425 {
1426 sess->server->dcc_ip = ((struct in_addr *) HostAddr->h_addr)->s_addr;
1427 EMIT_SIGNAL_TIMESTAMP (XP_TE_FOUNDIP, sess->server->server_session,
1428 inet_ntoa (*((struct in_addr *) HostAddr->h_addr)),
1429 NULL, NULL, NULL, 0, tags_data->timestamp);
1430 }
1431 }
1432
1433 void
inbound_user_info_start(session * sess,char * nick,const message_tags_data * tags_data)1434 inbound_user_info_start (session *sess, char *nick,
1435 const message_tags_data *tags_data)
1436 {
1437 /* set away to FALSE now, 301 may turn it back on */
1438 inbound_set_all_away_status (sess->server, nick, 0);
1439 }
1440
1441 /* reporting new information found about this user. chan may be NULL.
1442 * away may be 0xff to indicate UNKNOWN. */
1443
1444 void
inbound_user_info(session * sess,char * chan,char * user,char * host,char * servname,char * nick,char * realname,char * account,unsigned int away,const message_tags_data * tags_data)1445 inbound_user_info (session *sess, char *chan, char *user, char *host,
1446 char *servname, char *nick, char *realname,
1447 char *account, unsigned int away,
1448 const message_tags_data *tags_data)
1449 {
1450 server *serv = sess->server;
1451 session *who_sess;
1452 GSList *list;
1453 char *uhost = NULL;
1454
1455 if (user && host)
1456 {
1457 uhost = g_strdup_printf ("%s@%s", user, host);
1458 }
1459
1460 if (chan)
1461 {
1462 who_sess = find_channel (serv, chan);
1463 if (who_sess)
1464 userlist_add_hostname (who_sess, nick, uhost, realname, servname, account, away);
1465 else
1466 {
1467 if (serv->doing_dns && nick && host)
1468 do_dns (sess, nick, host, tags_data);
1469 }
1470 }
1471 else
1472 {
1473 /* came from WHOIS, not channel specific */
1474 for (list = sess_list; list; list = list->next)
1475 {
1476 sess = list->data;
1477 if (sess->type == SESS_CHANNEL && sess->server == serv)
1478 {
1479 userlist_add_hostname (sess, nick, uhost, realname, servname, account, away);
1480 }
1481 }
1482 }
1483
1484 g_free (uhost);
1485 }
1486
1487 int
inbound_banlist(session * sess,time_t stamp,char * chan,char * mask,char * banner,int rplcode,const message_tags_data * tags_data)1488 inbound_banlist (session *sess, time_t stamp, char *chan, char *mask,
1489 char *banner, int rplcode, const message_tags_data *tags_data)
1490 {
1491 char *time_str = ctime (&stamp);
1492 server *serv = sess->server;
1493 char *nl;
1494
1495 if (stamp <= 0 || time_str == NULL)
1496 {
1497 time_str = "";
1498 }
1499 else
1500 {
1501 if ((nl = strchr (time_str, '\n')))
1502 *nl = 0;
1503 }
1504
1505 sess = find_channel (serv, chan);
1506 if (!sess)
1507 {
1508 sess = serv->front_session;
1509 goto nowindow;
1510 }
1511
1512 if (!fe_add_ban_list (sess, mask, banner, time_str, rplcode))
1513 {
1514 nowindow:
1515
1516 EMIT_SIGNAL_TIMESTAMP (XP_TE_BANLIST, sess, chan, mask, banner, time_str,
1517 0, tags_data->timestamp);
1518 return TRUE;
1519 }
1520
1521 return TRUE;
1522 }
1523
1524 /* execute 1 end-of-motd command */
1525
1526 static int
inbound_exec_eom_cmd(char * str,void * sess)1527 inbound_exec_eom_cmd (char *str, void *sess)
1528 {
1529 char *cmd;
1530
1531 cmd = command_insert_vars ((session*)sess, (str[0] == '/') ? str + 1 : str);
1532 handle_command ((session*)sess, cmd, TRUE);
1533 g_free (cmd);
1534
1535 return 1;
1536 }
1537
1538 static int
inbound_nickserv_login(server * serv)1539 inbound_nickserv_login (server *serv)
1540 {
1541 /* this could grow ugly, but let's hope there won't be new NickServ types */
1542 switch (serv->loginmethod)
1543 {
1544 case LOGIN_MSG_NICKSERV:
1545 case LOGIN_NICKSERV:
1546 case LOGIN_CHALLENGEAUTH:
1547 #if 0
1548 case LOGIN_NS:
1549 case LOGIN_MSG_NS:
1550 case LOGIN_AUTH:
1551 #endif
1552 return 1;
1553 default:
1554 return 0;
1555 }
1556 }
1557
1558 void
inbound_login_end(session * sess,char * text,const message_tags_data * tags_data)1559 inbound_login_end (session *sess, char *text, const message_tags_data *tags_data)
1560 {
1561 GSList *cmdlist;
1562 commandentry *cmd;
1563 server *serv = sess->server;
1564 ircnet *net = serv->network;
1565
1566 if (!serv->end_of_motd)
1567 {
1568 if (prefs.hex_dcc_ip_from_server && serv->use_who)
1569 {
1570 serv->skip_next_userhost = TRUE;
1571 serv->p_get_ip_uh (serv, serv->nick); /* sends USERHOST mynick */
1572 }
1573 set_default_modes (serv);
1574
1575 if (net)
1576 {
1577 /* there may be more than 1, separated by \n */
1578
1579 cmdlist = net->commandlist;
1580 while (cmdlist)
1581 {
1582 cmd = cmdlist->data;
1583 inbound_exec_eom_cmd (cmd->command, sess);
1584 cmdlist = cmdlist->next;
1585 }
1586 }
1587 /* The previously ran commands can alter the state of the server */
1588 if (serv->network != net)
1589 return;
1590
1591 /* send nickserv password */
1592 if (net && net->pass && inbound_nickserv_login (serv))
1593 {
1594 serv->p_ns_identify (serv, net->pass);
1595 }
1596
1597 /* wait for join if command or nickserv set */
1598 if (net && prefs.hex_irc_join_delay
1599 && ((net->pass && inbound_nickserv_login (serv))
1600 || net->commandlist))
1601 {
1602 serv->joindelay_tag = fe_timeout_add_seconds (prefs.hex_irc_join_delay, check_autojoin_channels, serv);
1603 }
1604 else
1605 {
1606 check_autojoin_channels (serv);
1607 }
1608
1609 if (serv->supports_watch || serv->supports_monitor)
1610 {
1611 notify_send_watches (serv);
1612 }
1613
1614 serv->end_of_motd = TRUE;
1615 }
1616
1617 if (prefs.hex_irc_skip_motd && !serv->motd_skipped)
1618 {
1619 serv->motd_skipped = TRUE;
1620 EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL,
1621 NULL, NULL, 0, tags_data->timestamp);
1622 return;
1623 }
1624
1625 EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTD, serv->server_session, text, NULL, NULL,
1626 NULL, 0, tags_data->timestamp);
1627 }
1628
1629 void
inbound_identified(server * serv)1630 inbound_identified (server *serv) /* 'MODE +e MYSELF' on freenode */
1631 {
1632 if (serv->joindelay_tag)
1633 {
1634 /* stop waiting, just auto JOIN now */
1635 fe_timeout_remove (serv->joindelay_tag);
1636 serv->joindelay_tag = 0;
1637 check_autojoin_channels (serv);
1638 }
1639 }
1640
1641 static const char *sasl_mechanisms[] =
1642 {
1643 "PLAIN",
1644 "EXTERNAL"
1645 };
1646
1647 static void
inbound_toggle_caps(server * serv,const char * extensions_str,gboolean enable)1648 inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable)
1649 {
1650 char **extensions;
1651 gsize i;
1652
1653 extensions = g_strsplit (extensions_str, " ", 0);
1654
1655 for (i = 0; extensions[i]; i++)
1656 {
1657 const char *extension = extensions[i];
1658
1659 if (!strcmp (extension, "solanum.chat/identify-msg"))
1660 serv->have_idmsg = enable;
1661 else if (!strcmp (extension, "multi-prefix"))
1662 serv->have_namesx = enable;
1663 else if (!strcmp (extension, "account-notify"))
1664 serv->have_accnotify = enable;
1665 else if (!strcmp (extension, "extended-join"))
1666 serv->have_extjoin = enable;
1667 else if (!strcmp (extension, "userhost-in-names"))
1668 serv->have_uhnames = enable;
1669 else if (!strcmp (extension, "server-time")
1670 || !strcmp (extension, "znc.in/server-time")
1671 || !strcmp (extension, "znc.in/server-time-iso"))
1672 serv->have_server_time = enable;
1673 else if (!strcmp (extension, "away-notify"))
1674 serv->have_awaynotify = enable;
1675 else if (!strcmp (extension, "account-tag"))
1676 serv->have_account_tag = enable;
1677 else if (!strcmp (extension, "sasl"))
1678 {
1679 serv->have_sasl = enable;
1680 if (enable)
1681 {
1682 #ifdef USE_OPENSSL
1683 if (serv->loginmethod == LOGIN_SASLEXTERNAL)
1684 serv->sasl_mech = MECH_EXTERNAL;
1685 #endif
1686 /* Mechanism either defaulted to PLAIN or server gave us list */
1687 tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
1688 }
1689 }
1690 }
1691
1692 g_strfreev (extensions);
1693 }
1694
1695 void
inbound_cap_ack(server * serv,char * nick,char * extensions,const message_tags_data * tags_data)1696 inbound_cap_ack (server *serv, char *nick, char *extensions,
1697 const message_tags_data *tags_data)
1698 {
1699 EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
1700 NULL, NULL, 0, tags_data->timestamp);
1701
1702 inbound_toggle_caps (serv, extensions, TRUE);
1703 }
1704
1705 void
inbound_cap_del(server * serv,char * nick,char * extensions,const message_tags_data * tags_data)1706 inbound_cap_del (server *serv, char *nick, char *extensions,
1707 const message_tags_data *tags_data)
1708 {
1709 EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPDEL, serv->server_session, nick, extensions,
1710 NULL, NULL, 0, tags_data->timestamp);
1711
1712 inbound_toggle_caps (serv, extensions, FALSE);
1713 }
1714
1715 static const char * const supported_caps[] = {
1716 /* IRCv3.1 */
1717 "multi-prefix",
1718 "away-notify",
1719 "account-notify",
1720 "extended-join",
1721 /* "sasl", Handled manually */
1722
1723 /* IRCv3.2 */
1724 "server-time",
1725 "userhost-in-names",
1726 "cap-notify",
1727 "chghost",
1728 "setname",
1729 "invite-notify",
1730 "account-tag",
1731
1732 /* ZNC */
1733 "znc.in/server-time-iso",
1734 "znc.in/server-time",
1735
1736 /* Twitch */
1737 "twitch.tv/membership",
1738
1739 /* Solanum */
1740 "solanum.chat/identify-msg",
1741 };
1742
1743 static int
get_supported_mech(server * serv,const char * list)1744 get_supported_mech (server *serv, const char *list)
1745 {
1746 char **mechs = g_strsplit (list, ",", 0);
1747 gsize i;
1748 int ret = -1;
1749
1750 for (i = 0; mechs[i]; ++i)
1751 {
1752 #ifdef USE_OPENSSL
1753 if (serv->loginmethod == LOGIN_SASLEXTERNAL)
1754 {
1755 if (!strcmp (mechs[i], "EXTERNAL"))
1756 {
1757 ret = MECH_EXTERNAL;
1758 break;
1759 }
1760 }
1761 else
1762 #endif
1763 if (!strcmp (mechs[i], "PLAIN"))
1764 {
1765 ret = MECH_PLAIN;
1766 break;
1767 }
1768 }
1769
1770 g_strfreev (mechs);
1771 return ret;
1772 }
1773
1774 void
inbound_cap_ls(server * serv,char * nick,char * extensions_str,const message_tags_data * tags_data)1775 inbound_cap_ls (server *serv, char *nick, char *extensions_str,
1776 const message_tags_data *tags_data)
1777 {
1778 char buffer[500]; /* buffer for requesting capabilities and emitting the signal */
1779 gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */
1780 char **extensions;
1781 int i;
1782
1783 if (g_str_has_prefix (extensions_str, "* "))
1784 {
1785 serv->waiting_on_cap = TRUE;
1786 extensions_str += 2;
1787 extensions_str += extensions_str[0] == ':' ? 1 : 0;
1788 }
1789 else
1790 {
1791 serv->waiting_on_cap = FALSE;
1792 }
1793
1794 EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPLIST, serv->server_session, nick,
1795 extensions_str, NULL, NULL, 0, tags_data->timestamp);
1796
1797 extensions = g_strsplit (extensions_str, " ", 0);
1798
1799 strcpy (buffer, "CAP REQ :");
1800
1801 for (i=0; extensions[i]; i++)
1802 {
1803 char *extension = extensions[i];
1804 char *value;
1805 gsize x;
1806
1807 /* CAP 3.2 can provide values */
1808 if ((value = strchr (extension, '=')))
1809 {
1810 *value = '\0';
1811 value++;
1812 }
1813
1814 /* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
1815 if (!g_strcmp0 (extension, "sasl") &&
1816 ((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
1817 || serv->loginmethod == LOGIN_SASLEXTERNAL))
1818 {
1819 if (value)
1820 {
1821 int sasl_mech = get_supported_mech (serv, value);
1822 if (sasl_mech == -1) /* No supported mech */
1823 continue;
1824 serv->sasl_mech = sasl_mech;
1825 }
1826 want_cap = TRUE;
1827 serv->waiting_on_sasl = TRUE;
1828 g_strlcat (buffer, "sasl ", sizeof(buffer));
1829 continue;
1830 }
1831
1832 for (x = 0; x < G_N_ELEMENTS(supported_caps); ++x)
1833 {
1834 if (!g_strcmp0 (extension, supported_caps[x]))
1835 {
1836 g_strlcat (buffer, extension, sizeof(buffer));
1837 g_strlcat (buffer, " ", sizeof(buffer));
1838 want_cap = TRUE;
1839 }
1840 }
1841 }
1842
1843 g_strfreev (extensions);
1844
1845 if (want_cap)
1846 {
1847 /* buffer + 9 = emit buffer without "CAP REQ :" */
1848 EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
1849 buffer + 9, NULL, NULL, NULL, 0,
1850 tags_data->timestamp);
1851 tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
1852 }
1853 if (!serv->waiting_on_sasl && !serv->waiting_on_cap)
1854 {
1855 /* if we use SASL, CAP END is dealt via raw numerics */
1856 serv->sent_capend = TRUE;
1857 tcp_send_len (serv, "CAP END\r\n", 9);
1858 }
1859 }
1860
1861 void
inbound_cap_nak(server * serv,char * extensions_str,const message_tags_data * tags_data)1862 inbound_cap_nak (server *serv, char *extensions_str, const message_tags_data *tags_data)
1863 {
1864 char **extensions;
1865 int i;
1866
1867 extensions = g_strsplit (extensions_str, " ", 0);
1868 for (i=0; extensions[i]; i++)
1869 {
1870 if (!g_strcmp0 (extensions[i], "sasl"))
1871 serv->waiting_on_sasl = FALSE;
1872 }
1873
1874 if (!serv->waiting_on_cap && !serv->waiting_on_sasl && !serv->sent_capend)
1875 {
1876 serv->sent_capend = TRUE;
1877 tcp_send_len (serv, "CAP END\r\n", 9);
1878 }
1879
1880 g_strfreev (extensions);
1881 }
1882
1883 void
inbound_cap_list(server * serv,char * nick,char * extensions,const message_tags_data * tags_data)1884 inbound_cap_list (server *serv, char *nick, char *extensions,
1885 const message_tags_data *tags_data)
1886 {
1887 if (g_str_has_prefix (extensions, "* "))
1888 {
1889 extensions += 2;
1890 extensions += extensions[0] == ':' ? 1 : 0;
1891 }
1892 EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
1893 NULL, NULL, 0, tags_data->timestamp);
1894 }
1895
1896 void
inbound_sasl_authenticate(server * serv,char * data)1897 inbound_sasl_authenticate (server *serv, char *data)
1898 {
1899 ircnet *net = (ircnet*)serv->network;
1900 char *user, *pass = NULL;
1901 const char *mech = sasl_mechanisms[serv->sasl_mech];
1902
1903 /* Got a list of supported mechanisms from outdated inspircd
1904 * just ignore it as it goes against spec */
1905 if (strchr (data, ',') != NULL)
1906 return;
1907
1908 if (net->user && !(net->flags & FLAG_USE_GLOBAL))
1909 user = net->user;
1910 else
1911 user = prefs.hex_irc_user_name;
1912
1913 switch (serv->sasl_mech)
1914 {
1915 case MECH_PLAIN:
1916 pass = encode_sasl_pass_plain (user, serv->password);
1917 break;
1918 #ifdef USE_OPENSSL
1919 case MECH_EXTERNAL:
1920 pass = g_strdup ("+");
1921 break;
1922 #endif
1923 }
1924
1925 if (pass == NULL)
1926 {
1927 /* something went wrong abort */
1928 tcp_sendf (serv, "AUTHENTICATE *\r\n");
1929 return;
1930 }
1931
1932 tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass);
1933 g_free (pass);
1934
1935
1936 EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLAUTH, serv->server_session, user, (char*)mech,
1937 NULL, NULL, 0, 0);
1938 }
1939
1940 void
inbound_sasl_error(server * serv)1941 inbound_sasl_error (server *serv)
1942 {
1943 /* Just abort, not much we can do */
1944 tcp_sendf (serv, "AUTHENTICATE *\r\n");
1945 }
1946