PageRenderTime 51ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/src/common/proto-irc.c

#
C | 1262 lines | 1025 code | 171 blank | 66 comment | 191 complexity | 5f1b76a096dd81cb2f927c4a5b546392 MD5 | raw file
Possible License(s): GPL-2.0
  1. /* X-Chat
  2. * Copyright (C) 2002 Peter Zelezny.
  3. *
  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.
  8. *
  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.
  13. *
  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
  17. */
  18. /* IRC RFC1459(+commonly used extensions) protocol implementation */
  19. #include <unistd.h>
  20. #include <string.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <ctype.h>
  24. #include <stdarg.h>
  25. #include "xchat.h"
  26. #include "ctcp.h"
  27. #include "fe.h"
  28. #include "ignore.h"
  29. #include "inbound.h"
  30. #include "modes.h"
  31. #include "notify.h"
  32. #include "plugin.h"
  33. #include "server.h"
  34. #include "text.h"
  35. #include "outbound.h"
  36. #include "util.h"
  37. #include "xchatc.h"
  38. static void
  39. irc_login (server *serv, char *user, char *realname)
  40. {
  41. if (serv->password[0])
  42. tcp_sendf (serv, "PASS %s\r\n", serv->password);
  43. tcp_sendf (serv,
  44. "NICK %s\r\n"
  45. "USER %s %s %s :%s\r\n",
  46. serv->nick, user, user, serv->servername, realname);
  47. }
  48. static void
  49. irc_nickserv (server *serv, char *cmd, char *arg1, char *arg2, char *arg3)
  50. {
  51. /* are all ircd authors idiots? */
  52. switch (serv->nickservtype)
  53. {
  54. case 0:
  55. tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
  56. break;
  57. case 1:
  58. tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
  59. break;
  60. case 2:
  61. tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
  62. break;
  63. case 3:
  64. tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
  65. break;
  66. case 4:
  67. /* why couldn't QuakeNet implement one of the existing ones? */
  68. tcp_sendf (serv, "AUTH %s%s%s\r\n", cmd, arg1, arg2, arg3);
  69. }
  70. }
  71. static void
  72. irc_ns_identify (server *serv, char *pass)
  73. {
  74. irc_nickserv (serv, "IDENTIFY", pass, "", "");
  75. }
  76. static void
  77. irc_ns_ghost (server *serv, char *usname, char *pass)
  78. {
  79. if (serv->nickservtype != 4)
  80. irc_nickserv (serv, "GHOST", usname, " ", pass);
  81. }
  82. static void
  83. irc_join (server *serv, char *channel, char *key)
  84. {
  85. if (key[0])
  86. tcp_sendf (serv, "JOIN %s %s\r\n", channel, key);
  87. else
  88. tcp_sendf (serv, "JOIN %s\r\n", channel);
  89. }
  90. static void
  91. irc_join_list_flush (server *serv, GString *c, GString *k)
  92. {
  93. char *chanstr, *keystr;
  94. chanstr = g_string_free (c, FALSE);
  95. keystr = g_string_free (k, FALSE);
  96. if (chanstr[0])
  97. {
  98. if (keystr[0])
  99. tcp_sendf (serv, "JOIN %s %s\r\n", chanstr, keystr);
  100. else
  101. tcp_sendf (serv, "JOIN %s\r\n", chanstr);
  102. }
  103. g_free (chanstr);
  104. g_free (keystr);
  105. }
  106. /* join a whole list of channels & keys, split to multiple lines
  107. * to get around 512 limit */
  108. static void
  109. irc_join_list (server *serv, GSList *channels, GSList *keys)
  110. {
  111. GSList *clist;
  112. GSList *klist;
  113. GString *c = g_string_new (NULL);
  114. GString *k = g_string_new (NULL);
  115. int len;
  116. int add;
  117. int i, j;
  118. i = j = 0;
  119. len = 9; /* "JOIN<space><space>\r\n" */
  120. clist = channels;
  121. klist = keys;
  122. while (clist)
  123. {
  124. /* measure how many bytes this channel would add... */
  125. if (1)
  126. {
  127. add = strlen (clist->data);
  128. if (i != 0)
  129. add++; /* comma */
  130. }
  131. if (klist->data)
  132. {
  133. add += strlen (klist->data);
  134. }
  135. else
  136. {
  137. add++; /* 'x' filler */
  138. }
  139. if (j != 0)
  140. add++; /* comma */
  141. /* too big? dump buffer and start a fresh one */
  142. if (len + add > 512)
  143. {
  144. irc_join_list_flush (serv, c, k);
  145. c = g_string_new (NULL);
  146. k = g_string_new (NULL);
  147. i = j = 0;
  148. len = 9;
  149. }
  150. /* now actually add it to our GStrings */
  151. if (1)
  152. {
  153. add = strlen (clist->data);
  154. if (i != 0)
  155. {
  156. add++;
  157. g_string_append_c (c, ',');
  158. }
  159. g_string_append (c, clist->data);
  160. i++;
  161. }
  162. if (klist->data)
  163. {
  164. add += strlen (klist->data);
  165. if (j != 0)
  166. {
  167. add++;
  168. g_string_append_c (k, ',');
  169. }
  170. g_string_append (k, klist->data);
  171. j++;
  172. }
  173. else
  174. {
  175. add++;
  176. if (j != 0)
  177. {
  178. add++;
  179. g_string_append_c (k, ',');
  180. }
  181. g_string_append_c (k, 'x');
  182. j++;
  183. }
  184. len += add;
  185. klist = klist->next;
  186. clist = clist->next;
  187. }
  188. irc_join_list_flush (serv, c, k);
  189. }
  190. static void
  191. irc_part (server *serv, char *channel, char *reason)
  192. {
  193. if (reason[0])
  194. tcp_sendf (serv, "PART %s :%s\r\n", channel, reason);
  195. else
  196. tcp_sendf (serv, "PART %s\r\n", channel);
  197. }
  198. static void
  199. irc_quit (server *serv, char *reason)
  200. {
  201. if (reason[0])
  202. tcp_sendf (serv, "QUIT :%s\r\n", reason);
  203. else
  204. tcp_send_len (serv, "QUIT\r\n", 6);
  205. }
  206. static void
  207. irc_set_back (server *serv)
  208. {
  209. tcp_send_len (serv, "AWAY\r\n", 6);
  210. }
  211. static void
  212. irc_set_away (server *serv, char *reason)
  213. {
  214. if (reason)
  215. {
  216. if (!reason[0])
  217. reason = " ";
  218. }
  219. else
  220. {
  221. reason = " ";
  222. }
  223. tcp_sendf (serv, "AWAY :%s\r\n", reason);
  224. }
  225. static void
  226. irc_ctcp (server *serv, char *to, char *msg)
  227. {
  228. tcp_sendf (serv, "PRIVMSG %s :\001%s\001\r\n", to, msg);
  229. }
  230. static void
  231. irc_nctcp (server *serv, char *to, char *msg)
  232. {
  233. tcp_sendf (serv, "NOTICE %s :\001%s\001\r\n", to, msg);
  234. }
  235. static void
  236. irc_cycle (server *serv, char *channel, char *key)
  237. {
  238. tcp_sendf (serv, "PART %s\r\nJOIN %s %s\r\n", channel, channel, key);
  239. }
  240. static void
  241. irc_kick (server *serv, char *channel, char *nick, char *reason)
  242. {
  243. if (reason[0])
  244. tcp_sendf (serv, "KICK %s %s :%s\r\n", channel, nick, reason);
  245. else
  246. tcp_sendf (serv, "KICK %s %s\r\n", channel, nick);
  247. }
  248. static void
  249. irc_invite (server *serv, char *channel, char *nick)
  250. {
  251. tcp_sendf (serv, "INVITE %s %s\r\n", nick, channel);
  252. }
  253. static void
  254. irc_mode (server *serv, char *target, char *mode)
  255. {
  256. tcp_sendf (serv, "MODE %s %s\r\n", target, mode);
  257. }
  258. /* find channel info when joined */
  259. static void
  260. irc_join_info (server *serv, char *channel)
  261. {
  262. tcp_sendf (serv, "MODE %s\r\n", channel);
  263. }
  264. /* initiate userlist retreival */
  265. static void
  266. irc_user_list (server *serv, char *channel)
  267. {
  268. tcp_sendf (serv, "WHO %s\r\n", channel);
  269. }
  270. /* userhost */
  271. static void
  272. irc_userhost (server *serv, char *nick)
  273. {
  274. tcp_sendf (serv, "USERHOST %s\r\n", nick);
  275. }
  276. static void
  277. irc_away_status (server *serv, char *channel)
  278. {
  279. if (serv->have_whox)
  280. tcp_sendf (serv, "WHO %s %%ctnf,152\r\n", channel);
  281. else
  282. tcp_sendf (serv, "WHO %s\r\n", channel);
  283. }
  284. /*static void
  285. irc_get_ip (server *serv, char *nick)
  286. {
  287. tcp_sendf (serv, "WHO %s\r\n", nick);
  288. }*/
  289. /*
  290. * Command: WHOIS
  291. * Parameters: [<server>] <nickmask>[,<nickmask>[,...]]
  292. */
  293. static void
  294. irc_user_whois (server *serv, char *nicks)
  295. {
  296. tcp_sendf (serv, "WHOIS %s\r\n", nicks);
  297. }
  298. static void
  299. irc_message (server *serv, char *channel, char *text)
  300. {
  301. tcp_sendf (serv, "PRIVMSG %s :%s\r\n", channel, text);
  302. }
  303. static void
  304. irc_action (server *serv, char *channel, char *act)
  305. {
  306. tcp_sendf (serv, "PRIVMSG %s :\001ACTION %s\001\r\n", channel, act);
  307. }
  308. static void
  309. irc_notice (server *serv, char *channel, char *text)
  310. {
  311. tcp_sendf (serv, "NOTICE %s :%s\r\n", channel, text);
  312. }
  313. static void
  314. irc_topic (server *serv, char *channel, char *topic)
  315. {
  316. if (!topic)
  317. tcp_sendf (serv, "TOPIC %s :\r\n", channel);
  318. else if (topic[0])
  319. tcp_sendf (serv, "TOPIC %s :%s\r\n", channel, topic);
  320. else
  321. tcp_sendf (serv, "TOPIC %s\r\n", channel);
  322. }
  323. static void
  324. irc_list_channels (server *serv, char *arg, int min_users)
  325. {
  326. if (arg[0])
  327. {
  328. tcp_sendf (serv, "LIST %s\r\n", arg);
  329. return;
  330. }
  331. if (serv->use_listargs)
  332. tcp_sendf (serv, "LIST >%d,<10000\r\n", min_users - 1);
  333. else
  334. tcp_send_len (serv, "LIST\r\n", 6);
  335. }
  336. static void
  337. irc_names (server *serv, char *channel)
  338. {
  339. tcp_sendf (serv, "NAMES %s\r\n", channel);
  340. }
  341. static void
  342. irc_change_nick (server *serv, char *new_nick)
  343. {
  344. tcp_sendf (serv, "NICK %s\r\n", new_nick);
  345. }
  346. static void
  347. irc_ping (server *serv, char *to, char *timestring)
  348. {
  349. if (*to)
  350. tcp_sendf (serv, "PRIVMSG %s :\001PING %s\001\r\n", to, timestring);
  351. else
  352. tcp_sendf (serv, "PING %s\r\n", timestring);
  353. }
  354. static int
  355. irc_raw (server *serv, char *raw)
  356. {
  357. int len;
  358. char tbuf[4096];
  359. if (*raw)
  360. {
  361. len = strlen (raw);
  362. if (len < sizeof (tbuf) - 3)
  363. {
  364. len = snprintf (tbuf, sizeof (tbuf), "%s\r\n", raw);
  365. tcp_send_len (serv, tbuf, len);
  366. } else
  367. {
  368. tcp_send_len (serv, raw, len);
  369. tcp_send_len (serv, "\r\n", 2);
  370. }
  371. return TRUE;
  372. }
  373. return FALSE;
  374. }
  375. /* ============================================================== */
  376. /* ======================= IRC INPUT ============================ */
  377. /* ============================================================== */
  378. static void
  379. channel_date (session *sess, char *chan, char *timestr)
  380. {
  381. time_t timestamp = (time_t) atol (timestr);
  382. char *tim = ctime (&timestamp);
  383. tim[24] = 0; /* get rid of the \n */
  384. EMIT_SIGNAL (XP_TE_CHANDATE, sess, chan, tim, NULL, NULL, 0);
  385. }
  386. static void
  387. process_numeric (session * sess, int n,
  388. char *word[], char *word_eol[], char *text)
  389. {
  390. server *serv = sess->server;
  391. /* show whois is the server tab */
  392. session *whois_sess = serv->server_session;
  393. /* unless this setting is on */
  394. if (prefs.irc_whois_front)
  395. whois_sess = serv->front_session;
  396. switch (n)
  397. {
  398. case 1:
  399. inbound_login_start (sess, word[3], word[1]);
  400. /* if network is PTnet then you must get your IP address
  401. from "001" server message */
  402. if ((strncmp(word[7], "PTnet", 5) == 0) &&
  403. (strncmp(word[8], "IRC", 3) == 0) &&
  404. (strncmp(word[9], "Network", 7) == 0) &&
  405. (strrchr(word[10], '@') != NULL))
  406. {
  407. serv->use_who = FALSE;
  408. if (prefs.ip_from_server)
  409. inbound_foundip (sess, strrchr(word[10], '@')+1);
  410. }
  411. /* use /NICKSERV */
  412. if (strcasecmp (word[7], "DALnet") == 0 ||
  413. strcasecmp (word[7], "BRASnet") == 0)
  414. serv->nickservtype = 1;
  415. /* use /NS */
  416. else if (strcasecmp (word[7], "FreeNode") == 0)
  417. serv->nickservtype = 2;
  418. goto def;
  419. case 4: /* check the ircd type */
  420. serv->use_listargs = FALSE;
  421. serv->modes_per_line = 3; /* default to IRC RFC */
  422. if (strncmp (word[5], "bahamut", 7) == 0) /* DALNet */
  423. {
  424. serv->use_listargs = TRUE; /* use the /list args */
  425. } else if (strncmp (word[5], "u2.10.", 6) == 0) /* Undernet */
  426. {
  427. serv->use_listargs = TRUE; /* use the /list args */
  428. serv->modes_per_line = 6; /* allow 6 modes per line */
  429. } else if (strncmp (word[5], "glx2", 4) == 0)
  430. {
  431. serv->use_listargs = TRUE; /* use the /list args */
  432. }
  433. goto def;
  434. case 5:
  435. inbound_005 (serv, word);
  436. goto def;
  437. case 263: /*Server load is temporarily too heavy */
  438. if (fe_is_chanwindow (sess->server))
  439. {
  440. fe_chan_list_end (sess->server);
  441. fe_message (word_eol[5] + 1, FE_MSG_ERROR);
  442. }
  443. goto def;
  444. case 290: /* CAPAB reply */
  445. if (strstr (word_eol[1], "IDENTIFY-MSG"))
  446. {
  447. serv->have_idmsg = TRUE;
  448. break;
  449. }
  450. goto def;
  451. case 301:
  452. inbound_away (serv, word[4],
  453. (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5]);
  454. break;
  455. case 302:
  456. if (serv->skip_next_userhost)
  457. {
  458. char *eq = strchr (word[4], '=');
  459. if (eq)
  460. {
  461. *eq = 0;
  462. if (!serv->p_cmp (word[4] + 1, serv->nick))
  463. {
  464. char *at = strrchr (eq + 1, '@');
  465. if (at)
  466. inbound_foundip (sess, at + 1);
  467. }
  468. }
  469. serv->skip_next_userhost = FALSE;
  470. break;
  471. }
  472. else goto def;
  473. case 303:
  474. word[4]++;
  475. notify_markonline (serv, word);
  476. break;
  477. case 305:
  478. inbound_uback (serv);
  479. goto def;
  480. case 306:
  481. inbound_uaway (serv);
  482. goto def;
  483. case 312:
  484. if (!serv->skip_next_whois)
  485. EMIT_SIGNAL (XP_TE_WHOIS3, whois_sess, word[4], word_eol[5], NULL, NULL, 0);
  486. else
  487. inbound_user_info (sess, NULL, NULL, NULL, word[5], word[4], NULL, 0xff);
  488. break;
  489. case 311: /* WHOIS 1st line */
  490. serv->inside_whois = 1;
  491. inbound_user_info_start (sess, word[4]);
  492. if (!serv->skip_next_whois)
  493. EMIT_SIGNAL (XP_TE_WHOIS1, whois_sess, word[4], word[5],
  494. word[6], word_eol[8] + 1, 0);
  495. else
  496. inbound_user_info (sess, NULL, word[5], word[6], NULL, word[4],
  497. word_eol[8][0] == ':' ? word_eol[8] + 1 : word_eol[8], 0xff);
  498. break;
  499. case 314: /* WHOWAS */
  500. inbound_user_info_start (sess, word[4]);
  501. EMIT_SIGNAL (XP_TE_WHOIS1, whois_sess, word[4], word[5],
  502. word[6], word_eol[8] + 1, 0);
  503. break;
  504. case 317:
  505. if (!serv->skip_next_whois)
  506. {
  507. time_t timestamp = (time_t) atol (word[6]);
  508. long idle = atol (word[5]);
  509. char *tim;
  510. char outbuf[64];
  511. snprintf (outbuf, sizeof (outbuf),
  512. "%02ld:%02ld:%02ld", idle / 3600, (idle / 60) % 60,
  513. idle % 60);
  514. if (timestamp == 0)
  515. EMIT_SIGNAL (XP_TE_WHOIS4, whois_sess, word[4],
  516. outbuf, NULL, NULL, 0);
  517. else
  518. {
  519. tim = ctime (&timestamp);
  520. tim[19] = 0; /* get rid of the \n */
  521. EMIT_SIGNAL (XP_TE_WHOIS4T, whois_sess, word[4],
  522. outbuf, tim, NULL, 0);
  523. }
  524. }
  525. break;
  526. case 318: /* END OF WHOIS */
  527. if (!serv->skip_next_whois)
  528. EMIT_SIGNAL (XP_TE_WHOIS6, whois_sess, word[4], NULL,
  529. NULL, NULL, 0);
  530. serv->skip_next_whois = 0;
  531. serv->inside_whois = 0;
  532. break;
  533. case 313:
  534. case 319:
  535. if (!serv->skip_next_whois)
  536. EMIT_SIGNAL (XP_TE_WHOIS2, whois_sess, word[4],
  537. word_eol[5] + 1, NULL, NULL, 0);
  538. break;
  539. case 307: /* dalnet version */
  540. case 320: /* :is an identified user */
  541. if (!serv->skip_next_whois)
  542. EMIT_SIGNAL (XP_TE_WHOIS_ID, whois_sess, word[4],
  543. word_eol[5] + 1, NULL, NULL, 0);
  544. break;
  545. case 321:
  546. if (!fe_is_chanwindow (sess->server))
  547. EMIT_SIGNAL (XP_TE_CHANLISTHEAD, serv->server_session, NULL, NULL, NULL, NULL, 0);
  548. break;
  549. case 322:
  550. if (fe_is_chanwindow (sess->server))
  551. {
  552. fe_add_chan_list (sess->server, word[4], word[5], word_eol[6] + 1);
  553. } else
  554. {
  555. PrintTextf (serv->server_session, "%-16s %-7d %s\017\n",
  556. word[4], atoi (word[5]), word_eol[6] + 1);
  557. }
  558. break;
  559. case 323:
  560. if (!fe_is_chanwindow (sess->server))
  561. EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0);
  562. else
  563. fe_chan_list_end (sess->server);
  564. break;
  565. case 324:
  566. sess = find_channel (serv, word[4]);
  567. if (!sess)
  568. sess = serv->server_session;
  569. if (sess->ignore_mode)
  570. sess->ignore_mode = FALSE;
  571. else
  572. EMIT_SIGNAL (XP_TE_CHANMODES, sess, word[4], word_eol[5],
  573. NULL, NULL, 0);
  574. fe_update_mode_buttons (sess, 't', '-');
  575. fe_update_mode_buttons (sess, 'n', '-');
  576. fe_update_mode_buttons (sess, 's', '-');
  577. fe_update_mode_buttons (sess, 'i', '-');
  578. fe_update_mode_buttons (sess, 'p', '-');
  579. fe_update_mode_buttons (sess, 'm', '-');
  580. fe_update_mode_buttons (sess, 'l', '-');
  581. fe_update_mode_buttons (sess, 'k', '-');
  582. handle_mode (serv, word, word_eol, "", TRUE);
  583. break;
  584. case 329:
  585. sess = find_channel (serv, word[4]);
  586. if (sess)
  587. {
  588. if (sess->ignore_date)
  589. sess->ignore_date = FALSE;
  590. else
  591. channel_date (sess, word[4], word[5]);
  592. }
  593. break;
  594. case 330:
  595. if (!serv->skip_next_whois)
  596. EMIT_SIGNAL (XP_TE_WHOIS_AUTH, whois_sess, word[4],
  597. word_eol[6] + 1, word[5], NULL, 0);
  598. break;
  599. case 332:
  600. inbound_topic (serv, word[4],
  601. (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5]);
  602. break;
  603. case 333:
  604. inbound_topictime (serv, word[4], word[5], atol (word[6]));
  605. break;
  606. #if 0
  607. case 338: /* Undernet Real user@host, Real IP */
  608. EMIT_SIGNAL (XP_TE_WHOIS_REALHOST, sess, word[4], word[5], word[6],
  609. (word_eol[7][0]==':') ? word_eol[7]+1 : word_eol[7], 0);
  610. break;
  611. #endif
  612. case 341: /* INVITE ACK */
  613. EMIT_SIGNAL (XP_TE_UINVITE, sess, word[4], word[5], serv->servername,
  614. NULL, 0);
  615. break;
  616. case 352: /* WHO */
  617. {
  618. unsigned int away = 0;
  619. session *who_sess = find_channel (serv, word[4]);
  620. if (*word[9] == 'G')
  621. away = 1;
  622. inbound_user_info (sess, word[4], word[5], word[6], word[7],
  623. word[8], word_eol[11], away);
  624. /* try to show only user initiated whos */
  625. if (!who_sess || !who_sess->doing_who)
  626. EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1],
  627. word[2], NULL, 0);
  628. }
  629. break;
  630. case 354: /* undernet WHOX: used as a reply for irc_away_status */
  631. {
  632. unsigned int away = 0;
  633. session *who_sess;
  634. /* irc_away_status sends out a "152" */
  635. if (!strcmp (word[4], "152"))
  636. {
  637. who_sess = find_channel (serv, word[5]);
  638. if (*word[7] == 'G')
  639. away = 1;
  640. /* :SanJose.CA.us.undernet.org 354 z1 152 #zed1 z1 H@ */
  641. inbound_user_info (sess, word[5], 0, 0, 0, word[6], 0, away);
  642. /* try to show only user initiated whos */
  643. if (!who_sess || !who_sess->doing_who)
  644. EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
  645. word[1], word[2], NULL, 0);
  646. } else
  647. goto def;
  648. }
  649. break;
  650. case 315: /* END OF WHO */
  651. {
  652. session *who_sess;
  653. who_sess = find_channel (serv, word[4]);
  654. if (who_sess)
  655. {
  656. if (!who_sess->doing_who)
  657. EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
  658. word[1], word[2], NULL, 0);
  659. who_sess->doing_who = FALSE;
  660. } else
  661. {
  662. if (!serv->doing_dns)
  663. EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
  664. word[1], word[2], NULL, 0);
  665. serv->doing_dns = FALSE;
  666. }
  667. }
  668. break;
  669. case 348: /* +e-list entry */
  670. if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], TRUE))
  671. goto def;
  672. break;
  673. case 349: /* end of exemption list */
  674. sess = find_channel (serv, word[4]);
  675. if (!sess)
  676. {
  677. sess = serv->front_session;
  678. goto def;
  679. }
  680. if (!fe_is_banwindow (sess))
  681. goto def;
  682. fe_ban_list_end (sess, TRUE);
  683. break;
  684. case 353: /* NAMES */
  685. inbound_nameslist (serv, word[5],
  686. (word_eol[6][0] == ':') ? word_eol[6] + 1 : word_eol[6]);
  687. break;
  688. case 366:
  689. if (!inbound_nameslist_end (serv, word[4]))
  690. goto def;
  691. break;
  692. case 367: /* banlist entry */
  693. inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], FALSE);
  694. break;
  695. case 368:
  696. sess = find_channel (serv, word[4]);
  697. if (!sess)
  698. {
  699. sess = serv->front_session;
  700. goto def;
  701. }
  702. if (!fe_is_banwindow (sess))
  703. goto def;
  704. fe_ban_list_end (sess, FALSE);
  705. break;
  706. case 369: /* WHOWAS end */
  707. case 406: /* WHOWAS error */
  708. EMIT_SIGNAL (XP_TE_SERVTEXT, whois_sess, text, word[1], word[2], NULL, 0);
  709. serv->inside_whois = 0;
  710. break;
  711. case 372: /* motd text */
  712. case 375: /* motd start */
  713. if (!prefs.skipmotd || serv->motd_skipped)
  714. EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL,
  715. NULL, 0);
  716. break;
  717. case 376: /* end of motd */
  718. case 422: /* motd file is missing */
  719. inbound_login_end (sess, text);
  720. break;
  721. case 433: /* nickname in use */
  722. case 432: /* erroneous nickname */
  723. if (serv->end_of_motd)
  724. goto def;
  725. inbound_next_nick (sess, word[4]);
  726. break;
  727. case 437:
  728. if (serv->end_of_motd || is_channel (serv, word[4]))
  729. goto def;
  730. inbound_next_nick (sess, word[4]);
  731. break;
  732. case 471:
  733. EMIT_SIGNAL (XP_TE_USERLIMIT, sess, word[4], NULL, NULL, NULL, 0);
  734. break;
  735. case 473:
  736. EMIT_SIGNAL (XP_TE_INVITE, sess, word[4], NULL, NULL, NULL, 0);
  737. break;
  738. case 474:
  739. EMIT_SIGNAL (XP_TE_BANNED, sess, word[4], NULL, NULL, NULL, 0);
  740. break;
  741. case 475:
  742. EMIT_SIGNAL (XP_TE_KEYWORD, sess, word[4], NULL, NULL, NULL, 0);
  743. break;
  744. case 601:
  745. notify_set_offline (serv, word[4], FALSE);
  746. break;
  747. case 605:
  748. notify_set_offline (serv, word[4], TRUE);
  749. break;
  750. case 600:
  751. case 604:
  752. notify_set_online (serv, word[4]);
  753. break;
  754. default:
  755. if (serv->inside_whois && word[4][0])
  756. {
  757. /* some unknown WHOIS reply, ircd coders make them up weekly */
  758. if (!serv->skip_next_whois)
  759. EMIT_SIGNAL (XP_TE_WHOIS_SPECIAL, whois_sess, word[4],
  760. (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5],
  761. word[2], NULL, 0);
  762. return;
  763. }
  764. def:
  765. if (is_channel (serv, word[4]))
  766. {
  767. session *realsess = find_channel (serv, word[4]);
  768. if (!realsess)
  769. realsess = serv->server_session;
  770. EMIT_SIGNAL (XP_TE_SERVTEXT, realsess, text, word[1], word[2], NULL, 0);
  771. } else
  772. {
  773. EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1],
  774. word[2], NULL, 0);
  775. }
  776. }
  777. }
  778. /* handle named messages that starts with a ':' */
  779. static void
  780. process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
  781. {
  782. server *serv = sess->server;
  783. char ip[128], nick[NICKLEN];
  784. char *text, *ex;
  785. int len = strlen (type);
  786. /* fill in the "ip" and "nick" buffers */
  787. ex = strchr (word[1], '!');
  788. if (!ex) /* no '!', must be a server message */
  789. {
  790. safe_strcpy (ip, word[1], sizeof (ip));
  791. safe_strcpy (nick, word[1], sizeof (nick));
  792. } else
  793. {
  794. safe_strcpy (ip, ex + 1, sizeof (ip));
  795. ex[0] = 0;
  796. safe_strcpy (nick, word[1], sizeof (nick));
  797. ex[0] = '!';
  798. }
  799. if (len == 4)
  800. {
  801. guint32 t;
  802. t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
  803. /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
  804. switch (t)
  805. {
  806. case WORDL('J','O','I','N'):
  807. {
  808. char *chan = word[3];
  809. if (*chan == ':')
  810. chan++;
  811. if (!serv->p_cmp (nick, serv->nick))
  812. inbound_ujoin (serv, chan, nick, ip);
  813. else
  814. inbound_join (serv, chan, nick, ip);
  815. }
  816. return;
  817. case WORDL('K','I','C','K'):
  818. {
  819. char *kicked = word[4];
  820. char *reason = word_eol[5];
  821. if (*kicked)
  822. {
  823. if (*reason == ':')
  824. reason++;
  825. if (!strcmp (kicked, serv->nick))
  826. inbound_ukick (serv, word[3], nick, reason);
  827. else
  828. inbound_kick (serv, word[3], kicked, nick, reason);
  829. }
  830. }
  831. return;
  832. case WORDL('K','I','L','L'):
  833. EMIT_SIGNAL (XP_TE_KILL, sess, nick, word_eol[5], NULL, NULL, 0);
  834. return;
  835. case WORDL('M','O','D','E'):
  836. handle_mode (serv, word, word_eol, nick, FALSE); /* modes.c */
  837. return;
  838. case WORDL('N','I','C','K'):
  839. inbound_newnick (serv, nick, (word_eol[3][0] == ':')
  840. ? word_eol[3] + 1 : word_eol[3], FALSE);
  841. return;
  842. case WORDL('P','A','R','T'):
  843. {
  844. char *chan = word[3];
  845. char *reason = word_eol[4];
  846. if (*chan == ':')
  847. chan++;
  848. if (*reason == ':')
  849. reason++;
  850. if (!strcmp (nick, serv->nick))
  851. inbound_upart (serv, chan, ip, reason);
  852. else
  853. inbound_part (serv, chan, nick, ip, reason);
  854. }
  855. return;
  856. case WORDL('P','O','N','G'):
  857. inbound_ping_reply (serv->server_session,
  858. (word[4][0] == ':') ? word[4] + 1 : word[4], word[3]);
  859. return;
  860. case WORDL('Q','U','I','T'):
  861. inbound_quit (serv, nick, ip,
  862. (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3]);
  863. return;
  864. }
  865. goto garbage;
  866. }
  867. else if (len >= 5)
  868. {
  869. guint32 t;
  870. t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
  871. /* this should compile to a bunch of: CMP.L, JE ... nice & fast */
  872. switch (t)
  873. {
  874. case WORDL('I','N','V','I'):
  875. if (ignore_check (word[1], IG_INVI))
  876. return;
  877. if (word[4][0] == ':')
  878. EMIT_SIGNAL (XP_TE_INVITED, sess, word[4] + 1, nick,
  879. serv->servername, NULL, 0);
  880. else
  881. EMIT_SIGNAL (XP_TE_INVITED, sess, word[4], nick,
  882. serv->servername, NULL, 0);
  883. return;
  884. case WORDL('N','O','T','I'):
  885. {
  886. int id = FALSE; /* identified */
  887. text = word_eol[4];
  888. if (*text == ':')
  889. text++;
  890. if (serv->have_idmsg)
  891. {
  892. if (*text == '+')
  893. {
  894. id = TRUE;
  895. text++;
  896. } else if (*text == '-')
  897. text++;
  898. }
  899. if (!ignore_check (word[1], IG_NOTI))
  900. inbound_notice (serv, word[3], nick, text, ip, id);
  901. }
  902. return;
  903. case WORDL('P','R','I','V'):
  904. {
  905. char *to = word[3];
  906. int len;
  907. int id = FALSE; /* identified */
  908. if (*to)
  909. {
  910. text = word_eol[4];
  911. if (*text == ':')
  912. text++;
  913. if (serv->have_idmsg)
  914. {
  915. if (*text == '+')
  916. {
  917. id = TRUE;
  918. text++;
  919. } else if (*text == '-')
  920. text++;
  921. }
  922. len = strlen (text);
  923. if (text[0] == 1 && text[len - 1] == 1) /* ctcp */
  924. {
  925. text[len - 1] = 0;
  926. text++;
  927. if (strncasecmp (text, "ACTION", 6) != 0)
  928. flood_check (nick, ip, serv, sess, 0);
  929. if (strncasecmp (text, "DCC ", 4) == 0)
  930. /* redo this with handle_quotes TRUE */
  931. process_data_init (word[1], word_eol[1], word, word_eol, TRUE, FALSE);
  932. ctcp_handle (sess, to, nick, ip, text, word, word_eol, id);
  933. } else
  934. {
  935. if (is_channel (serv, to))
  936. {
  937. if (ignore_check (word[1], IG_CHAN))
  938. return;
  939. inbound_chanmsg (serv, NULL, to, nick, text, FALSE, id);
  940. } else
  941. {
  942. if (ignore_check (word[1], IG_PRIV))
  943. return;
  944. inbound_privmsg (serv, nick, ip, text, id);
  945. }
  946. }
  947. }
  948. }
  949. return;
  950. case WORDL('T','O','P','I'):
  951. inbound_topicnew (serv, nick, word[3],
  952. (word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4]);
  953. return;
  954. case WORDL('W','A','L','L'):
  955. text = word_eol[3];
  956. if (*text == ':')
  957. text++;
  958. EMIT_SIGNAL (XP_TE_WALLOPS, sess, nick, text, NULL, NULL, 0);
  959. return;
  960. }
  961. }
  962. garbage:
  963. /* unknown message */
  964. PrintTextf (sess, "GARBAGE: %s\n", word_eol[1]);
  965. }
  966. /* handle named messages that DON'T start with a ':' */
  967. static void
  968. process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol[])
  969. {
  970. sess = sess->server->server_session;
  971. if (!strncmp (buf, "PING ", 5))
  972. {
  973. tcp_sendf (sess->server, "PONG %s\r\n", buf + 5);
  974. return;
  975. }
  976. if (!strncmp (buf, "ERROR", 5))
  977. {
  978. EMIT_SIGNAL (XP_TE_SERVERERROR, sess, buf + 7, NULL, NULL, NULL, 0);
  979. return;
  980. }
  981. if (!strncmp (buf, "NOTICE ", 7))
  982. {
  983. buf = word_eol[3];
  984. if (*buf == ':')
  985. buf++;
  986. EMIT_SIGNAL (XP_TE_SERVNOTICE, sess, buf, sess->server->servername, NULL, NULL, 0);
  987. return;
  988. }
  989. EMIT_SIGNAL (XP_TE_SERVTEXT, sess, buf, sess->server->servername, rawname, NULL, 0);
  990. }
  991. /* irc_inline() - 1 single line received from serv */
  992. static void
  993. irc_inline (server *serv, char *buf, int len)
  994. {
  995. session *sess, *tmp;
  996. char *type, *text;
  997. char *word[PDIWORDS+1];
  998. char *word_eol[PDIWORDS+1];
  999. char pdibuf_static[522]; /* 1 line can potentially be 512*6 in utf8 */
  1000. char *pdibuf = pdibuf_static;
  1001. url_check_line (buf, len);
  1002. /* need more than 522? fall back to malloc */
  1003. if (len >= sizeof (pdibuf_static))
  1004. pdibuf = malloc (len + 1);
  1005. sess = serv->front_session;
  1006. /* Python relies on this */
  1007. word[PDIWORDS] = NULL;
  1008. word_eol[PDIWORDS] = NULL;
  1009. if (buf[0] == ':')
  1010. {
  1011. /* split line into words and words_to_end_of_line */
  1012. process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
  1013. /* find a context for this message */
  1014. if (is_channel (serv, word[3]))
  1015. {
  1016. tmp = find_channel (serv, word[3]);
  1017. if (tmp)
  1018. sess = tmp;
  1019. }
  1020. /* for server messages, the 2nd word is the "message type" */
  1021. type = word[2];
  1022. word[0] = type;
  1023. word_eol[1] = buf; /* keep the ":" for plugins */
  1024. if (plugin_emit_server (sess, type, word, word_eol))
  1025. goto xit;
  1026. word[1]++;
  1027. word_eol[1] = buf + 1; /* but not for xchat internally */
  1028. } else
  1029. {
  1030. process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
  1031. word[0] = type = word[1];
  1032. if (plugin_emit_server (sess, type, word, word_eol))
  1033. goto xit;
  1034. }
  1035. if (buf[0] != ':')
  1036. {
  1037. process_named_servermsg (sess, buf, word[0], word_eol);
  1038. goto xit;
  1039. }
  1040. /* see if the second word is a numeric */
  1041. if (isdigit ((unsigned char) word[2][0]))
  1042. {
  1043. text = word_eol[4];
  1044. if (*text == ':')
  1045. text++;
  1046. process_numeric (sess, atoi (word[2]), word, word_eol, text);
  1047. } else
  1048. {
  1049. process_named_msg (sess, type, word, word_eol);
  1050. }
  1051. xit:
  1052. if (pdibuf != pdibuf_static)
  1053. free (pdibuf);
  1054. }
  1055. void
  1056. proto_fill_her_up (server *serv)
  1057. {
  1058. serv->p_inline = irc_inline;
  1059. serv->p_invite = irc_invite;
  1060. serv->p_cycle = irc_cycle;
  1061. serv->p_ctcp = irc_ctcp;
  1062. serv->p_nctcp = irc_nctcp;
  1063. serv->p_quit = irc_quit;
  1064. serv->p_kick = irc_kick;
  1065. serv->p_part = irc_part;
  1066. serv->p_ns_identify = irc_ns_identify;
  1067. serv->p_ns_ghost = irc_ns_ghost;
  1068. serv->p_join = irc_join;
  1069. serv->p_join_list = irc_join_list;
  1070. serv->p_login = irc_login;
  1071. serv->p_join_info = irc_join_info;
  1072. serv->p_mode = irc_mode;
  1073. serv->p_user_list = irc_user_list;
  1074. serv->p_away_status = irc_away_status;
  1075. /*serv->p_get_ip = irc_get_ip;*/
  1076. serv->p_whois = irc_user_whois;
  1077. serv->p_get_ip = irc_user_list;
  1078. serv->p_get_ip_uh = irc_userhost;
  1079. serv->p_set_back = irc_set_back;
  1080. serv->p_set_away = irc_set_away;
  1081. serv->p_message = irc_message;
  1082. serv->p_action = irc_action;
  1083. serv->p_notice = irc_notice;
  1084. serv->p_topic = irc_topic;
  1085. serv->p_list_channels = irc_list_channels;
  1086. serv->p_change_nick = irc_change_nick;
  1087. serv->p_names = irc_names;
  1088. serv->p_ping = irc_ping;
  1089. serv->p_raw = irc_raw;
  1090. serv->p_cmp = rfc_casecmp; /* can be changed by 005 in modes.c */
  1091. }