restructure configure so pkg-config derived SSL flags get used
[rofl0r-ixchat.git] / src / common / inbound.c
blobb12cf5b86d4af022dfc2bc24055842b5e8cf5614
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #include <string.h>
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <time.h>
27 #define WANTARPA
28 #define WANTDNS
29 #include "inet.h"
31 #include "xchat.h"
32 #include "util.h"
33 #include "ignore.h"
34 #include "fe.h"
35 #include "modes.h"
36 #include "notify.h"
37 #include "outbound.h"
38 #include "inbound.h"
39 #include "server.h"
40 #include "servlist.h"
41 #include "text.h"
42 #include "ctcp.h"
43 #include "plugin.h"
44 #include "xchatc.h"
47 void
48 clear_channel (session *sess)
50 if (sess->channel[0])
51 strcpy (sess->waitchannel, sess->channel);
52 sess->channel[0] = 0;
53 sess->doing_who = FALSE;
54 sess->done_away_check = FALSE;
56 log_close (sess);
58 if (sess->current_modes)
60 free (sess->current_modes);
61 sess->current_modes = NULL;
64 if (sess->mode_timeout_tag)
66 fe_timeout_remove (sess->mode_timeout_tag);
67 sess->mode_timeout_tag = 0;
70 fe_clear_channel (sess);
71 userlist_clear (sess);
72 fe_set_nonchannel (sess, FALSE);
73 fe_set_title (sess);
76 void
77 set_topic (session *sess, char *topic, char *stripped_topic)
79 if (sess->topic)
80 free (sess->topic);
81 sess->topic = strdup (stripped_topic);
82 fe_set_topic (sess, topic, stripped_topic);
85 static session *
86 find_session_from_nick (char *nick, server *serv)
88 session *sess;
89 GSList *list = sess_list;
91 sess = find_dialog (serv, nick);
92 if (sess)
93 return sess;
95 if (serv->front_session)
97 if (userlist_find (serv->front_session, nick))
98 return serv->front_session;
101 if (current_sess && current_sess->server == serv)
103 if (userlist_find (current_sess, nick))
104 return current_sess;
107 while (list)
109 sess = list->data;
110 if (sess->server == serv)
112 if (userlist_find (sess, nick))
113 return sess;
115 list = list->next;
117 return 0;
120 static session *
121 inbound_open_dialog (server *serv, char *from)
123 session *sess;
125 sess = new_ircwindow (serv, from, SESS_DIALOG, 0);
126 /* for playing sounds */
127 EMIT_SIGNAL (XP_TE_OPENDIALOG, sess, NULL, NULL, NULL, NULL, 0);
129 return sess;
132 static void
133 inbound_make_idtext (server *serv, char *idtext, int max, int id)
135 idtext[0] = 0;
136 if (serv->have_idmsg || serv->have_accnotify)
138 if (id)
140 safe_strcpy (idtext, prefs.irc_id_ytext, max);
141 } else
143 safe_strcpy (idtext, prefs.irc_id_ntext, max);
145 /* convert codes like %C,%U to the proper ones */
146 check_special_chars (idtext, TRUE);
150 void
151 inbound_privmsg (server *serv, char *from, char *ip, char *text, int id)
153 session *sess;
154 struct User *user;
155 char idtext[64];
156 gboolean nodiag = FALSE;
158 sess = find_dialog (serv, from);
160 if (sess || prefs.autodialog)
162 /*0=ctcp 1=priv will set autodialog=0 here is flud detected */
163 if (!sess)
165 if (flood_check (from, ip, serv, current_sess, 1))
166 /* Create a dialog session */
167 sess = inbound_open_dialog (serv, from);
168 else
169 sess = serv->server_session;
170 if (!sess)
171 return; /* ?? */
174 if (ip && ip[0])
176 if (prefs.logging && sess->logfd != -1 &&
177 (!sess->topic || strcmp(sess->topic, ip)))
179 char tbuf[1024];
180 snprintf (tbuf, sizeof (tbuf), "[%s has address %s]\n", from, ip);
181 write (sess->logfd, tbuf, strlen (tbuf));
183 set_topic (sess, ip, ip);
185 inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, id);
186 return;
189 sess = find_session_from_nick (from, serv);
190 if (!sess)
192 sess = serv->front_session;
193 nodiag = TRUE; /* We don't want it to look like a normal message in front sess */
196 user = userlist_find (sess, from);
197 if (user)
199 user->lasttalk = time (0);
200 if (user->account)
201 id = TRUE;
204 inbound_make_idtext (serv, idtext, sizeof (idtext), id);
206 if (sess->type == SESS_DIALOG && !nodiag)
207 EMIT_SIGNAL (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0);
208 else
209 EMIT_SIGNAL (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, 0);
212 /* used for Alerts section. Masks can be separated by commas and spaces. */
214 gboolean
215 alert_match_word (char *word, char *masks)
217 char *p = masks;
218 char endchar;
219 int res;
221 if (masks[0] == 0)
222 return FALSE;
224 while (1)
226 /* if it's a 0, space or comma, the word has ended. */
227 if (*p == 0 || *p == ' ' || *p == ',')
229 endchar = *p;
230 *p = 0;
231 res = match (masks, word);
232 *p = endchar;
234 if (res)
235 return TRUE; /* yes, matched! */
237 masks = p + 1;
238 if (*p == 0)
239 return FALSE;
241 p++;
245 gboolean
246 alert_match_text (char *text, char *masks)
248 unsigned char *p = text;
249 unsigned char endchar;
250 int res;
252 if (masks[0] == 0)
253 return FALSE;
255 while (1)
257 if (*p >= '0' && *p <= '9')
259 p++;
260 continue;
263 /* if it's RFC1459 <special>, it can be inside a word */
264 switch (*p)
266 case '-': case '[': case ']': case '\\':
267 case '`': case '^': case '{': case '}':
268 case '_': case '|':
269 p++;
270 continue;
273 /* if it's a 0, space or comma, the word has ended. */
274 if (*p == 0 || *p == ' ' || *p == ',' ||
275 /* if it's anything BUT a letter, the word has ended. */
276 (!g_unichar_isalpha (g_utf8_get_char (p))))
278 endchar = *p;
279 *p = 0;
280 res = alert_match_word (text, masks);
281 *p = endchar;
283 if (res)
284 return TRUE; /* yes, matched! */
286 text = p + g_utf8_skip [p[0]];
287 if (*p == 0)
288 return FALSE;
291 p += g_utf8_skip [p[0]];
295 static int
296 is_hilight (char *from, char *text, session *sess, server *serv)
298 if (alert_match_word (from, prefs.irc_no_hilight))
299 return 0;
301 text = strip_color (text, -1, STRIP_ALL);
303 if (alert_match_text (text, serv->nick) ||
304 alert_match_text (text, prefs.irc_extra_hilight) ||
305 alert_match_word (from, prefs.irc_nick_hilight))
307 g_free (text);
308 if (sess != current_tab)
309 sess->nick_said = TRUE;
310 fe_set_hilight (sess);
311 return 1;
314 g_free (text);
315 return 0;
318 void
319 inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int fromme, int id)
321 session *def = sess;
322 server *serv = sess->server;
323 struct User *user;
324 char nickchar[2] = "\000";
325 char idtext[64];
326 int privaction = FALSE;
328 if (!fromme)
330 if (is_channel (serv, chan))
332 sess = find_channel (serv, chan);
333 } else
335 /* it's a private action! */
336 privaction = TRUE;
337 /* find a dialog tab for it */
338 sess = find_dialog (serv, from);
339 /* if non found, open a new one */
340 if (!sess && prefs.autodialog)
342 /* but only if it wouldn't flood */
343 if (flood_check (from, ip, serv, current_sess, 1))
344 sess = inbound_open_dialog (serv, from);
345 else
346 sess = serv->server_session;
348 if (!sess)
350 sess = find_session_from_nick (from, serv);
351 /* still not good? */
352 if (!sess)
353 sess = serv->front_session;
358 if (!sess)
359 sess = def;
361 if (sess != current_tab)
363 if (fromme)
365 sess->msg_said = FALSE;
366 sess->new_data = TRUE;
367 } else
369 sess->msg_said = TRUE;
370 sess->new_data = FALSE;
374 user = userlist_find (sess, from);
375 if (user)
377 nickchar[0] = user->prefix[0];
378 user->lasttalk = time (0);
379 if (user->account)
380 id = TRUE;
383 inbound_make_idtext (serv, idtext, sizeof (idtext), id);
385 if (!fromme && !privaction)
387 if (is_hilight (from, text, sess, serv))
389 EMIT_SIGNAL (XP_TE_HCHANACTION, sess, from, text, nickchar, idtext, 0);
390 return;
394 if (fromme)
395 EMIT_SIGNAL (XP_TE_UACTION, sess, from, text, nickchar, idtext, 0);
396 else if (!privaction)
397 EMIT_SIGNAL (XP_TE_CHANACTION, sess, from, text, nickchar, idtext, 0);
398 else if (sess->type == SESS_DIALOG)
399 EMIT_SIGNAL (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL, 0);
400 else
401 EMIT_SIGNAL (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, 0);
404 void
405 inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text, char fromme, int id)
407 struct User *user;
408 int hilight = FALSE;
409 char nickchar[2] = "\000";
410 char idtext[64];
412 if (!sess)
414 if (chan)
416 sess = find_channel (serv, chan);
417 if (!sess && !is_channel (serv, chan))
418 sess = find_dialog (serv, chan);
419 } else
421 sess = find_dialog (serv, from);
423 if (!sess)
424 return;
427 if (sess != current_tab)
429 sess->msg_said = TRUE;
430 sess->new_data = FALSE;
433 user = userlist_find (sess, from);
434 if (user)
436 if (user->account)
437 id = TRUE;
438 nickchar[0] = user->prefix[0];
439 user->lasttalk = time (0);
442 if (fromme)
444 if (prefs.auto_unmark_away && serv->is_away)
445 sess->server->p_set_back (sess->server);
446 EMIT_SIGNAL (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL, 0);
447 return;
450 inbound_make_idtext (serv, idtext, sizeof (idtext), id);
452 if (is_hilight (from, text, sess, serv))
453 hilight = TRUE;
455 if (sess->type == SESS_DIALOG)
456 EMIT_SIGNAL (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0);
457 else if (hilight)
458 EMIT_SIGNAL (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext, 0);
459 else
460 EMIT_SIGNAL (XP_TE_CHANMSG, sess, from, text, nickchar, idtext, 0);
463 void
464 inbound_newnick (server *serv, char *nick, char *newnick, int quiet)
466 int me = FALSE;
467 session *sess;
468 GSList *list = sess_list;
470 if (!serv->p_cmp (nick, serv->nick))
472 me = TRUE;
473 safe_strcpy (serv->nick, newnick, NICKLEN);
476 while (list)
478 sess = list->data;
479 if (sess->server == serv)
481 if (userlist_change (sess, nick, newnick) || (me && sess->type == SESS_SERVER))
483 if (!quiet)
485 if (me)
486 EMIT_SIGNAL (XP_TE_UCHANGENICK, sess, nick, newnick, NULL,
487 NULL, 0);
488 else
489 EMIT_SIGNAL (XP_TE_CHANGENICK, sess, nick, newnick, NULL,
490 NULL, 0);
493 if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
495 safe_strcpy (sess->channel, newnick, CHANLEN);
496 fe_set_channel (sess);
498 fe_set_title (sess);
500 list = list->next;
503 dcc_change_nick (serv, nick, newnick);
505 if (me)
506 fe_set_nick (serv, newnick);
509 /* find a "<none>" tab */
510 static session *
511 find_unused_session (server *serv)
513 session *sess;
514 GSList *list = sess_list;
515 while (list)
517 sess = (session *) list->data;
518 if (sess->type == SESS_CHANNEL && sess->channel[0] == 0 &&
519 sess->server == serv)
521 if (sess->waitchannel[0] == 0)
522 return sess;
524 list = list->next;
526 return 0;
529 static session *
530 find_session_from_waitchannel (char *chan, struct server *serv)
532 session *sess;
533 GSList *list = sess_list;
534 while (list)
536 sess = (session *) list->data;
537 if (sess->server == serv && sess->channel[0] == 0 && sess->type == SESS_CHANNEL)
539 if (!serv->p_cmp (chan, sess->waitchannel))
540 return sess;
542 list = list->next;
544 return 0;
547 void
548 inbound_ujoin (server *serv, char *chan, char *nick, char *ip)
550 session *sess;
552 /* already joined? probably a bnc */
553 sess = find_channel (serv, chan);
554 if (!sess)
556 /* see if a window is waiting to join this channel */
557 sess = find_session_from_waitchannel (chan, serv);
558 if (!sess)
560 /* find a "<none>" tab and use that */
561 sess = find_unused_session (serv);
562 if (!sess)
563 /* last resort, open a new tab/window */
564 sess = new_ircwindow (serv, chan, SESS_CHANNEL, 1);
568 safe_strcpy (sess->channel, chan, CHANLEN);
570 fe_set_channel (sess);
571 fe_set_title (sess);
572 fe_set_nonchannel (sess, TRUE);
573 userlist_clear (sess);
575 log_open_or_close (sess);
577 sess->waitchannel[0] = 0;
578 sess->ignore_date = TRUE;
579 sess->ignore_mode = TRUE;
580 sess->ignore_names = TRUE;
581 sess->end_of_names = FALSE;
583 /* sends a MODE */
584 serv->p_join_info (sess->server, chan);
586 EMIT_SIGNAL (XP_TE_UJOIN, sess, nick, chan, ip, NULL, 0);
588 if (prefs.userhost)
590 /* sends WHO #channel */
591 serv->p_user_list (sess->server, chan);
592 sess->doing_who = TRUE;
596 void
597 inbound_ukick (server *serv, char *chan, char *kicker, char *reason)
599 session *sess = find_channel (serv, chan);
600 if (sess)
602 EMIT_SIGNAL (XP_TE_UKICK, sess, serv->nick, chan, kicker, reason, 0);
603 clear_channel (sess);
604 if (prefs.autorejoin)
606 serv->p_join (serv, chan, sess->channelkey);
607 safe_strcpy (sess->waitchannel, chan, CHANLEN);
612 void
613 inbound_upart (server *serv, char *chan, char *ip, char *reason)
615 session *sess = find_channel (serv, chan);
616 if (sess)
618 if (*reason)
619 EMIT_SIGNAL (XP_TE_UPARTREASON, sess, serv->nick, ip, chan, reason,
621 else
622 EMIT_SIGNAL (XP_TE_UPART, sess, serv->nick, ip, chan, NULL, 0);
623 clear_channel (sess);
627 void
628 inbound_nameslist (server *serv, char *chan, char *names)
630 session *sess;
631 char name[NICKLEN];
632 int pos = 0;
634 sess = find_channel (serv, chan);
635 if (!sess)
637 EMIT_SIGNAL (XP_TE_USERSONCHAN, serv->server_session, chan, names, NULL,
638 NULL, 0);
639 return;
641 if (!sess->ignore_names)
642 EMIT_SIGNAL (XP_TE_USERSONCHAN, sess, chan, names, NULL, NULL, 0);
644 if (sess->end_of_names)
646 sess->end_of_names = FALSE;
647 userlist_clear (sess);
650 while (1)
652 switch (*names)
654 case 0:
655 name[pos] = 0;
656 if (pos != 0)
657 userlist_add (sess, name, 0, NULL, NULL);
658 return;
659 case ' ':
660 name[pos] = 0;
661 pos = 0;
662 userlist_add (sess, name, 0, NULL, NULL);
663 break;
664 default:
665 name[pos] = *names;
666 if (pos < (NICKLEN-1))
667 pos++;
669 names++;
673 void
674 inbound_topic (server *serv, char *chan, char *topic_text)
676 session *sess = find_channel (serv, chan);
677 char *stripped_topic;
679 if (sess)
681 stripped_topic = strip_color (topic_text, -1, STRIP_ALL);
682 set_topic (sess, topic_text, stripped_topic);
683 g_free (stripped_topic);
684 } else
685 sess = serv->server_session;
687 EMIT_SIGNAL (XP_TE_TOPIC, sess, chan, topic_text, NULL, NULL, 0);
690 void
691 inbound_topicnew (server *serv, char *nick, char *chan, char *topic)
693 session *sess;
694 char *stripped_topic;
696 sess = find_channel (serv, chan);
697 if (sess)
699 EMIT_SIGNAL (XP_TE_NEWTOPIC, sess, nick, topic, chan, NULL, 0);
700 stripped_topic = strip_color (topic, -1, STRIP_ALL);
701 set_topic (sess, topic, stripped_topic);
702 g_free (stripped_topic);
706 void
707 inbound_join (server *serv, char *chan, char *user, char *ip, char *account, char *realname)
709 session *sess = find_channel (serv, chan);
710 if (sess)
712 EMIT_SIGNAL (XP_TE_JOIN, sess, user, chan, ip, NULL, 0);
713 userlist_add (sess, user, ip, account, realname);
717 void
718 inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason)
720 session *sess = find_channel (serv, chan);
721 if (sess)
723 EMIT_SIGNAL (XP_TE_KICK, sess, kicker, user, chan, reason, 0);
724 userlist_remove (sess, user);
728 void
729 inbound_part (server *serv, char *chan, char *user, char *ip, char *reason)
731 session *sess = find_channel (serv, chan);
732 if (sess)
734 if (*reason)
735 EMIT_SIGNAL (XP_TE_PARTREASON, sess, user, ip, chan, reason, 0);
736 else
737 EMIT_SIGNAL (XP_TE_PART, sess, user, ip, chan, NULL, 0);
738 userlist_remove (sess, user);
742 void
743 inbound_topictime (server *serv, char *chan, char *nick, time_t stamp)
745 char *tim = ctime (&stamp);
746 session *sess = find_channel (serv, chan);
748 if (!sess)
749 sess = serv->server_session;
751 tim[24] = 0; /* get rid of the \n */
752 EMIT_SIGNAL (XP_TE_TOPICDATE, sess, chan, nick, tim, NULL, 0);
755 void
756 inbound_quit (server *serv, char *nick, char *ip, char *reason)
758 GSList *list = sess_list;
759 session *sess;
760 struct User *user;
761 int was_on_front_session = FALSE;
763 while (list)
765 sess = (session *) list->data;
766 if (sess->server == serv)
768 if (sess == current_sess)
769 was_on_front_session = TRUE;
770 if (user = userlist_find (sess, nick))
772 EMIT_SIGNAL (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0);
773 userlist_remove_user (sess, user);
774 } else if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
776 EMIT_SIGNAL (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0);
779 list = list->next;
782 notify_set_offline (serv, nick, was_on_front_session);
785 void
786 inbound_account (server *serv, char *nick, char *account)
788 session *sess = NULL;
789 GSList *list;
791 list = sess_list;
792 while (list)
794 sess = list->data;
795 if (sess->server == serv)
796 userlist_set_account (sess, nick, account);
797 list = list->next;
801 void
802 inbound_ping_reply (session *sess, char *timestring, char *from)
804 unsigned long tim, nowtim, dif;
805 int lag = 0;
806 char outbuf[64];
808 if (strncmp (timestring, "LAG", 3) == 0)
810 timestring += 3;
811 lag = 1;
814 tim = strtoul (timestring, NULL, 10);
815 nowtim = make_ping_time ();
816 dif = nowtim - tim;
818 sess->server->ping_recv = time (0);
820 if (lag)
822 sess->server->lag_sent = 0;
823 sess->server->lag = dif / 1000;
824 fe_set_lag (sess->server, dif / 100000);
825 return;
828 if (atol (timestring) == 0)
830 if (sess->server->lag_sent)
831 sess->server->lag_sent = 0;
832 else
833 EMIT_SIGNAL (XP_TE_PINGREP, sess, from, "?", NULL, NULL, 0);
834 } else
836 snprintf (outbuf, sizeof (outbuf), "%ld.%ld%ld", dif / 1000000, (dif / 100000) % 10, dif % 10);
837 EMIT_SIGNAL (XP_TE_PINGREP, sess, from, outbuf, NULL, NULL, 0);
841 static session *
842 find_session_from_type (int type, server *serv)
844 session *sess;
845 GSList *list = sess_list;
846 while (list)
848 sess = list->data;
849 if (sess->type == type && serv == sess->server)
850 return sess;
851 list = list->next;
853 return 0;
856 void
857 inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id)
859 char *po,*ptr=to;
860 session *sess = 0;
861 int server_notice = FALSE;
863 if (is_channel (serv, ptr))
864 sess = find_channel (serv, ptr);
866 if (!sess && ptr[0] == '@')
868 ptr++;
869 sess = find_channel (serv, ptr);
872 if (!sess && ptr[0] == '%')
874 ptr++;
875 sess = find_channel (serv, ptr);
878 if (!sess && ptr[0] == '+')
880 ptr++;
881 sess = find_channel (serv, ptr);
884 if (strcmp (nick, ip) == 0)
885 server_notice = TRUE;
887 if (!sess)
889 ptr = 0;
890 if (prefs.notices_tabs)
892 int stype = server_notice ? SESS_SNOTICES : SESS_NOTICES;
893 sess = find_session_from_type (stype, serv);
894 if (!sess)
896 if (stype == SESS_NOTICES)
897 sess = new_ircwindow (serv, "(notices)", SESS_NOTICES, 0);
898 else
899 sess = new_ircwindow (serv, "(snotices)", SESS_SNOTICES, 0);
900 fe_set_channel (sess);
901 fe_set_title (sess);
902 fe_set_nonchannel (sess, FALSE);
903 userlist_clear (sess);
904 log_open_or_close (sess);
906 /* Avoid redundancy with some Undernet notices */
907 if (!strncmp (msg, "*** Notice -- ", 14))
908 msg += 14;
909 } else
911 /* paranoia check */
912 if (msg[0] == '[' && (!serv->have_idmsg || id))
914 /* guess where chanserv meant to post this -sigh- */
915 if (!strcasecmp (nick, "ChanServ") && !find_dialog (serv, nick))
917 char *dest = strdup (msg + 1);
918 char *end = strchr (dest, ']');
919 if (end)
921 *end = 0;
922 sess = find_channel (serv, dest);
924 free (dest);
927 if (!sess)
928 sess = find_session_from_nick (nick, serv);
930 if (!sess)
932 if (server_notice)
933 sess = serv->server_session;
934 else
935 sess = serv->front_session;
939 if (msg[0] == 1)
941 msg++;
942 if (!strncmp (msg, "PING", 4))
944 inbound_ping_reply (sess, msg + 5, nick);
945 return;
948 po = strchr (msg, '\001');
949 if (po)
950 po[0] = 0;
952 if (server_notice)
953 EMIT_SIGNAL (XP_TE_SERVNOTICE, sess, msg, nick, NULL, NULL, 0);
954 else if (ptr)
955 EMIT_SIGNAL (XP_TE_CHANNOTICE, sess, nick, to, msg, NULL, 0);
956 else
957 EMIT_SIGNAL (XP_TE_NOTICE, sess, nick, msg, NULL, NULL, 0);
960 void
961 inbound_away (server *serv, char *nick, char *msg)
963 struct away_msg *away = server_away_find_message (serv, nick);
964 session *sess = NULL;
965 GSList *list;
967 if (away && !strcmp (msg, away->message)) /* Seen the msg before? */
969 if (prefs.show_away_once && !serv->inside_whois)
970 return;
971 } else
973 server_away_save_message (serv, nick, msg);
976 if (prefs.irc_whois_front)
977 sess = serv->front_session;
978 else
980 if (!serv->inside_whois)
981 sess = find_session_from_nick (nick, serv);
982 if (!sess)
983 sess = serv->server_session;
986 /* possibly hide the output */
987 if (!serv->inside_whois || !serv->skip_next_whois)
988 EMIT_SIGNAL (XP_TE_WHOIS5, sess, nick, msg, NULL, NULL, 0);
990 list = sess_list;
991 while (list)
993 sess = list->data;
994 if (sess->server == serv)
995 userlist_set_away (sess, nick, TRUE);
996 list = list->next;
1000 void
1001 inbound_away_notify (server *serv, char *nick, char *reason)
1003 session *sess = NULL;
1004 GSList *list;
1006 list = sess_list;
1007 while (list)
1009 sess = list->data;
1010 if (sess->server == serv)
1011 userlist_set_away (sess, nick, reason ? TRUE : FALSE);
1012 list = list->next;
1017 inbound_nameslist_end (server *serv, char *chan)
1019 session *sess;
1020 GSList *list;
1022 if (!strcmp (chan, "*"))
1024 list = sess_list;
1025 while (list)
1027 sess = list->data;
1028 if (sess->server == serv)
1030 sess->end_of_names = TRUE;
1031 sess->ignore_names = FALSE;
1033 list = list->next;
1035 return TRUE;
1037 sess = find_channel (serv, chan);
1038 if (sess)
1040 sess->end_of_names = TRUE;
1041 sess->ignore_names = FALSE;
1042 return TRUE;
1044 return FALSE;
1047 static gboolean
1048 check_autojoin_channels (server *serv)
1050 char *po;
1051 session *sess;
1052 GSList *list = sess_list;
1053 int i = 0;
1054 GSList *channels, *keys;
1056 /* shouldnt really happen, the io tag is destroyed in server.c */
1057 if (!is_server (serv))
1058 return FALSE;
1060 /* send auto join list */
1061 if (serv->autojoin)
1063 joinlist_split (serv->autojoin, &channels, &keys);
1064 serv->p_join_list (serv, channels, keys);
1065 joinlist_free (channels, keys);
1067 free (serv->autojoin);
1068 serv->autojoin = NULL;
1071 /* this is really only for re-connects when you
1072 * join channels not in the auto-join list. */
1073 channels = NULL;
1074 keys = NULL;
1075 while (list)
1077 sess = list->data;
1078 if (sess->server == serv)
1080 if (sess->willjoinchannel[0] != 0)
1082 strcpy (sess->waitchannel, sess->willjoinchannel);
1083 sess->willjoinchannel[0] = 0;
1085 po = strchr (sess->waitchannel, ',');
1086 if (po)
1087 *po = 0;
1088 po = strchr (sess->waitchannel, ' ');
1089 if (po)
1090 *po = 0;
1092 channels = g_slist_append (channels, g_strdup (sess->waitchannel));
1093 keys = g_slist_append (keys, g_strdup (sess->channelkey));
1094 i++;
1097 list = list->next;
1100 if (channels)
1102 serv->p_join_list (serv, channels, keys);
1103 joinlist_free (channels, keys);
1106 serv->joindelay_tag = 0;
1107 fe_server_event (serv, FE_SE_LOGGEDIN, i);
1108 return FALSE;
1111 void
1112 inbound_next_nick (session *sess, char *nick)
1114 char *newnick;
1115 server *serv = sess->server;
1116 ircnet *net;
1118 serv->nickcount++;
1120 switch (serv->nickcount)
1122 case 2:
1123 newnick = prefs.nick2;
1124 net = serv->network;
1125 /* use network specific "Second choice"? */
1126 if (net && !(net->flags & FLAG_USE_GLOBAL) && net->nick2)
1127 newnick = net->nick2;
1128 serv->p_change_nick (serv, newnick);
1129 EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL, 0);
1130 break;
1132 case 3:
1133 serv->p_change_nick (serv, prefs.nick3);
1134 EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, prefs.nick3, NULL, NULL, 0);
1135 break;
1137 default:
1138 EMIT_SIGNAL (XP_TE_NICKFAIL, sess, NULL, NULL, NULL, NULL, 0);
1142 void
1143 do_dns (session *sess, char *nick, char *host)
1145 char *po;
1146 char tbuf[1024];
1148 po = strrchr (host, '@');
1149 if (po)
1150 host = po + 1;
1151 EMIT_SIGNAL (XP_TE_RESOLVINGUSER, sess, nick, host, NULL, NULL, 0);
1152 snprintf (tbuf, sizeof (tbuf), "exec -d %s %s", prefs.dnsprogram, host);
1153 handle_command (sess, tbuf, FALSE);
1156 static void
1157 set_default_modes (server *serv)
1159 char modes[8];
1161 modes[0] = '+';
1162 modes[1] = '\0';
1164 if (prefs.wallops)
1165 strcat (modes, "w");
1166 if (prefs.servernotice)
1167 strcat (modes, "s");
1168 if (prefs.invisible)
1169 strcat (modes, "i");
1171 if (modes[1] != '\0')
1173 serv->p_mode (serv, serv->nick, modes);
1177 void
1178 inbound_login_start (session *sess, char *nick, char *servname)
1180 inbound_newnick (sess->server, sess->server->nick, nick, TRUE);
1181 server_set_name (sess->server, servname);
1182 if (sess->type == SESS_SERVER)
1183 log_open_or_close (sess);
1184 /* reset our away status */
1185 if (sess->server->reconnect_away)
1187 handle_command (sess->server->server_session, "away", FALSE);
1188 sess->server->reconnect_away = FALSE;
1192 static void
1193 inbound_set_all_away_status (server *serv, char *nick, unsigned int status)
1195 GSList *list;
1196 session *sess;
1198 list = sess_list;
1199 while (list)
1201 sess = list->data;
1202 if (sess->server == serv)
1203 userlist_set_away (sess, nick, status);
1204 list = list->next;
1208 void
1209 inbound_uaway (server *serv)
1211 serv->is_away = TRUE;
1212 serv->away_time = time (NULL);
1213 fe_set_away (serv);
1215 inbound_set_all_away_status (serv, serv->nick, 1);
1218 void
1219 inbound_uback (server *serv)
1221 serv->is_away = FALSE;
1222 serv->reconnect_away = FALSE;
1223 fe_set_away (serv);
1225 inbound_set_all_away_status (serv, serv->nick, 0);
1228 void
1229 inbound_foundip (session *sess, char *ip)
1231 struct hostent *HostAddr;
1233 HostAddr = gethostbyname (ip);
1234 if (HostAddr)
1236 prefs.dcc_ip = ((struct in_addr *) HostAddr->h_addr)->s_addr;
1237 EMIT_SIGNAL (XP_TE_FOUNDIP, sess,
1238 inet_ntoa (*((struct in_addr *) HostAddr->h_addr)),
1239 NULL, NULL, NULL, 0);
1243 void
1244 inbound_user_info_start (session *sess, char *nick)
1246 /* set away to FALSE now, 301 may turn it back on */
1247 inbound_set_all_away_status (sess->server, nick, 0);
1250 /* reporting new information found about this user. chan may be NULL.
1251 * away may be 0xff to indicate UNKNOWN. */
1253 void
1254 inbound_user_info (session *sess, char *chan, char *user, char *host,
1255 char *servname, char *nick, char *realname,
1256 char *account, unsigned int away)
1258 server *serv = sess->server;
1259 session *who_sess;
1260 GSList *list;
1261 char *uhost = NULL;
1263 if (user && host)
1265 uhost = g_malloc (strlen (user) + strlen (host) + 2);
1266 sprintf (uhost, "%s@%s", user, host);
1269 if (chan)
1271 who_sess = find_channel (serv, chan);
1272 if (who_sess)
1273 userlist_add_hostname (who_sess, nick, uhost, realname, servname, account, away);
1274 else
1276 if (serv->doing_dns && nick && host)
1277 do_dns (sess, nick, host);
1280 else
1282 /* came from WHOIS, not channel specific */
1283 for (list = sess_list; list; list = list->next)
1285 sess = list->data;
1286 if (sess->type == SESS_CHANNEL && sess->server == serv)
1288 userlist_add_hostname (sess, nick, uhost, realname, servname, account, away);
1293 g_free (uhost);
1297 inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, char *banner, int is_exemption)
1299 char *time_str = ctime (&stamp);
1300 server *serv = sess->server;
1302 time_str[19] = 0; /* get rid of the \n */
1303 if (stamp == 0)
1304 time_str = "";
1306 sess = find_channel (serv, chan);
1307 if (!sess)
1309 sess = serv->front_session;
1310 goto nowindow;
1313 if (!fe_is_banwindow (sess))
1315 nowindow:
1316 /* let proto-irc.c do the 'goto def' for exemptions */
1317 if (is_exemption)
1318 return FALSE;
1320 EMIT_SIGNAL (XP_TE_BANLIST, sess, chan, mask, banner, time_str, 0);
1321 return TRUE;
1324 fe_add_ban_list (sess, mask, banner, time_str, is_exemption);
1325 return TRUE;
1328 /* execute 1 end-of-motd command */
1330 static int
1331 inbound_exec_eom_cmd (char *str, void *sess)
1333 handle_command (sess, (str[0] == '/') ? str + 1 : str, TRUE);
1334 return 1;
1337 static int
1338 inbound_nickserv_login (server *serv)
1340 /* this could grow ugly, but let's hope there won't be new NickServ types */
1341 switch (serv->loginmethod)
1343 case LOGIN_MSG_NICKSERV:
1344 case LOGIN_NICKSERV:
1345 case LOGIN_CHALLENGEAUTH:
1346 #if 0
1347 case LOGIN_NS:
1348 case LOGIN_MSG_NS:
1349 case LOGIN_AUTH:
1350 #endif
1351 return 1;
1352 default:
1353 return 0;
1357 void
1358 inbound_login_end (session *sess, char *text)
1360 server *serv = sess->server;
1362 if (!serv->end_of_motd)
1364 if (prefs.ip_from_server && serv->use_who)
1366 serv->skip_next_userhost = TRUE;
1367 serv->p_get_ip_uh (serv, serv->nick); /* sends USERHOST mynick */
1369 set_default_modes (serv);
1371 if (serv->network)
1373 /* there may be more than 1, separated by \n */
1374 if (((ircnet *)serv->network)->command)
1376 token_foreach (((ircnet *)serv->network)->command, '\n', inbound_exec_eom_cmd, sess);
1379 /* send nickserv password */
1380 if (((ircnet *)serv->network)->pass && inbound_nickserv_login (serv))
1382 serv->p_ns_identify (serv, ((ircnet *)serv->network)->pass);
1386 /* send JOIN now or wait? */
1387 if (serv->network && ((ircnet *)serv->network)->pass && prefs.irc_join_delay && inbound_nickserv_login (serv))
1389 serv->joindelay_tag = fe_timeout_add (prefs.irc_join_delay * 1000, check_autojoin_channels, serv);
1391 else
1393 check_autojoin_channels (serv);
1396 if (serv->supports_watch || serv->supports_monitor)
1398 notify_send_watches (serv);
1401 serv->end_of_motd = TRUE;
1404 if (prefs.skipmotd && !serv->motd_skipped)
1406 serv->motd_skipped = TRUE;
1407 EMIT_SIGNAL (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL, NULL, NULL, 0);
1408 return;
1411 EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL, NULL, 0);
1414 void
1415 inbound_identified (server *serv) /* 'MODE +e MYSELF' on freenode */
1417 if (serv->joindelay_tag)
1419 /* stop waiting, just auto JOIN now */
1420 fe_timeout_remove (serv->joindelay_tag);
1421 serv->joindelay_tag = 0;
1422 check_autojoin_channels (serv);
1426 void
1427 inbound_cap_ack (server *serv, char *nick, char *extensions)
1429 EMIT_SIGNAL (XP_TE_CAPACK, serv->server_session, nick, extensions,
1430 NULL, NULL, 0);
1432 if (strstr (extensions, "identify-msg") != 0)
1434 serv->have_idmsg = TRUE;
1437 if (strstr (extensions, "multi-prefix") != 0)
1439 serv->have_namesx = TRUE;
1442 if (strstr (extensions, "away-notify") != 0)
1444 serv->have_awaynotify = TRUE;
1447 if (strstr (extensions, "account-notify") != 0)
1449 serv->have_accnotify = TRUE;
1452 if (strstr (extensions, "extended-join") != 0)
1454 serv->have_extjoin = TRUE;
1457 if (strstr (extensions, "sasl") != 0)
1459 serv->have_sasl = TRUE;
1460 serv->sent_saslauth = FALSE;
1462 #ifdef USE_OPENSSL
1463 if (serv->loginmethod == LOGIN_SASLEXTERNAL)
1465 serv->sasl_mech = MECH_EXTERNAL;
1466 tcp_send_len (serv, "AUTHENTICATE EXTERNAL\r\n", 23);
1468 else
1470 /* default to most secure, it will fallback if not supported */
1471 serv->sasl_mech = MECH_AES;
1472 tcp_send_len (serv, "AUTHENTICATE DH-AES\r\n", 21);
1474 #else
1475 serv->sasl_mech = MECH_PLAIN;
1476 tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20);
1477 #endif
1481 void
1482 inbound_cap_ls (server *serv, char *nick, char *extensions_str)
1484 char buffer[256]; /* buffer for requesting capabilities and emitting the signal */
1485 guint32 want_cap; /* format the CAP REQ string based on previous capabilities being requested or not */
1486 guint32 want_sasl; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */
1487 char **extensions;
1488 int i;
1490 EMIT_SIGNAL (XP_TE_CAPLIST, serv->server_session, nick, extensions_str,
1491 NULL, NULL, 0);
1492 want_cap = 0;
1493 want_sasl = 0;
1495 extensions = g_strsplit (extensions_str, " ", 0);
1497 strcpy (buffer, "CAP REQ :");
1499 for (i=0; extensions[i]; i++)
1501 const char *extension = extensions[i];
1503 if (!strcmp (extension, "identify-msg"))
1505 strcat (buffer, "identify-msg ");
1506 want_cap = 1;
1508 if (!strcmp (extension, "multi-prefix"))
1510 strcat (buffer, "multi-prefix ");
1511 want_cap = 1;
1513 if (!strcmp (extension, "away-notify"))
1515 strcat (buffer, "away-notify ");
1516 want_cap = 1;
1518 if (!strcmp (extension, "account-notify"))
1520 strcat (buffer, "account-notify ");
1521 want_cap = 1;
1523 if (!strcmp (extension, "extended-join"))
1525 strcat (buffer, "extended-join ");
1526 want_cap = 1;
1528 #if 0
1529 /* bouncers can prefix a name space to the extension so we should use.
1530 * znc <= 1.0 uses "znc.in/server-time" and newer use "znc.in/server-time-iso".
1532 if (!strcmp (extension, "znc.in/server-time-iso"))
1534 strcat (buffer, "znc.in/server-time-iso ");
1535 want_cap = 1;
1537 if (!strcmp (extension, "znc.in/server-time"))
1539 strcat (buffer, "znc.in/server-time ");
1540 want_cap = 1;
1542 else if (!strcmp (extension, "server-time"))
1544 strcat (buffer, "server-time ");
1545 want_cap = 1;
1547 #endif
1549 /* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
1550 if (!strcmp (extension, "sasl")
1551 && ((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
1552 || (serv->loginmethod == LOGIN_SASLEXTERNAL && serv->have_cert)))
1554 strcat (buffer, "sasl ");
1555 want_cap = 1;
1556 want_sasl = 1;
1560 g_strfreev (extensions);
1562 if (want_cap)
1564 /* buffer + 9 = emit buffer without "CAP REQ :" */
1565 EMIT_SIGNAL (XP_TE_CAPREQ, serv->server_session,
1566 buffer + 9, NULL, NULL, NULL, 0);
1567 tcp_sendf (serv, "%s\r\n", g_strchomp(buffer));
1569 if (!want_sasl)
1571 /* if we use SASL, CAP END is dealt via raw numerics */
1572 serv->sent_capend = TRUE;
1573 tcp_send_len (serv, "CAP END\r\n", 9);
1577 void
1578 inbound_cap_nak (server *serv)
1580 serv->sent_capend = TRUE;
1581 tcp_send_len (serv, "CAP END\r\n", 9);
1584 void
1585 inbound_cap_list (server *serv, char *nick, char *extensions)
1587 EMIT_SIGNAL (XP_TE_CAPACK, serv->server_session, nick, extensions,
1588 NULL, NULL, 0);
1591 static const char *sasl_mechanisms[] =
1593 "PLAIN",
1594 "DH-BLOWFISH",
1595 "DH-AES",
1596 "EXTERNAL"
1599 void
1600 inbound_sasl_supportedmechs (server *serv, char *list)
1602 int i;
1604 if (serv->sasl_mech != MECH_EXTERNAL)
1606 /* Use most secure one supported */
1607 for (i = MECH_AES; i >= MECH_PLAIN; i--)
1609 if (strstr (list, sasl_mechanisms[i]) != NULL)
1611 serv->sasl_mech = i;
1612 serv->retry_sasl = TRUE;
1613 tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[i]);
1614 return;
1619 /* Abort, none supported */
1620 serv->sent_saslauth = TRUE;
1621 tcp_sendf (serv, "AUTHENTICATE *\r\n");
1622 return;
1625 void
1626 inbound_sasl_authenticate (server *serv, char *data)
1628 ircnet *net = (ircnet*)serv->network;
1629 char *user, *pass = NULL;
1630 const char *mech = sasl_mechanisms[serv->sasl_mech];
1632 /* Got a list of supported mechanisms from inspircd */
1633 if (strchr (data, ',') != NULL)
1635 inbound_sasl_supportedmechs (serv, data);
1636 return;
1639 if (net->user && !(net->flags & FLAG_USE_GLOBAL))
1640 user = net->user;
1641 else
1642 user = prefs.username;
1644 switch (serv->sasl_mech)
1646 case MECH_PLAIN:
1647 pass = encode_sasl_pass_plain (user, serv->password);
1648 break;
1649 #ifdef USE_OPENSSL
1650 case MECH_BLOWFISH:
1651 pass = encode_sasl_pass_blowfish (user, serv->password, data);
1652 break;
1653 case MECH_AES:
1654 pass = encode_sasl_pass_aes (user, serv->password, data);
1655 break;
1656 case MECH_EXTERNAL:
1657 pass = g_strdup ("+");
1658 break;
1659 #endif
1662 if (pass == NULL)
1664 /* something went wrong abort */
1665 serv->sent_saslauth = TRUE; /* prevent trying PLAIN */
1666 tcp_sendf (serv, "AUTHENTICATE *\r\n");
1667 return;
1670 serv->sent_saslauth = TRUE;
1671 tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass);
1672 g_free (pass);
1675 EMIT_SIGNAL (XP_TE_SASLAUTH, serv->server_session, user, (char*)mech,
1676 NULL, NULL, 0);
1680 inbound_sasl_error (server *serv)
1682 if (serv->retry_sasl && !serv->sent_saslauth)
1683 return 1;
1685 /* If server sent 904 before we sent password,
1686 * mech not support so fallback to next mech */
1687 if (!serv->sent_saslauth && serv->sasl_mech != MECH_EXTERNAL && serv->sasl_mech != MECH_PLAIN)
1689 serv->sasl_mech -= 1;
1690 tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
1691 return 1;
1693 return 0;