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] & serv
->loginmethod
== 7)
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
->loginmethod
)
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\r\n", arg1
, arg2
);
82 irc_ns_identify (server
*serv
, char *pass
)
84 if (serv
->loginmethod
== 5) /* QuakeNet needs to do everything in its own ways... */
86 irc_nickserv (serv
, "", serv
->nick
, pass
, "");
90 irc_nickserv (serv
, "IDENTIFY", pass
, "", "");
95 irc_ns_ghost (server
*serv
, char *usname
, char *pass
)
97 if (serv
->loginmethod
!= 5)
98 irc_nickserv (serv
, "GHOST", usname
, " ", pass
);
102 irc_join (server
*serv
, char *channel
, char *key
)
105 tcp_sendf (serv
, "JOIN %s %s\r\n", channel
, key
);
107 tcp_sendf (serv
, "JOIN %s\r\n", channel
);
111 irc_join_list_flush (server
*serv
, GString
*c
, GString
*k
)
113 char *chanstr
, *keystr
;
115 chanstr
= g_string_free (c
, FALSE
);
116 keystr
= g_string_free (k
, FALSE
);
120 tcp_sendf (serv
, "JOIN %s %s\r\n", chanstr
, keystr
);
122 tcp_sendf (serv
, "JOIN %s\r\n", chanstr
);
128 /* join a whole list of channels & keys, split to multiple lines
129 * to get around 512 limit */
132 irc_join_list (server
*serv
, GSList
*channels
, GSList
*keys
)
136 GString
*c
= g_string_new (NULL
);
137 GString
*k
= g_string_new (NULL
);
143 len
= 9; /* "JOIN<space><space>\r\n" */
149 /* measure how many bytes this channel would add... */
152 add
= strlen (clist
->data
);
159 add
+= strlen (klist
->data
);
163 add
++; /* 'x' filler */
169 /* too big? dump buffer and start a fresh one */
172 irc_join_list_flush (serv
, c
, k
);
174 c
= g_string_new (NULL
);
175 k
= g_string_new (NULL
);
180 /* now actually add it to our GStrings */
183 add
= strlen (clist
->data
);
187 g_string_append_c (c
, ',');
189 g_string_append (c
, clist
->data
);
195 add
+= strlen (klist
->data
);
199 g_string_append_c (k
, ',');
201 g_string_append (k
, klist
->data
);
210 g_string_append_c (k
, ',');
212 g_string_append_c (k
, 'x');
222 irc_join_list_flush (serv
, c
, k
);
226 irc_part (server
*serv
, char *channel
, char *reason
)
229 tcp_sendf (serv
, "PART %s :%s\r\n", channel
, reason
);
231 tcp_sendf (serv
, "PART %s\r\n", channel
);
235 irc_quit (server
*serv
, char *reason
)
238 tcp_sendf (serv
, "QUIT :%s\r\n", reason
);
240 tcp_send_len (serv
, "QUIT\r\n", 6);
244 irc_set_back (server
*serv
)
246 tcp_send_len (serv
, "AWAY\r\n", 6);
250 irc_set_away (server
*serv
, char *reason
)
262 tcp_sendf (serv
, "AWAY :%s\r\n", reason
);
266 irc_ctcp (server
*serv
, char *to
, char *msg
)
268 tcp_sendf (serv
, "PRIVMSG %s :\001%s\001\r\n", to
, msg
);
272 irc_nctcp (server
*serv
, char *to
, char *msg
)
274 tcp_sendf (serv
, "NOTICE %s :\001%s\001\r\n", to
, msg
);
278 irc_cycle (server
*serv
, char *channel
, char *key
)
280 tcp_sendf (serv
, "PART %s\r\nJOIN %s %s\r\n", channel
, channel
, key
);
284 irc_kick (server
*serv
, char *channel
, char *nick
, char *reason
)
287 tcp_sendf (serv
, "KICK %s %s :%s\r\n", channel
, nick
, reason
);
289 tcp_sendf (serv
, "KICK %s %s\r\n", channel
, nick
);
293 irc_invite (server
*serv
, char *channel
, char *nick
)
295 tcp_sendf (serv
, "INVITE %s %s\r\n", nick
, channel
);
299 irc_mode (server
*serv
, char *target
, char *mode
)
301 tcp_sendf (serv
, "MODE %s %s\r\n", target
, mode
);
304 /* find channel info when joined */
307 irc_join_info (server
*serv
, char *channel
)
309 tcp_sendf (serv
, "MODE %s\r\n", channel
);
312 /* initiate userlist retreival */
315 irc_user_list (server
*serv
, char *channel
)
318 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
320 tcp_sendf (serv
, "WHO %s\r\n", channel
);
326 irc_userhost (server
*serv
, char *nick
)
328 tcp_sendf (serv
, "USERHOST %s\r\n", nick
);
332 irc_away_status (server
*serv
, char *channel
)
335 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
337 tcp_sendf (serv
, "WHO %s\r\n", channel
);
341 irc_get_ip (server *serv, char *nick)
343 tcp_sendf (serv, "WHO %s\r\n", nick);
349 * Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
352 irc_user_whois (server
*serv
, char *nicks
)
354 tcp_sendf (serv
, "WHOIS %s\r\n", nicks
);
358 irc_message (server
*serv
, char *channel
, char *text
)
360 tcp_sendf (serv
, "PRIVMSG %s :%s\r\n", channel
, text
);
364 irc_action (server
*serv
, char *channel
, char *act
)
366 tcp_sendf (serv
, "PRIVMSG %s :\001ACTION %s\001\r\n", channel
, act
);
370 irc_notice (server
*serv
, char *channel
, char *text
)
372 tcp_sendf (serv
, "NOTICE %s :%s\r\n", channel
, text
);
376 irc_topic (server
*serv
, char *channel
, char *topic
)
379 tcp_sendf (serv
, "TOPIC %s :\r\n", channel
);
381 tcp_sendf (serv
, "TOPIC %s :%s\r\n", channel
, topic
);
383 tcp_sendf (serv
, "TOPIC %s\r\n", channel
);
387 irc_list_channels (server
*serv
, char *arg
, int min_users
)
391 tcp_sendf (serv
, "LIST %s\r\n", arg
);
395 if (serv
->use_listargs
)
396 tcp_sendf (serv
, "LIST >%d,<10000\r\n", min_users
- 1);
398 tcp_send_len (serv
, "LIST\r\n", 6);
402 irc_names (server
*serv
, char *channel
)
404 tcp_sendf (serv
, "NAMES %s\r\n", channel
);
408 irc_change_nick (server
*serv
, char *new_nick
)
410 tcp_sendf (serv
, "NICK %s\r\n", new_nick
);
414 irc_ping (server
*serv
, char *to
, char *timestring
)
417 tcp_sendf (serv
, "PRIVMSG %s :\001PING %s\001\r\n", to
, timestring
);
419 tcp_sendf (serv
, "PING %s\r\n", timestring
);
423 irc_raw (server
*serv
, char *raw
)
430 if (len
< sizeof (tbuf
) - 3)
432 len
= snprintf (tbuf
, sizeof (tbuf
), "%s\r\n", raw
);
433 tcp_send_len (serv
, tbuf
, len
);
436 tcp_send_len (serv
, raw
, len
);
437 tcp_send_len (serv
, "\r\n", 2);
444 /* ============================================================== */
445 /* ======================= IRC INPUT ============================ */
446 /* ============================================================== */
450 channel_date (session
*sess
, char *chan
, char *timestr
)
452 time_t timestamp
= (time_t) atol (timestr
);
453 char *tim
= ctime (×tamp
);
454 tim
[24] = 0; /* get rid of the \n */
455 EMIT_SIGNAL (XP_TE_CHANDATE
, sess
, chan
, tim
, NULL
, NULL
, 0);
459 process_numeric (session
* sess
, int n
,
460 char *word
[], char *word_eol
[], char *text
)
462 server
*serv
= sess
->server
;
463 /* show whois is the server tab */
464 session
*whois_sess
= serv
->server_session
;
466 /* unless this setting is on */
467 if (prefs
.irc_whois_front
)
468 whois_sess
= serv
->front_session
;
475 inbound_login_start (sess
, word
[3], word
[1]);
476 /* if network is PTnet then you must get your IP address
477 from "001" server message */
478 if ((strncmp(word
[7], "PTnet", 5) == 0) &&
479 (strncmp(word
[8], "IRC", 3) == 0) &&
480 (strncmp(word
[9], "Network", 7) == 0) &&
481 (strrchr(word
[10], '@') != NULL
))
483 serv
->use_who
= FALSE
;
484 if (prefs
.ip_from_server
)
485 inbound_foundip (sess
, strrchr(word
[10], '@')+1);
490 case 4: /* check the ircd type */
491 serv
->use_listargs
= FALSE
;
492 serv
->modes_per_line
= 3; /* default to IRC RFC */
493 if (strncmp (word
[5], "bahamut", 7) == 0) /* DALNet */
495 serv
->use_listargs
= TRUE
; /* use the /list args */
496 } else if (strncmp (word
[5], "u2.10.", 6) == 0) /* Undernet */
498 serv
->use_listargs
= TRUE
; /* use the /list args */
499 serv
->modes_per_line
= 6; /* allow 6 modes per line */
500 } else if (strncmp (word
[5], "glx2", 4) == 0)
502 serv
->use_listargs
= TRUE
; /* use the /list args */
507 inbound_005 (serv
, word
);
510 case 263: /*Server load is temporarily too heavy */
511 if (fe_is_chanwindow (sess
->server
))
513 fe_chan_list_end (sess
->server
);
514 fe_message (word_eol
[5] + 1, FE_MSG_ERROR
);
519 inbound_away (serv
, word
[4],
520 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
524 if (serv
->skip_next_userhost
)
526 char *eq
= strchr (word
[4], '=');
530 if (!serv
->p_cmp (word
[4] + 1, serv
->nick
))
532 char *at
= strrchr (eq
+ 1, '@');
534 inbound_foundip (sess
, at
+ 1);
538 serv
->skip_next_userhost
= FALSE
;
545 notify_markonline (serv
, word
);
549 inbound_uback (serv
);
553 inbound_uaway (serv
);
557 if (!serv
->skip_next_whois
)
558 EMIT_SIGNAL (XP_TE_WHOIS3
, whois_sess
, word
[4], word_eol
[5], NULL
, NULL
, 0);
560 inbound_user_info (sess
, NULL
, NULL
, NULL
, word
[5], word
[4], NULL
, NULL
, 0xff);
563 case 311: /* WHOIS 1st line */
564 serv
->inside_whois
= 1;
565 inbound_user_info_start (sess
, word
[4]);
566 if (!serv
->skip_next_whois
)
567 EMIT_SIGNAL (XP_TE_WHOIS1
, whois_sess
, word
[4], word
[5],
568 word
[6], word_eol
[8] + 1, 0);
570 inbound_user_info (sess
, NULL
, word
[5], word
[6], NULL
, word
[4],
571 word_eol
[8][0] == ':' ? word_eol
[8] + 1 : word_eol
[8], NULL
, 0xff);
574 case 314: /* WHOWAS */
575 inbound_user_info_start (sess
, word
[4]);
576 EMIT_SIGNAL (XP_TE_WHOIS1
, whois_sess
, word
[4], word
[5],
577 word
[6], word_eol
[8] + 1, 0);
581 if (!serv
->skip_next_whois
)
583 time_t timestamp
= (time_t) atol (word
[6]);
584 long idle
= atol (word
[5]);
588 snprintf (outbuf
, sizeof (outbuf
),
589 "%02ld:%02ld:%02ld", idle
/ 3600, (idle
/ 60) % 60,
592 EMIT_SIGNAL (XP_TE_WHOIS4
, whois_sess
, word
[4],
593 outbuf
, NULL
, NULL
, 0);
596 tim
= ctime (×tamp
);
597 tim
[19] = 0; /* get rid of the \n */
598 EMIT_SIGNAL (XP_TE_WHOIS4T
, whois_sess
, word
[4],
599 outbuf
, tim
, NULL
, 0);
604 case 318: /* END OF WHOIS */
605 if (!serv
->skip_next_whois
)
606 EMIT_SIGNAL (XP_TE_WHOIS6
, whois_sess
, word
[4], NULL
,
608 serv
->skip_next_whois
= 0;
609 serv
->inside_whois
= 0;
614 if (!serv
->skip_next_whois
)
615 EMIT_SIGNAL (XP_TE_WHOIS2
, whois_sess
, word
[4],
616 word_eol
[5] + 1, NULL
, NULL
, 0);
619 case 307: /* dalnet version */
620 case 320: /* :is an identified user */
621 if (!serv
->skip_next_whois
)
622 EMIT_SIGNAL (XP_TE_WHOIS_ID
, whois_sess
, word
[4],
623 word_eol
[5] + 1, NULL
, NULL
, 0);
627 if (!fe_is_chanwindow (sess
->server
))
628 EMIT_SIGNAL (XP_TE_CHANLISTHEAD
, serv
->server_session
, NULL
, NULL
, NULL
, NULL
, 0);
632 if (fe_is_chanwindow (sess
->server
))
634 fe_add_chan_list (sess
->server
, word
[4], word
[5], word_eol
[6] + 1);
637 PrintTextf (serv
->server_session
, "%-16s %-7d %s\017\n",
638 word
[4], atoi (word
[5]), word_eol
[6] + 1);
643 if (!fe_is_chanwindow (sess
->server
))
644 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1], word
[2], NULL
, 0);
646 fe_chan_list_end (sess
->server
);
650 sess
= find_channel (serv
, word
[4]);
652 sess
= serv
->server_session
;
653 if (sess
->ignore_mode
)
654 sess
->ignore_mode
= FALSE
;
656 EMIT_SIGNAL (XP_TE_CHANMODES
, sess
, word
[4], word_eol
[5],
658 fe_update_mode_buttons (sess
, 't', '-');
659 fe_update_mode_buttons (sess
, 'n', '-');
660 fe_update_mode_buttons (sess
, 's', '-');
661 fe_update_mode_buttons (sess
, 'i', '-');
662 fe_update_mode_buttons (sess
, 'p', '-');
663 fe_update_mode_buttons (sess
, 'm', '-');
664 fe_update_mode_buttons (sess
, 'l', '-');
665 fe_update_mode_buttons (sess
, 'k', '-');
666 handle_mode (serv
, word
, word_eol
, "", TRUE
);
670 sess
= find_channel (serv
, word
[4]);
673 if (sess
->ignore_date
)
674 sess
->ignore_date
= FALSE
;
676 channel_date (sess
, word
[4], word
[5]);
681 if (!serv
->skip_next_whois
)
682 EMIT_SIGNAL (XP_TE_WHOIS_AUTH
, whois_sess
, word
[4],
683 word_eol
[6] + 1, word
[5], NULL
, 0);
684 inbound_user_info (sess
, NULL
, NULL
, NULL
, NULL
, word
[4], NULL
, word
[5], 0xff);
688 inbound_topic (serv
, word
[4],
689 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
693 inbound_topictime (serv
, word
[4], word
[5], atol (word
[6]));
697 case 338: /* Undernet Real user@host, Real IP */
698 EMIT_SIGNAL (XP_TE_WHOIS_REALHOST
, sess
, word
[4], word
[5], word
[6],
699 (word_eol
[7][0]==':') ? word_eol
[7]+1 : word_eol
[7], 0);
703 case 341: /* INVITE ACK */
704 EMIT_SIGNAL (XP_TE_UINVITE
, sess
, word
[4], word
[5], serv
->servername
,
710 unsigned int away
= 0;
711 session
*who_sess
= find_channel (serv
, word
[4]);
716 inbound_user_info (sess
, word
[4], word
[5], word
[6], word
[7],
717 word
[8], word_eol
[11], NULL
, away
);
719 /* try to show only user initiated whos */
720 if (!who_sess
|| !who_sess
->doing_who
)
721 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
726 case 354: /* undernet WHOX: used as a reply for irc_away_status */
728 unsigned int away
= 0;
731 /* irc_away_status and irc_user_list sends out a "152" */
732 if (!strcmp (word
[4], "152"))
734 who_sess
= find_channel (serv
, word
[5]);
736 if (*word
[10] == 'G')
739 /* :server 354 yournick 152 #channel ~ident host servname nick H account :realname */
740 inbound_user_info (sess
, word
[5], word
[6], word
[7], word
[8],
741 word
[9], word_eol
[12]+1, word
[11], away
);
743 /* try to show only user initiated whos */
744 if (!who_sess
|| !who_sess
->doing_who
)
745 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
746 word
[1], word
[2], NULL
, 0);
752 case 315: /* END OF WHO */
755 who_sess
= find_channel (serv
, word
[4]);
758 if (!who_sess
->doing_who
)
759 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
760 word
[1], word
[2], NULL
, 0);
761 who_sess
->doing_who
= FALSE
;
764 if (!serv
->doing_dns
)
765 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
766 word
[1], word
[2], NULL
, 0);
767 serv
->doing_dns
= FALSE
;
772 case 348: /* +e-list entry */
773 if (!inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], TRUE
))
777 case 349: /* end of exemption list */
778 sess
= find_channel (serv
, word
[4]);
781 sess
= serv
->front_session
;
784 if (!fe_is_banwindow (sess
))
786 fe_ban_list_end (sess
, TRUE
);
789 case 353: /* NAMES */
790 inbound_nameslist (serv
, word
[5],
791 (word_eol
[6][0] == ':') ? word_eol
[6] + 1 : word_eol
[6]);
795 if (!inbound_nameslist_end (serv
, word
[4]))
799 case 367: /* banlist entry */
800 inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], FALSE
);
804 sess
= find_channel (serv
, word
[4]);
807 sess
= serv
->front_session
;
810 if (!fe_is_banwindow (sess
))
812 fe_ban_list_end (sess
, FALSE
);
815 case 369: /* WHOWAS end */
816 case 406: /* WHOWAS error */
817 EMIT_SIGNAL (XP_TE_SERVTEXT
, whois_sess
, text
, word
[1], word
[2], NULL
, 0);
818 serv
->inside_whois
= 0;
821 case 372: /* motd text */
822 case 375: /* motd start */
823 if (!prefs
.skipmotd
|| serv
->motd_skipped
)
824 EMIT_SIGNAL (XP_TE_MOTD
, serv
->server_session
, text
, NULL
, NULL
,
828 case 376: /* end of motd */
829 case 422: /* motd file is missing */
830 inbound_login_end (sess
, text
);
833 case 433: /* nickname in use */
834 case 432: /* erroneous nickname */
835 if (serv
->end_of_motd
)
837 inbound_next_nick (sess
, word
[4]);
841 if (serv
->end_of_motd
|| is_channel (serv
, word
[4]))
843 inbound_next_nick (sess
, word
[4]);
847 EMIT_SIGNAL (XP_TE_USERLIMIT
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
851 EMIT_SIGNAL (XP_TE_INVITE
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
855 EMIT_SIGNAL (XP_TE_BANNED
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
859 EMIT_SIGNAL (XP_TE_KEYWORD
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
863 notify_set_offline (serv
, word
[4], FALSE
);
867 notify_set_offline (serv
, word
[4], TRUE
);
872 notify_set_online (serv
, word
[4]);
875 case 730: /* RPL_MONONLINE */
876 ex
= strchr (word
[4], '!'); /* only send the nick */
879 notify_set_online (serv
, word
[4] + 1);
882 case 731: /* RPL_MONOFFLINE */
883 ex
= strchr (word
[4], '!'); /* only send the nick */
886 notify_set_offline (serv
, word
[4] + 1, FALSE
);
889 case 900: /* successful SASL 'logged in as ' */
890 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, word_eol
[6]+1, word
[1], word
[2], NULL
, 0);
892 case 903: /* successful SASL auth */
893 case 904: /* aborted SASL auth */
894 case 905: /* failed SASL auth */
895 case 906: /* registration completes before SASL auth */
896 case 907: /* attempting to re-auth after a successful auth */
897 EMIT_SIGNAL (XP_TE_SASLRESPONSE
, serv
->server_session
, word
[1], word
[2], word
[3], ++word_eol
[4], 0);
898 tcp_send_len (serv
, "CAP END\r\n", 9);
903 if (serv
->inside_whois
&& word
[4][0])
905 /* some unknown WHOIS reply, ircd coders make them up weekly */
906 if (!serv
->skip_next_whois
)
907 EMIT_SIGNAL (XP_TE_WHOIS_SPECIAL
, whois_sess
, word
[4],
908 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5],
914 if (is_channel (serv
, word
[4]))
916 session
*realsess
= find_channel (serv
, word
[4]);
918 realsess
= serv
->server_session
;
919 EMIT_SIGNAL (XP_TE_SERVTEXT
, realsess
, text
, word
[1], word
[2], NULL
, 0);
922 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
928 /* handle named messages that starts with a ':' */
931 process_named_msg (session
*sess
, char *type
, char *word
[], char *word_eol
[])
933 server
*serv
= sess
->server
;
934 char ip
[128], nick
[NICKLEN
];
936 int len
= strlen (type
);
938 /* fill in the "ip" and "nick" buffers */
939 ex
= strchr (word
[1], '!');
940 if (!ex
) /* no '!', must be a server message */
942 safe_strcpy (ip
, word
[1], sizeof (ip
));
943 safe_strcpy (nick
, word
[1], sizeof (nick
));
946 safe_strcpy (ip
, ex
+ 1, sizeof (ip
));
948 safe_strcpy (nick
, word
[1], sizeof (nick
));
956 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
957 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
961 case WORDL('A','C','C','O'):
962 inbound_account (serv
, nick
, word
[3]);
965 case WORDL('J','O','I','N'):
967 char *chan
= word
[3];
968 char *account
= word
[4];
969 char *realname
= word_eol
[5] + 1;
973 if (!serv
->p_cmp (nick
, serv
->nick
))
974 inbound_ujoin (serv
, chan
, nick
, ip
);
976 inbound_join (serv
, chan
, nick
, ip
, account
, realname
);
980 case WORDL('K','I','C','K'):
982 char *kicked
= word
[4];
983 char *reason
= word_eol
[5];
988 if (!strcmp (kicked
, serv
->nick
))
989 inbound_ukick (serv
, word
[3], nick
, reason
);
991 inbound_kick (serv
, word
[3], kicked
, nick
, reason
);
996 case WORDL('K','I','L','L'):
997 EMIT_SIGNAL (XP_TE_KILL
, sess
, nick
, word_eol
[5], NULL
, NULL
, 0);
1000 case WORDL('M','O','D','E'):
1001 handle_mode (serv
, word
, word_eol
, nick
, FALSE
); /* modes.c */
1004 case WORDL('N','I','C','K'):
1005 inbound_newnick (serv
, nick
, (word_eol
[3][0] == ':')
1006 ? word_eol
[3] + 1 : word_eol
[3], FALSE
);
1009 case WORDL('P','A','R','T'):
1011 char *chan
= word
[3];
1012 char *reason
= word_eol
[4];
1018 if (!strcmp (nick
, serv
->nick
))
1019 inbound_upart (serv
, chan
, ip
, reason
);
1021 inbound_part (serv
, chan
, nick
, ip
, reason
);
1025 case WORDL('P','O','N','G'):
1026 inbound_ping_reply (serv
->server_session
,
1027 (word
[4][0] == ':') ? word
[4] + 1 : word
[4], word
[3]);
1030 case WORDL('Q','U','I','T'):
1031 inbound_quit (serv
, nick
, ip
,
1032 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : word_eol
[3]);
1035 case WORDL('A','W','A','Y'):
1036 inbound_away_notify (serv
, nick
,
1037 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : NULL
);
1046 guint32 want_cap
; /* format the CAP REQ string based on previous capabilities being requested or not */
1047 guint32 want_sasl
; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */
1048 char *pass
; /* buffer for SASL password */
1049 char buffer
[256]; /* buffer for requesting capabilities and emitting the signal */
1051 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
1054 case WORDL('C','A','P','\0'):
1055 if (strncasecmp (word
[4], "ACK", 3) == 0)
1057 EMIT_SIGNAL (XP_TE_CAPACK
, sess
->server
->server_session
, word
[1], word
[5][0]==':' ? ++word_eol
[5] : word_eol
[5], NULL
, NULL
, 0);
1059 if (strstr (word_eol
[5], "identify-msg") != 0)
1061 serv
->have_idmsg
= TRUE
;
1064 if (strstr (word_eol
[5], "multi-prefix") != 0)
1066 serv
->have_namesx
= TRUE
;
1069 if (strstr (word_eol
[5], "away-notify") != 0)
1071 serv
->have_awaynotify
= TRUE
;
1074 if (strstr (word_eol
[5], "account-notify") != 0)
1076 serv
->have_accnotify
= TRUE
;
1079 if (strstr (word_eol
[5], "extended-join") != 0)
1081 serv
->have_extjoin
= TRUE
;
1084 if (strstr (word_eol
[5], "sasl") != 0)
1086 serv
->have_sasl
= TRUE
;
1087 EMIT_SIGNAL (XP_TE_SASLAUTH
, serv
->server_session
, sess
->server
->sasluser
, NULL
, NULL
, NULL
, 0);
1088 tcp_send_len (serv
, "AUTHENTICATE PLAIN\r\n", 20);
1090 pass
= encode_sasl_pass (sess
->server
->sasluser
, sess
->server
->password
);
1091 tcp_sendf (sess
->server
, "AUTHENTICATE %s\r\n", pass
);
1095 else if (strncasecmp (word
[4], "LS", 2) == 0)
1097 EMIT_SIGNAL (XP_TE_CAPLIST
, serv
->server_session
, word
[1], word
[5][0]==':' ? ++word_eol
[5] : word_eol
[5], NULL
, NULL
, 0);
1101 strcpy (buffer
, "CAP REQ :");
1103 if (strstr (word_eol
[5], "identify-msg") != 0)
1105 strcat (buffer
, "identify-msg ");
1108 if (strstr (word_eol
[5], "multi-prefix") != 0)
1110 strcat (buffer
, "multi-prefix ");
1113 if (strstr (word_eol
[5], "away-notify") != 0)
1115 strcat (buffer
, "away-notify ");
1118 if (strstr (word_eol
[5], "account-notify") != 0)
1120 strcat (buffer
, "account-notify ");
1123 if (strstr (word_eol
[5], "extended-join") != 0)
1125 strcat (buffer
, "extended-join ");
1128 /* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
1129 if (strstr (word_eol
[5], "sasl") != 0 && strlen (sess
->server
->password
) != 0 && serv
->loginmethod
== 6)
1131 strcat (buffer
, "sasl ");
1138 /* buffer + 9 = emit buffer without "CAP REQ :" */
1139 EMIT_SIGNAL (XP_TE_CAPREQ
, sess
->server
->server_session
, buffer
+ 9, NULL
, NULL
, NULL
, 0);
1140 tcp_sendf (serv
, "%s\r\n", buffer
);
1144 /* if we use SASL, CAP END is dealt via raw numerics */
1145 tcp_send_len (serv
, "CAP END\r\n", 9);
1148 else if (strncasecmp (word
[4], "NAK", 3) == 0)
1150 tcp_send_len (serv
, "CAP END\r\n", 9);
1162 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
1163 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
1166 case WORDL('I','N','V','I'):
1167 if (ignore_check (word
[1], IG_INVI
))
1170 if (word
[4][0] == ':')
1171 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4] + 1, nick
,
1172 serv
->servername
, NULL
, 0);
1174 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4], nick
,
1175 serv
->servername
, NULL
, 0);
1179 case WORDL('N','O','T','I'):
1181 int id
= FALSE
; /* identified */
1187 if (serv
->have_idmsg
)
1193 } else if (*text
== '-')
1197 if (!ignore_check (word
[1], IG_NOTI
))
1198 inbound_notice (serv
, word
[3], nick
, text
, ip
, id
);
1202 case WORDL('P','R','I','V'):
1206 int id
= FALSE
; /* identified */
1212 if (serv
->have_idmsg
)
1218 } else if (*text
== '-')
1221 len
= strlen (text
);
1222 if (text
[0] == 1 && text
[len
- 1] == 1) /* ctcp */
1226 if (strncasecmp (text
, "ACTION", 6) != 0)
1227 flood_check (nick
, ip
, serv
, sess
, 0);
1228 if (strncasecmp (text
, "DCC ", 4) == 0)
1229 /* redo this with handle_quotes TRUE */
1230 process_data_init (word
[1], word_eol
[1], word
, word_eol
, TRUE
, FALSE
);
1231 ctcp_handle (sess
, to
, nick
, ip
, text
, word
, word_eol
, id
);
1234 if (is_channel (serv
, to
))
1236 if (ignore_check (word
[1], IG_CHAN
))
1238 inbound_chanmsg (serv
, NULL
, to
, nick
, text
, FALSE
, id
);
1241 if (ignore_check (word
[1], IG_PRIV
))
1243 inbound_privmsg (serv
, nick
, ip
, text
, id
);
1250 case WORDL('T','O','P','I'):
1251 inbound_topicnew (serv
, nick
, word
[3],
1252 (word_eol
[4][0] == ':') ? word_eol
[4] + 1 : word_eol
[4]);
1255 case WORDL('W','A','L','L'):
1259 EMIT_SIGNAL (XP_TE_WALLOPS
, sess
, nick
, text
, NULL
, NULL
, 0);
1265 /* unknown message */
1266 PrintTextf (sess
, "GARBAGE: %s\n", word_eol
[1]);
1269 /* handle named messages that DON'T start with a ':' */
1272 process_named_servermsg (session
*sess
, char *buf
, char *rawname
, char *word_eol
[])
1274 sess
= sess
->server
->server_session
;
1276 if (!strncmp (buf
, "PING ", 5))
1278 tcp_sendf (sess
->server
, "PONG %s\r\n", buf
+ 5);
1281 if (!strncmp (buf
, "ERROR", 5))
1283 EMIT_SIGNAL (XP_TE_SERVERERROR
, sess
, buf
+ 7, NULL
, NULL
, NULL
, 0);
1286 if (!strncmp (buf
, "NOTICE ", 7))
1291 EMIT_SIGNAL (XP_TE_SERVNOTICE
, sess
, buf
, sess
->server
->servername
, NULL
, NULL
, 0);
1294 if (!strncmp (buf
, "AUTHENTICATE +", 14)) /* omit SASL "empty" responses */
1299 EMIT_SIGNAL (XP_TE_SERVTEXT
, sess
, buf
, sess
->server
->servername
, rawname
, NULL
, 0);
1302 /* irc_inline() - 1 single line received from serv */
1305 irc_inline (server
*serv
, char *buf
, int len
)
1307 session
*sess
, *tmp
;
1309 char *word
[PDIWORDS
+1];
1310 char *word_eol
[PDIWORDS
+1];
1311 char pdibuf_static
[522]; /* 1 line can potentially be 512*6 in utf8 */
1312 char *pdibuf
= pdibuf_static
;
1314 url_check_line (buf
, len
);
1316 /* need more than 522? fall back to malloc */
1317 if (len
>= sizeof (pdibuf_static
))
1318 pdibuf
= malloc (len
+ 1);
1320 sess
= serv
->front_session
;
1322 /* Python relies on this */
1323 word
[PDIWORDS
] = NULL
;
1324 word_eol
[PDIWORDS
] = NULL
;
1328 /* split line into words and words_to_end_of_line */
1329 process_data_init (pdibuf
, buf
, word
, word_eol
, FALSE
, FALSE
);
1331 /* find a context for this message */
1332 if (is_channel (serv
, word
[3]))
1334 tmp
= find_channel (serv
, word
[3]);
1339 /* for server messages, the 2nd word is the "message type" */
1343 word_eol
[1] = buf
; /* keep the ":" for plugins */
1344 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1347 word_eol
[1] = buf
+ 1; /* but not for xchat internally */
1351 process_data_init (pdibuf
, buf
, word
, word_eol
, FALSE
, FALSE
);
1352 word
[0] = type
= word
[1];
1353 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1359 process_named_servermsg (sess
, buf
, word
[0], word_eol
);
1363 /* see if the second word is a numeric */
1364 if (isdigit ((unsigned char) word
[2][0]))
1370 process_numeric (sess
, atoi (word
[2]), word
, word_eol
, text
);
1373 process_named_msg (sess
, type
, word
, word_eol
);
1377 if (pdibuf
!= pdibuf_static
)
1382 proto_fill_her_up (server
*serv
)
1384 serv
->p_inline
= irc_inline
;
1385 serv
->p_invite
= irc_invite
;
1386 serv
->p_cycle
= irc_cycle
;
1387 serv
->p_ctcp
= irc_ctcp
;
1388 serv
->p_nctcp
= irc_nctcp
;
1389 serv
->p_quit
= irc_quit
;
1390 serv
->p_kick
= irc_kick
;
1391 serv
->p_part
= irc_part
;
1392 serv
->p_ns_identify
= irc_ns_identify
;
1393 serv
->p_ns_ghost
= irc_ns_ghost
;
1394 serv
->p_join
= irc_join
;
1395 serv
->p_join_list
= irc_join_list
;
1396 serv
->p_login
= irc_login
;
1397 serv
->p_join_info
= irc_join_info
;
1398 serv
->p_mode
= irc_mode
;
1399 serv
->p_user_list
= irc_user_list
;
1400 serv
->p_away_status
= irc_away_status
;
1401 /*serv->p_get_ip = irc_get_ip;*/
1402 serv
->p_whois
= irc_user_whois
;
1403 serv
->p_get_ip
= irc_user_list
;
1404 serv
->p_get_ip_uh
= irc_userhost
;
1405 serv
->p_set_back
= irc_set_back
;
1406 serv
->p_set_away
= irc_set_away
;
1407 serv
->p_message
= irc_message
;
1408 serv
->p_action
= irc_action
;
1409 serv
->p_notice
= irc_notice
;
1410 serv
->p_topic
= irc_topic
;
1411 serv
->p_list_channels
= irc_list_channels
;
1412 serv
->p_change_nick
= irc_change_nick
;
1413 serv
->p_names
= irc_names
;
1414 serv
->p_ping
= irc_ping
;
1415 serv
->p_raw
= irc_raw
;
1416 serv
->p_cmp
= rfc_casecmp
; /* can be changed by 005 in modes.c */