restructure configure so pkg-config derived SSL flags get used
[rofl0r-ixchat.git] / src / common / proto-irc.c
blobc151021acb2b6e4a9f2ab7501cf153e387db6da2
1 /* X-Chat
2 * Copyright (C) 2002 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 /* IRC RFC1459(+commonly used extensions) protocol implementation */
21 #include <unistd.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <stdarg.h>
28 #include "xchat.h"
29 #include "ctcp.h"
30 #include "fe.h"
31 #include "ignore.h"
32 #include "inbound.h"
33 #include "modes.h"
34 #include "notify.h"
35 #include "plugin.h"
36 #include "server.h"
37 #include "text.h"
38 #include "outbound.h"
39 #include "util.h"
40 #include "xchatc.h"
41 #include "servlist.h"
42 #include "url.h"
44 static void
45 irc_login (server *serv, char *user, char *realname)
47 tcp_sendf (serv, "CAP LS\r\n"); /* start with CAP LS as Charybdis sasl.txt suggests */
48 serv->sent_capend = FALSE; /* track if we have finished */
50 if (serv->password[0] && serv->loginmethod == LOGIN_PASS)
52 tcp_sendf (serv, "PASS %s\r\n", serv->password);
55 tcp_sendf (serv,
56 "NICK %s\r\n"
57 "USER %s %s %s :%s\r\n",
58 serv->nick, user, user, serv->servername, realname);
61 static void
62 irc_nickserv (server *serv, char *cmd, char *arg1, char *arg2, char *arg3)
64 /* are all ircd authors idiots? */
65 switch (serv->loginmethod)
67 case LOGIN_MSG_NICKSERV:
68 tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
69 break;
70 case LOGIN_NICKSERV:
71 tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
72 break;
73 default: /* This may not work but at least it tries something when using /id or /ghost cmd */
74 tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
75 break;
76 #if 0
77 case LOGIN_MSG_NS:
78 tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
79 break;
80 case LOGIN_NS:
81 tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
82 break;
83 case LOGIN_AUTH:
84 /* why couldn't QuakeNet implement one of the existing ones? */
85 tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2);
86 break;
87 #endif
91 static void
92 irc_ns_identify (server *serv, char *pass)
94 switch (serv->loginmethod)
96 case LOGIN_CHALLENGEAUTH:
97 tcp_sendf (serv, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK); /* request a challenge from Q */
98 break;
99 #if 0
100 case LOGIN_AUTH:
101 irc_nickserv (serv, "", serv->nick, pass, "");
102 break;
103 #endif
104 default:
105 irc_nickserv (serv, "IDENTIFY", pass, "", "");
109 static void
110 irc_ns_ghost (server *serv, char *usname, char *pass)
112 if (serv->loginmethod != LOGIN_CHALLENGEAUTH)
114 irc_nickserv (serv, "GHOST", usname, " ", pass);
118 static void
119 irc_join (server *serv, char *channel, char *key)
121 if (key[0])
122 tcp_sendf (serv, "JOIN %s %s\r\n", channel, key);
123 else
124 tcp_sendf (serv, "JOIN %s\r\n", channel);
127 static void
128 irc_join_list_flush (server *serv, GString *c, GString *k)
130 char *chanstr, *keystr;
132 chanstr = g_string_free (c, FALSE);
133 keystr = g_string_free (k, FALSE);
134 if (chanstr[0])
136 if (keystr[0])
137 tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr);
138 else
139 tcp_sendf (serv, "JOIN %s\r\n", chanstr);
141 g_free (chanstr);
142 g_free (keystr);
145 /* join a whole list of channels & keys, split to multiple lines
146 * to get around 512 limit */
148 static void
149 irc_join_list (server *serv, GSList *channels, GSList *keys)
151 GSList *clist;
152 GSList *klist;
153 GString *c = g_string_new (NULL);
154 GString *k = g_string_new (NULL);
155 int len;
156 int add;
157 int i, j;
159 i = j = 0;
160 len = 9; /* "JOIN<space><space>\r\n" */
161 clist = channels;
162 klist = keys;
164 while (clist)
166 /* measure how many bytes this channel would add... */
167 if (1)
169 add = strlen (clist->data);
170 if (i != 0)
171 add++; /* comma */
174 if (klist->data)
176 add += strlen (klist->data);
178 else
180 add++; /* 'x' filler */
183 if (j != 0)
184 add++; /* comma */
186 /* too big? dump buffer and start a fresh one */
187 if (len + add > 512)
189 irc_join_list_flush (serv, c, k);
191 c = g_string_new (NULL);
192 k = g_string_new (NULL);
193 i = j = 0;
194 len = 9;
197 /* now actually add it to our GStrings */
198 if (1)
200 add = strlen (clist->data);
201 if (i != 0)
203 add++;
204 g_string_append_c (c, ',');
206 g_string_append (c, clist->data);
207 i++;
210 if (klist->data)
212 add += strlen (klist->data);
213 if (j != 0)
215 add++;
216 g_string_append_c (k, ',');
218 g_string_append (k, klist->data);
219 j++;
221 else
223 add++;
224 if (j != 0)
226 add++;
227 g_string_append_c (k, ',');
229 g_string_append_c (k, 'x');
230 j++;
233 len += add;
235 klist = klist->next;
236 clist = clist->next;
239 irc_join_list_flush (serv, c, k);
242 static void
243 irc_part (server *serv, char *channel, char *reason)
245 if (reason[0])
246 tcp_sendf (serv, "PART %s :%s\r\n", channel, reason);
247 else
248 tcp_sendf (serv, "PART %s\r\n", channel);
251 static void
252 irc_quit (server *serv, char *reason)
254 if (reason[0])
255 tcp_sendf (serv, "QUIT :%s\r\n", reason);
256 else
257 tcp_send_len (serv, "QUIT\r\n", 6);
260 static void
261 irc_set_back (server *serv)
263 tcp_send_len (serv, "AWAY\r\n", 6);
266 static void
267 irc_set_away (server *serv, char *reason)
269 if (reason)
271 if (!reason[0])
272 reason = " ";
274 else
276 reason = " ";
279 tcp_sendf (serv, "AWAY :%s\r\n", reason);
282 static void
283 irc_ctcp (server *serv, char *to, char *msg)
285 tcp_sendf (serv, "PRIVMSG %s :\001%s\001\r\n", to, msg);
288 static void
289 irc_nctcp (server *serv, char *to, char *msg)
291 tcp_sendf (serv, "NOTICE %s :\001%s\001\r\n", to, msg);
294 static void
295 irc_cycle (server *serv, char *channel, char *key)
297 tcp_sendf (serv, "PART %s\r\nJOIN %s %s\r\n", channel, channel, key);
300 static void
301 irc_kick (server *serv, char *channel, char *nick, char *reason)
303 if (reason[0])
304 tcp_sendf (serv, "KICK %s %s :%s\r\n", channel, nick, reason);
305 else
306 tcp_sendf (serv, "KICK %s %s\r\n", channel, nick);
309 static void
310 irc_invite (server *serv, char *channel, char *nick)
312 tcp_sendf (serv, "INVITE %s %s\r\n", nick, channel);
315 static void
316 irc_mode (server *serv, char *target, char *mode)
318 tcp_sendf (serv, "MODE %s %s\r\n", target, mode);
321 /* find channel info when joined */
323 static void
324 irc_join_info (server *serv, char *channel)
326 tcp_sendf (serv, "MODE %s\r\n", channel);
329 /* initiate userlist retreival */
331 static void
332 irc_user_list (server *serv, char *channel)
334 if (serv->have_whox)
335 tcp_sendf (serv, "WHO %s %%chtsunfra,152\r\n", channel);
336 else
337 tcp_sendf (serv, "WHO %s\r\n", channel);
340 /* userhost */
342 static void
343 irc_userhost (server *serv, char *nick)
345 tcp_sendf (serv, "USERHOST %s\r\n", nick);
348 static void
349 irc_away_status (server *serv, char *channel)
351 if (serv->have_whox)
352 tcp_sendf (serv, "WHO %s %%chtsunfra,152\r\n", channel);
353 else
354 tcp_sendf (serv, "WHO %s\r\n", channel);
357 /*static void
358 irc_get_ip (server *serv, char *nick)
360 tcp_sendf (serv, "WHO %s\r\n", nick);
365 * Command: WHOIS
366 * Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
368 static void
369 irc_user_whois (server *serv, char *nicks)
371 tcp_sendf (serv, "WHOIS %s\r\n", nicks);
374 static void
375 irc_message (server *serv, char *channel, char *text)
377 tcp_sendf (serv, "PRIVMSG %s :%s\r\n", channel, text);
380 static void
381 irc_action (server *serv, char *channel, char *act)
383 tcp_sendf (serv, "PRIVMSG %s :\001ACTION %s\001\r\n", channel, act);
386 static void
387 irc_notice (server *serv, char *channel, char *text)
389 tcp_sendf (serv, "NOTICE %s :%s\r\n", channel, text);
392 static void
393 irc_topic (server *serv, char *channel, char *topic)
395 if (!topic)
396 tcp_sendf (serv, "TOPIC %s :\r\n", channel);
397 else if (topic[0])
398 tcp_sendf (serv, "TOPIC %s :%s\r\n", channel, topic);
399 else
400 tcp_sendf (serv, "TOPIC %s\r\n", channel);
403 static void
404 irc_list_channels (server *serv, char *arg, int min_users)
406 if (arg[0])
408 tcp_sendf (serv, "LIST %s\r\n", arg);
409 return;
412 if (serv->use_listargs)
413 tcp_sendf (serv, "LIST >%d,<10000\r\n", min_users - 1);
414 else
415 tcp_send_len (serv, "LIST\r\n", 6);
418 static void
419 irc_names (server *serv, char *channel)
421 tcp_sendf (serv, "NAMES %s\r\n", channel);
424 static void
425 irc_change_nick (server *serv, char *new_nick)
427 tcp_sendf (serv, "NICK %s\r\n", new_nick);
430 static void
431 irc_ping (server *serv, char *to, char *timestring)
433 if (*to)
434 tcp_sendf (serv, "PRIVMSG %s :\001PING %s\001\r\n", to, timestring);
435 else
436 tcp_sendf (serv, "PING %s\r\n", timestring);
439 static int
440 irc_raw (server *serv, char *raw)
442 int len;
443 char tbuf[4096];
444 if (*raw)
446 len = strlen (raw);
447 if (len < sizeof (tbuf) - 3)
449 len = snprintf (tbuf, sizeof (tbuf), "%s\r\n", raw);
450 tcp_send_len (serv, tbuf, len);
451 } else
453 tcp_send_len (serv, raw, len);
454 tcp_send_len (serv, "\r\n", 2);
456 return TRUE;
458 return FALSE;
461 /* ============================================================== */
462 /* ======================= IRC INPUT ============================ */
463 /* ============================================================== */
466 static void
467 channel_date (session *sess, char *chan, char *timestr)
469 time_t timestamp = (time_t) atol (timestr);
470 char *tim = ctime (&timestamp);
471 tim[24] = 0; /* get rid of the \n */
472 EMIT_SIGNAL (XP_TE_CHANDATE, sess, chan, tim, NULL, NULL, 0);
475 static void
476 process_numeric (session * sess, int n,
477 char *word[], char *word_eol[], char *text)
479 server *serv = sess->server;
480 /* show whois is the server tab */
481 session *whois_sess = serv->server_session;
483 /* unless this setting is on */
484 if (prefs.irc_whois_front)
485 whois_sess = serv->front_session;
487 char *ex;
489 switch (n)
491 case 1:
492 inbound_login_start (sess, word[3], word[1]);
493 /* if network is PTnet then you must get your IP address
494 from "001" server message */
495 if ((strncmp(word[7], "PTnet", 5) == 0) &&
496 (strncmp(word[8], "IRC", 3) == 0) &&
497 (strncmp(word[9], "Network", 7) == 0) &&
498 (strrchr(word[10], '@') != NULL))
500 serv->use_who = FALSE;
501 if (prefs.ip_from_server)
502 inbound_foundip (sess, strrchr(word[10], '@')+1);
505 goto def;
507 case 4: /* check the ircd type */
508 serv->use_listargs = FALSE;
509 serv->modes_per_line = 3; /* default to IRC RFC */
510 if (strncmp (word[5], "bahamut", 7) == 0) /* DALNet */
512 serv->use_listargs = TRUE; /* use the /list args */
513 } else if (strncmp (word[5], "u2.10.", 6) == 0) /* Undernet */
515 serv->use_listargs = TRUE; /* use the /list args */
516 serv->modes_per_line = 6; /* allow 6 modes per line */
517 } else if (strncmp (word[5], "glx2", 4) == 0)
519 serv->use_listargs = TRUE; /* use the /list args */
521 goto def;
523 case 5:
524 inbound_005 (serv, word);
525 goto def;
527 case 263: /*Server load is temporarily too heavy */
528 if (fe_is_chanwindow (sess->server))
530 fe_chan_list_end (sess->server);
531 fe_message (word_eol[5] + 1, FE_MSG_ERROR);
533 goto def;
535 case 301:
536 inbound_away (serv, word[4],
537 (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5]);
538 break;
540 case 302:
541 if (serv->skip_next_userhost)
543 char *eq = strchr (word[4], '=');
544 if (eq)
546 *eq = 0;
547 if (!serv->p_cmp (word[4] + 1, serv->nick))
549 char *at = strrchr (eq + 1, '@');
550 if (at)
551 inbound_foundip (sess, at + 1);
555 serv->skip_next_userhost = FALSE;
556 break;
558 else goto def;
560 case 303:
561 word[4]++;
562 notify_markonline (serv, word);
563 break;
565 case 305:
566 inbound_uback (serv);
567 goto def;
569 case 306:
570 inbound_uaway (serv);
571 goto def;
573 case 312:
574 if (!serv->skip_next_whois)
575 EMIT_SIGNAL (XP_TE_WHOIS3, whois_sess, word[4], word_eol[5], NULL, NULL, 0);
576 else
577 inbound_user_info (sess, NULL, NULL, NULL, word[5], word[4], NULL, NULL, 0xff);
578 break;
580 case 311: /* WHOIS 1st line */
581 serv->inside_whois = 1;
582 inbound_user_info_start (sess, word[4]);
583 if (!serv->skip_next_whois)
584 EMIT_SIGNAL (XP_TE_WHOIS1, whois_sess, word[4], word[5],
585 word[6], word_eol[8] + 1, 0);
586 else
587 inbound_user_info (sess, NULL, word[5], word[6], NULL, word[4],
588 word_eol[8][0] == ':' ? word_eol[8] + 1 : word_eol[8], NULL, 0xff);
589 break;
591 case 314: /* WHOWAS */
592 inbound_user_info_start (sess, word[4]);
593 EMIT_SIGNAL (XP_TE_WHOIS1, whois_sess, word[4], word[5],
594 word[6], word_eol[8] + 1, 0);
595 break;
597 case 317:
598 if (!serv->skip_next_whois)
600 time_t timestamp = (time_t) atol (word[6]);
601 long idle = atol (word[5]);
602 char *tim;
603 char outbuf[64];
605 snprintf (outbuf, sizeof (outbuf),
606 "%02ld:%02ld:%02ld", idle / 3600, (idle / 60) % 60,
607 idle % 60);
608 if (timestamp == 0)
609 EMIT_SIGNAL (XP_TE_WHOIS4, whois_sess, word[4],
610 outbuf, NULL, NULL, 0);
611 else
613 tim = ctime (&timestamp);
614 tim[19] = 0; /* get rid of the \n */
615 EMIT_SIGNAL (XP_TE_WHOIS4T, whois_sess, word[4],
616 outbuf, tim, NULL, 0);
619 break;
621 case 318: /* END OF WHOIS */
622 if (!serv->skip_next_whois)
623 EMIT_SIGNAL (XP_TE_WHOIS6, whois_sess, word[4], NULL,
624 NULL, NULL, 0);
625 serv->skip_next_whois = 0;
626 serv->inside_whois = 0;
627 break;
629 case 313:
630 case 319:
631 if (!serv->skip_next_whois)
632 EMIT_SIGNAL (XP_TE_WHOIS2, whois_sess, word[4],
633 word_eol[5] + 1, NULL, NULL, 0);
634 break;
636 case 307: /* dalnet version */
637 case 320: /* :is an identified user */
638 if (!serv->skip_next_whois)
639 EMIT_SIGNAL (XP_TE_WHOIS_ID, whois_sess, word[4],
640 word_eol[5] + 1, NULL, NULL, 0);
641 break;
643 case 321:
644 if (!fe_is_chanwindow (sess->server))
645 EMIT_SIGNAL (XP_TE_CHANLISTHEAD, serv->server_session, NULL, NULL, NULL, NULL, 0);
646 break;
648 case 322:
649 if (fe_is_chanwindow (sess->server))
651 fe_add_chan_list (sess->server, word[4], word[5], word_eol[6] + 1);
652 } else
654 PrintTextf (serv->server_session, "%-16s %-7d %s\017\n",
655 word[4], atoi (word[5]), word_eol[6] + 1);
657 break;
659 case 323:
660 if (!fe_is_chanwindow (sess->server))
661 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0);
662 else
663 fe_chan_list_end (sess->server);
664 break;
666 case 324:
667 sess = find_channel (serv, word[4]);
668 if (!sess)
669 sess = serv->server_session;
670 if (sess->ignore_mode)
671 sess->ignore_mode = FALSE;
672 else
673 EMIT_SIGNAL (XP_TE_CHANMODES, sess, word[4], word_eol[5],
674 NULL, NULL, 0);
675 fe_update_mode_buttons (sess, 't', '-');
676 fe_update_mode_buttons (sess, 'n', '-');
677 fe_update_mode_buttons (sess, 's', '-');
678 fe_update_mode_buttons (sess, 'i', '-');
679 fe_update_mode_buttons (sess, 'p', '-');
680 fe_update_mode_buttons (sess, 'm', '-');
681 fe_update_mode_buttons (sess, 'l', '-');
682 fe_update_mode_buttons (sess, 'k', '-');
683 handle_mode (serv, word, word_eol, "", TRUE);
684 break;
686 case 329:
687 sess = find_channel (serv, word[4]);
688 if (sess)
690 if (sess->ignore_date)
691 sess->ignore_date = FALSE;
692 else
693 channel_date (sess, word[4], word[5]);
695 break;
697 case 330:
698 if (!serv->skip_next_whois)
699 EMIT_SIGNAL (XP_TE_WHOIS_AUTH, whois_sess, word[4],
700 word_eol[6] + 1, word[5], NULL, 0);
701 inbound_user_info (sess, NULL, NULL, NULL, NULL, word[4], NULL, word[5], 0xff);
702 break;
704 case 332:
705 inbound_topic (serv, word[4],
706 (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5]);
707 break;
709 case 333:
710 inbound_topictime (serv, word[4], word[5], atol (word[6]));
711 break;
713 #if 0
714 case 338: /* Undernet Real user@host, Real IP */
715 EMIT_SIGNAL (XP_TE_WHOIS_REALHOST, sess, word[4], word[5], word[6],
716 (word_eol[7][0]==':') ? word_eol[7]+1 : word_eol[7], 0);
717 break;
718 #endif
720 case 341: /* INVITE ACK */
721 EMIT_SIGNAL (XP_TE_UINVITE, sess, word[4], word[5], serv->servername,
722 NULL, 0);
723 break;
725 case 352: /* WHO */
727 unsigned int away = 0;
728 session *who_sess = find_channel (serv, word[4]);
730 if (*word[9] == 'G')
731 away = 1;
733 inbound_user_info (sess, word[4], word[5], word[6], word[7],
734 word[8], word_eol[11], NULL, away);
736 /* try to show only user initiated whos */
737 if (!who_sess || !who_sess->doing_who)
738 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1],
739 word[2], NULL, 0);
741 break;
743 case 354: /* undernet WHOX: used as a reply for irc_away_status */
745 unsigned int away = 0;
746 session *who_sess;
748 /* irc_away_status and irc_user_list sends out a "152" */
749 if (!strcmp (word[4], "152"))
751 who_sess = find_channel (serv, word[5]);
753 if (*word[10] == 'G')
754 away = 1;
756 /* :server 354 yournick 152 #channel ~ident host servname nick H account :realname */
757 inbound_user_info (sess, word[5], word[6], word[7], word[8],
758 word[9], word_eol[12]+1, word[11], away);
760 /* try to show only user initiated whos */
761 if (!who_sess || !who_sess->doing_who)
762 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
763 word[1], word[2], NULL, 0);
764 } else
765 goto def;
767 break;
769 case 315: /* END OF WHO */
771 session *who_sess;
772 who_sess = find_channel (serv, word[4]);
773 if (who_sess)
775 if (!who_sess->doing_who)
776 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
777 word[1], word[2], NULL, 0);
778 who_sess->doing_who = FALSE;
779 } else
781 if (!serv->doing_dns)
782 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
783 word[1], word[2], NULL, 0);
784 serv->doing_dns = FALSE;
787 break;
789 case 348: /* +e-list entry */
790 if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], TRUE))
791 goto def;
792 break;
794 case 349: /* end of exemption list */
795 sess = find_channel (serv, word[4]);
796 if (!sess)
798 sess = serv->front_session;
799 goto def;
801 if (!fe_is_banwindow (sess))
802 goto def;
803 fe_ban_list_end (sess, TRUE);
804 break;
806 case 353: /* NAMES */
807 inbound_nameslist (serv, word[5],
808 (word_eol[6][0] == ':') ? word_eol[6] + 1 : word_eol[6]);
809 break;
811 case 366:
812 if (!inbound_nameslist_end (serv, word[4]))
813 goto def;
814 break;
816 case 367: /* banlist entry */
817 inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], FALSE);
818 break;
820 case 368:
821 sess = find_channel (serv, word[4]);
822 if (!sess)
824 sess = serv->front_session;
825 goto def;
827 if (!fe_is_banwindow (sess))
828 goto def;
829 fe_ban_list_end (sess, FALSE);
830 break;
832 case 369: /* WHOWAS end */
833 case 406: /* WHOWAS error */
834 EMIT_SIGNAL (XP_TE_SERVTEXT, whois_sess, text, word[1], word[2], NULL, 0);
835 serv->inside_whois = 0;
836 break;
838 case 372: /* motd text */
839 case 375: /* motd start */
840 if (!prefs.skipmotd || serv->motd_skipped)
841 EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL,
842 NULL, 0);
843 break;
845 case 376: /* end of motd */
846 case 422: /* motd file is missing */
847 inbound_login_end (sess, text);
848 break;
850 case 433: /* nickname in use */
851 case 432: /* erroneous nickname */
852 if (serv->end_of_motd)
853 goto def;
854 inbound_next_nick (sess, word[4]);
855 break;
857 case 437:
858 if (serv->end_of_motd || is_channel (serv, word[4]))
859 goto def;
860 inbound_next_nick (sess, word[4]);
861 break;
863 case 471:
864 EMIT_SIGNAL (XP_TE_USERLIMIT, sess, word[4], NULL, NULL, NULL, 0);
865 break;
867 case 473:
868 EMIT_SIGNAL (XP_TE_INVITE, sess, word[4], NULL, NULL, NULL, 0);
869 break;
871 case 474:
872 EMIT_SIGNAL (XP_TE_BANNED, sess, word[4], NULL, NULL, NULL, 0);
873 break;
875 case 475:
876 EMIT_SIGNAL (XP_TE_KEYWORD, sess, word[4], NULL, NULL, NULL, 0);
877 break;
879 case 601:
880 notify_set_offline (serv, word[4], FALSE);
881 break;
883 case 605:
884 notify_set_offline (serv, word[4], TRUE);
885 break;
887 case 600:
888 case 604:
889 notify_set_online (serv, word[4]);
890 break;
892 case 730: /* RPL_MONONLINE */
893 ex = strchr (word[4], '!'); /* only send the nick */
894 if (ex)
895 ex[0] = 0;
896 notify_set_online (serv, word[4] + 1);
897 break;
899 case 731: /* RPL_MONOFFLINE */
900 ex = strchr (word[4], '!'); /* only send the nick */
901 if (ex)
902 ex[0] = 0;
903 notify_set_offline (serv, word[4] + 1, FALSE);
904 break;
906 case 900: /* successful SASL 'logged in as ' */
907 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, word_eol[6]+1, word[1], word[2], NULL, 0);
908 break;
909 case 903: /* successful SASL auth */
910 case 904: /* failed SASL auth */
911 if (inbound_sasl_error (serv))
912 break; /* might retry */
913 case 905: /* failed SASL auth */
914 case 906: /* aborted */
915 case 907: /* attempting to re-auth after a successful auth */
916 EMIT_SIGNAL (XP_TE_SASLRESPONSE, serv->server_session, word[1], word[2], word[3], ++word_eol[4], 0);
917 if (!serv->sent_capend)
919 serv->sent_capend = TRUE;
920 tcp_send_len (serv, "CAP END\r\n", 9);
922 break;
923 case 908: /* Supported SASL Mechs */
924 inbound_sasl_supportedmechs (serv, word[4]);
925 break;
927 default:
929 if (serv->inside_whois && word[4][0])
931 /* some unknown WHOIS reply, ircd coders make them up weekly */
932 if (!serv->skip_next_whois)
933 EMIT_SIGNAL (XP_TE_WHOIS_SPECIAL, whois_sess, word[4],
934 (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5],
935 word[2], NULL, 0);
936 return;
939 def:
940 if (is_channel (serv, word[4]))
942 session *realsess = find_channel (serv, word[4]);
943 if (!realsess)
944 realsess = serv->server_session;
945 EMIT_SIGNAL (XP_TE_SERVTEXT, realsess, text, word[1], word[2], NULL, 0);
946 } else
948 EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1],
949 word[2], NULL, 0);
954 /* handle named messages that starts with a ':' */
956 static void
957 process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
959 server *serv = sess->server;
960 char ip[128], nick[NICKLEN];
961 char *text, *ex;
962 int len = strlen (type);
964 /* fill in the "ip" and "nick" buffers */
965 ex = strchr (word[1], '!');
966 if (!ex) /* no '!', must be a server message */
968 safe_strcpy (ip, word[1], sizeof (ip));
969 safe_strcpy (nick, word[1], sizeof (nick));
970 } else
972 safe_strcpy (ip, ex + 1, sizeof (ip));
973 ex[0] = 0;
974 safe_strcpy (nick, word[1], sizeof (nick));
975 ex[0] = '!';
978 if (len == 4)
980 guint32 t;
982 t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
983 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
984 switch (t)
987 case WORDL('A','C','C','O'):
988 inbound_account (serv, nick, word[3]);
989 return;
991 case WORDL('J','O','I','N'):
993 char *chan = word[3];
994 char *account = word[4];
995 char *realname = word_eol[5] + 1;
997 if (*chan == ':')
998 chan++;
999 if (!serv->p_cmp (nick, serv->nick))
1000 inbound_ujoin (serv, chan, nick, ip);
1001 else
1002 inbound_join (serv, chan, nick, ip, account, realname);
1004 return;
1006 case WORDL('K','I','C','K'):
1008 char *kicked = word[4];
1009 char *reason = word_eol[5];
1010 if (*kicked)
1012 if (*reason == ':')
1013 reason++;
1014 if (!strcmp (kicked, serv->nick))
1015 inbound_ukick (serv, word[3], nick, reason);
1016 else
1017 inbound_kick (serv, word[3], kicked, nick, reason);
1020 return;
1022 case WORDL('K','I','L','L'):
1023 EMIT_SIGNAL (XP_TE_KILL, sess, nick, word_eol[5], NULL, NULL, 0);
1024 return;
1026 case WORDL('M','O','D','E'):
1027 handle_mode (serv, word, word_eol, nick, FALSE); /* modes.c */
1028 return;
1030 case WORDL('N','I','C','K'):
1031 inbound_newnick (serv, nick, (word_eol[3][0] == ':')
1032 ? word_eol[3] + 1 : word_eol[3], FALSE);
1033 return;
1035 case WORDL('P','A','R','T'):
1037 char *chan = word[3];
1038 char *reason = word_eol[4];
1040 if (*chan == ':')
1041 chan++;
1042 if (*reason == ':')
1043 reason++;
1044 if (!strcmp (nick, serv->nick))
1045 inbound_upart (serv, chan, ip, reason);
1046 else
1047 inbound_part (serv, chan, nick, ip, reason);
1049 return;
1051 case WORDL('P','O','N','G'):
1052 inbound_ping_reply (serv->server_session,
1053 (word[4][0] == ':') ? word[4] + 1 : word[4], word[3]);
1054 return;
1056 case WORDL('Q','U','I','T'):
1057 inbound_quit (serv, nick, ip,
1058 (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3]);
1059 return;
1061 case WORDL('A','W','A','Y'):
1062 inbound_away_notify (serv, nick,
1063 (word_eol[3][0] == ':') ? word_eol[3] + 1 : NULL);
1064 return;
1067 goto garbage;
1070 else if(len == 3) {
1071 guint32 t;
1073 t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
1074 switch (t)
1076 case WORDL('C','A','P','\0'):
1077 if (strncasecmp (word[4], "ACK", 3) == 0)
1079 inbound_cap_ack (serv, word[1],
1080 word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5]);
1082 else if (strncasecmp (word[4], "LS", 2) == 0)
1084 inbound_cap_ls (serv, word[1],
1085 word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5]);
1087 else if (strncasecmp (word[4], "NAK", 3) == 0)
1089 inbound_cap_nak (serv);
1091 else if (strncasecmp (word[4], "LIST", 4) == 0)
1093 inbound_cap_list (serv, word[1],
1094 word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5]);
1097 return;
1102 else if (len >= 5)
1104 guint32 t;
1106 t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
1107 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
1108 switch (t)
1110 case WORDL('I','N','V','I'):
1111 if (ignore_check (word[1], IG_INVI))
1112 return;
1114 if (word[4][0] == ':')
1115 EMIT_SIGNAL (XP_TE_INVITED, sess, word[4] + 1, nick,
1116 serv->servername, NULL, 0);
1117 else
1118 EMIT_SIGNAL (XP_TE_INVITED, sess, word[4], nick,
1119 serv->servername, NULL, 0);
1121 return;
1123 case WORDL('N','O','T','I'):
1125 int id = FALSE;
1127 text = word_eol[4];
1128 if (*text == ':')
1130 text++;
1133 #ifdef USE_OPENSSL
1134 if (!strncmp (text, "CHALLENGE ", 10)) /* QuakeNet CHALLENGE upon our request */
1136 char *response = challengeauth_response (((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.username, serv->password, word[5]);
1138 tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n",
1139 CHALLENGEAUTH_NICK,
1140 ((ircnet *)serv->network)->user ? ((ircnet *)serv->network)->user : prefs.username,
1141 response,
1142 CHALLENGEAUTH_ALGO);
1144 g_free (response);
1145 return; /* omit the CHALLENGE <hash> ALGOS message */
1147 #endif
1149 if (serv->have_idmsg)
1151 if (*text == '+')
1153 id = TRUE;
1154 text++;
1155 } else if (*text == '-')
1156 text++;
1159 if (!ignore_check (word[1], IG_NOTI))
1160 inbound_notice (serv, word[3], nick, text, ip, id);
1162 return;
1164 case WORDL('P','R','I','V'):
1166 char *to = word[3];
1167 int len;
1168 int id = FALSE; /* identified */
1169 if (*to)
1171 text = word_eol[4];
1172 if (*text == ':')
1173 text++;
1174 if (serv->have_idmsg)
1176 if (*text == '+')
1178 id = TRUE;
1179 text++;
1180 } else if (*text == '-')
1181 text++;
1183 len = strlen (text);
1184 if (text[0] == 1 && text[len - 1] == 1) /* ctcp */
1186 text[len - 1] = 0;
1187 text++;
1188 if (strncasecmp (text, "ACTION", 6) != 0)
1189 flood_check (nick, ip, serv, sess, 0);
1190 if (strncasecmp (text, "DCC ", 4) == 0)
1191 /* redo this with handle_quotes TRUE */
1192 process_data_init (word[1], word_eol[1], word, word_eol, TRUE, FALSE);
1193 ctcp_handle (sess, to, nick, ip, text, word, word_eol, id);
1194 } else
1196 if (is_channel (serv, to))
1198 if (ignore_check (word[1], IG_CHAN))
1199 return;
1200 inbound_chanmsg (serv, NULL, to, nick, text, FALSE, id);
1201 } else
1203 if (ignore_check (word[1], IG_PRIV))
1204 return;
1205 inbound_privmsg (serv, nick, ip, text, id);
1210 return;
1212 case WORDL('T','O','P','I'):
1213 inbound_topicnew (serv, nick, word[3],
1214 (word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4]);
1215 return;
1217 case WORDL('W','A','L','L'):
1218 text = word_eol[3];
1219 if (*text == ':')
1220 text++;
1221 EMIT_SIGNAL (XP_TE_WALLOPS, sess, nick, text, NULL, NULL, 0);
1222 return;
1226 garbage:
1227 /* unknown message */
1228 PrintTextf (sess, "GARBAGE: %s\n", word_eol[1]);
1231 /* handle named messages that DON'T start with a ':' */
1233 static void
1234 process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol[])
1236 sess = sess->server->server_session;
1238 if (!strncmp (buf, "PING ", 5))
1240 tcp_sendf (sess->server, "PONG %s\r\n", buf + 5);
1241 return;
1243 if (!strncmp (buf, "ERROR", 5))
1245 EMIT_SIGNAL (XP_TE_SERVERERROR, sess, buf + 7, NULL, NULL, NULL, 0);
1246 return;
1248 if (!strncmp (buf, "NOTICE ", 7))
1250 buf = word_eol[3];
1251 if (*buf == ':')
1252 buf++;
1253 EMIT_SIGNAL (XP_TE_SERVNOTICE, sess, buf, sess->server->servername, NULL, NULL, 0);
1254 return;
1256 if (!strncmp (buf, "AUTHENTICATE", 12))
1258 inbound_sasl_authenticate (sess->server, word_eol[2]);
1259 return;
1262 EMIT_SIGNAL (XP_TE_SERVTEXT, sess, buf, sess->server->servername, rawname, NULL, 0);
1265 /* irc_inline() - 1 single line received from serv */
1267 static void
1268 irc_inline (server *serv, char *buf, int len)
1270 session *sess, *tmp;
1271 char *type, *text;
1272 char *word[PDIWORDS+1];
1273 char *word_eol[PDIWORDS+1];
1274 char pdibuf_static[522]; /* 1 line can potentially be 512*6 in utf8 */
1275 char *pdibuf = pdibuf_static;
1277 url_check_line (buf, len);
1279 /* need more than 522? fall back to malloc */
1280 if (len >= sizeof (pdibuf_static))
1281 pdibuf = malloc (len + 1);
1283 sess = serv->front_session;
1285 /* Python relies on this */
1286 word[PDIWORDS] = NULL;
1287 word_eol[PDIWORDS] = NULL;
1289 if (buf[0] == ':')
1291 /* split line into words and words_to_end_of_line */
1292 process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
1294 /* find a context for this message */
1295 if (is_channel (serv, word[3]))
1297 tmp = find_channel (serv, word[3]);
1298 if (tmp)
1299 sess = tmp;
1302 /* for server messages, the 2nd word is the "message type" */
1303 type = word[2];
1305 word[0] = type;
1306 word_eol[1] = buf; /* keep the ":" for plugins */
1307 if (plugin_emit_server (sess, type, word, word_eol))
1308 goto xit;
1309 word[1]++;
1310 word_eol[1] = buf + 1; /* but not for xchat internally */
1312 } else
1314 process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
1315 word[0] = type = word[1];
1316 if (plugin_emit_server (sess, type, word, word_eol))
1317 goto xit;
1320 if (buf[0] != ':')
1322 process_named_servermsg (sess, buf, word[0], word_eol);
1323 goto xit;
1326 /* see if the second word is a numeric */
1327 if (isdigit ((unsigned char) word[2][0]))
1329 text = word_eol[4];
1330 if (*text == ':')
1331 text++;
1333 process_numeric (sess, atoi (word[2]), word, word_eol, text);
1334 } else
1336 process_named_msg (sess, type, word, word_eol);
1339 xit:
1340 if (pdibuf != pdibuf_static)
1341 free (pdibuf);
1344 void
1345 proto_fill_her_up (server *serv)
1347 serv->p_inline = irc_inline;
1348 serv->p_invite = irc_invite;
1349 serv->p_cycle = irc_cycle;
1350 serv->p_ctcp = irc_ctcp;
1351 serv->p_nctcp = irc_nctcp;
1352 serv->p_quit = irc_quit;
1353 serv->p_kick = irc_kick;
1354 serv->p_part = irc_part;
1355 serv->p_ns_identify = irc_ns_identify;
1356 serv->p_ns_ghost = irc_ns_ghost;
1357 serv->p_join = irc_join;
1358 serv->p_join_list = irc_join_list;
1359 serv->p_login = irc_login;
1360 serv->p_join_info = irc_join_info;
1361 serv->p_mode = irc_mode;
1362 serv->p_user_list = irc_user_list;
1363 serv->p_away_status = irc_away_status;
1364 /*serv->p_get_ip = irc_get_ip;*/
1365 serv->p_whois = irc_user_whois;
1366 serv->p_get_ip = irc_user_list;
1367 serv->p_get_ip_uh = irc_userhost;
1368 serv->p_set_back = irc_set_back;
1369 serv->p_set_away = irc_set_away;
1370 serv->p_message = irc_message;
1371 serv->p_action = irc_action;
1372 serv->p_notice = irc_notice;
1373 serv->p_topic = irc_topic;
1374 serv->p_list_channels = irc_list_channels;
1375 serv->p_change_nick = irc_change_nick;
1376 serv->p_names = irc_names;
1377 serv->p_ping = irc_ping;
1378 serv->p_raw = irc_raw;
1379 serv->p_cmp = rfc_casecmp; /* can be changed by 005 in modes.c */