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 */
44 irc_login (server
*serv
, char *user
, char *realname
)
46 tcp_sendf (serv
, "CAP LS\r\n"); /* start with CAP LS as Charybdis sasl.txt suggests */
48 if (serv
->password
[0])
49 tcp_sendf (serv
, "PASS %s\r\n", serv
->password
);
53 "USER %s %s %s :%s\r\n",
54 serv
->nick
, user
, user
, serv
->servername
, realname
);
58 irc_nickserv (server
*serv
, char *cmd
, char *arg1
, char *arg2
, char *arg3
)
60 /* are all ircd authors idiots? */
61 switch (serv
->nickservtype
)
64 tcp_sendf (serv
, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
67 tcp_sendf (serv
, "NICKSERV %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
70 tcp_sendf (serv
, "NS %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
73 tcp_sendf (serv
, "PRIVMSG NS :%s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
76 /* why couldn't QuakeNet implement one of the existing ones? */
77 tcp_sendf (serv
, "AUTH %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
82 irc_ns_identify (server
*serv
, char *pass
)
84 irc_nickserv (serv
, "IDENTIFY", pass
, "", "");
88 irc_ns_ghost (server
*serv
, char *usname
, char *pass
)
90 if (serv
->nickservtype
!= 4)
91 irc_nickserv (serv
, "GHOST", usname
, " ", pass
);
95 irc_join (server
*serv
, char *channel
, char *key
)
98 tcp_sendf (serv
, "JOIN %s %s\r\n", channel
, key
);
100 tcp_sendf (serv
, "JOIN %s\r\n", channel
);
104 irc_join_list_flush (server
*serv
, GString
*c
, GString
*k
)
106 char *chanstr
, *keystr
;
108 chanstr
= g_string_free (c
, FALSE
);
109 keystr
= g_string_free (k
, FALSE
);
113 tcp_sendf (serv
, "JOIN %s %s\r\n", chanstr
, keystr
);
115 tcp_sendf (serv
, "JOIN %s\r\n", chanstr
);
121 /* join a whole list of channels & keys, split to multiple lines
122 * to get around 512 limit */
125 irc_join_list (server
*serv
, GSList
*channels
, GSList
*keys
)
129 GString
*c
= g_string_new (NULL
);
130 GString
*k
= g_string_new (NULL
);
136 len
= 9; /* "JOIN<space><space>\r\n" */
142 /* measure how many bytes this channel would add... */
145 add
= strlen (clist
->data
);
152 add
+= strlen (klist
->data
);
156 add
++; /* 'x' filler */
162 /* too big? dump buffer and start a fresh one */
165 irc_join_list_flush (serv
, c
, k
);
167 c
= g_string_new (NULL
);
168 k
= g_string_new (NULL
);
173 /* now actually add it to our GStrings */
176 add
= strlen (clist
->data
);
180 g_string_append_c (c
, ',');
182 g_string_append (c
, clist
->data
);
188 add
+= strlen (klist
->data
);
192 g_string_append_c (k
, ',');
194 g_string_append (k
, klist
->data
);
203 g_string_append_c (k
, ',');
205 g_string_append_c (k
, 'x');
215 irc_join_list_flush (serv
, c
, k
);
219 irc_part (server
*serv
, char *channel
, char *reason
)
222 tcp_sendf (serv
, "PART %s :%s\r\n", channel
, reason
);
224 tcp_sendf (serv
, "PART %s\r\n", channel
);
228 irc_quit (server
*serv
, char *reason
)
231 tcp_sendf (serv
, "QUIT :%s\r\n", reason
);
233 tcp_send_len (serv
, "QUIT\r\n", 6);
237 irc_set_back (server
*serv
)
239 tcp_send_len (serv
, "AWAY\r\n", 6);
243 irc_set_away (server
*serv
, char *reason
)
255 tcp_sendf (serv
, "AWAY :%s\r\n", reason
);
259 irc_ctcp (server
*serv
, char *to
, char *msg
)
261 tcp_sendf (serv
, "PRIVMSG %s :\001%s\001\r\n", to
, msg
);
265 irc_nctcp (server
*serv
, char *to
, char *msg
)
267 tcp_sendf (serv
, "NOTICE %s :\001%s\001\r\n", to
, msg
);
271 irc_cycle (server
*serv
, char *channel
, char *key
)
273 tcp_sendf (serv
, "PART %s\r\nJOIN %s %s\r\n", channel
, channel
, key
);
277 irc_kick (server
*serv
, char *channel
, char *nick
, char *reason
)
280 tcp_sendf (serv
, "KICK %s %s :%s\r\n", channel
, nick
, reason
);
282 tcp_sendf (serv
, "KICK %s %s\r\n", channel
, nick
);
286 irc_invite (server
*serv
, char *channel
, char *nick
)
288 tcp_sendf (serv
, "INVITE %s %s\r\n", nick
, channel
);
292 irc_mode (server
*serv
, char *target
, char *mode
)
294 tcp_sendf (serv
, "MODE %s %s\r\n", target
, mode
);
297 /* find channel info when joined */
300 irc_join_info (server
*serv
, char *channel
)
302 tcp_sendf (serv
, "MODE %s\r\n", channel
);
305 /* initiate userlist retreival */
308 irc_user_list (server
*serv
, char *channel
)
311 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
313 tcp_sendf (serv
, "WHO %s\r\n", channel
);
319 irc_userhost (server
*serv
, char *nick
)
321 tcp_sendf (serv
, "USERHOST %s\r\n", nick
);
325 irc_away_status (server
*serv
, char *channel
)
328 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
330 tcp_sendf (serv
, "WHO %s\r\n", channel
);
334 irc_get_ip (server *serv, char *nick)
336 tcp_sendf (serv, "WHO %s\r\n", nick);
342 * Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
345 irc_user_whois (server
*serv
, char *nicks
)
347 tcp_sendf (serv
, "WHOIS %s\r\n", nicks
);
351 irc_message (server
*serv
, char *channel
, char *text
)
353 tcp_sendf (serv
, "PRIVMSG %s :%s\r\n", channel
, text
);
357 irc_action (server
*serv
, char *channel
, char *act
)
359 tcp_sendf (serv
, "PRIVMSG %s :\001ACTION %s\001\r\n", channel
, act
);
363 irc_notice (server
*serv
, char *channel
, char *text
)
365 tcp_sendf (serv
, "NOTICE %s :%s\r\n", channel
, text
);
369 irc_topic (server
*serv
, char *channel
, char *topic
)
372 tcp_sendf (serv
, "TOPIC %s :\r\n", channel
);
374 tcp_sendf (serv
, "TOPIC %s :%s\r\n", channel
, topic
);
376 tcp_sendf (serv
, "TOPIC %s\r\n", channel
);
380 irc_list_channels (server
*serv
, char *arg
, int min_users
)
384 tcp_sendf (serv
, "LIST %s\r\n", arg
);
388 if (serv
->use_listargs
)
389 tcp_sendf (serv
, "LIST >%d,<10000\r\n", min_users
- 1);
391 tcp_send_len (serv
, "LIST\r\n", 6);
395 irc_names (server
*serv
, char *channel
)
397 tcp_sendf (serv
, "NAMES %s\r\n", channel
);
401 irc_change_nick (server
*serv
, char *new_nick
)
403 tcp_sendf (serv
, "NICK %s\r\n", new_nick
);
407 irc_ping (server
*serv
, char *to
, char *timestring
)
410 tcp_sendf (serv
, "PRIVMSG %s :\001PING %s\001\r\n", to
, timestring
);
412 tcp_sendf (serv
, "PING %s\r\n", timestring
);
416 irc_raw (server
*serv
, char *raw
)
423 if (len
< sizeof (tbuf
) - 3)
425 len
= snprintf (tbuf
, sizeof (tbuf
), "%s\r\n", raw
);
426 tcp_send_len (serv
, tbuf
, len
);
429 tcp_send_len (serv
, raw
, len
);
430 tcp_send_len (serv
, "\r\n", 2);
437 /* ============================================================== */
438 /* ======================= IRC INPUT ============================ */
439 /* ============================================================== */
443 channel_date (session
*sess
, char *chan
, char *timestr
)
445 time_t timestamp
= (time_t) atol (timestr
);
446 char *tim
= ctime (×tamp
);
447 tim
[24] = 0; /* get rid of the \n */
448 EMIT_SIGNAL (XP_TE_CHANDATE
, sess
, chan
, tim
, NULL
, NULL
, 0);
452 process_numeric (session
* sess
, int n
,
453 char *word
[], char *word_eol
[], char *text
)
455 server
*serv
= sess
->server
;
456 /* show whois is the server tab */
457 session
*whois_sess
= serv
->server_session
;
459 /* unless this setting is on */
460 if (prefs
.irc_whois_front
)
461 whois_sess
= serv
->front_session
;
468 inbound_login_start (sess
, word
[3], word
[1]);
469 /* if network is PTnet then you must get your IP address
470 from "001" server message */
471 if ((strncmp(word
[7], "PTnet", 5) == 0) &&
472 (strncmp(word
[8], "IRC", 3) == 0) &&
473 (strncmp(word
[9], "Network", 7) == 0) &&
474 (strrchr(word
[10], '@') != NULL
))
476 serv
->use_who
= FALSE
;
477 if (prefs
.ip_from_server
)
478 inbound_foundip (sess
, strrchr(word
[10], '@')+1);
482 if (strcasecmp (word
[7], "DALnet") == 0 ||
483 strcasecmp (word
[7], "BRASnet") == 0)
484 serv
->nickservtype
= 1;
487 else if (strcasecmp (word
[7], "FreeNode") == 0)
488 serv
->nickservtype
= 2;
492 case 4: /* check the ircd type */
493 serv
->use_listargs
= FALSE
;
494 serv
->modes_per_line
= 3; /* default to IRC RFC */
495 if (strncmp (word
[5], "bahamut", 7) == 0) /* DALNet */
497 serv
->use_listargs
= TRUE
; /* use the /list args */
498 } else if (strncmp (word
[5], "u2.10.", 6) == 0) /* Undernet */
500 serv
->use_listargs
= TRUE
; /* use the /list args */
501 serv
->modes_per_line
= 6; /* allow 6 modes per line */
502 } else if (strncmp (word
[5], "glx2", 4) == 0)
504 serv
->use_listargs
= TRUE
; /* use the /list args */
509 inbound_005 (serv
, word
);
512 case 263: /*Server load is temporarily too heavy */
513 if (fe_is_chanwindow (sess
->server
))
515 fe_chan_list_end (sess
->server
);
516 fe_message (word_eol
[5] + 1, FE_MSG_ERROR
);
521 inbound_away (serv
, word
[4],
522 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
526 if (serv
->skip_next_userhost
)
528 char *eq
= strchr (word
[4], '=');
532 if (!serv
->p_cmp (word
[4] + 1, serv
->nick
))
534 char *at
= strrchr (eq
+ 1, '@');
536 inbound_foundip (sess
, at
+ 1);
540 serv
->skip_next_userhost
= FALSE
;
547 notify_markonline (serv
, word
);
551 inbound_uback (serv
);
555 inbound_uaway (serv
);
559 if (!serv
->skip_next_whois
)
560 EMIT_SIGNAL (XP_TE_WHOIS3
, whois_sess
, word
[4], word_eol
[5], NULL
, NULL
, 0);
562 inbound_user_info (sess
, NULL
, NULL
, NULL
, word
[5], word
[4], NULL
, NULL
, 0xff);
565 case 311: /* WHOIS 1st line */
566 serv
->inside_whois
= 1;
567 inbound_user_info_start (sess
, word
[4]);
568 if (!serv
->skip_next_whois
)
569 EMIT_SIGNAL (XP_TE_WHOIS1
, whois_sess
, word
[4], word
[5],
570 word
[6], word_eol
[8] + 1, 0);
572 inbound_user_info (sess
, NULL
, word
[5], word
[6], NULL
, word
[4],
573 word_eol
[8][0] == ':' ? word_eol
[8] + 1 : word_eol
[8], NULL
, 0xff);
576 case 314: /* WHOWAS */
577 inbound_user_info_start (sess
, word
[4]);
578 EMIT_SIGNAL (XP_TE_WHOIS1
, whois_sess
, word
[4], word
[5],
579 word
[6], word_eol
[8] + 1, 0);
583 if (!serv
->skip_next_whois
)
585 time_t timestamp
= (time_t) atol (word
[6]);
586 long idle
= atol (word
[5]);
590 snprintf (outbuf
, sizeof (outbuf
),
591 "%02ld:%02ld:%02ld", idle
/ 3600, (idle
/ 60) % 60,
594 EMIT_SIGNAL (XP_TE_WHOIS4
, whois_sess
, word
[4],
595 outbuf
, NULL
, NULL
, 0);
598 tim
= ctime (×tamp
);
599 tim
[19] = 0; /* get rid of the \n */
600 EMIT_SIGNAL (XP_TE_WHOIS4T
, whois_sess
, word
[4],
601 outbuf
, tim
, NULL
, 0);
606 case 318: /* END OF WHOIS */
607 if (!serv
->skip_next_whois
)
608 EMIT_SIGNAL (XP_TE_WHOIS6
, whois_sess
, word
[4], NULL
,
610 serv
->skip_next_whois
= 0;
611 serv
->inside_whois
= 0;
616 if (!serv
->skip_next_whois
)
617 EMIT_SIGNAL (XP_TE_WHOIS2
, whois_sess
, word
[4],
618 word_eol
[5] + 1, NULL
, NULL
, 0);
621 case 307: /* dalnet version */
622 case 320: /* :is an identified user */
623 if (!serv
->skip_next_whois
)
624 EMIT_SIGNAL (XP_TE_WHOIS_ID
, whois_sess
, word
[4],
625 word_eol
[5] + 1, NULL
, NULL
, 0);
629 if (!fe_is_chanwindow (sess
->server
))
630 EMIT_SIGNAL (XP_TE_CHANLISTHEAD
, serv
->server_session
, NULL
, NULL
, NULL
, NULL
, 0);
634 if (fe_is_chanwindow (sess
->server
))
636 fe_add_chan_list (sess
->server
, word
[4], word
[5], word_eol
[6] + 1);
639 PrintTextf (serv
->server_session
, "%-16s %-7d %s\017\n",
640 word
[4], atoi (word
[5]), word_eol
[6] + 1);
645 if (!fe_is_chanwindow (sess
->server
))
646 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1], word
[2], NULL
, 0);
648 fe_chan_list_end (sess
->server
);
652 sess
= find_channel (serv
, word
[4]);
654 sess
= serv
->server_session
;
655 if (sess
->ignore_mode
)
656 sess
->ignore_mode
= FALSE
;
658 EMIT_SIGNAL (XP_TE_CHANMODES
, sess
, word
[4], word_eol
[5],
660 fe_update_mode_buttons (sess
, 't', '-');
661 fe_update_mode_buttons (sess
, 'n', '-');
662 fe_update_mode_buttons (sess
, 's', '-');
663 fe_update_mode_buttons (sess
, 'i', '-');
664 fe_update_mode_buttons (sess
, 'p', '-');
665 fe_update_mode_buttons (sess
, 'm', '-');
666 fe_update_mode_buttons (sess
, 'l', '-');
667 fe_update_mode_buttons (sess
, 'k', '-');
668 handle_mode (serv
, word
, word_eol
, "", TRUE
);
672 sess
= find_channel (serv
, word
[4]);
675 if (sess
->ignore_date
)
676 sess
->ignore_date
= FALSE
;
678 channel_date (sess
, word
[4], word
[5]);
683 if (!serv
->skip_next_whois
)
684 EMIT_SIGNAL (XP_TE_WHOIS_AUTH
, whois_sess
, word
[4],
685 word_eol
[6] + 1, word
[5], NULL
, 0);
686 inbound_user_info (sess
, NULL
, NULL
, NULL
, NULL
, word
[4], NULL
, word
[5], 0xff);
690 inbound_topic (serv
, word
[4],
691 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
695 inbound_topictime (serv
, word
[4], word
[5], atol (word
[6]));
699 case 338: /* Undernet Real user@host, Real IP */
700 EMIT_SIGNAL (XP_TE_WHOIS_REALHOST
, sess
, word
[4], word
[5], word
[6],
701 (word_eol
[7][0]==':') ? word_eol
[7]+1 : word_eol
[7], 0);
705 case 341: /* INVITE ACK */
706 EMIT_SIGNAL (XP_TE_UINVITE
, sess
, word
[4], word
[5], serv
->servername
,
712 unsigned int away
= 0;
713 session
*who_sess
= find_channel (serv
, word
[4]);
718 inbound_user_info (sess
, word
[4], word
[5], word
[6], word
[7],
719 word
[8], word_eol
[11], NULL
, away
);
721 /* try to show only user initiated whos */
722 if (!who_sess
|| !who_sess
->doing_who
)
723 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
728 case 354: /* undernet WHOX: used as a reply for irc_away_status */
730 unsigned int away
= 0;
733 /* irc_away_status and irc_user_list sends out a "152" */
734 if (!strcmp (word
[4], "152"))
736 who_sess
= find_channel (serv
, word
[5]);
738 if (*word
[10] == 'G')
741 /* :server 354 yournick 152 #channel ~ident host servname nick H account :realname */
742 inbound_user_info (sess
, word
[5], word
[6], word
[7], word
[8],
743 word
[9], word_eol
[12]+1, word
[11], away
);
745 /* try to show only user initiated whos */
746 if (!who_sess
|| !who_sess
->doing_who
)
747 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
748 word
[1], word
[2], NULL
, 0);
754 case 315: /* END OF WHO */
757 who_sess
= find_channel (serv
, word
[4]);
760 if (!who_sess
->doing_who
)
761 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
762 word
[1], word
[2], NULL
, 0);
763 who_sess
->doing_who
= FALSE
;
766 if (!serv
->doing_dns
)
767 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
768 word
[1], word
[2], NULL
, 0);
769 serv
->doing_dns
= FALSE
;
774 case 348: /* +e-list entry */
775 if (!inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], TRUE
))
779 case 349: /* end of exemption list */
780 sess
= find_channel (serv
, word
[4]);
783 sess
= serv
->front_session
;
786 if (!fe_is_banwindow (sess
))
788 fe_ban_list_end (sess
, TRUE
);
791 case 353: /* NAMES */
792 inbound_nameslist (serv
, word
[5],
793 (word_eol
[6][0] == ':') ? word_eol
[6] + 1 : word_eol
[6]);
797 if (!inbound_nameslist_end (serv
, word
[4]))
801 case 367: /* banlist entry */
802 inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], FALSE
);
806 sess
= find_channel (serv
, word
[4]);
809 sess
= serv
->front_session
;
812 if (!fe_is_banwindow (sess
))
814 fe_ban_list_end (sess
, FALSE
);
817 case 369: /* WHOWAS end */
818 case 406: /* WHOWAS error */
819 EMIT_SIGNAL (XP_TE_SERVTEXT
, whois_sess
, text
, word
[1], word
[2], NULL
, 0);
820 serv
->inside_whois
= 0;
823 case 372: /* motd text */
824 case 375: /* motd start */
825 if (!prefs
.skipmotd
|| serv
->motd_skipped
)
826 EMIT_SIGNAL (XP_TE_MOTD
, serv
->server_session
, text
, NULL
, NULL
,
830 case 376: /* end of motd */
831 case 422: /* motd file is missing */
832 inbound_login_end (sess
, text
);
835 case 433: /* nickname in use */
836 case 432: /* erroneous nickname */
837 if (serv
->end_of_motd
)
839 inbound_next_nick (sess
, word
[4]);
843 if (serv
->end_of_motd
|| is_channel (serv
, word
[4]))
845 inbound_next_nick (sess
, word
[4]);
849 EMIT_SIGNAL (XP_TE_USERLIMIT
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
853 EMIT_SIGNAL (XP_TE_INVITE
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
857 EMIT_SIGNAL (XP_TE_BANNED
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
861 EMIT_SIGNAL (XP_TE_KEYWORD
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
865 notify_set_offline (serv
, word
[4], FALSE
);
869 notify_set_offline (serv
, word
[4], TRUE
);
874 notify_set_online (serv
, word
[4]);
877 case 730: /* RPL_MONONLINE */
878 ex
= strchr (word
[4], '!'); /* only send the nick */
881 notify_set_online (serv
, word
[4] + 1);
884 case 731: /* RPL_MONOFFLINE */
885 ex
= strchr (word
[4], '!'); /* only send the nick */
888 notify_set_offline (serv
, word
[4] + 1, FALSE
);
891 case 900: /* successful SASL 'logged in as ' */
892 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, word_eol
[6]+1, word
[1], 900, NULL
, 0);
894 case 903: /* successful SASL auth */
895 case 904: /* aborted SASL auth */
896 case 905: /* failed SASL auth */
897 case 906: /* registration completes before SASL auth */
898 case 907: /* attempting to re-auth after a successful auth */
899 EMIT_SIGNAL (XP_TE_SASLRESPONSE
, serv
->server_session
, word
[1], word
[2], word
[3], ++word_eol
[4], 0);
900 tcp_send_len (serv
, "CAP END\r\n", 9);
905 if (serv
->inside_whois
&& word
[4][0])
907 /* some unknown WHOIS reply, ircd coders make them up weekly */
908 if (!serv
->skip_next_whois
)
909 EMIT_SIGNAL (XP_TE_WHOIS_SPECIAL
, whois_sess
, word
[4],
910 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5],
916 if (is_channel (serv
, word
[4]))
918 session
*realsess
= find_channel (serv
, word
[4]);
920 realsess
= serv
->server_session
;
921 EMIT_SIGNAL (XP_TE_SERVTEXT
, realsess
, text
, word
[1], word
[2], NULL
, 0);
924 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
930 /* handle named messages that starts with a ':' */
933 process_named_msg (session
*sess
, char *type
, char *word
[], char *word_eol
[])
935 server
*serv
= sess
->server
;
936 char ip
[128], nick
[NICKLEN
];
938 int len
= strlen (type
);
940 /* fill in the "ip" and "nick" buffers */
941 ex
= strchr (word
[1], '!');
942 if (!ex
) /* no '!', must be a server message */
944 safe_strcpy (ip
, word
[1], sizeof (ip
));
945 safe_strcpy (nick
, word
[1], sizeof (nick
));
948 safe_strcpy (ip
, ex
+ 1, sizeof (ip
));
950 safe_strcpy (nick
, word
[1], sizeof (nick
));
958 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
959 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
963 case WORDL('A','C','C','O'):
964 inbound_account (serv
, nick
, word
[3]);
967 case WORDL('J','O','I','N'):
969 char *chan
= word
[3];
970 char *account
= word
[4];
971 char *realname
= word_eol
[5] + 1;
975 if (!serv
->p_cmp (nick
, serv
->nick
))
976 inbound_ujoin (serv
, chan
, nick
, ip
);
978 inbound_join (serv
, chan
, nick
, ip
, account
, realname
);
982 case WORDL('K','I','C','K'):
984 char *kicked
= word
[4];
985 char *reason
= word_eol
[5];
990 if (!strcmp (kicked
, serv
->nick
))
991 inbound_ukick (serv
, word
[3], nick
, reason
);
993 inbound_kick (serv
, word
[3], kicked
, nick
, reason
);
998 case WORDL('K','I','L','L'):
999 EMIT_SIGNAL (XP_TE_KILL
, sess
, nick
, word_eol
[5], NULL
, NULL
, 0);
1002 case WORDL('M','O','D','E'):
1003 handle_mode (serv
, word
, word_eol
, nick
, FALSE
); /* modes.c */
1006 case WORDL('N','I','C','K'):
1007 inbound_newnick (serv
, nick
, (word_eol
[3][0] == ':')
1008 ? word_eol
[3] + 1 : word_eol
[3], FALSE
);
1011 case WORDL('P','A','R','T'):
1013 char *chan
= word
[3];
1014 char *reason
= word_eol
[4];
1020 if (!strcmp (nick
, serv
->nick
))
1021 inbound_upart (serv
, chan
, ip
, reason
);
1023 inbound_part (serv
, chan
, nick
, ip
, reason
);
1027 case WORDL('P','O','N','G'):
1028 inbound_ping_reply (serv
->server_session
,
1029 (word
[4][0] == ':') ? word
[4] + 1 : word
[4], word
[3]);
1032 case WORDL('Q','U','I','T'):
1033 inbound_quit (serv
, nick
, ip
,
1034 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : word_eol
[3]);
1037 case WORDL('A','W','A','Y'):
1038 inbound_away_notify (serv
, nick
,
1039 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : NULL
);
1048 guint32 want_cap
; /* format the CAP REQ string based on previous capabilities being requested or not */
1049 guint32 want_sasl
; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */
1050 char *pass
; /* buffer for SASL password */
1051 char buffer
[256]; /* buffer for requesting capabilities and emitting the signal */
1053 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
1056 case WORDL('C','A','P','\0'):
1057 if (strncasecmp (word
[4], "ACK", 3) == 0)
1059 EMIT_SIGNAL (XP_TE_CAPACK
, sess
->server
->server_session
, word
[1], word
[5][0]==':' ? ++word_eol
[5] : word_eol
[5], NULL
, NULL
, 0);
1061 if (strstr (word_eol
[5], "identify-msg") != 0)
1063 serv
->have_idmsg
= TRUE
;
1066 if (strstr (word_eol
[5], "multi-prefix") != 0)
1068 serv
->have_namesx
= TRUE
;
1071 if (strstr (word_eol
[5], "away-notify") != 0)
1073 serv
->have_awaynotify
= TRUE
;
1076 if (strstr (word_eol
[5], "account-notify") != 0)
1078 serv
->have_accnotify
= TRUE
;
1081 if (strstr (word_eol
[5], "extended-join") != 0)
1083 serv
->have_extjoin
= TRUE
;
1086 if (strstr (word_eol
[5], "sasl") != 0)
1088 serv
->have_sasl
= TRUE
;
1089 EMIT_SIGNAL (XP_TE_SASLAUTH
, serv
->server_session
, sess
->server
->sasluser
, NULL
, NULL
, NULL
, 0);
1090 tcp_send_len (serv
, "AUTHENTICATE PLAIN\r\n", 20);
1092 pass
= encode_sasl_pass (sess
->server
->sasluser
, sess
->server
->saslpassword
);
1093 tcp_sendf (sess
->server
, "AUTHENTICATE %s\r\n", pass
);
1097 else if (strncasecmp (word
[4], "LS", 2) == 0)
1099 EMIT_SIGNAL (XP_TE_CAPLIST
, serv
->server_session
, word
[1], word
[5][0]==':' ? ++word_eol
[5] : word_eol
[5], NULL
, NULL
, 0);
1103 if (strstr (word_eol
[5], "identify-msg") != 0)
1105 strcpy (buffer
, "CAP REQ :identify-msg");
1108 if (strstr (word_eol
[5], "multi-prefix") != 0)
1110 want_cap
? strcat (buffer
, " multi-prefix") : strcpy (buffer
, "CAP REQ :multi-prefix");
1113 if (strstr (word_eol
[5], "away-notify") != 0)
1115 want_cap
? strcat (buffer
, " away-notify") : strcpy (buffer
, "CAP REQ :away-notify");
1118 if (strstr (word_eol
[5], "account-notify") != 0)
1120 want_cap
? strcat (buffer
, " account-notify") : strcpy (buffer
, "CAP REQ :account-notify");
1124 if (strstr (word_eol
[5], "extended-join") != 0)
1126 want_cap
? strcat (buffer
, " extended-join") : strcpy (buffer
, "CAP REQ :extended-join");
1129 /* if the SASL password is set, request SASL auth */
1130 if (strstr (word_eol
[5], "sasl") != 0 && strlen (sess
->server
->saslpassword
) != 0)
1132 want_cap
? strcat (buffer
, " sasl") : strcpy (buffer
, "CAP REQ :sasl");
1139 /* buffer + 9 = emit buffer without "CAP REQ :" */
1140 EMIT_SIGNAL (XP_TE_CAPREQ
, sess
->server
->server_session
, buffer
+ 9, NULL
, NULL
, NULL
, 0);
1141 tcp_sendf (serv
, "%s\r\n", buffer
);
1145 /* if we use SASL, CAP END is dealt via raw numerics */
1146 tcp_send_len (serv
, "CAP END\r\n", 9);
1149 else if (strncasecmp (word
[4], "NAK", 3) == 0)
1151 tcp_send_len (serv
, "CAP END\r\n", 9);
1163 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
1164 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
1167 case WORDL('I','N','V','I'):
1168 if (ignore_check (word
[1], IG_INVI
))
1171 if (word
[4][0] == ':')
1172 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4] + 1, nick
,
1173 serv
->servername
, NULL
, 0);
1175 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4], nick
,
1176 serv
->servername
, NULL
, 0);
1180 case WORDL('N','O','T','I'):
1182 int id
= FALSE
; /* identified */
1188 if (serv
->have_idmsg
)
1194 } else if (*text
== '-')
1198 if (!ignore_check (word
[1], IG_NOTI
))
1199 inbound_notice (serv
, word
[3], nick
, text
, ip
, id
);
1203 case WORDL('P','R','I','V'):
1207 int id
= FALSE
; /* identified */
1213 if (serv
->have_idmsg
)
1219 } else if (*text
== '-')
1222 len
= strlen (text
);
1223 if (text
[0] == 1 && text
[len
- 1] == 1) /* ctcp */
1227 if (strncasecmp (text
, "ACTION", 6) != 0)
1228 flood_check (nick
, ip
, serv
, sess
, 0);
1229 if (strncasecmp (text
, "DCC ", 4) == 0)
1230 /* redo this with handle_quotes TRUE */
1231 process_data_init (word
[1], word_eol
[1], word
, word_eol
, TRUE
, FALSE
);
1232 ctcp_handle (sess
, to
, nick
, ip
, text
, word
, word_eol
, id
);
1235 if (is_channel (serv
, to
))
1237 if (ignore_check (word
[1], IG_CHAN
))
1239 inbound_chanmsg (serv
, NULL
, to
, nick
, text
, FALSE
, id
);
1242 if (ignore_check (word
[1], IG_PRIV
))
1244 inbound_privmsg (serv
, nick
, ip
, text
, id
);
1251 case WORDL('T','O','P','I'):
1252 inbound_topicnew (serv
, nick
, word
[3],
1253 (word_eol
[4][0] == ':') ? word_eol
[4] + 1 : word_eol
[4]);
1256 case WORDL('W','A','L','L'):
1260 EMIT_SIGNAL (XP_TE_WALLOPS
, sess
, nick
, text
, NULL
, NULL
, 0);
1266 /* unknown message */
1267 PrintTextf (sess
, "GARBAGE: %s\n", word_eol
[1]);
1270 /* handle named messages that DON'T start with a ':' */
1273 process_named_servermsg (session
*sess
, char *buf
, char *rawname
, char *word_eol
[])
1275 sess
= sess
->server
->server_session
;
1277 if (!strncmp (buf
, "PING ", 5))
1279 tcp_sendf (sess
->server
, "PONG %s\r\n", buf
+ 5);
1282 if (!strncmp (buf
, "ERROR", 5))
1284 EMIT_SIGNAL (XP_TE_SERVERERROR
, sess
, buf
+ 7, NULL
, NULL
, NULL
, 0);
1287 if (!strncmp (buf
, "NOTICE ", 7))
1292 EMIT_SIGNAL (XP_TE_SERVNOTICE
, sess
, buf
, sess
->server
->servername
, NULL
, NULL
, 0);
1295 if (!strncmp (buf
, "AUTHENTICATE +", 14)) /* omit SASL "empty" responses */
1300 EMIT_SIGNAL (XP_TE_SERVTEXT
, sess
, buf
, sess
->server
->servername
, rawname
, NULL
, 0);
1303 /* irc_inline() - 1 single line received from serv */
1306 irc_inline (server
*serv
, char *buf
, int len
)
1308 session
*sess
, *tmp
;
1310 char *word
[PDIWORDS
+1];
1311 char *word_eol
[PDIWORDS
+1];
1312 char pdibuf_static
[522]; /* 1 line can potentially be 512*6 in utf8 */
1313 char *pdibuf
= pdibuf_static
;
1315 url_check_line (buf
, len
);
1317 /* need more than 522? fall back to malloc */
1318 if (len
>= sizeof (pdibuf_static
))
1319 pdibuf
= malloc (len
+ 1);
1321 sess
= serv
->front_session
;
1323 /* Python relies on this */
1324 word
[PDIWORDS
] = NULL
;
1325 word_eol
[PDIWORDS
] = NULL
;
1329 /* split line into words and words_to_end_of_line */
1330 process_data_init (pdibuf
, buf
, word
, word_eol
, FALSE
, FALSE
);
1332 /* find a context for this message */
1333 if (is_channel (serv
, word
[3]))
1335 tmp
= find_channel (serv
, word
[3]);
1340 /* for server messages, the 2nd word is the "message type" */
1344 word_eol
[1] = buf
; /* keep the ":" for plugins */
1345 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1348 word_eol
[1] = buf
+ 1; /* but not for xchat internally */
1352 process_data_init (pdibuf
, buf
, word
, word_eol
, FALSE
, FALSE
);
1353 word
[0] = type
= word
[1];
1354 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1360 process_named_servermsg (sess
, buf
, word
[0], word_eol
);
1364 /* see if the second word is a numeric */
1365 if (isdigit ((unsigned char) word
[2][0]))
1371 process_numeric (sess
, atoi (word
[2]), word
, word_eol
, text
);
1374 process_named_msg (sess
, type
, word
, word_eol
);
1378 if (pdibuf
!= pdibuf_static
)
1383 proto_fill_her_up (server
*serv
)
1385 serv
->p_inline
= irc_inline
;
1386 serv
->p_invite
= irc_invite
;
1387 serv
->p_cycle
= irc_cycle
;
1388 serv
->p_ctcp
= irc_ctcp
;
1389 serv
->p_nctcp
= irc_nctcp
;
1390 serv
->p_quit
= irc_quit
;
1391 serv
->p_kick
= irc_kick
;
1392 serv
->p_part
= irc_part
;
1393 serv
->p_ns_identify
= irc_ns_identify
;
1394 serv
->p_ns_ghost
= irc_ns_ghost
;
1395 serv
->p_join
= irc_join
;
1396 serv
->p_join_list
= irc_join_list
;
1397 serv
->p_login
= irc_login
;
1398 serv
->p_join_info
= irc_join_info
;
1399 serv
->p_mode
= irc_mode
;
1400 serv
->p_user_list
= irc_user_list
;
1401 serv
->p_away_status
= irc_away_status
;
1402 /*serv->p_get_ip = irc_get_ip;*/
1403 serv
->p_whois
= irc_user_whois
;
1404 serv
->p_get_ip
= irc_user_list
;
1405 serv
->p_get_ip_uh
= irc_userhost
;
1406 serv
->p_set_back
= irc_set_back
;
1407 serv
->p_set_away
= irc_set_away
;
1408 serv
->p_message
= irc_message
;
1409 serv
->p_action
= irc_action
;
1410 serv
->p_notice
= irc_notice
;
1411 serv
->p_topic
= irc_topic
;
1412 serv
->p_list_channels
= irc_list_channels
;
1413 serv
->p_change_nick
= irc_change_nick
;
1414 serv
->p_names
= irc_names
;
1415 serv
->p_ping
= irc_ping
;
1416 serv
->p_raw
= irc_raw
;
1417 serv
->p_cmp
= rfc_casecmp
; /* can be changed by 005 in modes.c */