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 */
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
);
57 "USER %s %s %s :%s\r\n",
58 serv
->nick
, user
, user
, serv
->servername
, realname
);
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
);
71 tcp_sendf (serv
, "NICKSERV %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
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
);
78 tcp_sendf (serv
, "PRIVMSG NS :%s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
81 tcp_sendf (serv
, "NS %s %s%s%s\r\n", cmd
, arg1
, arg2
, arg3
);
84 /* why couldn't QuakeNet implement one of the existing ones? */
85 tcp_sendf (serv
, "AUTH %s %s\r\n", arg1
, arg2
);
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 */
101 irc_nickserv (serv
, "", serv
->nick
, pass
, "");
105 irc_nickserv (serv
, "IDENTIFY", pass
, "", "");
110 irc_ns_ghost (server
*serv
, char *usname
, char *pass
)
112 if (serv
->loginmethod
!= LOGIN_CHALLENGEAUTH
)
114 irc_nickserv (serv
, "GHOST", usname
, " ", pass
);
119 irc_join (server
*serv
, char *channel
, char *key
)
122 tcp_sendf (serv
, "JOIN %s %s\r\n", channel
, key
);
124 tcp_sendf (serv
, "JOIN %s\r\n", channel
);
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
);
137 tcp_sendf (serv
, "JOIN %s %s\r\n", chanstr
, keystr
);
139 tcp_sendf (serv
, "JOIN %s\r\n", chanstr
);
145 /* join a whole list of channels & keys, split to multiple lines
146 * to get around 512 limit */
149 irc_join_list (server
*serv
, GSList
*channels
, GSList
*keys
)
153 GString
*c
= g_string_new (NULL
);
154 GString
*k
= g_string_new (NULL
);
160 len
= 9; /* "JOIN<space><space>\r\n" */
166 /* measure how many bytes this channel would add... */
169 add
= strlen (clist
->data
);
176 add
+= strlen (klist
->data
);
180 add
++; /* 'x' filler */
186 /* too big? dump buffer and start a fresh one */
189 irc_join_list_flush (serv
, c
, k
);
191 c
= g_string_new (NULL
);
192 k
= g_string_new (NULL
);
197 /* now actually add it to our GStrings */
200 add
= strlen (clist
->data
);
204 g_string_append_c (c
, ',');
206 g_string_append (c
, clist
->data
);
212 add
+= strlen (klist
->data
);
216 g_string_append_c (k
, ',');
218 g_string_append (k
, klist
->data
);
227 g_string_append_c (k
, ',');
229 g_string_append_c (k
, 'x');
239 irc_join_list_flush (serv
, c
, k
);
243 irc_part (server
*serv
, char *channel
, char *reason
)
246 tcp_sendf (serv
, "PART %s :%s\r\n", channel
, reason
);
248 tcp_sendf (serv
, "PART %s\r\n", channel
);
252 irc_quit (server
*serv
, char *reason
)
255 tcp_sendf (serv
, "QUIT :%s\r\n", reason
);
257 tcp_send_len (serv
, "QUIT\r\n", 6);
261 irc_set_back (server
*serv
)
263 tcp_send_len (serv
, "AWAY\r\n", 6);
267 irc_set_away (server
*serv
, char *reason
)
279 tcp_sendf (serv
, "AWAY :%s\r\n", reason
);
283 irc_ctcp (server
*serv
, char *to
, char *msg
)
285 tcp_sendf (serv
, "PRIVMSG %s :\001%s\001\r\n", to
, msg
);
289 irc_nctcp (server
*serv
, char *to
, char *msg
)
291 tcp_sendf (serv
, "NOTICE %s :\001%s\001\r\n", to
, msg
);
295 irc_cycle (server
*serv
, char *channel
, char *key
)
297 tcp_sendf (serv
, "PART %s\r\nJOIN %s %s\r\n", channel
, channel
, key
);
301 irc_kick (server
*serv
, char *channel
, char *nick
, char *reason
)
304 tcp_sendf (serv
, "KICK %s %s :%s\r\n", channel
, nick
, reason
);
306 tcp_sendf (serv
, "KICK %s %s\r\n", channel
, nick
);
310 irc_invite (server
*serv
, char *channel
, char *nick
)
312 tcp_sendf (serv
, "INVITE %s %s\r\n", nick
, channel
);
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 */
324 irc_join_info (server
*serv
, char *channel
)
326 tcp_sendf (serv
, "MODE %s\r\n", channel
);
329 /* initiate userlist retreival */
332 irc_user_list (server
*serv
, char *channel
)
335 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
337 tcp_sendf (serv
, "WHO %s\r\n", channel
);
343 irc_userhost (server
*serv
, char *nick
)
345 tcp_sendf (serv
, "USERHOST %s\r\n", nick
);
349 irc_away_status (server
*serv
, char *channel
)
352 tcp_sendf (serv
, "WHO %s %%chtsunfra,152\r\n", channel
);
354 tcp_sendf (serv
, "WHO %s\r\n", channel
);
358 irc_get_ip (server *serv, char *nick)
360 tcp_sendf (serv, "WHO %s\r\n", nick);
366 * Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
369 irc_user_whois (server
*serv
, char *nicks
)
371 tcp_sendf (serv
, "WHOIS %s\r\n", nicks
);
375 irc_message (server
*serv
, char *channel
, char *text
)
377 tcp_sendf (serv
, "PRIVMSG %s :%s\r\n", channel
, text
);
381 irc_action (server
*serv
, char *channel
, char *act
)
383 tcp_sendf (serv
, "PRIVMSG %s :\001ACTION %s\001\r\n", channel
, act
);
387 irc_notice (server
*serv
, char *channel
, char *text
)
389 tcp_sendf (serv
, "NOTICE %s :%s\r\n", channel
, text
);
393 irc_topic (server
*serv
, char *channel
, char *topic
)
396 tcp_sendf (serv
, "TOPIC %s :\r\n", channel
);
398 tcp_sendf (serv
, "TOPIC %s :%s\r\n", channel
, topic
);
400 tcp_sendf (serv
, "TOPIC %s\r\n", channel
);
404 irc_list_channels (server
*serv
, char *arg
, int min_users
)
408 tcp_sendf (serv
, "LIST %s\r\n", arg
);
412 if (serv
->use_listargs
)
413 tcp_sendf (serv
, "LIST >%d,<10000\r\n", min_users
- 1);
415 tcp_send_len (serv
, "LIST\r\n", 6);
419 irc_names (server
*serv
, char *channel
)
421 tcp_sendf (serv
, "NAMES %s\r\n", channel
);
425 irc_change_nick (server
*serv
, char *new_nick
)
427 tcp_sendf (serv
, "NICK %s\r\n", new_nick
);
431 irc_ping (server
*serv
, char *to
, char *timestring
)
434 tcp_sendf (serv
, "PRIVMSG %s :\001PING %s\001\r\n", to
, timestring
);
436 tcp_sendf (serv
, "PING %s\r\n", timestring
);
440 irc_raw (server
*serv
, char *raw
)
447 if (len
< sizeof (tbuf
) - 3)
449 len
= snprintf (tbuf
, sizeof (tbuf
), "%s\r\n", raw
);
450 tcp_send_len (serv
, tbuf
, len
);
453 tcp_send_len (serv
, raw
, len
);
454 tcp_send_len (serv
, "\r\n", 2);
461 /* ============================================================== */
462 /* ======================= IRC INPUT ============================ */
463 /* ============================================================== */
467 channel_date (session
*sess
, char *chan
, char *timestr
)
469 time_t timestamp
= (time_t) atol (timestr
);
470 char *tim
= ctime (×tamp
);
471 tim
[24] = 0; /* get rid of the \n */
472 EMIT_SIGNAL (XP_TE_CHANDATE
, sess
, chan
, tim
, NULL
, NULL
, 0);
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
;
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);
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 */
524 inbound_005 (serv
, word
);
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
);
536 inbound_away (serv
, word
[4],
537 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
541 if (serv
->skip_next_userhost
)
543 char *eq
= strchr (word
[4], '=');
547 if (!serv
->p_cmp (word
[4] + 1, serv
->nick
))
549 char *at
= strrchr (eq
+ 1, '@');
551 inbound_foundip (sess
, at
+ 1);
555 serv
->skip_next_userhost
= FALSE
;
562 notify_markonline (serv
, word
);
566 inbound_uback (serv
);
570 inbound_uaway (serv
);
574 if (!serv
->skip_next_whois
)
575 EMIT_SIGNAL (XP_TE_WHOIS3
, whois_sess
, word
[4], word_eol
[5], NULL
, NULL
, 0);
577 inbound_user_info (sess
, NULL
, NULL
, NULL
, word
[5], word
[4], NULL
, NULL
, 0xff);
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);
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);
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);
598 if (!serv
->skip_next_whois
)
600 time_t timestamp
= (time_t) atol (word
[6]);
601 long idle
= atol (word
[5]);
605 snprintf (outbuf
, sizeof (outbuf
),
606 "%02ld:%02ld:%02ld", idle
/ 3600, (idle
/ 60) % 60,
609 EMIT_SIGNAL (XP_TE_WHOIS4
, whois_sess
, word
[4],
610 outbuf
, NULL
, NULL
, 0);
613 tim
= ctime (×tamp
);
614 tim
[19] = 0; /* get rid of the \n */
615 EMIT_SIGNAL (XP_TE_WHOIS4T
, whois_sess
, word
[4],
616 outbuf
, tim
, NULL
, 0);
621 case 318: /* END OF WHOIS */
622 if (!serv
->skip_next_whois
)
623 EMIT_SIGNAL (XP_TE_WHOIS6
, whois_sess
, word
[4], NULL
,
625 serv
->skip_next_whois
= 0;
626 serv
->inside_whois
= 0;
631 if (!serv
->skip_next_whois
)
632 EMIT_SIGNAL (XP_TE_WHOIS2
, whois_sess
, word
[4],
633 word_eol
[5] + 1, NULL
, NULL
, 0);
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);
644 if (!fe_is_chanwindow (sess
->server
))
645 EMIT_SIGNAL (XP_TE_CHANLISTHEAD
, serv
->server_session
, NULL
, NULL
, NULL
, NULL
, 0);
649 if (fe_is_chanwindow (sess
->server
))
651 fe_add_chan_list (sess
->server
, word
[4], word
[5], word_eol
[6] + 1);
654 PrintTextf (serv
->server_session
, "%-16s %-7d %s\017\n",
655 word
[4], atoi (word
[5]), word_eol
[6] + 1);
660 if (!fe_is_chanwindow (sess
->server
))
661 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1], word
[2], NULL
, 0);
663 fe_chan_list_end (sess
->server
);
667 sess
= find_channel (serv
, word
[4]);
669 sess
= serv
->server_session
;
670 if (sess
->ignore_mode
)
671 sess
->ignore_mode
= FALSE
;
673 EMIT_SIGNAL (XP_TE_CHANMODES
, sess
, word
[4], word_eol
[5],
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
);
687 sess
= find_channel (serv
, word
[4]);
690 if (sess
->ignore_date
)
691 sess
->ignore_date
= FALSE
;
693 channel_date (sess
, word
[4], word
[5]);
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);
705 inbound_topic (serv
, word
[4],
706 (word_eol
[5][0] == ':') ? word_eol
[5] + 1 : word_eol
[5]);
710 inbound_topictime (serv
, word
[4], word
[5], atol (word
[6]));
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);
720 case 341: /* INVITE ACK */
721 EMIT_SIGNAL (XP_TE_UINVITE
, sess
, word
[4], word
[5], serv
->servername
,
727 unsigned int away
= 0;
728 session
*who_sess
= find_channel (serv
, word
[4]);
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],
743 case 354: /* undernet WHOX: used as a reply for irc_away_status */
745 unsigned int away
= 0;
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')
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);
769 case 315: /* END OF WHO */
772 who_sess
= find_channel (serv
, word
[4]);
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
;
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
;
789 case 348: /* +e-list entry */
790 if (!inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], TRUE
))
794 case 349: /* end of exemption list */
795 sess
= find_channel (serv
, word
[4]);
798 sess
= serv
->front_session
;
801 if (!fe_is_banwindow (sess
))
803 fe_ban_list_end (sess
, TRUE
);
806 case 353: /* NAMES */
807 inbound_nameslist (serv
, word
[5],
808 (word_eol
[6][0] == ':') ? word_eol
[6] + 1 : word_eol
[6]);
812 if (!inbound_nameslist_end (serv
, word
[4]))
816 case 367: /* banlist entry */
817 inbound_banlist (sess
, atol (word
[7]), word
[4], word
[5], word
[6], FALSE
);
821 sess
= find_channel (serv
, word
[4]);
824 sess
= serv
->front_session
;
827 if (!fe_is_banwindow (sess
))
829 fe_ban_list_end (sess
, FALSE
);
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;
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
,
845 case 376: /* end of motd */
846 case 422: /* motd file is missing */
847 inbound_login_end (sess
, text
);
850 case 433: /* nickname in use */
851 case 432: /* erroneous nickname */
852 if (serv
->end_of_motd
)
854 inbound_next_nick (sess
, word
[4]);
858 if (serv
->end_of_motd
|| is_channel (serv
, word
[4]))
860 inbound_next_nick (sess
, word
[4]);
864 EMIT_SIGNAL (XP_TE_USERLIMIT
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
868 EMIT_SIGNAL (XP_TE_INVITE
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
872 EMIT_SIGNAL (XP_TE_BANNED
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
876 EMIT_SIGNAL (XP_TE_KEYWORD
, sess
, word
[4], NULL
, NULL
, NULL
, 0);
880 notify_set_offline (serv
, word
[4], FALSE
);
884 notify_set_offline (serv
, word
[4], TRUE
);
889 notify_set_online (serv
, word
[4]);
892 case 730: /* RPL_MONONLINE */
893 ex
= strchr (word
[4], '!'); /* only send the nick */
896 notify_set_online (serv
, word
[4] + 1);
899 case 731: /* RPL_MONOFFLINE */
900 ex
= strchr (word
[4], '!'); /* only send the nick */
903 notify_set_offline (serv
, word
[4] + 1, FALSE
);
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);
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);
923 case 908: /* Supported SASL Mechs */
924 inbound_sasl_supportedmechs (serv
, word
[4]);
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],
940 if (is_channel (serv
, word
[4]))
942 session
*realsess
= find_channel (serv
, word
[4]);
944 realsess
= serv
->server_session
;
945 EMIT_SIGNAL (XP_TE_SERVTEXT
, realsess
, text
, word
[1], word
[2], NULL
, 0);
948 EMIT_SIGNAL (XP_TE_SERVTEXT
, serv
->server_session
, text
, word
[1],
954 /* handle named messages that starts with a ':' */
957 process_named_msg (session
*sess
, char *type
, char *word
[], char *word_eol
[])
959 server
*serv
= sess
->server
;
960 char ip
[128], nick
[NICKLEN
];
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
));
972 safe_strcpy (ip
, ex
+ 1, sizeof (ip
));
974 safe_strcpy (nick
, word
[1], sizeof (nick
));
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 */
987 case WORDL('A','C','C','O'):
988 inbound_account (serv
, nick
, word
[3]);
991 case WORDL('J','O','I','N'):
993 char *chan
= word
[3];
994 char *account
= word
[4];
995 char *realname
= word_eol
[5] + 1;
999 if (!serv
->p_cmp (nick
, serv
->nick
))
1000 inbound_ujoin (serv
, chan
, nick
, ip
);
1002 inbound_join (serv
, chan
, nick
, ip
, account
, realname
);
1006 case WORDL('K','I','C','K'):
1008 char *kicked
= word
[4];
1009 char *reason
= word_eol
[5];
1014 if (!strcmp (kicked
, serv
->nick
))
1015 inbound_ukick (serv
, word
[3], nick
, reason
);
1017 inbound_kick (serv
, word
[3], kicked
, nick
, reason
);
1022 case WORDL('K','I','L','L'):
1023 EMIT_SIGNAL (XP_TE_KILL
, sess
, nick
, word_eol
[5], NULL
, NULL
, 0);
1026 case WORDL('M','O','D','E'):
1027 handle_mode (serv
, word
, word_eol
, nick
, FALSE
); /* modes.c */
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
);
1035 case WORDL('P','A','R','T'):
1037 char *chan
= word
[3];
1038 char *reason
= word_eol
[4];
1044 if (!strcmp (nick
, serv
->nick
))
1045 inbound_upart (serv
, chan
, ip
, reason
);
1047 inbound_part (serv
, chan
, nick
, ip
, reason
);
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]);
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]);
1061 case WORDL('A','W','A','Y'):
1062 inbound_away_notify (serv
, nick
,
1063 (word_eol
[3][0] == ':') ? word_eol
[3] + 1 : NULL
);
1073 t
= WORDL((guint8
)type
[0], (guint8
)type
[1], (guint8
)type
[2], (guint8
)type
[3]);
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]);
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 */
1110 case WORDL('I','N','V','I'):
1111 if (ignore_check (word
[1], IG_INVI
))
1114 if (word
[4][0] == ':')
1115 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4] + 1, nick
,
1116 serv
->servername
, NULL
, 0);
1118 EMIT_SIGNAL (XP_TE_INVITED
, sess
, word
[4], nick
,
1119 serv
->servername
, NULL
, 0);
1123 case WORDL('N','O','T','I'):
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",
1140 ((ircnet
*)serv
->network
)->user
? ((ircnet
*)serv
->network
)->user
: prefs
.username
,
1142 CHALLENGEAUTH_ALGO
);
1145 return; /* omit the CHALLENGE <hash> ALGOS message */
1149 if (serv
->have_idmsg
)
1155 } else if (*text
== '-')
1159 if (!ignore_check (word
[1], IG_NOTI
))
1160 inbound_notice (serv
, word
[3], nick
, text
, ip
, id
);
1164 case WORDL('P','R','I','V'):
1168 int id
= FALSE
; /* identified */
1174 if (serv
->have_idmsg
)
1180 } else if (*text
== '-')
1183 len
= strlen (text
);
1184 if (text
[0] == 1 && text
[len
- 1] == 1) /* ctcp */
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
);
1196 if (is_channel (serv
, to
))
1198 if (ignore_check (word
[1], IG_CHAN
))
1200 inbound_chanmsg (serv
, NULL
, to
, nick
, text
, FALSE
, id
);
1203 if (ignore_check (word
[1], IG_PRIV
))
1205 inbound_privmsg (serv
, nick
, ip
, text
, id
);
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]);
1217 case WORDL('W','A','L','L'):
1221 EMIT_SIGNAL (XP_TE_WALLOPS
, sess
, nick
, text
, NULL
, NULL
, 0);
1227 /* unknown message */
1228 PrintTextf (sess
, "GARBAGE: %s\n", word_eol
[1]);
1231 /* handle named messages that DON'T start with a ':' */
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);
1243 if (!strncmp (buf
, "ERROR", 5))
1245 EMIT_SIGNAL (XP_TE_SERVERERROR
, sess
, buf
+ 7, NULL
, NULL
, NULL
, 0);
1248 if (!strncmp (buf
, "NOTICE ", 7))
1253 EMIT_SIGNAL (XP_TE_SERVNOTICE
, sess
, buf
, sess
->server
->servername
, NULL
, NULL
, 0);
1256 if (!strncmp (buf
, "AUTHENTICATE", 12))
1258 inbound_sasl_authenticate (sess
->server
, word_eol
[2]);
1262 EMIT_SIGNAL (XP_TE_SERVTEXT
, sess
, buf
, sess
->server
->servername
, rawname
, NULL
, 0);
1265 /* irc_inline() - 1 single line received from serv */
1268 irc_inline (server
*serv
, char *buf
, int len
)
1270 session
*sess
, *tmp
;
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
;
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]);
1302 /* for server messages, the 2nd word is the "message type" */
1306 word_eol
[1] = buf
; /* keep the ":" for plugins */
1307 if (plugin_emit_server (sess
, type
, word
, word_eol
))
1310 word_eol
[1] = buf
+ 1; /* but not for xchat internally */
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
))
1322 process_named_servermsg (sess
, buf
, word
[0], word_eol
);
1326 /* see if the second word is a numeric */
1327 if (isdigit ((unsigned char) word
[2][0]))
1333 process_numeric (sess
, atoi (word
[2]), word
, word_eol
, text
);
1336 process_named_msg (sess
, type
, word
, word_eol
);
1340 if (pdibuf
!= pdibuf_static
)
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 */