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 */
47 serv
->sent_capend
= FALSE
; /* track if we have finished */
49 if (serv
->password
[0] && serv
->loginmethod
== LOGIN_PASS
)
51 tcp_sendf (serv
, "PASS %s\r\n", serv
->password
);
56 "USER %s %s %s :%s\r\n",
57 serv
->nick
, user
, user
, serv
->servername
, realname
);
61 irc_nickserv (server
*serv
, char *cmd
, char *arg1
, char *arg2
, char *arg3
)
63 /* are all ircd authors idiots? */
64 switch (serv
->loginmethod
)
66 case LOGIN_MSG_NICKSERV
:
67 tcp_sendf (serv
, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
70 tcp_sendf (serv
, "NICKSERV %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
72 default: /* This may not work but at least it tries something when using /id or /ghost cmd */
73 tcp_sendf (serv
, "NICKSERV %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
77 tcp_sendf (serv
, "PRIVMSG NS :%s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
80 tcp_sendf (serv
, "NS %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
83 /* why couldn't QuakeNet implement one of the existing ones? */
84 tcp_sendf (serv
, "AUTH %s %s\r\n", arg1
, arg2
);
91 irc_ns_identify (server
*serv
, char *pass
)
93 switch (serv
->loginmethod
)
95 case LOGIN_CHALLENGEAUTH
:
96 tcp_sendf (serv
, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK
); /* request a challenge from Q */
100 irc_nickserv (serv
, "", serv
->nick
, pass
, "");
104 irc_nickserv (serv
, "IDENTIFY", pass
, "", "");
109 irc_ns_ghost (server
*serv
, char *usname
, char *pass
)
111 if (serv
->loginmethod
!= LOGIN_CHALLENGEAUTH
)
113 irc_nickserv (serv
, "GHOST", usname
, " ", pass
);
118 irc_join (server
*serv
, char *channel
, char *key
)
121 tcp_sendf (serv
, "JOIN %s %s\r\n", channel
, key
);
123 tcp_sendf (serv
, "JOIN %s\r\n", channel
);
127 irc_join_list_flush (server
*serv
, GString
*c
, GString
*k
)
129 char *chanstr
, *keystr
;
131 chanstr
= g_string_free (c
, FALSE
);
132 keystr
= g_string_free (k
, FALSE
);
136 tcp_sendf (serv
, "JOIN %s %s\r\n", chanstr
, keystr
);
138 tcp_sendf (serv
, "JOIN %s\r\n", chanstr
);
144 /* join a whole list of channels & keys, split to multiple lines
145 * to get around 512 limit */
148 irc_join_list (server
*serv
, GSList
*channels
, GSList
*keys
)
152 GString
*c
= g_string_new (NULL
);
153 GString
*k
= g_string_new (NULL
);
159 len
= 9; /* "JOIN<space><space>\r\n" */
165 /* measure how many bytes this channel would add... */
168 add
= strlen (clist
->data
);
175 add
+= strlen (klist
->data
);
179 add
++; /* 'x' filler */
185 /* too big? dump buffer and start a fresh one */
188 irc_join_list_flush (serv
, c
, k
);
190 c
= g_string_new (NULL
);
191 k
= g_string_new (NULL
);
196 /* now actually add it to our GStrings */
199 add
= strlen (clist
->data
);
203 g_string_append_c (c
, ',');
205 g_string_append (c
, clist
->data
);
211 add
+= strlen (klist
->data
);
215 g_string_append_c (k
, ',');
217 g_string_append (k
, klist
->data
);
226 g_string_append_c (k
, ',');
228 g_string_append_c (k
, 'x');
238 irc_join_list_flush (serv
, c
, k
);
242 irc_part (server
*serv
, char *channel
, char *reason
)
245 tcp_sendf (serv
, "PART %s :%s\r\n", channel
, reason
);
247 tcp_sendf (serv
, "PART %s\r\n", channel
);
251 irc_quit (server
*serv
, char *reason
)
254 tcp_sendf (serv
, "QUIT :%s\r\n", reason
);
256 tcp_send_len (serv
, "QUIT\r\n", 6);
260 irc_set_back (server
*serv
)
262 tcp_send_len (serv
, "AWAY\r\n", 6);
266 irc_set_away (server
*serv
, char *reason
)
278 tcp_sendf (serv
, "AWAY :%s\r\n", reason
);
282 irc_ctcp (server
*serv
, char *to
, char *msg
)
284 tcp_sendf (serv
, "PRIVMSG %s :\001%s\001\r\n", to
, msg
);
288 irc_nctcp (server
*serv
, char *to
, char *msg
)
290 tcp_sendf (serv
, "NOTICE %s :\001%s\001\r\n", to
, msg
);
294 irc_cycle (server
*serv
, char *channel
, char *key
)
296 tcp_sendf (serv
, "PART %s\r\nJOIN %s %s\r\n", channel
, channel
, key
);
300 irc_kick (server
*serv
, char *channel
, char *nick
, char *reason
)
303 tcp_sendf (serv
, "KICK %s %s :%s\r\n", channel
, nick
, reason
);
305 tcp_sendf (serv
, "KICK %s %s\r\n", channel
, nick
);
309 irc_invite (server
*serv
, char *channel
, char *nick
)
311 tcp_sendf (serv
, "INVITE %s %s\r\n", nick
, channel
);
315 irc_mode (server
*serv
, char *target
, char *mode
)
317 tcp_sendf (serv
, "MODE %s %s\r\n", target
, mode
);
320 /* find channel info when joined */
323 irc_join_info (server
*serv
, char *channel
)
325 tcp_sendf (serv
, "MODE %s\r\n", channel
);
328 /* initiate userlist retreival */
331 irc_user_list (server
*serv
, char *channel
)
334 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
336 tcp_sendf (serv
, "WHO %s\r\n", channel
);
342 irc_userhost (server
*serv
, char *nick
)
344 tcp_sendf (serv
, "USERHOST %s\r\n", nick
);
348 irc_away_status (server
*serv
, char *channel
)
351 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
353 tcp_sendf (serv
, "WHO %s\r\n", channel
);
357 irc_get_ip (server *serv, char *nick)
359 tcp_sendf (serv, "WHO %s\r\n", nick);
365 * Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
368 irc_user_whois (server
*serv
, char *nicks
)
370 tcp_sendf (serv
, "WHOIS %s\r\n", nicks
);
374 irc_message (server
*serv
, char *channel
, char *text
)
376 tcp_sendf (serv
, "PRIVMSG %s :%s\r\n", channel
, text
);
380 irc_action (server
*serv
, char *channel
, char *act
)
382 tcp_sendf (serv
, "PRIVMSG %s :\001ACTION %s\001\r\n", channel
, act
);
386 irc_notice (server
*serv
, char *channel
, char *text
)
388 tcp_sendf (serv
, "NOTICE %s :%s\r\n", channel
, text
);
392 irc_topic (server
*serv
, char *channel
, char *topic
)
395 tcp_sendf (serv
, "TOPIC %s :\r\n", channel
);
397 tcp_sendf (serv
, "TOPIC %s :%s\r\n", channel
, topic
);
399 tcp_sendf (serv
, "TOPIC %s\r\n", channel
);
403 irc_list_channels (server
*serv
, char *arg
, int min_users
)
407 tcp_sendf (serv
, "LIST %s\r\n", arg
);
411 if (serv
->use_listargs
)
412 tcp_sendf (serv
, "LIST >%d,<10000\r\n", min_users
- 1);
414 tcp_send_len (serv
, "LIST\r\n", 6);
418 irc_names (server
*serv
, char *channel
)
420 tcp_sendf (serv
, "NAMES %s\r\n", channel
);
424 irc_change_nick (server
*serv
, char *new_nick
)
426 tcp_sendf (serv
, "NICK %s\r\n", new_nick
);
430 irc_ping (server
*serv
, char *to
, char *timestring
)
433 tcp_sendf (serv
, "PRIVMSG %s :\001PING %s\001\r\n", to
, timestring
);
435 tcp_sendf (serv
, "PING %s\r\n", timestring
);
439 irc_raw (server
*serv
, char *raw
)
446 if (len
< sizeof (tbuf
) - 3)
448 len
= snprintf (tbuf
, sizeof (tbuf
), "%s\r\n", raw
);
449 tcp_send_len (serv
, tbuf
, len
);
452 tcp_send_len (serv
, raw
, len
);
453 tcp_send_len (serv
, "\r\n", 2);
460 /* ============================================================== */
461 /* ======================= IRC INPUT ============================ */
462 /* ============================================================== */
466 channel_date (session
*sess
, char *chan
, char *timestr
)
468 time_t timestamp
= (time_t) atol (timestr
);
469 char *tim
= ctime (×tamp
);
470 tim
[24] = 0; /* get rid of the \n */
471 EMIT_SIGNAL (XP_TE_CHANDATE
, sess
, chan
, tim
, NULL
, NULL
, 0);
475 process_numeric (session
* sess
, int n
,
476 char *word
[], char *word_eol
[], char *text
)
478 server
*serv
= sess
->server
;
479 /* show whois is the server tab */
480 session
*whois_sess
= serv
->server_session
;
482 /* unless this setting is on */
483 if (prefs
.irc_whois_front
)
484 whois_sess
= serv
->front_session
;
491 inbound_login_start (sess
, word
[3], word
[1]);
492 /* if network is PTnet then you must get your IP address
493 from "001" server message */
494 if ((strncmp(word
[7], "PTnet", 5) == 0) &&
495 (strncmp(word
[8], "IRC", 3) == 0) &&
496 (strncmp(word
[9], "Network", 7) == 0) &&
497 (strrchr(word
[10], '@') != NULL
))
499 serv
->use_who
= FALSE
;
500 if (prefs
.ip_from_server
)
501 inbound_foundip (sess
, strrchr(word
[10], '@')+1);
506 case 4: /* check the ircd type */
507 serv
->use_listargs
= FALSE
;
508 serv
->modes_per_line
= 3; /* default to IRC RFC */
509 if (strncmp (word
[5], "bahamut", 7) == 0) /* DALNet */
511 serv
->use_listargs
= TRUE
; /* use the /list args */
512 } else if (strncmp (word
[5], "u2.10.", 6) == 0) /* Undernet */
514 serv
->use_listargs
= TRUE
; /* use the /list args */
515 serv
->modes_per_line
= 6; /* allow 6 modes per line */
516 } else if (strncmp (word
[5], "glx2", 4) == 0)
518 serv
->use_listargs
= TRUE
; /* use the /list args */
523 inbound_005 (serv
, word
);
526 case 263: /*Server load is temporarily too heavy */
527 if (fe_is_chanwindow (sess
->server
))
529 fe_chan_list_end (sess
->server
);
530 fe_message (word_eol
[5] + 1, FE_MSG_ERROR
);
535 inbound_away (serv
, word
[4],
536 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
540 if (serv
->skip_next_userhost
)
542 char *eq
= strchr (word
[4], '=');
546 if (!serv
->p_cmp (word
[4] + 1, serv
->nick
))
548 char *at
= strrchr (eq
+ 1, '@');
550 inbound_foundip (sess
, at
+ 1);
554 serv
->skip_next_userhost
= FALSE
;
561 notify_markonline (serv
, word
);
565 inbound_uback (serv
);
569 inbound_uaway (serv
);
573 if (!serv
->skip_next_whois
)
574 EMIT_SIGNAL (XP_TE_WHOIS3
, whois_sess
, word
[4], word_eol
[5], NULL
, NULL
, 0);
576 inbound_user_info (sess
, NULL
, NULL
, NULL
, word
[5], word
[4], NULL
, NULL
, 0xff);
579 case 311: /* WHOIS 1st line */
580 serv
->inside_whois
= 1;
581 inbound_user_info_start (sess
, word
[4]);
582 if (!serv
->skip_next_whois
)
583 EMIT_SIGNAL (XP_TE_WHOIS1
, whois_sess
, word
[4], word
[5],
584 word
[6], word_eol
[8] + 1, 0);
586 inbound_user_info (sess
, NULL
, word
[5], word
[6], NULL
, word
[4],
587 word_eol
[8][0] == ':' ? word_eol
[8] + 1 : word_eol
[8], NULL
, 0xff);
590 case 314: /* WHOWAS */
591 inbound_user_info_start (sess
, word
[4]);
592 EMIT_SIGNAL (XP_TE_WHOIS1
, whois_sess
, word
[4], word
[5],
593 word
[6], word_eol
[8] + 1, 0);
597 if (!serv
->skip_next_whois
)
599 time_t timestamp
= (time_t) atol (word
[6]);
600 long idle
= atol (word
[5]);
604 snprintf (outbuf
, sizeof (outbuf
),
605 "%02ld:%02ld:%02ld", idle
/ 3600, (idle
/ 60) % 60,
608 EMIT_SIGNAL (XP_TE_WHOIS4
, whois_sess
, word
[4],
609 outbuf
, NULL
, NULL
, 0);
612 tim
= ctime (×tamp
);
613 tim
[19] = 0; /* get rid of the \n */
614 EMIT_SIGNAL (XP_TE_WHOIS4T
, whois_sess
, word
[4],
615 outbuf
, tim
, NULL
, 0);
620 case 318: /* END OF WHOIS */
621 if (!serv
->skip_next_whois
)
622 EMIT_SIGNAL (XP_TE_WHOIS6
, whois_sess
, word
[4], NULL
,
624 serv
->skip_next_whois
= 0;
625 serv
->inside_whois
= 0;
630 if (!serv
->skip_next_whois
)
631 EMIT_SIGNAL (XP_TE_WHOIS2
, whois_sess
, word
[4],
632 word_eol
[5] + 1, NULL
, NULL
, 0);
635 case 307: /* dalnet version */
636 case 320: /* :is an identified user */
637 if (!serv
->skip_next_whois
)
638 EMIT_SIGNAL (XP_TE_WHOIS_ID
, whois_sess
, word
[4],
639 word_eol
[5] + 1, NULL
, NULL
, 0);
643 if (!fe_is_chanwindow (sess
->server
))
644 EMIT_SIGNAL (XP_TE_CHANLISTHEAD
, serv
->server_session
, NULL
, NULL
, NULL
, NULL
, 0);
648 if (fe_is_chanwindow (sess
->server
))
650 fe_add_chan_list (sess
->server
, word
[4], word
[5], word_eol
[6] + 1);
653 PrintTextf (serv
->server_session
, "%-16s %-7d %s\017\n",
654 word
[4], atoi (word
[5]), word_eol
[6] + 1);
659 if (!fe_is_chanwindow (sess
->server
))
660 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1], word
[2], NULL
, 0);
662 fe_chan_list_end (sess
->server
);
666 sess
= find_channel (serv
, word
[4]);
668 sess
= serv
->server_session
;
669 if (sess
->ignore_mode
)
670 sess
->ignore_mode
= FALSE
;
672 EMIT_SIGNAL (XP_TE_CHANMODES
, sess
, word
[4], word_eol
[5],
674 fe_update_mode_buttons (sess
, 't', '-');
675 fe_update_mode_buttons (sess
, 'n', '-');
676 fe_update_mode_buttons (sess
, 's', '-');
677 fe_update_mode_buttons (sess
, 'i', '-');
678 fe_update_mode_buttons (sess
, 'p', '-');
679 fe_update_mode_buttons (sess
, 'm', '-');
680 fe_update_mode_buttons (sess
, 'l', '-');
681 fe_update_mode_buttons (sess
, 'k', '-');
682 handle_mode (serv
, word
, word_eol
, "", TRUE
);
686 sess
= find_channel (serv
, word
[4]);
689 if (sess
->ignore_date
)
690 sess
->ignore_date
= FALSE
;
692 channel_date (sess
, word
[4], word
[5]);
697 if (!serv
->skip_next_whois
)
698 EMIT_SIGNAL (XP_TE_WHOIS_AUTH
, whois_sess
, word
[4],
699 word_eol
[6] + 1, word
[5], NULL
, 0);
700 inbound_user_info (sess
, NULL
, NULL
, NULL
, NULL
, word
[4], NULL
, word
[5], 0xff);
704 inbound_topic (serv
, word
[4],
705 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
709 inbound_topictime (serv
, word
[4], word
[5], atol (word
[6]));
713 case 338: /* Undernet Real user@host, Real IP */
714 EMIT_SIGNAL (XP_TE_WHOIS_REALHOST
, sess
, word
[4], word
[5], word
[6],
715 (word_eol
[7][0]==':') ? word_eol
[7]+1 : word_eol
[7], 0);
719 case 341: /* INVITE ACK */
720 EMIT_SIGNAL (XP_TE_UINVITE
, sess
, word
[4], word
[5], serv
->servername
,
726 unsigned int away
= 0;
727 session
*who_sess
= find_channel (serv
, word
[4]);
732 inbound_user_info (sess
, word
[4], word
[5], word
[6], word
[7],
733 word
[8], word_eol
[11], NULL
, away
);
735 /* try to show only user initiated whos */
736 if (!who_sess
|| !who_sess
->doing_who
)
737 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
742 case 354: /* undernet WHOX: used as a reply for irc_away_status */
744 unsigned int away
= 0;
747 /* irc_away_status and irc_user_list sends out a "152" */
748 if (!strcmp (word
[4], "152"))
750 who_sess
= find_channel (serv
, word
[5]);
752 if (*word
[10] == 'G')
755 /* :server 354 yournick 152 #channel ~ident host servname nick H account :realname */
756 inbound_user_info (sess
, word
[5], word
[6], word
[7], word
[8],
757 word
[9], word_eol
[12]+1, word
[11], away
);
759 /* try to show only user initiated whos */
760 if (!who_sess
|| !who_sess
->doing_who
)
761 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
762 word
[1], word
[2], NULL
, 0);
768 case 315: /* END OF WHO */
771 who_sess
= find_channel (serv
, word
[4]);
774 if (!who_sess
->doing_who
)
775 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
776 word
[1], word
[2], NULL
, 0);
777 who_sess
->doing_who
= FALSE
;
780 if (!serv
->doing_dns
)
781 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
,
782 word
[1], word
[2], NULL
, 0);
783 serv
->doing_dns
= FALSE
;
788 case 348: /* +e-list entry */
789 if (!inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], TRUE
))
793 case 349: /* end of exemption list */
794 sess
= find_channel (serv
, word
[4]);
797 sess
= serv
->front_session
;
800 if (!fe_is_banwindow (sess
))
802 fe_ban_list_end (sess
, TRUE
);
805 case 353: /* NAMES */
806 inbound_nameslist (serv
, word
[5],
807 (word_eol
[6][0] == ':') ? word_eol
[6] + 1 : word_eol
[6]);
811 if (!inbound_nameslist_end (serv
, word
[4]))
815 case 367: /* banlist entry */
816 inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], FALSE
);
820 sess
= find_channel (serv
, word
[4]);
823 sess
= serv
->front_session
;
826 if (!fe_is_banwindow (sess
))
828 fe_ban_list_end (sess
, FALSE
);
831 case 369: /* WHOWAS end */
832 case 406: /* WHOWAS error */
833 EMIT_SIGNAL (XP_TE_SERVTEXT
, whois_sess
, text
, word
[1], word
[2], NULL
, 0);
834 serv
->inside_whois
= 0;
837 case 372: /* motd text */
838 case 375: /* motd start */
839 if (!prefs
.skipmotd
|| serv
->motd_skipped
)
840 EMIT_SIGNAL (XP_TE_MOTD
, serv
->server_session
, text
, NULL
, NULL
,
844 case 376: /* end of motd */
845 case 422: /* motd file is missing */
846 inbound_login_end (sess
, text
);
849 case 433: /* nickname in use */
850 case 432: /* erroneous nickname */
851 if (serv
->end_of_motd
)
853 inbound_next_nick (sess
, word
[4]);
857 if (serv
->end_of_motd
|| is_channel (serv
, word
[4]))
859 inbound_next_nick (sess
, word
[4]);
863 EMIT_SIGNAL (XP_TE_USERLIMIT
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
867 EMIT_SIGNAL (XP_TE_INVITE
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
871 EMIT_SIGNAL (XP_TE_BANNED
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
875 EMIT_SIGNAL (XP_TE_KEYWORD
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
879 notify_set_offline (serv
, word
[4], FALSE
);
883 notify_set_offline (serv
, word
[4], TRUE
);
888 notify_set_online (serv
, word
[4]);
891 case 730: /* RPL_MONONLINE */
892 ex
= strchr (word
[4], '!'); /* only send the nick */
895 notify_set_online (serv
, word
[4] + 1);
898 case 731: /* RPL_MONOFFLINE */
899 ex
= strchr (word
[4], '!'); /* only send the nick */
902 notify_set_offline (serv
, word
[4] + 1, FALSE
);
905 case 900: /* successful SASL 'logged in as ' */
906 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, word_eol
[6]+1, word
[1], word
[2], NULL
, 0);
908 case 903: /* successful SASL auth */
909 case 904: /* failed SASL auth */
910 if (inbound_sasl_error (serv
))
911 break; /* might retry */
912 case 905: /* failed SASL auth */
913 case 906: /* aborted */
914 case 907: /* attempting to re-auth after a successful auth */
915 EMIT_SIGNAL (XP_TE_SASLRESPONSE
, serv
->server_session
, word
[1], word
[2], word
[3], ++word_eol
[4], 0);
916 if (!serv
->sent_capend
)
918 serv
->sent_capend
= TRUE
;
919 tcp_send_len (serv
, "CAP END\r\n", 9);
925 if (serv
->inside_whois
&& word
[4][0])
927 /* some unknown WHOIS reply, ircd coders make them up weekly */
928 if (!serv
->skip_next_whois
)
929 EMIT_SIGNAL (XP_TE_WHOIS_SPECIAL
, whois_sess
, word
[4],
930 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5],
936 if (is_channel (serv
, word
[4]))
938 session
*realsess
= find_channel (serv
, word
[4]);
940 realsess
= serv
->server_session
;
941 EMIT_SIGNAL (XP_TE_SERVTEXT
, realsess
, text
, word
[1], word
[2], NULL
, 0);
944 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
950 /* handle named messages that starts with a ':' */
953 process_named_msg (session
*sess
, char *type
, char *word
[], char *word_eol
[])
955 server
*serv
= sess
->server
;
956 char ip
[128], nick
[NICKLEN
];
958 int len
= strlen (type
);
960 /* fill in the "ip" and "nick" buffers */
961 ex
= strchr (word
[1], '!');
962 if (!ex
) /* no '!', must be a server message */
964 safe_strcpy (ip
, word
[1], sizeof (ip
));
965 safe_strcpy (nick
, word
[1], sizeof (nick
));
968 safe_strcpy (ip
, ex
+ 1, sizeof (ip
));
970 safe_strcpy (nick
, word
[1], sizeof (nick
));
978 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
979 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
983 case WORDL('A','C','C','O'):
984 inbound_account (serv
, nick
, word
[3]);
987 case WORDL('J','O','I','N'):
989 char *chan
= word
[3];
990 char *account
= word
[4];
991 char *realname
= word_eol
[5] + 1;
995 if (!serv
->p_cmp (nick
, serv
->nick
))
996 inbound_ujoin (serv
, chan
, nick
, ip
);
998 inbound_join (serv
, chan
, nick
, ip
, account
, realname
);
1002 case WORDL('K','I','C','K'):
1004 char *kicked
= word
[4];
1005 char *reason
= word_eol
[5];
1010 if (!strcmp (kicked
, serv
->nick
))
1011 inbound_ukick (serv
, word
[3], nick
, reason
);
1013 inbound_kick (serv
, word
[3], kicked
, nick
, reason
);
1018 case WORDL('K','I','L','L'):
1019 EMIT_SIGNAL (XP_TE_KILL
, sess
, nick
, word_eol
[5], NULL
, NULL
, 0);
1022 case WORDL('M','O','D','E'):
1023 handle_mode (serv
, word
, word_eol
, nick
, FALSE
); /* modes.c */
1026 case WORDL('N','I','C','K'):
1027 inbound_newnick (serv
, nick
, (word_eol
[3][0] == ':')
1028 ? word_eol
[3] + 1 : word_eol
[3], FALSE
);
1031 case WORDL('P','A','R','T'):
1033 char *chan
= word
[3];
1034 char *reason
= word_eol
[4];
1040 if (!strcmp (nick
, serv
->nick
))
1041 inbound_upart (serv
, chan
, ip
, reason
);
1043 inbound_part (serv
, chan
, nick
, ip
, reason
);
1047 case WORDL('P','O','N','G'):
1048 inbound_ping_reply (serv
->server_session
,
1049 (word
[4][0] == ':') ? word
[4] + 1 : word
[4], word
[3]);
1052 case WORDL('Q','U','I','T'):
1053 inbound_quit (serv
, nick
, ip
,
1054 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : word_eol
[3]);
1057 case WORDL('A','W','A','Y'):
1058 inbound_away_notify (serv
, nick
,
1059 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : NULL
);
1069 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
1072 case WORDL('C','A','P','\0'):
1073 if (strncasecmp (word
[4], "ACK", 3) == 0)
1075 inbound_cap_ack (serv
, word
[1],
1076 word
[5][0] == ':' ? word_eol
[5] + 1 : word_eol
[5]);
1078 else if (strncasecmp (word
[4], "LS", 2) == 0)
1080 inbound_cap_ls (serv
, word
[1],
1081 word
[5][0] == ':' ? word_eol
[5] + 1 : word_eol
[5]);
1083 else if (strncasecmp (word
[4], "NAK", 3) == 0)
1085 inbound_cap_nak (serv
);
1087 else if (strncasecmp (word
[4], "LIST", 4) == 0)
1089 inbound_cap_list (serv
, word
[1],
1090 word
[5][0] == ':' ? word_eol
[5] + 1 : word_eol
[5]);
1102 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
1103 /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
1106 case WORDL('I','N','V','I'):
1107 if (ignore_check (word
[1], IG_INVI
))
1110 if (word
[4][0] == ':')
1111 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4] + 1, nick
,
1112 serv
->servername
, NULL
, 0);
1114 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4], nick
,
1115 serv
->servername
, NULL
, 0);
1119 case WORDL('N','O','T','I'):
1130 if (!strncmp (text
, "CHALLENGE ", 10)) /* QuakeNet CHALLENGE upon our request */
1132 response
= challengeauth_response (((ircnet
*)serv
->network
)->user
? ((ircnet
*)serv
->network
)->user
: prefs
.username
, serv
->password
, word
[5]);
1134 tcp_sendf (serv
, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n",
1136 ((ircnet
*)serv
->network
)->user
? ((ircnet
*)serv
->network
)->user
: prefs
.username
,
1138 CHALLENGEAUTH_ALGO
);
1141 return; /* omit the CHALLENGE <hash> ALGOS message */
1144 if (serv
->have_idmsg
)
1150 } else if (*text
== '-')
1154 if (!ignore_check (word
[1], IG_NOTI
))
1155 inbound_notice (serv
, word
[3], nick
, text
, ip
, id
);
1159 case WORDL('P','R','I','V'):
1163 int id
= FALSE
; /* identified */
1169 if (serv
->have_idmsg
)
1175 } else if (*text
== '-')
1178 len
= strlen (text
);
1179 if (text
[0] == 1 && text
[len
- 1] == 1) /* ctcp */
1183 if (strncasecmp (text
, "ACTION", 6) != 0)
1184 flood_check (nick
, ip
, serv
, sess
, 0);
1185 if (strncasecmp (text
, "DCC ", 4) == 0)
1186 /* redo this with handle_quotes TRUE */
1187 process_data_init (word
[1], word_eol
[1], word
, word_eol
, TRUE
, FALSE
);
1188 ctcp_handle (sess
, to
, nick
, ip
, text
, word
, word_eol
, id
);
1191 if (is_channel (serv
, to
))
1193 if (ignore_check (word
[1], IG_CHAN
))
1195 inbound_chanmsg (serv
, NULL
, to
, nick
, text
, FALSE
, id
);
1198 if (ignore_check (word
[1], IG_PRIV
))
1200 inbound_privmsg (serv
, nick
, ip
, text
, id
);
1207 case WORDL('T','O','P','I'):
1208 inbound_topicnew (serv
, nick
, word
[3],
1209 (word_eol
[4][0] == ':') ? word_eol
[4] + 1 : word_eol
[4]);
1212 case WORDL('W','A','L','L'):
1216 EMIT_SIGNAL (XP_TE_WALLOPS
, sess
, nick
, text
, NULL
, NULL
, 0);
1222 /* unknown message */
1223 PrintTextf (sess
, "GARBAGE: %s\n", word_eol
[1]);
1226 /* handle named messages that DON'T start with a ':' */
1229 process_named_servermsg (session
*sess
, char *buf
, char *rawname
, char *word_eol
[])
1231 sess
= sess
->server
->server_session
;
1233 if (!strncmp (buf
, "PING ", 5))
1235 tcp_sendf (sess
->server
, "PONG %s\r\n", buf
+ 5);
1238 if (!strncmp (buf
, "ERROR", 5))
1240 EMIT_SIGNAL (XP_TE_SERVERERROR
, sess
, buf
+ 7, NULL
, NULL
, NULL
, 0);
1243 if (!strncmp (buf
, "NOTICE ", 7))
1248 EMIT_SIGNAL (XP_TE_SERVNOTICE
, sess
, buf
, sess
->server
->servername
, NULL
, NULL
, 0);
1251 if (!strncmp (buf
, "AUTHENTICATE", 12))
1253 inbound_sasl_authenticate (sess
->server
, word_eol
[2]);
1257 EMIT_SIGNAL (XP_TE_SERVTEXT
, sess
, buf
, sess
->server
->servername
, rawname
, NULL
, 0);
1260 /* irc_inline() - 1 single line received from serv */
1263 irc_inline (server
*serv
, char *buf
, int len
)
1265 session
*sess
, *tmp
;
1267 char *word
[PDIWORDS
+1];
1268 char *word_eol
[PDIWORDS
+1];
1269 char pdibuf_static
[522]; /* 1 line can potentially be 512*6 in utf8 */
1270 char *pdibuf
= pdibuf_static
;
1272 url_check_line (buf
, len
);
1274 /* need more than 522? fall back to malloc */
1275 if (len
>= sizeof (pdibuf_static
))
1276 pdibuf
= malloc (len
+ 1);
1278 sess
= serv
->front_session
;
1280 /* Python relies on this */
1281 word
[PDIWORDS
] = NULL
;
1282 word_eol
[PDIWORDS
] = NULL
;
1286 /* split line into words and words_to_end_of_line */
1287 process_data_init (pdibuf
, buf
, word
, word_eol
, FALSE
, FALSE
);
1289 /* find a context for this message */
1290 if (is_channel (serv
, word
[3]))
1292 tmp
= find_channel (serv
, word
[3]);
1297 /* for server messages, the 2nd word is the "message type" */
1301 word_eol
[1] = buf
; /* keep the ":" for plugins */
1302 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1305 word_eol
[1] = buf
+ 1; /* but not for xchat internally */
1309 process_data_init (pdibuf
, buf
, word
, word_eol
, FALSE
, FALSE
);
1310 word
[0] = type
= word
[1];
1311 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1317 process_named_servermsg (sess
, buf
, word
[0], word_eol
);
1321 /* see if the second word is a numeric */
1322 if (isdigit ((unsigned char) word
[2][0]))
1328 process_numeric (sess
, atoi (word
[2]), word
, word_eol
, text
);
1331 process_named_msg (sess
, type
, word
, word_eol
);
1335 if (pdibuf
!= pdibuf_static
)
1340 proto_fill_her_up (server
*serv
)
1342 serv
->p_inline
= irc_inline
;
1343 serv
->p_invite
= irc_invite
;
1344 serv
->p_cycle
= irc_cycle
;
1345 serv
->p_ctcp
= irc_ctcp
;
1346 serv
->p_nctcp
= irc_nctcp
;
1347 serv
->p_quit
= irc_quit
;
1348 serv
->p_kick
= irc_kick
;
1349 serv
->p_part
= irc_part
;
1350 serv
->p_ns_identify
= irc_ns_identify
;
1351 serv
->p_ns_ghost
= irc_ns_ghost
;
1352 serv
->p_join
= irc_join
;
1353 serv
->p_join_list
= irc_join_list
;
1354 serv
->p_login
= irc_login
;
1355 serv
->p_join_info
= irc_join_info
;
1356 serv
->p_mode
= irc_mode
;
1357 serv
->p_user_list
= irc_user_list
;
1358 serv
->p_away_status
= irc_away_status
;
1359 /*serv->p_get_ip = irc_get_ip;*/
1360 serv
->p_whois
= irc_user_whois
;
1361 serv
->p_get_ip
= irc_user_list
;
1362 serv
->p_get_ip_uh
= irc_userhost
;
1363 serv
->p_set_back
= irc_set_back
;
1364 serv
->p_set_away
= irc_set_away
;
1365 serv
->p_message
= irc_message
;
1366 serv
->p_action
= irc_action
;
1367 serv
->p_notice
= irc_notice
;
1368 serv
->p_topic
= irc_topic
;
1369 serv
->p_list_channels
= irc_list_channels
;
1370 serv
->p_change_nick
= irc_change_nick
;
1371 serv
->p_names
= irc_names
;
1372 serv
->p_ping
= irc_ping
;
1373 serv
->p_raw
= irc_raw
;
1374 serv
->p_cmp
= rfc_casecmp
; /* can be changed by 005 in modes.c */