PageRenderTime 88ms CodeModel.GetById 37ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/outbound.cpp

https://bitbucket.org/ZachThibeau/pchat-irc
C++ | 4323 lines | 3628 code | 531 blank | 164 comment | 772 complexity | a58c9f02e7870bf087ca9704557a5602 MD5 | raw file
Possible License(s): GPL-2.0
  1. /* X-Chat
  2. * Copyright (C) 1998 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. #define _GNU_SOURCE // for memrchr
  19. #include <string.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <limits.h>
  24. #include <errno.h>
  25. #define WANTSOCKET
  26. #define WANTARPA
  27. #include "inet.h"
  28. #ifndef WIN32
  29. #include <sys/wait.h>
  30. #endif
  31. #ifdef __GNUC__
  32. #include <unistd.h>
  33. #endif
  34. #include <time.h>
  35. #include <signal.h>
  36. #include <sys/stat.h>
  37. #include <fcntl.h>
  38. #include "xchat.h"
  39. #include "xchatc.h"
  40. #include "outbound.h"
  41. #include "cfgfiles.h" // xchat_fopen_file()
  42. #include "chanopt.h"
  43. #include "dcc.h"
  44. #include "fe.h"
  45. #include "ignore.h"
  46. #include "inbound.h"
  47. #include "modes.h"
  48. #include "network.h" // net_ip()
  49. #include "notify.h"
  50. #include "plugin.h"
  51. #include "server.h"
  52. #include "servlist.h"
  53. #include "text.h"
  54. #include "tree.h"
  55. #include "userlist.h"
  56. #include "util.h"
  57. #ifdef USE_DEBUG
  58. extern int current_mem_usage;
  59. #endif
  60. #define TBUFSIZE 4096
  61. static void help(session *sess, char *tbuf, char *helpcmd, int quiet);
  62. static int cmd_server(session *sess, char *tbuf, char *word[], char *word_eol[]);
  63. static void handle_say(session *sess, char *text, int check_spch);
  64. static void notj_msg(session *sess)
  65. {
  66. PrintText(sess, _("No channel joined. Try /join #<channel>\n"));
  67. }
  68. void notc_msg(session *sess)
  69. {
  70. PrintText(sess, _("Not connected. Try /server <host> [<port>]\n"));
  71. }
  72. static char* random_line(char *file_name)
  73. {
  74. FILE *fh;
  75. char buf[512];
  76. int lines, ran;
  77. if (!file_name[0])
  78. goto nofile;
  79. fh = xchat_fopen_file(file_name, "r", 0);
  80. if (!fh)
  81. {
  82. nofile:
  83. // reason is not a file, an actual reason!
  84. return strdup(file_name);
  85. }
  86. // count number of lines in file
  87. lines = 0;
  88. while (fgets(buf, sizeof(buf), fh))
  89. lines++;
  90. if (lines < 1)
  91. goto nofile;
  92. // go down a random number
  93. rewind(fh);
  94. ran = RAND_INT(lines);
  95. do
  96. {
  97. fgets(buf, sizeof(buf), fh);
  98. lines--;
  99. }
  100. while (lines > ran);
  101. fclose(fh);
  102. buf[strlen(buf) - 1] = 0; // remove the trailing '\n'
  103. return strdup(buf);
  104. }
  105. void server_sendpart(server* serv, char *channel, char *reason)
  106. {
  107. if (!reason)
  108. {
  109. reason = random_line(prefs.partreason);
  110. serv->p_part(serv, channel, reason);
  111. free(reason);
  112. }
  113. else
  114. {
  115. // reason set by /quit, /close argument
  116. serv->p_part(serv, channel, reason);
  117. }
  118. }
  119. void server_sendquit(session* sess)
  120. {
  121. char *rea, *colrea;
  122. if (!sess->quitreason)
  123. {
  124. colrea = strdup(prefs.quitreason);
  125. check_special_chars(colrea, FALSE);
  126. rea = random_line(colrea);
  127. free(colrea);
  128. sess->server->p_quit(sess->server, rea);
  129. free(rea);
  130. }
  131. else
  132. {
  133. // reason set by /quit, /close argument
  134. sess->server->p_quit(sess->server, sess->quitreason);
  135. }
  136. }
  137. void process_data_init(char *buf, char *cmd, char *word[],
  138. char *word_eol[], bool handle_quotes,
  139. bool allow_escape_quotes)
  140. {
  141. int wordcount = 2;
  142. int space = FALSE;
  143. int quote = FALSE;
  144. int j = 0;
  145. int len;
  146. word[0] = "\000\000";
  147. word_eol[0] = "\000\000";
  148. word[1] = buf;
  149. word_eol[1] = cmd;
  150. while (1)
  151. {
  152. switch (*cmd)
  153. {
  154. case 0:
  155. buf[j] = 0;
  156. for (j = wordcount; j < PDIWORDS; j++)
  157. {
  158. word[j] = "\000\000";
  159. word_eol[j] = "\000\000";
  160. }
  161. return;
  162. case '\042':
  163. if (!handle_quotes)
  164. goto def;
  165. // two quotes turn into 1
  166. if (allow_escape_quotes && cmd[1] == '\042')
  167. {
  168. cmd++;
  169. goto def;
  170. }
  171. if (quote)
  172. {
  173. quote = FALSE;
  174. space = FALSE;
  175. } else
  176. quote = TRUE;
  177. cmd++;
  178. break;
  179. case ' ':
  180. if (!quote)
  181. {
  182. if (!space)
  183. {
  184. buf[j] = 0;
  185. j++;
  186. if (wordcount < PDIWORDS)
  187. {
  188. word[wordcount] = &buf[j];
  189. word_eol[wordcount] = cmd + 1;
  190. wordcount++;
  191. }
  192. space = TRUE;
  193. }
  194. cmd++;
  195. break;
  196. }
  197. default:
  198. def:
  199. space = FALSE;
  200. len = g_utf8_skip[((unsigned char*)cmd)[0]];
  201. if (len == 1)
  202. {
  203. buf[j] = *cmd;
  204. j++;
  205. cmd++;
  206. }
  207. else
  208. {
  209. // skip past a multi-byte utf8 char
  210. memcpy(buf + j, cmd, len);
  211. j += len;
  212. cmd += len;
  213. }
  214. }
  215. }
  216. }
  217. static int cmd_addbutton(session *sess, char *tbuf, char *word[], char *word_eol[])
  218. {
  219. if (*word[2] && *word_eol[3])
  220. {
  221. if (sess->type == SESS_DIALOG)
  222. {
  223. list_addentry(&dlgbutton_list, word_eol[3], word[2]);
  224. fe_dlgbuttons_update(sess);
  225. }
  226. else
  227. {
  228. list_addentry(&button_list, word_eol[3], word[2]);
  229. fe_buttons_update(sess);
  230. }
  231. return TRUE;
  232. }
  233. return FALSE;
  234. }
  235. static int cmd_allchannels(session *sess, char *tbuf, char *word[], char *word_eol[])
  236. {
  237. GSList *list = sess_list;
  238. if (!*word_eol[2])
  239. return FALSE;
  240. while (list)
  241. {
  242. sess = (session*)list->data;
  243. if (sess->type == SESS_CHANNEL && sess->channel[0] && sess->server->connected)
  244. {
  245. handle_command(sess, word_eol[2], FALSE);
  246. }
  247. list = list->next;
  248. }
  249. return TRUE;
  250. }
  251. static int cmd_allchannelslocal(session *sess, char *tbuf, char *word[], char *word_eol[])
  252. {
  253. GSList *list = sess_list;
  254. server *serv = sess->server;
  255. if (!*word_eol[2])
  256. return FALSE;
  257. while (list)
  258. {
  259. sess = (session*)list->data;
  260. if (sess->type == SESS_CHANNEL && sess->channel[0] &&
  261. sess->server->connected && sess->server == serv)
  262. {
  263. handle_command(sess, word_eol[2], FALSE);
  264. }
  265. list = list->next;
  266. }
  267. return TRUE;
  268. }
  269. static int cmd_allservers(session *sess, char *tbuf, char *word[], char *word_eol[])
  270. {
  271. GSList *list;
  272. server *serv;
  273. if (!*word_eol[2])
  274. return FALSE;
  275. list = serv_list;
  276. while (list)
  277. {
  278. serv = (server*)list->data;
  279. if (serv->connected)
  280. handle_command(serv->front_session, word_eol[2], FALSE);
  281. list = list->next;
  282. }
  283. return TRUE;
  284. }
  285. static int cmd_away(session *sess, char *tbuf, char *word[], char *word_eol[])
  286. {
  287. GSList *list;
  288. char *reason = word_eol[2];
  289. if (!(*reason))
  290. {
  291. if (sess->server->is_away)
  292. {
  293. if (sess->server->last_away_reason)
  294. PrintTextf(sess, _("Already marked away: %s\n"), sess->server->last_away_reason);
  295. return FALSE;
  296. }
  297. if (sess->server->reconnect_away)
  298. reason = sess->server->last_away_reason;
  299. else
  300. // must manage memory pointed to by random_line()
  301. reason = random_line(prefs.awayreason);
  302. }
  303. sess->server->p_set_away(sess->server, reason);
  304. if (prefs.show_away_message)
  305. {
  306. snprintf(tbuf, TBUFSIZE, "me is away: %s", reason);
  307. for (list = sess_list; list; list = list->next)
  308. {
  309. // am I the right server and not a dialog box
  310. if (((session*)list->data)->server == sess->server
  311. && ((session*)list->data)->type == SESS_CHANNEL
  312. && ((session*)list->data)->channel[0])
  313. {
  314. handle_command((session*)list->data, tbuf, TRUE);
  315. }
  316. }
  317. }
  318. if (sess->server->last_away_reason != reason)
  319. {
  320. if (sess->server->last_away_reason)
  321. free(sess->server->last_away_reason);
  322. if (reason == word_eol[2])
  323. sess->server->last_away_reason = strdup(reason);
  324. else
  325. sess->server->last_away_reason = reason;
  326. }
  327. if (!sess->server->connected)
  328. sess->server->reconnect_away = 1;
  329. return TRUE;
  330. }
  331. static int cmd_back(session *sess, char *tbuf, char *word[], char *word_eol[])
  332. {
  333. GSList *list;
  334. unsigned int gone;
  335. if (sess->server->is_away)
  336. {
  337. sess->server->p_set_back(sess->server);
  338. if (prefs.show_away_message)
  339. {
  340. gone = time(NULL) - sess->server->away_time;
  341. sprintf(tbuf, "me is back (gone %.2d:%.2d:%.2d)", gone / 3600,
  342. (gone / 60) % 60, gone % 60);
  343. for (list = sess_list; list; list = list->next)
  344. {
  345. // am I the right server and not a dialog box
  346. if (((session*)list->data)->server == sess->server
  347. && ((session*)list->data)->type == SESS_CHANNEL
  348. && ((session*)list->data)->channel[0])
  349. {
  350. handle_command((session*)list->data, tbuf, TRUE);
  351. }
  352. }
  353. }
  354. }
  355. else
  356. {
  357. PrintText(sess, _("Already marked back.\n"));
  358. }
  359. if (sess->server->last_away_reason)
  360. free(sess->server->last_away_reason);
  361. sess->server->last_away_reason = NULL;
  362. return TRUE;
  363. }
  364. static void ban(session* sess, char *tbuf, char *mask, char *bantypestr, int deop)
  365. {
  366. int bantype;
  367. User *user;
  368. char *at, *dot, *lastdot;
  369. char username[64], fullhost[128], domain[128], *mode, *p2;
  370. server *serv = sess->server;
  371. user = userlist_find(sess, mask);
  372. if (user && user->hostname) // it's a nickname, let's find a proper ban mask
  373. {
  374. if (deop)
  375. {
  376. mode = "-o+b ";
  377. p2 = user->nick;
  378. }
  379. else
  380. {
  381. mode = "+b";
  382. p2 = "";
  383. }
  384. mask = user->hostname;
  385. at = strchr(mask, '@'); // FIXME: utf8
  386. if (!at)
  387. return; // can't happen?
  388. *at = 0;
  389. if (mask[0] == '~' || mask[0] == '+' ||
  390. mask[0] == '=' || mask[0] == '^' || mask[0] == '-')
  391. {
  392. // the ident is prefixed with something, we replace that sign with a *
  393. safe_strcpy(username+1, mask+1, sizeof(username)-1);
  394. username[0] = '*';
  395. }
  396. else if (at - mask < USERNAMELEN)
  397. {
  398. // we just add an * in the begining of the ident
  399. safe_strcpy(username+1, mask, sizeof(username)-1);
  400. username[0] = '*';
  401. }
  402. else
  403. {
  404. // ident might be too long, we just ban what it gives and add an * in the end
  405. safe_strcpy(username, mask, sizeof(username));
  406. }
  407. *at = '@';
  408. safe_strcpy(fullhost, at + 1, sizeof(fullhost));
  409. dot = strchr(fullhost, '.');
  410. if (dot)
  411. {
  412. safe_strcpy(domain, dot, sizeof(domain));
  413. }
  414. else
  415. {
  416. safe_strcpy(domain, fullhost, sizeof(domain));
  417. }
  418. if (*bantypestr)
  419. bantype = atoi(bantypestr);
  420. else
  421. bantype = prefs.bantype;
  422. tbuf[0] = 0;
  423. if (inet_addr(fullhost) != -1) // "fullhost" is really an IP number
  424. {
  425. lastdot = strrchr(fullhost, '.');
  426. if (!lastdot)
  427. return; // can't happen?
  428. *lastdot = 0;
  429. strcpy(domain, fullhost);
  430. *lastdot = '.';
  431. switch (bantype)
  432. {
  433. case 0:
  434. snprintf(tbuf, TBUFSIZE, "%s%s *!*@%s.*", mode, p2, domain);
  435. break;
  436. case 1:
  437. snprintf(tbuf, TBUFSIZE, "%s%s *!*@%s", mode, p2, fullhost);
  438. break;
  439. case 2:
  440. snprintf(tbuf, TBUFSIZE, "%s%s *!%s@%s.*", mode, p2, username, domain);
  441. break;
  442. case 3:
  443. snprintf(tbuf, TBUFSIZE, "%s%s *!%s@%s", mode, p2, username, fullhost);
  444. break;
  445. }
  446. }
  447. else
  448. {
  449. switch (bantype)
  450. {
  451. case 0:
  452. snprintf(tbuf, TBUFSIZE, "%s%s *!*@*%s", mode, p2, domain);
  453. break;
  454. case 1:
  455. snprintf(tbuf, TBUFSIZE, "%s%s *!*@%s", mode, p2, fullhost);
  456. break;
  457. case 2:
  458. snprintf(tbuf, TBUFSIZE, "%s%s *!%s@*%s", mode, p2, username, domain);
  459. break;
  460. case 3:
  461. snprintf(tbuf, TBUFSIZE, "%s%s *!%s@%s", mode, p2, username, fullhost);
  462. break;
  463. }
  464. }
  465. }
  466. else
  467. {
  468. snprintf(tbuf, TBUFSIZE, "+b %s", mask);
  469. }
  470. serv->p_mode(serv, sess->channel, tbuf);
  471. }
  472. static int cmd_ban(session *sess, char *tbuf, char *word[], char *word_eol[])
  473. {
  474. char *mask = word[2];
  475. if (*mask)
  476. {
  477. ban(sess, tbuf, mask, word[3], 0);
  478. }
  479. else
  480. {
  481. sess->server->p_mode(sess->server, sess->channel, "+b"); // banlist
  482. }
  483. return TRUE;
  484. }
  485. static int cmd_unban(session *sess, char *tbuf, char *word[], char *word_eol[])
  486. {
  487. // Allow more than one mask in /unban -- tvk
  488. int i = 2;
  489. while (1)
  490. {
  491. if (!*word[i])
  492. {
  493. if (i == 2)
  494. return FALSE;
  495. send_channel_modes(sess, tbuf, word, 2, i, '-', 'b', 0);
  496. return TRUE;
  497. }
  498. i++;
  499. }
  500. }
  501. static int cmd_chanopt(session *sess, char *tbuf, char *word[], char *word_eol[])
  502. {
  503. // chanopt.c
  504. return chanopt_command(sess, tbuf, word, word_eol);
  505. }
  506. static int cmd_charset(session *sess, char *tbuf, char *word[], char *word_eol[])
  507. {
  508. server *serv = sess->server;
  509. const char *locale = NULL;
  510. int offset = 0;
  511. if (strcmp(word[2], "-quiet") == 0)
  512. offset++;
  513. if (!word[2 + offset][0])
  514. {
  515. g_get_charset(&locale);
  516. PrintTextf(sess, "Current charset: %s\n",
  517. serv->encoding ? serv->encoding : locale);
  518. return TRUE;
  519. }
  520. if (servlist_check_encoding(word[2 + offset]))
  521. {
  522. server_set_encoding(serv, word[2 + offset]);
  523. if (offset < 1)
  524. PrintTextf(sess, "Charset changed to: %s\n", word[2 + offset]);
  525. }
  526. else
  527. {
  528. PrintTextf(sess, "\0034Unknown charset:\017 %s\n", word[2 + offset]);
  529. }
  530. return TRUE;
  531. }
  532. static int cmd_clear(session *sess, char *tbuf, char *word[], char *word_eol[])
  533. {
  534. GSList *list = sess_list;
  535. char *reason = word_eol[2];
  536. if (strcasecmp(reason, "HISTORY") == 0)
  537. {
  538. history_free(&sess->history);
  539. return TRUE;
  540. }
  541. if (strncasecmp(reason, "all", 3) == 0)
  542. {
  543. while (list)
  544. {
  545. sess = (session*)list->data;
  546. if (!sess->nick_said)
  547. fe_text_clear((session*)list->data, 0);
  548. list = list->next;
  549. }
  550. return TRUE;
  551. }
  552. if (reason[0] != '-' && !isdigit(reason[0]) && reason[0] != 0)
  553. return FALSE;
  554. fe_text_clear(sess, atoi(reason));
  555. return TRUE;
  556. }
  557. static int cmd_close(session *sess, char *tbuf, char *word[], char *word_eol[])
  558. {
  559. GSList *list;
  560. if (strcmp(word[2], "-m") == 0)
  561. {
  562. list = sess_list;
  563. while (list)
  564. {
  565. sess = (session*)list->data;
  566. list = list->next;
  567. if (sess->type == SESS_DIALOG)
  568. fe_close_window(sess);
  569. }
  570. }
  571. else
  572. {
  573. if (*word_eol[2])
  574. sess->quitreason = word_eol[2];
  575. fe_close_window(sess);
  576. }
  577. return TRUE;
  578. }
  579. static int cmd_ctcp(session *sess, char *tbuf, char *word[], char *word_eol[])
  580. {
  581. int mbl;
  582. char *to = word[2];
  583. if (*to)
  584. {
  585. char *msg = word_eol[3];
  586. if (*msg)
  587. {
  588. unsigned char *cmd = (unsigned char*)msg;
  589. // make the first word upper case (as per RFC)
  590. while (1)
  591. {
  592. if (*cmd == ' ' || *cmd == 0)
  593. break;
  594. mbl = g_utf8_skip[*cmd];
  595. if (mbl == 1)
  596. *cmd = toupper(*cmd);
  597. cmd += mbl;
  598. }
  599. sess->server->p_ctcp(sess->server, to, msg);
  600. EMIT_SIGNAL(XP_TE_CTCPSEND, sess, to, msg, NULL, NULL, 0);
  601. return TRUE;
  602. }
  603. }
  604. return FALSE;
  605. }
  606. typedef void (*print)(void*, char*, ...);
  607. static int cmd_country(session *sess, char *tbuf, char *word[], char *word_eol[])
  608. {
  609. char *code = word[2];
  610. if (*code)
  611. {
  612. // search?
  613. if (strcmp(code, "-s") == 0)
  614. {
  615. country_search(word[3], sess, (print)PrintTextf);
  616. return TRUE;
  617. }
  618. // search, but forgot the -s
  619. if (strchr(code, '*'))
  620. {
  621. country_search(code, sess, (print)PrintTextf);
  622. return TRUE;
  623. }
  624. sprintf(tbuf, "%s = %s\n", code, country(code));
  625. PrintText(sess, tbuf);
  626. return TRUE;
  627. }
  628. return FALSE;
  629. }
  630. static int cmd_cycle(session *sess, char *tbuf, char *word[], char *word_eol[])
  631. {
  632. char *key = sess->channelkey;
  633. char *chan = word[2];
  634. if (!*chan)
  635. chan = sess->channel;
  636. if (*chan && sess->type == SESS_CHANNEL)
  637. {
  638. sess->server->p_cycle(sess->server, chan, key);
  639. return TRUE;
  640. }
  641. return FALSE;
  642. }
  643. static int cmd_dcc(session *sess, char *tbuf, char *word[], char *word_eol[])
  644. {
  645. int goodtype;
  646. DCC *dcc = 0;
  647. char *type = word[2];
  648. if (*type)
  649. {
  650. if (!strcasecmp(type, "HELP"))
  651. return FALSE;
  652. if (!strcasecmp(type, "CLOSE"))
  653. {
  654. if (*word[3] && *word[4])
  655. {
  656. goodtype = 0;
  657. if (!strcasecmp(word[3], "SEND"))
  658. {
  659. dcc = find_dcc(word[4], word[5], TYPE_SEND);
  660. dcc_abort(sess, dcc);
  661. goodtype = TRUE;
  662. }
  663. if (!strcasecmp(word[3], "GET"))
  664. {
  665. dcc = find_dcc(word[4], word[5], TYPE_RECV);
  666. dcc_abort(sess, dcc);
  667. goodtype = TRUE;
  668. }
  669. if (!strcasecmp(word[3], "CHAT"))
  670. {
  671. dcc = find_dcc(word[4], "", TYPE_CHATRECV);
  672. if (!dcc)
  673. dcc = find_dcc(word[4], "", TYPE_CHATSEND);
  674. dcc_abort(sess, dcc);
  675. goodtype = TRUE;
  676. }
  677. if (!goodtype)
  678. return FALSE;
  679. if (!dcc)
  680. EMIT_SIGNAL(XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
  681. return TRUE;
  682. }
  683. return FALSE;
  684. }
  685. if ((!strcasecmp(type, "CHAT")) || (!strcasecmp(type, "PCHAT")))
  686. {
  687. char *nick = word[3];
  688. int passive = (!strcasecmp(type, "PCHAT")) ? 1 : 0;
  689. if (*nick)
  690. dcc_chat(sess, nick, passive);
  691. return TRUE;
  692. }
  693. if (!strcasecmp(type, "LIST"))
  694. {
  695. dcc_show_list(sess);
  696. return TRUE;
  697. }
  698. if (!strcasecmp(type, "GET"))
  699. {
  700. char *nick = word[3];
  701. char *file = word[4];
  702. if (!*file)
  703. {
  704. if (*nick)
  705. dcc_get_nick(sess, nick);
  706. }
  707. else
  708. {
  709. dcc = find_dcc(nick, file, TYPE_RECV);
  710. if (dcc)
  711. dcc_get(dcc);
  712. else
  713. EMIT_SIGNAL(XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
  714. }
  715. return TRUE;
  716. }
  717. if ((!strcasecmp(type, "SEND")) || (!strcasecmp(type, "PSEND")))
  718. {
  719. int i = 3, maxcps;
  720. char *nick, *file;
  721. int passive = (!strcasecmp(type, "PSEND")) ? 1 : 0;
  722. nick = word[i];
  723. if (!*nick)
  724. return FALSE;
  725. maxcps = prefs.dcc_max_send_cps;
  726. if (!strncasecmp(nick, "-maxcps=", 8))
  727. {
  728. maxcps = atoi(nick + 8);
  729. i++;
  730. nick = word[i];
  731. if (!*nick)
  732. return FALSE;
  733. }
  734. i++;
  735. file = word[i];
  736. if (!*file)
  737. {
  738. fe_dcc_send_filereq(sess, nick, maxcps, passive);
  739. return TRUE;
  740. }
  741. do
  742. {
  743. dcc_send(sess, nick, file, maxcps, passive);
  744. i++;
  745. file = word[i];
  746. }
  747. while (*file);
  748. return TRUE;
  749. }
  750. return FALSE;
  751. }
  752. dcc_show_list(sess);
  753. return TRUE;
  754. }
  755. static int cmd_debug(session *sess, char *tbuf, char *word[], char *word_eol[])
  756. {
  757. session *s;
  758. server *v;
  759. GSList *list = sess_list;
  760. PrintText(sess, "Session T Channel WaitChan WillChan Server\n");
  761. while (list)
  762. {
  763. s = (session*)list->data;
  764. sprintf(tbuf, "%p %1x %-10.10s %-10.10s %-10.10s %p\n",
  765. s, s->type, s->channel, s->waitchannel,
  766. s->willjoinchannel, s->server);
  767. PrintText(sess, tbuf);
  768. list = list->next;
  769. }
  770. list = serv_list;
  771. PrintText(sess, "Server Sock Name\n");
  772. while (list)
  773. {
  774. v = (server*)list->data;
  775. sprintf(tbuf, "%p %-5d %s\n",
  776. v, v->sok, v->servername);
  777. PrintText(sess, tbuf);
  778. list = list->next;
  779. }
  780. sprintf(tbuf,
  781. "\nfront_session: %p\n"
  782. "current_tab: %p\n\n",
  783. sess->server->front_session, current_tab);
  784. PrintText(sess, tbuf);
  785. #ifdef USE_DEBUG
  786. sprintf(tbuf, "current mem: %d\n\n", current_mem_usage);
  787. PrintText(sess, tbuf);
  788. #endif /* !MEMORY_DEBUG */
  789. return TRUE;
  790. }
  791. static int cmd_delbutton(session *sess, char *tbuf, char *word[], char *word_eol[])
  792. {
  793. if (*word[2])
  794. {
  795. if (sess->type == SESS_DIALOG)
  796. {
  797. if (list_delentry(&dlgbutton_list, word[2]))
  798. fe_dlgbuttons_update(sess);
  799. }
  800. else
  801. {
  802. if (list_delentry(&button_list, word[2]))
  803. fe_buttons_update(sess);
  804. }
  805. return TRUE;
  806. }
  807. return FALSE;
  808. }
  809. static int cmd_dehop(session *sess, char *tbuf, char *word[], char *word_eol[])
  810. {
  811. int i = 2;
  812. while (1)
  813. {
  814. if (!*word[i])
  815. {
  816. if (i == 2)
  817. return FALSE;
  818. send_channel_modes(sess, tbuf, word, 2, i, '-', 'h', 0);
  819. return TRUE;
  820. }
  821. i++;
  822. }
  823. }
  824. static int cmd_deop(session *sess, char *tbuf, char *word[], char *word_eol[])
  825. {
  826. int i = 2;
  827. while (1)
  828. {
  829. if (!*word[i])
  830. {
  831. if (i == 2)
  832. return FALSE;
  833. send_channel_modes(sess, tbuf, word, 2, i, '-', 'o', 0);
  834. return TRUE;
  835. }
  836. i++;
  837. }
  838. }
  839. typedef struct
  840. {
  841. char **nicks;
  842. int i;
  843. session *sess;
  844. char *reason;
  845. char *tbuf;
  846. } multidata;
  847. static int mdehop_cb(User *user, multidata *data)
  848. {
  849. if (user->hop && !user->me)
  850. {
  851. data->nicks[data->i] = user->nick;
  852. data->i++;
  853. }
  854. return TRUE;
  855. }
  856. static int cmd_mdehop(session *sess, char *tbuf, char *word[], char *word_eol[])
  857. {
  858. char **nicks = (char**)malloc(sizeof(char*) * sess->hops);
  859. multidata data;
  860. data.nicks = nicks;
  861. data.i = 0;
  862. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)mdehop_cb, &data);
  863. send_channel_modes(sess, tbuf, nicks, 0, data.i, '-', 'h', 0);
  864. free(nicks);
  865. return TRUE;
  866. }
  867. static int mdeop_cb(User *user, multidata *data)
  868. {
  869. if (user->op && !user->me)
  870. {
  871. data->nicks[data->i] = user->nick;
  872. data->i++;
  873. }
  874. return TRUE;
  875. }
  876. static int cmd_mdeop(session *sess, char *tbuf, char *word[], char *word_eol[])
  877. {
  878. char **nicks = (char**)malloc(sizeof(char*) * sess->ops);
  879. multidata data;
  880. data.nicks = nicks;
  881. data.i = 0;
  882. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)mdeop_cb, &data);
  883. send_channel_modes(sess, tbuf, nicks, 0, data.i, '-', 'o', 0);
  884. free(nicks);
  885. return TRUE;
  886. }
  887. GSList *menu_list = NULL;
  888. static void
  889. menu_free (menu_entry *me)
  890. {
  891. free (me->path);
  892. if (me->label)
  893. free (me->label);
  894. if (me->cmd)
  895. free (me->cmd);
  896. if (me->ucmd)
  897. free (me->ucmd);
  898. if (me->group)
  899. free (me->group);
  900. if (me->icon)
  901. free (me->icon);
  902. free (me);
  903. }
  904. /* strings equal? but ignore underscores */
  905. int
  906. menu_streq (const char *s1, const char *s2, int def)
  907. {
  908. /* for separators */
  909. if (s1 == NULL && s2 == NULL)
  910. return 0;
  911. if (s1 == NULL || s2 == NULL)
  912. return 1;
  913. while (*s1)
  914. {
  915. if (*s1 == '_')
  916. s1++;
  917. if (*s2 == '_')
  918. s2++;
  919. if (*s1 != *s2)
  920. return 1;
  921. s1++;
  922. s2++;
  923. }
  924. if (!*s2)
  925. return 0;
  926. return def;
  927. }
  928. static menu_entry *
  929. menu_entry_find (char *path, char *label)
  930. {
  931. GSList *list;
  932. menu_entry *me;
  933. list = menu_list;
  934. while (list)
  935. {
  936. me = (menu_entry*)list->data;
  937. if (!strcmp (path, me->path))
  938. {
  939. if (me->label && label && !strcmp (label, me->label))
  940. return me;
  941. }
  942. list = list->next;
  943. }
  944. return NULL;
  945. }
  946. static void
  947. menu_del_children (char *path, char *label)
  948. {
  949. GSList *list, *next;
  950. menu_entry *me;
  951. char buf[512];
  952. if (!label)
  953. label = "";
  954. if (path[0])
  955. snprintf (buf, sizeof (buf), "%s/%s", path, label);
  956. else
  957. snprintf (buf, sizeof (buf), "%s", label);
  958. list = menu_list;
  959. while (list)
  960. {
  961. me = (menu_entry*)list->data;
  962. next = list->next;
  963. if (!menu_streq (buf, me->path, 0))
  964. {
  965. menu_list = g_slist_remove (menu_list, me);
  966. menu_free (me);
  967. }
  968. list = next;
  969. }
  970. }
  971. static int
  972. menu_del (char *path, char *label)
  973. {
  974. GSList *list;
  975. menu_entry *me;
  976. list = menu_list;
  977. while (list)
  978. {
  979. me = (menu_entry*)list->data;
  980. if (!menu_streq (me->label, label, 1) && !menu_streq (me->path, path, 1))
  981. {
  982. menu_list = g_slist_remove (menu_list, me);
  983. fe_menu_del (me);
  984. menu_free (me);
  985. /* delete this item's children, if any */
  986. menu_del_children (path, label);
  987. return 1;
  988. }
  989. list = list->next;
  990. }
  991. return 0;
  992. }
  993. static char menu_is_mainmenu_root(char *path, short *offset)
  994. {
  995. static const char *menus[] = {"\x4$TAB","\x5$TRAY","\x4$URL","\x5$NICK","\x5$CHAN"};
  996. int i;
  997. for (i = 0; i < 5; i++)
  998. {
  999. if (!strncmp(path, menus[i] + 1, menus[i][0]))
  1000. {
  1001. *offset = menus[i][0] + 1; // number of bytes to offset the root
  1002. return 0; // is not main menu
  1003. }
  1004. }
  1005. *offset = 0;
  1006. return 1; // is main menu
  1007. }
  1008. static void
  1009. menu_add (char *path, char *label, char *cmd, char *ucmd, int pos, int state, int markup, int enable, int mod, int key, char *group, char *icon)
  1010. {
  1011. menu_entry *me;
  1012. /* already exists? */
  1013. me = menu_entry_find (path, label);
  1014. if (me)
  1015. {
  1016. /* update only */
  1017. me->state = state;
  1018. me->enable = enable;
  1019. fe_menu_update (me);
  1020. return;
  1021. }
  1022. me = (menu_entry*)malloc(sizeof(menu_entry));
  1023. me->pos = pos;
  1024. me->modifier = mod;
  1025. me->is_main = menu_is_mainmenu_root (path, &me->root_offset);
  1026. me->state = state;
  1027. me->markup = markup;
  1028. me->enable = enable;
  1029. me->key = key;
  1030. me->path = strdup (path);
  1031. me->label = NULL;
  1032. me->cmd = NULL;
  1033. me->ucmd = NULL;
  1034. me->group = NULL;
  1035. me->icon = NULL;
  1036. if (label)
  1037. me->label = strdup (label);
  1038. if (cmd)
  1039. me->cmd = strdup (cmd);
  1040. if (ucmd)
  1041. me->ucmd = strdup (ucmd);
  1042. if (group)
  1043. me->group = strdup (group);
  1044. if (icon)
  1045. me->icon = strdup (icon);
  1046. menu_list = g_slist_append (menu_list, me);
  1047. label = fe_menu_add (me);
  1048. if (label)
  1049. {
  1050. /* FE has given us a stripped label */
  1051. free (me->label);
  1052. me->label = strdup (label);
  1053. g_free (label); /* this is from pango */
  1054. }
  1055. }
  1056. static int cmd_menu(session *sess, char *tbuf, char *word[], char *word_eol[])
  1057. {
  1058. int idx = 2;
  1059. int len;
  1060. int pos = 0xffff;
  1061. int state;
  1062. int toggle = FALSE;
  1063. int enable = TRUE;
  1064. int markup = FALSE;
  1065. int key = 0;
  1066. int mod = 0;
  1067. char *label;
  1068. char *group = NULL;
  1069. char *icon = NULL;
  1070. if (!word[2][0] || !word[3][0])
  1071. return FALSE;
  1072. // -eX enabled or not?
  1073. if (word[idx][0] == '-' && word[idx][1] == 'e')
  1074. {
  1075. enable = atoi(word[idx] + 2);
  1076. idx++;
  1077. }
  1078. // -i<ICONFILE>
  1079. if (word[idx][0] == '-' && word[idx][1] == 'i')
  1080. {
  1081. icon = word[idx] + 2;
  1082. idx++;
  1083. }
  1084. // -k<mod>,<key> key binding
  1085. if (word[idx][0] == '-' && word[idx][1] == 'k')
  1086. {
  1087. char *comma = strchr(word[idx], ',');
  1088. if (!comma)
  1089. return FALSE;
  1090. mod = atoi(word[idx] + 2);
  1091. key = atoi(comma + 1);
  1092. idx++;
  1093. }
  1094. // -m to specify PangoMarkup language
  1095. if (word[idx][0] == '-' && word[idx][1] == 'm')
  1096. {
  1097. markup = TRUE;
  1098. idx++;
  1099. }
  1100. // -pX to specify menu position
  1101. if (word[idx][0] == '-' && word[idx][1] == 'p')
  1102. {
  1103. pos = atoi(word[idx] + 2);
  1104. idx++;
  1105. }
  1106. // -rSTATE,GROUP to specify a radio item
  1107. if (word[idx][0] == '-' && word[idx][1] == 'r')
  1108. {
  1109. state = atoi(word[idx] + 2);
  1110. group = word[idx] + 4;
  1111. idx++;
  1112. }
  1113. // -tX to specify toggle item with default state
  1114. if (word[idx][0] == '-' && word[idx][1] == 't')
  1115. {
  1116. state = atoi(word[idx] + 2);
  1117. idx++;
  1118. toggle = TRUE;
  1119. }
  1120. if (word[idx+1][0] == 0)
  1121. return FALSE;
  1122. // the path
  1123. path_part(word[idx+1], tbuf, 512);
  1124. len = strlen(tbuf);
  1125. if (len)
  1126. tbuf[len - 1] = 0;
  1127. // the name of the item
  1128. label = file_part(word[idx + 1]);
  1129. if (label[0] == '-' && label[1] == 0)
  1130. label = NULL; // separator
  1131. if (markup)
  1132. {
  1133. char *p; // to force pango closing tags through
  1134. for (p = label; *p; p++)
  1135. if (*p == 3)
  1136. *p = '/';
  1137. }
  1138. if (!strcasecmp(word[idx], "ADD"))
  1139. {
  1140. if (toggle)
  1141. {
  1142. menu_add(tbuf, label, word[idx + 2], word[idx + 3], pos, state, markup, enable, mod, key, NULL, NULL);
  1143. }
  1144. else
  1145. {
  1146. if (word[idx + 2][0])
  1147. menu_add(tbuf, label, word[idx + 2], NULL, pos, state, markup, enable, mod, key, group, icon);
  1148. else
  1149. menu_add(tbuf, label, NULL, NULL, pos, state, markup, enable, mod, key, group, icon);
  1150. }
  1151. return TRUE;
  1152. }
  1153. if (!strcasecmp(word[idx], "DEL"))
  1154. {
  1155. menu_del(tbuf, label);
  1156. return TRUE;
  1157. }
  1158. return FALSE;
  1159. }
  1160. static int mkick_cb(User *user, multidata *data)
  1161. {
  1162. if (!user->op && !user->me)
  1163. data->sess->server->p_kick(data->sess->server, data->sess->channel, user->nick, data->reason);
  1164. return TRUE;
  1165. }
  1166. static int mkickops_cb(User *user, multidata *data)
  1167. {
  1168. if (user->op && !user->me)
  1169. data->sess->server->p_kick(data->sess->server, data->sess->channel, user->nick, data->reason);
  1170. return TRUE;
  1171. }
  1172. static int cmd_mkick(session *sess, char *tbuf, char *word[], char *word_eol[])
  1173. {
  1174. multidata data;
  1175. data.sess = sess;
  1176. data.reason = word_eol[2];
  1177. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)mkickops_cb, &data);
  1178. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)mkick_cb, &data);
  1179. return TRUE;
  1180. }
  1181. static int cmd_devoice(session *sess, char *tbuf, char *word[], char *word_eol[])
  1182. {
  1183. int i = 2;
  1184. while (1)
  1185. {
  1186. if (!*word[i])
  1187. {
  1188. if (i == 2)
  1189. return FALSE;
  1190. send_channel_modes(sess, tbuf, word, 2, i, '-', 'v', 0);
  1191. return TRUE;
  1192. }
  1193. i++;
  1194. }
  1195. }
  1196. static int cmd_discon(session *sess, char *tbuf, char *word[], char *word_eol[])
  1197. {
  1198. sess->server->disconnect(sess, TRUE, -1);
  1199. return TRUE;
  1200. }
  1201. static int cmd_dns(session *sess, char *tbuf, char *word[], char *word_eol[])
  1202. {
  1203. #ifdef WIN32
  1204. PrintText(sess, "DNS is not implemented in Windows.\n");
  1205. return TRUE;
  1206. #else
  1207. char *nick = word[2];
  1208. User *user;
  1209. if (*nick)
  1210. {
  1211. if (strchr(nick, '.') == NULL)
  1212. {
  1213. user = userlist_find(sess, nick);
  1214. if (user && user->hostname)
  1215. {
  1216. do_dns(sess, user->nick, user->hostname);
  1217. }
  1218. else
  1219. {
  1220. sess->server->p_get_ip(sess->server, nick);
  1221. sess->server->doing_dns = TRUE;
  1222. }
  1223. }
  1224. else
  1225. {
  1226. snprintf(tbuf, TBUFSIZE, "exec -d %s %s", prefs.dnsprogram, nick);
  1227. handle_command(sess, tbuf, FALSE);
  1228. }
  1229. return TRUE;
  1230. }
  1231. return FALSE;
  1232. #endif
  1233. }
  1234. static int cmd_echo(session *sess, char *tbuf, char *word[], char *word_eol[])
  1235. {
  1236. PrintText(sess, word_eol[2]);
  1237. return TRUE;
  1238. }
  1239. #ifndef WIN32
  1240. static void exec_check_process(session *sess)
  1241. {
  1242. int val;
  1243. if (sess->running_exec == NULL)
  1244. return;
  1245. val = waitpid(sess->running_exec->childpid, NULL, WNOHANG);
  1246. if (val == -1 || val > 0)
  1247. {
  1248. close(sess->running_exec->myfd);
  1249. fe_input_remove(sess->running_exec->iotag);
  1250. free(sess->running_exec);
  1251. sess->running_exec = NULL;
  1252. }
  1253. }
  1254. #ifndef __EMX__
  1255. static int cmd_execs(session *sess, char *tbuf, char *word[], char *word_eol[])
  1256. {
  1257. int r;
  1258. exec_check_process(sess);
  1259. if (sess->running_exec == NULL)
  1260. {
  1261. EMIT_SIGNAL(XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
  1262. return FALSE;
  1263. }
  1264. r = kill(sess->running_exec->childpid, SIGSTOP);
  1265. if (r == -1)
  1266. PrintText(sess, "Error in kill(2)\n");
  1267. return TRUE;
  1268. }
  1269. static int cmd_execc(session *sess, char *tbuf, char *word[], char *word_eol[])
  1270. {
  1271. int r;
  1272. exec_check_process(sess);
  1273. if (sess->running_exec == NULL)
  1274. {
  1275. EMIT_SIGNAL(XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
  1276. return FALSE;
  1277. }
  1278. r = kill(sess->running_exec->childpid, SIGCONT);
  1279. if (r == -1)
  1280. PrintText(sess, "Error in kill(2)\n");
  1281. return TRUE;
  1282. }
  1283. static int cmd_execk(session *sess, char *tbuf, char *word[], char *word_eol[])
  1284. {
  1285. int r;
  1286. exec_check_process(sess);
  1287. if (sess->running_exec == NULL)
  1288. {
  1289. EMIT_SIGNAL(XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
  1290. return FALSE;
  1291. }
  1292. if (strcmp(word[2], "-9") == 0)
  1293. r = kill(sess->running_exec->childpid, SIGKILL);
  1294. else
  1295. r = kill(sess->running_exec->childpid, SIGTERM);
  1296. if (r == -1)
  1297. PrintText(sess, "Error in kill(2)\n");
  1298. return TRUE;
  1299. }
  1300. /* OS/2 Can't have the /EXECW command because it uses pipe(2) not socketpair
  1301. and thus it is simplex --AGL*/
  1302. static int cmd_execw(session *sess, char *tbuf, char *word[], char *word_eol[])
  1303. {
  1304. int len;
  1305. char *temp;
  1306. exec_check_process(sess);
  1307. if (sess->running_exec == NULL)
  1308. {
  1309. EMIT_SIGNAL(XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
  1310. return FALSE;
  1311. }
  1312. len = strlen(word_eol[2]);
  1313. temp = (char*)malloc(len + 2);
  1314. sprintf(temp, "%s\n", word_eol[2]);
  1315. PrintText(sess, temp);
  1316. write(sess->running_exec->myfd, temp, len + 1);
  1317. free(temp);
  1318. return TRUE;
  1319. }
  1320. #endif /* !__EMX__ */
  1321. // convert ANSI escape color codes to mIRC codes
  1322. static short escconv[] =
  1323. // 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
  1324. { 1,4,3,5,2,10,6,1, 1,7,9,8,12,11,13,1 };
  1325. static void exec_handle_colors(char *buf, int len)
  1326. {
  1327. char numb[16];
  1328. char *nbuf;
  1329. int i = 0, j = 0, k = 0, firstn = 0, col, colf = 0, colb = 0;
  1330. int esc = FALSE, backc = FALSE, bold = FALSE;
  1331. // any escape codes in this text?
  1332. if (strchr(buf, 27) == 0)
  1333. return;
  1334. nbuf = (char*)malloc(len + 1);
  1335. while (i < len)
  1336. {
  1337. switch (buf[i])
  1338. {
  1339. case '\r':
  1340. break;
  1341. case 27:
  1342. esc = TRUE;
  1343. break;
  1344. case ';':
  1345. if (!esc)
  1346. goto norm;
  1347. backc = TRUE;
  1348. numb[k] = 0;
  1349. firstn = atoi(numb);
  1350. k = 0;
  1351. break;
  1352. case '[':
  1353. if (!esc)
  1354. goto norm;
  1355. break;
  1356. default:
  1357. if (esc)
  1358. {
  1359. if (buf[i] >= 'A' && buf[i] <= 'z')
  1360. {
  1361. if (buf[i] == 'm')
  1362. {
  1363. // ^[[0m
  1364. if (k == 0 || (numb[0] == '0' && k == 1))
  1365. {
  1366. nbuf[j] = '\017';
  1367. j++;
  1368. bold = FALSE;
  1369. goto cont;
  1370. }
  1371. numb[k] = 0;
  1372. col = atoi(numb);
  1373. backc = FALSE;
  1374. if (firstn == 1)
  1375. bold = TRUE;
  1376. if (firstn >= 30 && firstn <= 37)
  1377. colf = firstn - 30;
  1378. if (col >= 40)
  1379. {
  1380. colb = col - 40;
  1381. backc = TRUE;
  1382. }
  1383. if (col >= 30 && col <= 37)
  1384. colf = col - 30;
  1385. if (bold)
  1386. colf += 8;
  1387. if (backc)
  1388. {
  1389. colb = escconv[colb % 14];
  1390. colf = escconv[colf % 14];
  1391. j += sprintf(&nbuf[j], "\003%d,%02d", colf, colb);
  1392. }
  1393. else
  1394. {
  1395. colf = escconv[colf % 14];
  1396. j += sprintf(&nbuf[j], "\003%02d", colf);
  1397. }
  1398. }
  1399. cont: esc = FALSE;
  1400. backc = FALSE;
  1401. k = 0;
  1402. }
  1403. else
  1404. {
  1405. if (isdigit((unsigned char)buf[i]) && k < (sizeof(numb) - 1))
  1406. {
  1407. numb[k] = buf[i];
  1408. k++;
  1409. }
  1410. }
  1411. }
  1412. else
  1413. {
  1414. norm: nbuf[j] = buf[i];
  1415. j++;
  1416. }
  1417. }
  1418. i++;
  1419. }
  1420. nbuf[j] = 0;
  1421. memcpy(buf, nbuf, j + 1);
  1422. free(nbuf);
  1423. }
  1424. #ifndef HAVE_MEMRCHR
  1425. static void* memrchr(const void *block, int c, size_t size)
  1426. {
  1427. unsigned char *p;
  1428. for (p = (unsigned char*)block + size; p != block; p--)
  1429. if (*p == c)
  1430. return p;
  1431. return 0;
  1432. }
  1433. #endif
  1434. static bool exec_data(GIOChannel *source, GIOCondition condition, nbexec *s)
  1435. {
  1436. char *buf, *readpos, *rest;
  1437. int rd, len;
  1438. int sok = s->myfd;
  1439. len = s->buffill;
  1440. if (len) {
  1441. // append new data to buffered incomplete line
  1442. buf = (char*)malloc(len + 2050);
  1443. memcpy(buf, s->linebuf, len);
  1444. readpos = buf + len;
  1445. free(s->linebuf);
  1446. s->linebuf = NULL;
  1447. }
  1448. else
  1449. readpos = buf = (char*)malloc(2050);
  1450. rd = read(sok, readpos, 2048);
  1451. if (rd < 1)
  1452. {
  1453. // The process has died
  1454. kill(s->childpid, SIGKILL);
  1455. if (len) {
  1456. buf[len] = '\0';
  1457. exec_handle_colors(buf, len);
  1458. if (s->tochannel)
  1459. {
  1460. // must turn off auto-completion temporarily
  1461. unsigned int old = prefs.nickcompletion;
  1462. prefs.nickcompletion = 0;
  1463. handle_multiline(s->sess, buf, FALSE, TRUE);
  1464. prefs.nickcompletion = old;
  1465. }
  1466. else
  1467. PrintText(s->sess, buf);
  1468. }
  1469. free(buf);
  1470. waitpid(s->childpid, NULL, 0);
  1471. s->sess->running_exec = NULL;
  1472. fe_input_remove(s->iotag);
  1473. close(sok);
  1474. free(s);
  1475. return TRUE;
  1476. }
  1477. len += rd;
  1478. buf[len] = '\0';
  1479. rest = (char*)memrchr(buf, '\n', len);
  1480. if (rest)
  1481. rest++;
  1482. else
  1483. rest = buf;
  1484. if (*rest) {
  1485. s->buffill = len - (rest - buf); // = strlen(rest)
  1486. s->linebuf = (char*)malloc(s->buffill);
  1487. memcpy(s->linebuf, rest, s->buffill);
  1488. *rest = '\0';
  1489. len -= s->buffill; // possibly 0
  1490. }
  1491. else
  1492. s->buffill = 0;
  1493. if (len) {
  1494. exec_handle_colors(buf, len);
  1495. if (s->tochannel)
  1496. handle_multiline(s->sess, buf, FALSE, TRUE);
  1497. else
  1498. PrintText(s->sess, buf);
  1499. }
  1500. free(buf);
  1501. return TRUE;
  1502. }
  1503. static int cmd_exec(session *sess, char *tbuf, char *word[], char *word_eol[])
  1504. {
  1505. int tochannel = FALSE;
  1506. char *cmd = word_eol[2];
  1507. int fds[2], pid = 0;
  1508. nbexec *s;
  1509. int shell = TRUE;
  1510. int fd;
  1511. if (*cmd)
  1512. {
  1513. exec_check_process(sess);
  1514. if (sess->running_exec != NULL)
  1515. {
  1516. EMIT_SIGNAL(XP_TE_ALREADYPROCESS, sess, NULL, NULL, NULL, NULL, 0);
  1517. return TRUE;
  1518. }
  1519. if (!strcmp(word[2], "-d"))
  1520. {
  1521. if (!*word[3])
  1522. return FALSE;
  1523. cmd = word_eol[3];
  1524. shell = FALSE;
  1525. }
  1526. else if (!strcmp(word[2], "-o"))
  1527. {
  1528. if (!*word[3])
  1529. return FALSE;
  1530. cmd = word_eol[3];
  1531. tochannel = TRUE;
  1532. }
  1533. if (shell)
  1534. {
  1535. if (access("/bin/sh", X_OK) != 0)
  1536. {
  1537. fe_message(_("I need /bin/sh to run!\n"), FE_MSG_ERROR);
  1538. return TRUE;
  1539. }
  1540. }
  1541. #ifdef __EMX__ /* if os/2 */
  1542. if (pipe(fds) < 0)
  1543. {
  1544. PrintText(sess, "Pipe create error\n");
  1545. return FALSE;
  1546. }
  1547. setmode(fds[0], O_BINARY);
  1548. setmode(fds[1], O_BINARY);
  1549. #else
  1550. if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds) == -1)
  1551. {
  1552. PrintText(sess, "socketpair(2) failed\n");
  1553. return FALSE;
  1554. }
  1555. #endif
  1556. s = (nbexec*)malloc(sizeof(nbexec));
  1557. memset(s, 0, sizeof(*s));
  1558. s->myfd = fds[0];
  1559. s->tochannel = tochannel;
  1560. s->sess = sess;
  1561. pid = fork();
  1562. if (pid == 0)
  1563. {
  1564. // This is the child's context
  1565. close(0);
  1566. close(1);
  1567. close(2);
  1568. // Close parent's end of pipe
  1569. close(s->myfd);
  1570. // Copy the child end of the pipe to stdout and stderr
  1571. dup2(fds[1], 1);
  1572. dup2(fds[1], 2);
  1573. // Also copy it to stdin so we can write to it
  1574. dup2(fds[1], 0);
  1575. // Now close all open file descriptors except stdin, stdout and stderr
  1576. for (fd = 3; fd < 1024; fd++)
  1577. close(fd);
  1578. // Now we call /bin/sh to run our cmd ; made it more friendly -DC1
  1579. if (shell)
  1580. {
  1581. execl("/bin/sh", "sh", "-c", cmd, NULL);
  1582. }
  1583. else
  1584. {
  1585. char **argv;
  1586. int argc;
  1587. my_poptParseArgvString(cmd, &argc, &argv);
  1588. execvp(argv[0], argv);
  1589. }
  1590. // not reached unless error
  1591. //printf("exec error\n");
  1592. fflush(stdout);
  1593. fflush(stdin);
  1594. _exit(0);
  1595. }
  1596. if (pid == -1)
  1597. {
  1598. // Parent context, fork() failed
  1599. PrintText(sess, "Error in fork(2)\n");
  1600. close(fds[0]);
  1601. close(fds[1]);
  1602. }
  1603. else
  1604. {
  1605. // Parent path
  1606. close(fds[1]);
  1607. s->childpid = pid;
  1608. s->iotag = fe_input_add(s->myfd, FIA_READ|FIA_EX, (void*)exec_data, s);
  1609. sess->running_exec = s;
  1610. return TRUE;
  1611. }
  1612. }
  1613. return FALSE;
  1614. }
  1615. #endif
  1616. static int cmd_flushq(session *sess, char *tbuf, char *word[], char *word_eol[])
  1617. {
  1618. sprintf(tbuf, "Flushing server send queue, %d bytes.\n", sess->server->sendq_len);
  1619. PrintText(sess, tbuf);
  1620. sess->server->flush_queue(sess->server);
  1621. return TRUE;
  1622. }
  1623. static int cmd_quit(session *sess, char *tbuf, char *word[], char *word_eol[])
  1624. {
  1625. if (*word_eol[2])
  1626. sess->quitreason = word_eol[2];
  1627. sess->server->disconnect(sess, TRUE, -1);
  1628. sess->quitreason = NULL;
  1629. return 2;
  1630. }
  1631. static int cmd_gate(session *sess, char *tbuf, char *word[], char *word_eol[])
  1632. {
  1633. char *server_name = word[2];
  1634. server *serv = sess->server;
  1635. if (*server_name)
  1636. {
  1637. char *port = word[3];
  1638. #ifdef USE_OPENSSL
  1639. serv->use_ssl = FALSE;
  1640. #endif
  1641. server_fill_her_up(serv);
  1642. if (*port)
  1643. serv->connect(serv, server_name, atoi(port), TRUE);
  1644. else
  1645. serv->connect(serv, server_name, 23, TRUE);
  1646. return TRUE;
  1647. }
  1648. return FALSE;
  1649. }
  1650. typedef struct
  1651. {
  1652. char *cmd;
  1653. session *sess;
  1654. } getvalinfo;
  1655. static void get_int_cb(int cancel, int val, getvalinfo *info)
  1656. {
  1657. char buf[512];
  1658. if (!cancel)
  1659. {
  1660. snprintf(buf, sizeof(buf), "%s %d", info->cmd, val);
  1661. if (is_session(info->sess))
  1662. handle_command(info->sess, buf, FALSE);
  1663. }
  1664. free(info->cmd);
  1665. free(info);
  1666. }
  1667. static int cmd_getint(session *sess, char *tbuf, char *word[], char *word_eol[])
  1668. {
  1669. getvalinfo *info;
  1670. if (!word[4][0])
  1671. return FALSE;
  1672. info = (getvalinfo*)malloc(sizeof(*info));
  1673. info->cmd = strdup(word[3]);
  1674. info->sess = sess;
  1675. fe_get_int(word[4], atoi(word[2]), (void*)get_int_cb, info);
  1676. return TRUE;
  1677. }
  1678. static void get_file_cb(char *cmd, char *file)
  1679. {
  1680. char buf[1024 + 128];
  1681. /* execute the command once per file, then once more with
  1682. no args*/
  1683. if (file)
  1684. {
  1685. snprintf(buf, sizeof(buf), "%s %s", cmd, file);
  1686. handle_command(current_sess, buf, FALSE);
  1687. }
  1688. else
  1689. {
  1690. handle_command(current_sess, cmd, FALSE);
  1691. free(cmd);
  1692. }
  1693. }
  1694. typedef void (*callback)(void*, char*);
  1695. static int cmd_getfile(session *sess, char *tbuf, char *word[], char *word_eol[])
  1696. {
  1697. int idx = 2;
  1698. int flags = 0;
  1699. if (!word[3][0])
  1700. return FALSE;
  1701. if (!strcmp(word[2], "-folder"))
  1702. {
  1703. flags |= FRF_CHOOSEFOLDER;
  1704. idx++;
  1705. }
  1706. if (!strcmp(word[idx], "-multi"))
  1707. {
  1708. flags |= FRF_MULTIPLE;
  1709. idx++;
  1710. }
  1711. if (!strcmp(word[idx], "-save"))
  1712. {
  1713. flags |= FRF_WRITE;
  1714. idx++;
  1715. }
  1716. fe_get_file(word[idx+1], word[idx+2], (callback)get_file_cb, strdup(word[idx]), flags);
  1717. return TRUE;
  1718. }
  1719. static void get_str_cb(int cancel, char *val, getvalinfo *info)
  1720. {
  1721. char buf[512];
  1722. if (!cancel)
  1723. {
  1724. snprintf(buf, sizeof(buf), "%s %s", info->cmd, val);
  1725. if (is_session(info->sess))
  1726. handle_command(info->sess, buf, FALSE);
  1727. }
  1728. free(info->cmd);
  1729. free(info);
  1730. }
  1731. static int cmd_getstr(session *sess, char *tbuf, char *word[], char *word_eol[])
  1732. {
  1733. getvalinfo *info;
  1734. if (!word[4][0])
  1735. return FALSE;
  1736. info = (getvalinfo*)malloc(sizeof(*info));
  1737. info->cmd = strdup(word[3]);
  1738. info->sess = sess;
  1739. fe_get_str(word[4], word[2], (void*)get_str_cb, info);
  1740. return TRUE;
  1741. }
  1742. static int cmd_ghost(session *sess, char *tbuf, char *word[], char *word_eol[])
  1743. {
  1744. if (!word[2][0])
  1745. return FALSE;
  1746. sess->server->p_ns_ghost(sess->server, word[2], word[3]);
  1747. return TRUE;
  1748. }
  1749. static int cmd_gui(session *sess, char *tbuf, char *word[], char *word_eol[])
  1750. {
  1751. switch (str_ihash((const unsigned char*)word[2]))
  1752. {
  1753. case 0x058b836e: fe_ctrl_gui(sess, FE_GUI_APPLY, 0); break; // APPLY
  1754. case 0xac1eee45: fe_ctrl_gui(sess, FE_GUI_ATTACH, 2); break; // ATTACH
  1755. case 0x05a72f63: fe_ctrl_gui(sess, FE_GUI_COLOR, atoi(word[3])); break; // COLOR
  1756. case 0xb06a1793: fe_ctrl_gui(sess, FE_GUI_ATTACH, 1); break; // DETACH
  1757. case 0x05cfeff0: fe_ctrl_gui(sess, FE_GUI_FLASH, 0); break; // FLASH
  1758. case 0x05d154d8: fe_ctrl_gui(sess, FE_GUI_FOCUS, 0); break; // FOCUS
  1759. case 0x0030dd42: fe_ctrl_gui(sess, FE_GUI_HIDE, 0); break; // HIDE
  1760. case 0x61addbe3: fe_ctrl_gui(sess, FE_GUI_ICONIFY, 0); break; // ICONIFY
  1761. case 0xc0851aaa: fe_message(word[3], FE_MSG_INFO|FE_MSG_MARKUP); break; // MSGBOX
  1762. case 0x0035dafd: fe_ctrl_gui(sess, FE_GUI_SHOW, 0); break; // SHOW
  1763. case 0x0033155f: // MENU
  1764. if (!strcasecmp(word[3], "TOGGLE"))
  1765. fe_ctrl_gui(sess, FE_GUI_MENU, 0);
  1766. else
  1767. return FALSE;
  1768. break;
  1769. default:
  1770. return FALSE;
  1771. }
  1772. return TRUE;
  1773. }
  1774. typedef struct
  1775. {
  1776. int longfmt;
  1777. int i, t;
  1778. char *buf;
  1779. } help_list;
  1780. static void show_help_line(session *sess, help_list *hl, char *name, char *usage)
  1781. {
  1782. int j, len, max;
  1783. char *p;
  1784. if (name[0] == '.') // hidden command?
  1785. return;
  1786. if (hl->longfmt) // long format for /HELP -l
  1787. {
  1788. if (!usage || usage[0] == 0)
  1789. PrintTextf(sess, " \0034%s\003 :\n", name);
  1790. else
  1791. PrintTextf(sess, " \0034%s\003 : %s\n", name, _(usage));
  1792. return;
  1793. }
  1794. // append the name into buffer, but convert to uppercase
  1795. len = strlen(hl->buf);
  1796. p = name;
  1797. while (*p)
  1798. {
  1799. hl->buf[len] = toupper((unsigned char)*p);
  1800. len++;
  1801. p++;
  1802. }
  1803. hl->buf[len] = 0;
  1804. hl->t++;
  1805. if (hl->t == 5)
  1806. {
  1807. hl->t = 0;
  1808. strcat(hl->buf, "\n");
  1809. PrintText(sess, hl->buf);
  1810. hl->buf[0] = ' ';
  1811. hl->buf[1] = ' ';
  1812. hl->buf[2] = 0;
  1813. }
  1814. else
  1815. {
  1816. // append some spaces after the command name
  1817. max = strlen(name);
  1818. if (max < 10)
  1819. {
  1820. max = 10 - max;
  1821. for (j = 0; j < max; j++)
  1822. {
  1823. hl->buf[len] = ' ';
  1824. len++;
  1825. hl->buf[len] = 0;
  1826. }
  1827. }
  1828. }
  1829. }
  1830. typedef void (*cb)(session*, void*, char*, char*);
  1831. static int cmd_help(session *sess, char *tbuf, char *word[], char *word_eol[])
  1832. {
  1833. int i = 0, longfmt = 0;
  1834. char *helpcmd = 0;
  1835. GSList *list;
  1836. if (tbuf)
  1837. helpcmd = word[2];
  1838. if (*helpcmd && strcmp(helpcmd, "-l") == 0)
  1839. longfmt = 1;
  1840. if (*helpcmd && !longfmt)
  1841. {
  1842. help(sess, tbuf, helpcmd, FALSE);
  1843. }
  1844. else
  1845. {
  1846. popup *pop;
  1847. char *buf = (char*)malloc(4096);
  1848. help_list hl;
  1849. hl.longfmt = longfmt;
  1850. hl.buf = buf;
  1851. PrintTextf(sess, "\n%s\n\n", _("Commands Available:"));
  1852. buf[0] = ' ';
  1853. buf[1] = ' ';
  1854. buf[2] = 0;
  1855. hl.t = 0;
  1856. hl.i = 0;
  1857. while (xc_cmds[i].name)
  1858. {
  1859. show_help_line(sess, &hl, xc_cmds[i].name, xc_cmds[i].help);
  1860. i++;
  1861. }
  1862. strcat(buf, "\n");
  1863. PrintText(sess, buf);
  1864. PrintTextf(sess, "\n%s\n\n", _("User defined commands:"));
  1865. buf[0] = ' ';
  1866. buf[1] = ' ';
  1867. buf[2] = 0;
  1868. hl.t = 0;
  1869. hl.i = 0;
  1870. list = command_list;
  1871. while (list)
  1872. {
  1873. pop = (popup*)list->data;
  1874. show_help_line(sess, &hl, pop->name, pop->cmd);
  1875. list = list->next;
  1876. }
  1877. strcat(buf, "\n");
  1878. PrintText(sess, buf);
  1879. PrintTextf(sess, "\n%s\n\n", _("Plugin defined commands:"));
  1880. buf[0] = ' ';
  1881. buf[1] = ' ';
  1882. buf[2] = 0;
  1883. hl.t = 0;
  1884. hl.i = 0;
  1885. plugin_command_foreach(sess, &hl, (cb)show_help_line);
  1886. strcat(buf, "\n");
  1887. PrintText(sess, buf);
  1888. free(buf);
  1889. PrintTextf(sess, "\n%s\n\n", _("Type /HELP <command> for more information, or /HELP -l"));
  1890. }
  1891. return TRUE;
  1892. }
  1893. static int cmd_id(session *sess, char *tbuf, char *word[], char *word_eol[])
  1894. {
  1895. if (word[2][0])
  1896. {
  1897. sess->server->p_ns_identify(sess->server, word[2]);
  1898. return TRUE;
  1899. }
  1900. return FALSE;
  1901. }
  1902. static int cmd_ignore(session *sess, char *tbuf, char *word[], char *word_eol[])
  1903. {
  1904. int i;
  1905. int type = 0;
  1906. int quiet = 0;
  1907. char *mask;
  1908. if (!*word[2])
  1909. {
  1910. ignore_showlist(sess);
  1911. return TRUE;
  1912. }
  1913. if (!*word[3])
  1914. return FALSE;
  1915. i = 3;
  1916. while (1)
  1917. {
  1918. if (!*word[i])
  1919. {
  1920. if (type == 0)
  1921. return FALSE;
  1922. mask = word[2];
  1923. if (strchr(mask, '?') == NULL &&
  1924. strchr(mask, '*') == NULL &&
  1925. userlist_find(sess, mask))
  1926. {
  1927. mask = tbuf;
  1928. snprintf(tbuf, TBUFSIZE, "%s!*@*", word[2]);
  1929. }
  1930. i = ignore_add(mask, type);
  1931. if (quiet)
  1932. return TRUE;
  1933. switch (i)
  1934. {
  1935. case 1:
  1936. EMIT_SIGNAL(XP_TE_IGNOREADD, sess, mask, NULL, NULL, NULL, 0);
  1937. break;
  1938. case 2: // old ignore changed
  1939. EMIT_SIGNAL(XP_TE_IGNORECHANGE, sess, mask, NULL, NULL, NULL, 0);
  1940. }
  1941. return TRUE;
  1942. }
  1943. if (!strcasecmp(word[i], "UNIGNORE"))
  1944. type |= IG_UNIG;
  1945. else if (!strcasecmp(word[i], "ALL"))
  1946. type |= IG_PRIV | IG_NOTI | IG_CHAN | IG_CTCP | IG_INVI | IG_DCC;
  1947. else if (!strcasecmp(word[i], "PRIV"))
  1948. type |= IG_PRIV;
  1949. else if (!strcasecmp(word[i], "NOTI"))
  1950. type |= IG_NOTI;
  1951. else if (!strcasecmp(word[i], "CHAN"))
  1952. type |= IG_CHAN;
  1953. else if (!strcasecmp(word[i], "CTCP"))
  1954. type |= IG_CTCP;
  1955. else if (!strcasecmp(word[i], "INVI"))
  1956. type |= IG_INVI;
  1957. else if (!strcasecmp(word[i], "QUIET"))
  1958. quiet = 1;
  1959. else if (!strcasecmp(word[i], "NOSAVE"))
  1960. type |= IG_NOSAVE;
  1961. else if (!strcasecmp(word[i], "DCC"))
  1962. type |= IG_DCC;
  1963. else
  1964. {
  1965. sprintf(tbuf, _("Unknown arg '%s' ignored."), word[i]);
  1966. PrintText(sess, tbuf);
  1967. }
  1968. i++;
  1969. }
  1970. }
  1971. static int cmd_invite(session *sess, char *tbuf, char *word[], char *word_eol[])
  1972. {
  1973. if (!*word[2])
  1974. return FALSE;
  1975. if (*word[3])
  1976. sess->server->p_invite(sess->server, word[3], word[2]);
  1977. else
  1978. sess->server->p_invite(sess->server, sess->channel, word[2]);
  1979. return TRUE;
  1980. }
  1981. static int cmd_join(session *sess, char *tbuf, char *word[], char *word_eol[])
  1982. {
  1983. char *chan = word[2];
  1984. if (*chan)
  1985. {
  1986. char *po, *pass = word[3];
  1987. sess->server->p_join(sess->server, chan, pass);
  1988. if (sess->channel[0] == 0 && sess->waitchannel[0])
  1989. {
  1990. po = strchr(chan, ',');
  1991. if (po)
  1992. *po = 0;
  1993. safe_strcpy(sess->waitchannel, chan, CHANLEN);
  1994. }
  1995. return TRUE;
  1996. }
  1997. return FALSE;
  1998. }
  1999. static int cmd_kick(session *sess, char *tbuf, char *word[], char *word_eol[])
  2000. {
  2001. char *nick = word[2];
  2002. char *reason = word_eol[3];
  2003. if (*nick)
  2004. {
  2005. sess->server->p_kick(sess->server, sess->channel, nick, reason);
  2006. return TRUE;
  2007. }
  2008. return FALSE;
  2009. }
  2010. static int cmd_kickban(session *sess, char *tbuf, char *word[], char *word_eol[])
  2011. {
  2012. char *nick = word[2];
  2013. char *reason = word_eol[3];
  2014. User *user;
  2015. if (*nick)
  2016. {
  2017. // if the reason is a 1 digit number, treat it as a bantype
  2018. user = userlist_find(sess, nick);
  2019. if (isdigit((unsigned char)reason[0]) && reason[1] == 0)
  2020. {
  2021. ban(sess, tbuf, nick, reason, user && user->op);
  2022. reason[0] = 0;
  2023. } else
  2024. ban(sess, tbuf, nick, "", user && user->op);
  2025. sess->server->p_kick(sess->server, sess->channel, nick, reason);
  2026. return TRUE;
  2027. }
  2028. return FALSE;
  2029. }
  2030. static int cmd_killall(session *sess, char *tbuf, char *word[], char *word_eol[])
  2031. {
  2032. xchat_exit();
  2033. return 2;
  2034. }
  2035. static int cmd_lagcheck(session *sess, char *tbuf, char *word[], char *word_eol[])
  2036. {
  2037. lag_check();
  2038. return TRUE;
  2039. }
  2040. static void lastlog(session *sess, char *search, bool regexp)
  2041. {
  2042. session *lastlog_sess;
  2043. if (!is_session(sess))
  2044. return;
  2045. lastlog_sess = find_dialog(sess->server, "(lastlog)");
  2046. if (!lastlog_sess)
  2047. lastlog_sess = new_ircwindow(sess->server, "(lastlog)", SESS_DIALOG, 0);
  2048. lastlog_sess->lastlog_sess = sess;
  2049. lastlog_sess->lastlog_regexp = regexp; // remember the search type
  2050. fe_text_clear(lastlog_sess, 0);
  2051. fe_lastlog(sess, lastlog_sess, search, regexp);
  2052. }
  2053. static int cmd_lastlog(session *sess, char *tbuf, char *word[], char *word_eol[])
  2054. {
  2055. if (*word_eol[2])
  2056. {
  2057. if (!strcmp(word[2], "-r"))
  2058. lastlog(sess, word_eol[3], TRUE);
  2059. else
  2060. lastlog(sess, word_eol[2], FALSE);
  2061. return TRUE;
  2062. }
  2063. return FALSE;
  2064. }
  2065. static int cmd_list(session *sess, char *tbuf, char *word[], char *word_eol[])
  2066. {
  2067. sess->server->p_list_channels(sess->server, word_eol[2], 1);
  2068. return TRUE;
  2069. }
  2070. bool load_perform_file(session *sess, char *file)
  2071. {
  2072. char tbuf[1024 + 4];
  2073. char *nl;
  2074. FILE *fp;
  2075. fp = xchat_fopen_file(file, "r", XOF_FULLPATH);
  2076. if (!fp)
  2077. return FALSE;
  2078. tbuf[1024] = 0;
  2079. while (fgets(tbuf, 1024, fp))
  2080. {
  2081. nl = strchr(tbuf, '\n');
  2082. if (nl == tbuf) // skip empty commands
  2083. continue;
  2084. if (nl)
  2085. *nl = 0;
  2086. if (tbuf[0] == prefs.cmdchar[0])
  2087. handle_command(sess, tbuf + 1, TRUE);
  2088. else
  2089. handle_command(sess, tbuf, TRUE);
  2090. }
  2091. fclose(fp);
  2092. return TRUE;
  2093. }
  2094. static int cmd_load(session *sess, char *tbuf, char *word[], char *word_eol[])
  2095. {
  2096. char *error, *arg, *file;
  2097. int len;
  2098. if (!word[2][0])
  2099. return FALSE;
  2100. if (strcmp (word[2], "-e") == 0)
  2101. {
  2102. file = expand_homedir(word[3]);
  2103. if (!load_perform_file(sess, file))
  2104. {
  2105. PrintTextf(sess, _("Cannot access %s\n"), file);
  2106. PrintText(sess, errorstring(errno));
  2107. }
  2108. free(file);
  2109. return TRUE;
  2110. }
  2111. #ifdef USE_PLUGIN
  2112. len = strlen(word[2]);
  2113. #ifdef WIN32
  2114. if (len > 4 && strcasecmp(".dll", word[2] + len - 4) == 0)
  2115. #else
  2116. #if defined(__hpux)
  2117. if (len > 3 && strcasecmp(".sl", word[2] + len - 3) == 0)
  2118. #else
  2119. if (len > 3 && strcasecmp(".so", word[2] + len - 3) == 0)
  2120. #endif
  2121. #endif
  2122. {
  2123. arg = NULL;
  2124. if (word_eol[3][0])
  2125. arg = word_eol[3];
  2126. file = expand_homedir(word[2]);
  2127. error = plugin_load(sess, file, arg);
  2128. free(file);
  2129. if (error)
  2130. PrintText(sess, error);
  2131. return TRUE;
  2132. }
  2133. #endif
  2134. sprintf(tbuf, "Unknown file type %s. Maybe you need to install the Perl or Python plugin?\n", word[2]);
  2135. PrintText(sess, tbuf);
  2136. return FALSE;
  2137. }
  2138. static int cmd_me(session *sess, char *tbuf, char *word[], char *word_eol[])
  2139. {
  2140. char *act = word_eol[2];
  2141. if (!(*act))
  2142. return FALSE;
  2143. if (sess->type == SESS_SERVER)
  2144. {
  2145. notj_msg(sess);
  2146. return TRUE;
  2147. }
  2148. snprintf(tbuf, TBUFSIZE, "\001ACTION %s\001\r", act);
  2149. // first try through DCC CHAT
  2150. if (dcc_write_chat(sess->channel, tbuf))
  2151. {
  2152. // print it to screen
  2153. inbound_action(sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
  2154. }
  2155. else
  2156. {
  2157. // DCC CHAT failed, try through server
  2158. if (sess->server->connected)
  2159. {
  2160. sess->server->p_action(sess->server, sess->channel, act);
  2161. // print it to screen
  2162. inbound_action(sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
  2163. }
  2164. else
  2165. {
  2166. notc_msg(sess);
  2167. }
  2168. }
  2169. return TRUE;
  2170. }
  2171. static int cmd_mode(session *sess, char *tbuf, char *word[], char *word_eol[])
  2172. {
  2173. /* +channel channels are dying, let those servers whine about modes.
  2174. * return info about current channel if available and no info is given*/
  2175. if ((*word[2] == '+') || (*word[2] == 0) || (!is_channel(sess->server, word[2]) &&
  2176. !(rfc_casecmp(sess->server->nick, word[2]) == 0)))
  2177. {
  2178. if(sess->channel[0] == 0)
  2179. return FALSE;
  2180. sess->server->p_mode(sess->server, sess->channel, word_eol[2]);
  2181. }
  2182. else
  2183. sess->server->p_mode(sess->server, word[2], word_eol[3]);
  2184. return TRUE;
  2185. }
  2186. static int mop_cb(User *user, multidata *data)
  2187. {
  2188. if (!user->op)
  2189. {
  2190. data->nicks[data->i] = user->nick;
  2191. data->i++;
  2192. }
  2193. return TRUE;
  2194. }
  2195. static int cmd_mop(session *sess, char *tbuf, char *word[], char *word_eol[])
  2196. {
  2197. char **nicks = (char**)malloc(sizeof(char*) * (sess->total - sess->ops));
  2198. multidata data;
  2199. data.nicks = nicks;
  2200. data.i = 0;
  2201. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)mop_cb, &data);
  2202. send_channel_modes(sess, tbuf, nicks, 0, data.i, '+', 'o', 0);
  2203. free(nicks);
  2204. return TRUE;
  2205. }
  2206. static int cmd_msg(session *sess, char *tbuf, char *word[], char *word_eol[])
  2207. {
  2208. char *nick = word[2];
  2209. char *msg = word_eol[3];
  2210. session *newsess;
  2211. if (*nick)
  2212. {
  2213. if (*msg)
  2214. {
  2215. if (strcmp(nick, ".") == 0)
  2216. { // /msg the last nick /msg'ed
  2217. if (sess->lastnick[0])
  2218. nick = sess->lastnick;
  2219. }
  2220. else
  2221. {
  2222. safe_strcpy(sess->lastnick, nick, NICKLEN); // prime the last nick memory
  2223. }
  2224. if (*nick == '=')
  2225. {
  2226. nick++;
  2227. if (!dcc_write_chat(nick, msg))
  2228. {
  2229. EMIT_SIGNAL(XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
  2230. return TRUE;
  2231. }
  2232. }
  2233. else
  2234. {
  2235. if (!sess->server->connected)
  2236. {
  2237. notc_msg(sess);
  2238. return TRUE;
  2239. }
  2240. sess->server->p_message(sess->server, nick, msg);
  2241. }
  2242. newsess = find_dialog(sess->server, nick);
  2243. if (!newsess)
  2244. {
  2245. newsess = find_channel(sess->server, nick);
  2246. if (!newsess && prefs.gui_auto_open_msg)
  2247. {
  2248. newsess = new_ircwindow(sess->server, nick, SESS_DIALOG, 0);
  2249. }
  2250. }
  2251. if (newsess)
  2252. inbound_chanmsg(newsess->server, NULL, newsess->channel,
  2253. newsess->server->nick, msg, TRUE, FALSE);
  2254. else
  2255. {
  2256. // mask out passwords
  2257. if (strcasecmp(nick, "nickserv") == 0 &&
  2258. strncasecmp(msg, "identify ", 9) == 0)
  2259. msg = "identify ****";
  2260. EMIT_SIGNAL(XP_TE_MSGSEND, sess, nick, msg, NULL, NULL, 0);
  2261. }
  2262. return TRUE;
  2263. }
  2264. }
  2265. return FALSE;
  2266. }
  2267. static int cmd_names(session *sess, char *tbuf, char *word[], char *word_eol[])
  2268. {
  2269. if (*word[2])
  2270. sess->server->p_names(sess->server, word[2]);
  2271. else
  2272. sess->server->p_names(sess->server, sess->channel);
  2273. return TRUE;
  2274. }
  2275. static int cmd_nctcp(session *sess, char *tbuf, char *word[], char *word_eol[])
  2276. {
  2277. if (*word_eol[3])
  2278. {
  2279. sess->server->p_nctcp(sess->server, word[2], word_eol[3]);
  2280. return TRUE;
  2281. }
  2282. return FALSE;
  2283. }
  2284. static int cmd_newserver(session *sess, char *tbuf, char *word[], char *word_eol[])
  2285. {
  2286. if (strcmp(word[2], "-noconnect") == 0)
  2287. {
  2288. new_ircwindow(NULL, word[3], SESS_SERVER, 0);
  2289. return TRUE;
  2290. }
  2291. sess = new_ircwindow(NULL, NULL, SESS_SERVER, 0);
  2292. cmd_server(sess, tbuf, word, word_eol);
  2293. return TRUE;
  2294. }
  2295. static int cmd_nick(session *sess, char *tbuf, char *word[], char *word_eol[])
  2296. {
  2297. char *nick = word[2];
  2298. if (*nick)
  2299. {
  2300. if (sess->server->connected)
  2301. sess->server->p_change_nick(sess->server, nick);
  2302. else
  2303. inbound_newnick(sess->server, sess->server->nick, nick, TRUE);
  2304. return TRUE;
  2305. }
  2306. return FALSE;
  2307. }
  2308. static int cmd_notice(session *sess, char *tbuf, char *word[], char *word_eol[])
  2309. {
  2310. if (*word[2] && *word_eol[3])
  2311. {
  2312. sess->server->p_notice(sess->server, word[2], word_eol[3]);
  2313. EMIT_SIGNAL(XP_TE_NOTICESEND, sess, word[2], word_eol[3], NULL, NULL, 0);
  2314. return TRUE;
  2315. }
  2316. return FALSE;
  2317. }
  2318. static int cmd_notify(session *sess, char *tbuf, char *word[], char *word_eol[])
  2319. {
  2320. int i = 1;
  2321. char *net = NULL;
  2322. if (*word[2])
  2323. {
  2324. if (strcmp(word[2], "-n") == 0) // comma sep network list
  2325. {
  2326. net = word[3];
  2327. i += 2;
  2328. }
  2329. while (1)
  2330. {
  2331. i++;
  2332. if (!*word[i])
  2333. break;
  2334. if (notify_deluser(word[i]))
  2335. {
  2336. EMIT_SIGNAL(XP_TE_DELNOTIFY, sess, word[i], NULL, NULL, NULL, 0);
  2337. return TRUE;
  2338. }
  2339. if (net && strcmp(net, "ASK") == 0)
  2340. fe_notify_ask(word[i], NULL);
  2341. else
  2342. {
  2343. notify_adduser(word[i], net);
  2344. EMIT_SIGNAL(XP_TE_ADDNOTIFY, sess, word[i], NULL, NULL, NULL, 0);
  2345. }
  2346. }
  2347. } else
  2348. notify_showlist(sess);
  2349. return TRUE;
  2350. }
  2351. static int cmd_op(session *sess, char *tbuf, char *word[], char *word_eol[])
  2352. {
  2353. int i = 2;
  2354. while (1)
  2355. {
  2356. if (!*word[i])
  2357. {
  2358. if (i == 2)
  2359. return FALSE;
  2360. send_channel_modes(sess, tbuf, word, 2, i, '+', 'o', 0);
  2361. return TRUE;
  2362. }
  2363. i++;
  2364. }
  2365. }
  2366. static int cmd_part(session *sess, char *tbuf, char *word[], char *word_eol[])
  2367. {
  2368. char *chan = word[2];
  2369. char *reason = word_eol[3];
  2370. if (!*chan)
  2371. chan = sess->channel;
  2372. if ((*chan) && is_channel(sess->server, chan))
  2373. {
  2374. if (reason[0] == 0)
  2375. reason = NULL;
  2376. server_sendpart(sess->server, chan, reason);
  2377. return TRUE;
  2378. }
  2379. return FALSE;
  2380. }
  2381. static int cmd_ping(session *sess, char *tbuf, char *word[], char *word_eol[])
  2382. {
  2383. char timestring[64];
  2384. unsigned long tim;
  2385. char *to = word[2];
  2386. tim = make_ping_time();
  2387. snprintf(timestring, sizeof(timestring), "%lu", tim);
  2388. sess->server->p_ping(sess->server, to, timestring);
  2389. return TRUE;
  2390. }
  2391. void open_query(server *serv, char *nick, bool focus_existing)
  2392. {
  2393. session *sess;
  2394. sess = find_dialog(serv, nick);
  2395. if (!sess)
  2396. new_ircwindow(serv, nick, SESS_DIALOG, 1);
  2397. else if (focus_existing)
  2398. fe_ctrl_gui(sess, FE_GUI_FOCUS, 0); // bring-to-front
  2399. }
  2400. static int cmd_query(session *sess, char *tbuf, char *word[], char *word_eol[])
  2401. {
  2402. char *nick = word[2];
  2403. bool focus = TRUE;
  2404. if (strcmp(word[2], "-nofocus") == 0)
  2405. {
  2406. nick = word[3];
  2407. focus = FALSE;
  2408. }
  2409. if (*nick && !is_channel(sess->server, nick))
  2410. {
  2411. open_query(sess->server, nick, focus);
  2412. return TRUE;
  2413. }
  2414. return FALSE;
  2415. }
  2416. static int cmd_quote(session *sess, char *tbuf, char *word[], char *word_eol[])
  2417. {
  2418. char *raw = word_eol[2];
  2419. return sess->server->p_raw(sess->server, raw);
  2420. }
  2421. static int cmd_reconnect(session *sess, char *tbuf, char *word[], char *word_eol[])
  2422. {
  2423. int tmp = prefs.recon_delay;
  2424. GSList *list;
  2425. server *serv = sess->server;
  2426. prefs.recon_delay = 0;
  2427. if (!strcasecmp(word[2], "ALL"))
  2428. {
  2429. list = serv_list;
  2430. while (list)
  2431. {
  2432. serv = (server*)list->data;
  2433. if (serv->connected)
  2434. serv->auto_reconnect(serv, TRUE, -1);
  2435. list = list->next;
  2436. }
  2437. }
  2438. /* If it isn't "ALL" and there is something
  2439. there it *should* be a server they are trying to connect to*/
  2440. else if (*word[2])
  2441. {
  2442. int offset = 0;
  2443. #ifdef USE_OPENSSL
  2444. int use_ssl = FALSE;
  2445. if (strcmp(word[2], "-ssl") == 0)
  2446. {
  2447. use_ssl = TRUE;
  2448. offset++; // args move up by 1 word
  2449. }
  2450. serv->use_ssl = use_ssl;
  2451. serv->accept_invalid_cert = TRUE;
  2452. #endif
  2453. if (*word[4+offset])
  2454. safe_strcpy(serv->password, word[4+offset], sizeof(serv->password));
  2455. if (*word[3+offset])
  2456. serv->port = atoi(word[3+offset]);
  2457. safe_strcpy(serv->hostname, word[2+offset], sizeof(serv->hostname));
  2458. serv->auto_reconnect(serv, TRUE, -1);
  2459. }
  2460. else
  2461. {
  2462. serv->auto_reconnect(serv, TRUE, -1);
  2463. }
  2464. prefs.recon_delay = tmp;
  2465. return TRUE;
  2466. }
  2467. static int cmd_recv(session *sess, char *tbuf, char *word[], char *word_eol[])
  2468. {
  2469. if (*word_eol[2])
  2470. {
  2471. sess->server->p_inline(sess->server, word_eol[2], strlen(word_eol[2]));
  2472. return TRUE;
  2473. }
  2474. return FALSE;
  2475. }
  2476. static int cmd_say(session *sess, char *tbuf, char *word[], char *word_eol[])
  2477. {
  2478. char *speech = word_eol[2];
  2479. if (*speech)
  2480. {
  2481. handle_say(sess, speech, FALSE);
  2482. return TRUE;
  2483. }
  2484. return FALSE;
  2485. }
  2486. static int cmd_send(session *sess, char *tbuf, char *word[], char *word_eol[])
  2487. {
  2488. unsigned int addr;
  2489. socklen_t len;
  2490. sockaddr_in SAddr;
  2491. if (!word[2][0])
  2492. return FALSE;
  2493. addr = dcc_get_my_address();
  2494. if (addr == 0)
  2495. {
  2496. // use the one from our connected server socket
  2497. memset(&SAddr, 0, sizeof(sockaddr_in));
  2498. len = sizeof(SAddr);
  2499. getsockname(sess->server->sok, (sockaddr*)&SAddr, (socklen_t*)&len);
  2500. addr = SAddr.sin_addr.s_addr;
  2501. }
  2502. addr = ntohl(addr);
  2503. if ((addr & 0xffff0000) == 0xc0a80000 || // 192.168.x.x
  2504. (addr & 0xff000000) == 0x0a000000) // 10.x.x.x
  2505. // we got a private net address, let's PSEND or it'll fail
  2506. snprintf(tbuf, 512, "DCC PSEND %s", word_eol[2]);
  2507. else
  2508. snprintf(tbuf, 512, "DCC SEND %s", word_eol[2]);
  2509. handle_command(sess, tbuf, FALSE);
  2510. return TRUE;
  2511. }
  2512. static int cmd_setcursor(session *sess, char *tbuf, char *word[], char *word_eol[])
  2513. {
  2514. int delta = FALSE;
  2515. if (*word[2])
  2516. {
  2517. if (word[2][0] == '-' || word[2][0] == '+')
  2518. delta = TRUE;
  2519. fe_set_inputbox_cursor(sess, delta, atoi(word[2]));
  2520. return TRUE;
  2521. }
  2522. return FALSE;
  2523. }
  2524. static int cmd_settab(session *sess, char *tbuf, char *word[], char *word_eol[])
  2525. {
  2526. if (*word_eol[2])
  2527. {
  2528. strcpy(tbuf, sess->channel);
  2529. safe_strcpy(sess->channel, word_eol[2], CHANLEN);
  2530. fe_set_channel(sess);
  2531. strcpy(sess->channel, tbuf);
  2532. }
  2533. return TRUE;
  2534. }
  2535. static int cmd_settext(session *sess, char *tbuf, char *word[], char *word_eol[])
  2536. {
  2537. fe_set_inputbox_contents(sess, word_eol[2]);
  2538. return TRUE;
  2539. }
  2540. static int cmd_splay(session *sess, char *tbuf, char *word[], char *word_eol[])
  2541. {
  2542. if (*word[2])
  2543. {
  2544. sound_play(word[2], FALSE);
  2545. return TRUE;
  2546. }
  2547. return FALSE;
  2548. }
  2549. static int parse_irc_url(char *url, char *server_name[], char *port[], char *channel[], int *use_ssl)
  2550. {
  2551. char *co;
  2552. #ifdef USE_OPENSSL
  2553. if (strncasecmp("ircs://", url, 7) == 0)
  2554. {
  2555. *use_ssl = TRUE;
  2556. *server_name = url + 7;
  2557. goto urlserv;
  2558. }
  2559. #endif
  2560. if (strncasecmp("irc://", url, 6) == 0)
  2561. {
  2562. *server_name = url + 6;
  2563. #ifdef USE_OPENSSL
  2564. urlserv:
  2565. #endif
  2566. // check for port
  2567. co = strchr(*server_name, ':');
  2568. if (co)
  2569. {
  2570. *port = co + 1;
  2571. *co = 0;
  2572. } else
  2573. co = *server_name;
  2574. // check for channel - mirc style
  2575. co = strchr(co + 1, '/');
  2576. if (co)
  2577. {
  2578. *co = 0;
  2579. co++;
  2580. if (*co == '#')
  2581. *channel = co+1;
  2582. else
  2583. *channel = co;
  2584. }
  2585. return TRUE;
  2586. }
  2587. return FALSE;
  2588. }
  2589. static int cmd_server(session *sess, char *tbuf, char *word[], char *word_eol[])
  2590. {
  2591. int offset = 0;
  2592. char *server_name = NULL;
  2593. char *port = NULL;
  2594. char *pass = NULL;
  2595. char *channel = NULL;
  2596. int use_ssl = FALSE;
  2597. int is_url = TRUE;
  2598. server *serv = sess->server;
  2599. #ifdef USE_OPENSSL
  2600. // BitchX uses -ssl, mIRC uses -e, let's support both
  2601. if (strcmp(word[2], "-ssl") == 0 || strcmp(word[2], "-e") == 0)
  2602. {
  2603. use_ssl = TRUE;
  2604. offset++; // args move up by 1 word
  2605. }
  2606. #endif
  2607. if (!parse_irc_url(word[2 + offset], &server_name, &port, &channel, &use_ssl))
  2608. {
  2609. is_url = FALSE;
  2610. server_name = word[2 + offset];
  2611. }
  2612. if (port)
  2613. pass = word[3 + offset];
  2614. else
  2615. {
  2616. port = word[3 + offset];
  2617. pass = word[4 + offset];
  2618. }
  2619. if (!(*server_name))
  2620. return FALSE;
  2621. sess->server->network = NULL;
  2622. // dont clear it for /servchan
  2623. if (strncasecmp(word_eol[1], "SERVCHAN ", 9))
  2624. sess->willjoinchannel[0] = 0;
  2625. if (channel)
  2626. {
  2627. sess->willjoinchannel[0] = '#';
  2628. safe_strcpy(sess->willjoinchannel + 1, channel, CHANLEN - 1);
  2629. }
  2630. // support +7000 style ports like mIRC
  2631. if (port[0] == '+')
  2632. {
  2633. port++;
  2634. #ifdef USE_OPENSSL
  2635. use_ssl = TRUE;
  2636. #endif
  2637. }
  2638. if (*pass)
  2639. {
  2640. safe_strcpy(serv->password, pass, sizeof(serv->password));
  2641. }
  2642. #ifdef USE_OPENSSL
  2643. serv->use_ssl = use_ssl;
  2644. serv->accept_invalid_cert = TRUE;
  2645. #endif
  2646. // try to connect by Network name
  2647. if (servlist_connect_by_netname(sess, server_name, !is_url))
  2648. return TRUE;
  2649. if (*port)
  2650. {
  2651. serv->connect(serv, server_name, atoi(port), FALSE);
  2652. }
  2653. else
  2654. {
  2655. // -1 for default port
  2656. serv->connect(serv, server_name, -1, FALSE);
  2657. }
  2658. // try to associate this connection with a listed network
  2659. if (!serv->network)
  2660. // search for this hostname in the entire server list
  2661. serv->network = servlist_net_find_from_server(server_name);
  2662. // may return NULL, but that's OK
  2663. return TRUE;
  2664. }
  2665. static int cmd_servchan(session *sess, char *tbuf, char *word[], char *word_eol[])
  2666. {
  2667. int offset = 0;
  2668. #ifdef USE_OPENSSL
  2669. if (strcmp(word[2], "-ssl") == 0)
  2670. offset++;
  2671. #endif
  2672. if (*word[4 + offset])
  2673. {
  2674. safe_strcpy(sess->willjoinchannel, word[4 + offset], CHANLEN);
  2675. return cmd_server(sess, tbuf, word, word_eol);
  2676. }
  2677. return FALSE;
  2678. }
  2679. static int cmd_topic(session *sess, char *tbuf, char *word[], char *word_eol[])
  2680. {
  2681. if (word[2][0] && is_channel (sess->server, word[2]))
  2682. sess->server->p_topic(sess->server, word[2], word_eol[3]);
  2683. else
  2684. sess->server->p_topic(sess->server, sess->channel, word_eol[2]);
  2685. return TRUE;
  2686. }
  2687. static int cmd_tray(session *sess, char *tbuf, char *word[], char *word_eol[])
  2688. {
  2689. if (strcmp(word[2], "-b") == 0)
  2690. {
  2691. fe_tray_set_balloon(word[3], word[4][0] ? word[4] : NULL);
  2692. return TRUE;
  2693. }
  2694. if (strcmp(word[2], "-t") == 0)
  2695. {
  2696. fe_tray_set_tooltip(word[3][0] ? word[3] : NULL);
  2697. return TRUE;
  2698. }
  2699. if (strcmp(word[2], "-i") == 0)
  2700. {
  2701. fe_tray_set_icon((feicon)atoi(word[3]));
  2702. return TRUE;
  2703. }
  2704. if (strcmp(word[2], "-f") != 0)
  2705. return FALSE;
  2706. if (!word[3][0])
  2707. {
  2708. fe_tray_set_file(NULL); // default xchat icon
  2709. return TRUE;
  2710. }
  2711. if (!word[4][0])
  2712. {
  2713. fe_tray_set_file(word[3]); // fixed custom icon
  2714. return TRUE;
  2715. }
  2716. // flash between 2 icons
  2717. fe_tray_set_flash(word[4], word[5][0] ? word[5] : NULL, atoi(word[3]));
  2718. return TRUE;
  2719. }
  2720. static int cmd_unignore(session *sess, char *tbuf, char *word[], char *word_eol[])
  2721. {
  2722. char *mask = word[2];
  2723. char *arg = word[3];
  2724. if (*mask)
  2725. {
  2726. if (ignore_del(mask, NULL))
  2727. {
  2728. if (strcasecmp(arg, "QUIET"))
  2729. EMIT_SIGNAL(XP_TE_IGNOREREMOVE, sess, mask, NULL, NULL, NULL, 0);
  2730. }
  2731. return TRUE;
  2732. }
  2733. return FALSE;
  2734. }
  2735. static int cmd_unload(session *sess, char *tbuf, char *word[], char *word_eol[])
  2736. {
  2737. #ifdef USE_PLUGIN
  2738. int len, by_file = FALSE;
  2739. len = strlen(word[2]);
  2740. #ifdef WIN32
  2741. if (len > 4 && strcasecmp(word[2] + len - 4, ".dll") == 0)
  2742. #else
  2743. #if defined(__hpux)
  2744. if (len > 3 && strcasecmp(word[2] + len - 3, ".sl") == 0)
  2745. #else
  2746. if (len > 3 && strcasecmp(word[2] + len - 3, ".so") == 0)
  2747. #endif
  2748. #endif
  2749. by_file = TRUE;
  2750. switch (plugin_kill(word[2], by_file))
  2751. {
  2752. case 0:
  2753. PrintText(sess, _("No such plugin found.\n"));
  2754. break;
  2755. case 1:
  2756. return TRUE;
  2757. case 2:
  2758. PrintText(sess, _("That plugin is refusing to unload.\n"));
  2759. break;
  2760. }
  2761. #endif
  2762. return FALSE;
  2763. }
  2764. static server* find_server_from_hostname(char *hostname)
  2765. {
  2766. GSList *list = serv_list;
  2767. server *serv;
  2768. while (list)
  2769. {
  2770. serv = (server*)list->data;
  2771. if (!strcasecmp(hostname, serv->hostname) && serv->connected)
  2772. return serv;
  2773. list = list->next;
  2774. }
  2775. return NULL;
  2776. }
  2777. static server* find_server_from_net(void *net)
  2778. {
  2779. GSList *list = serv_list;
  2780. server *serv;
  2781. while (list)
  2782. {
  2783. serv = (server*)list->data;
  2784. if (serv->network == net && serv->connected)
  2785. return serv;
  2786. list = list->next;
  2787. }
  2788. return NULL;
  2789. }
  2790. static void url_join_only(server *serv, char *tbuf, char *channel)
  2791. {
  2792. // already connected, JOIN only. FIXME: support keys?
  2793. tbuf[0] = '#';
  2794. // tbuf is 4kb
  2795. safe_strcpy(tbuf + 1, channel, 256);
  2796. serv->p_join(serv, tbuf, "");
  2797. }
  2798. static int cmd_url(session *sess, char *tbuf, char *word[], char *word_eol[])
  2799. {
  2800. if (word[2][0])
  2801. {
  2802. char *server_name = NULL;
  2803. char *port = NULL;
  2804. char *channel = NULL;
  2805. char *url = strdup(word[2]);
  2806. int use_ssl = FALSE;
  2807. void *net;
  2808. server *serv;
  2809. if (parse_irc_url(url, &server_name, &port, &channel, &use_ssl))
  2810. {
  2811. // maybe we're already connected to this net
  2812. // check for "FreeNode"
  2813. net = servlist_net_find(server_name, NULL, strcasecmp);
  2814. // check for "irc.eu.freenode.net"
  2815. if (!net)
  2816. net = servlist_net_find_from_server(server_name);
  2817. if (net)
  2818. {
  2819. // found the network, but are we connected?
  2820. serv = find_server_from_net(net);
  2821. if (serv)
  2822. {
  2823. url_join_only(serv, tbuf, channel);
  2824. free(url);
  2825. return TRUE;
  2826. }
  2827. }
  2828. else
  2829. {
  2830. // an un-listed connection
  2831. serv = find_server_from_hostname(server_name);
  2832. if (serv)
  2833. {
  2834. url_join_only(serv, tbuf, channel);
  2835. free(url);
  2836. return TRUE;
  2837. }
  2838. }
  2839. // not connected to this net, open new window
  2840. cmd_newserver(sess, tbuf, word, word_eol);
  2841. } else
  2842. fe_open_url(word[2]);
  2843. free(url);
  2844. return TRUE;
  2845. }
  2846. return FALSE;
  2847. }
  2848. static int userlist_cb(User *user, session *sess)
  2849. {
  2850. time_t lt;
  2851. if (!user->lasttalk)
  2852. lt = 0;
  2853. else
  2854. lt = time(0) - user->lasttalk;
  2855. PrintTextf(sess,
  2856. "\00306%s\t\00314[\00310%-38s\00314] \017ov\0033=\017%d%d away=%u lt\0033=\017%d\n",
  2857. user->nick, user->hostname, user->op, user->voice, user->away, lt);
  2858. return TRUE;
  2859. }
  2860. static int cmd_uselect(session *sess, char *tbuf, char *word[], char *word_eol[])
  2861. {
  2862. int idx = 2;
  2863. int clear = TRUE;
  2864. int scroll = FALSE;
  2865. if (strcmp(word[2], "-a") == 0) // ADD (don't clear selections)
  2866. {
  2867. clear = FALSE;
  2868. idx++;
  2869. }
  2870. if (strcmp(word[idx], "-s") == 0) // SCROLL TO
  2871. {
  2872. scroll = TRUE;
  2873. idx++;
  2874. }
  2875. // always valid, no args means clear the selection list
  2876. fe_uselect(sess, word + idx, clear, scroll);
  2877. return TRUE;
  2878. }
  2879. static int cmd_userlist(session *sess, char *tbuf, char *word[], char *word_eol[])
  2880. {
  2881. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)userlist_cb, sess);
  2882. return TRUE;
  2883. }
  2884. static int wallchop_cb(User *user, multidata *data)
  2885. {
  2886. if (user->op)
  2887. {
  2888. if (data->i)
  2889. strcat(data->tbuf, ",");
  2890. strcat(data->tbuf, user->nick);
  2891. data->i++;
  2892. }
  2893. if (data->i == 5)
  2894. {
  2895. data->i = 0;
  2896. sprintf(data->tbuf + strlen(data->tbuf),
  2897. " :[@%s] %s", data->sess->channel, data->reason);
  2898. data->sess->server->p_raw(data->sess->server, data->tbuf);
  2899. strcpy(data->tbuf, "NOTICE ");
  2900. }
  2901. return TRUE;
  2902. }
  2903. static int cmd_wallchop(session *sess, char *tbuf, char *word[], char *word_eol[])
  2904. {
  2905. multidata data;
  2906. if (!(*word_eol[2]))
  2907. return FALSE;
  2908. strcpy(tbuf, "NOTICE ");
  2909. data.reason = word_eol[2];
  2910. data.tbuf = tbuf;
  2911. data.i = 0;
  2912. data.sess = sess;
  2913. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)wallchop_cb, &data);
  2914. if (data.i)
  2915. {
  2916. sprintf(tbuf + strlen(tbuf),
  2917. " :[@%s] %s", sess->channel, word_eol[2]);
  2918. sess->server->p_raw(sess->server, tbuf);
  2919. }
  2920. return TRUE;
  2921. }
  2922. static int cmd_wallchan(session *sess, char *tbuf, char *word[], char *word_eol[])
  2923. {
  2924. GSList *list;
  2925. if (*word_eol[2])
  2926. {
  2927. list = sess_list;
  2928. while (list)
  2929. {
  2930. sess = (session*)list->data;
  2931. if (sess->type == SESS_CHANNEL)
  2932. {
  2933. inbound_chanmsg(sess->server, NULL, sess->channel,
  2934. sess->server->nick, word_eol[2], TRUE, FALSE);
  2935. sess->server->p_message(sess->server, sess->channel, word_eol[2]);
  2936. }
  2937. list = list->next;
  2938. }
  2939. return TRUE;
  2940. }
  2941. return FALSE;
  2942. }
  2943. static int cmd_hop(session *sess, char *tbuf, char *word[], char *word_eol[])
  2944. {
  2945. int i = 2;
  2946. while (1)
  2947. {
  2948. if (!*word[i])
  2949. {
  2950. if (i == 2)
  2951. return FALSE;
  2952. send_channel_modes(sess, tbuf, word, 2, i, '+', 'h', 0);
  2953. return TRUE;
  2954. }
  2955. i++;
  2956. }
  2957. }
  2958. static int cmd_voice(session *sess, char *tbuf, char *word[], char *word_eol[])
  2959. {
  2960. int i = 2;
  2961. while (1)
  2962. {
  2963. if (!*word[i])
  2964. {
  2965. if (i == 2)
  2966. return FALSE;
  2967. send_channel_modes(sess, tbuf, word, 2, i, '+', 'v', 0);
  2968. return TRUE;
  2969. }
  2970. i++;
  2971. }
  2972. }
  2973. // *MUST* be kept perfectly sorted for the bsearch to work
  2974. const commands xc_cmds[] = {
  2975. {"ADDBUTTON", cmd_addbutton, 0, 0, 1,
  2976. N_("ADDBUTTON <name> <action>, adds a button under the user-list")},
  2977. {"ALLCHAN", cmd_allchannels, 0, 0, 1,
  2978. N_("ALLCHAN <cmd>, sends a command to all channels you're in")},
  2979. {"ALLCHANL", cmd_allchannelslocal, 0, 0, 1,
  2980. N_("ALLCHANL <cmd>, sends a command to all channels you're in")},
  2981. {"ALLSERV", cmd_allservers, 0, 0, 1,
  2982. N_("ALLSERV <cmd>, sends a command to all servers you're in")},
  2983. {"AWAY", cmd_away, 1, 0, 1, N_("AWAY [<reason>], sets you away")},
  2984. {"BACK", cmd_back, 1, 0, 1, N_("BACK, sets you back (not away)")},
  2985. {"BAN", cmd_ban, 1, 1, 1,
  2986. N_("BAN <mask> [<bantype>], bans everyone matching the mask from the current channel. If they are already on the channel this doesn't kick them (needs chanop)")},
  2987. {"CHANOPT", cmd_chanopt, 0, 0, 1, N_("CHANOPT [-quiet] <variable> [<value>]")},
  2988. {"CHARSET", cmd_charset, 0, 0, 1, 0},
  2989. {"CLEAR", cmd_clear, 0, 0, 1, N_("CLEAR [ALL|HISTORY], Clears the current text window or command history")},
  2990. {"CLOSE", cmd_close, 0, 0, 1, N_("CLOSE, Closes the current window/tab")},
  2991. {"COUNTRY", cmd_country, 0, 0, 1,
  2992. N_("COUNTRY [-s] <code|wildcard>, finds a country code, eg: au = australia")},
  2993. {"CTCP", cmd_ctcp, 1, 0, 1,
  2994. N_("CTCP <nick> <message>, send the CTCP message to nick, common messages are VERSION and USERINFO")},
  2995. {"CYCLE", cmd_cycle, 1, 1, 1,
  2996. N_("CYCLE [<channel>], parts the current or given channel and immediately rejoins")},
  2997. {"DCC", cmd_dcc, 0, 0, 1,
  2998. N_("\n"
  2999. "DCC GET <nick> - accept an offered file\n"
  3000. "DCC SEND [-maxcps=#] <nick> [file] - send a file to someone\n"
  3001. "DCC PSEND [-maxcps=#] <nick> [file] - send a file using passive mode\n"
  3002. "DCC LIST - show DCC list\n"
  3003. "DCC CHAT <nick> - offer DCC CHAT to someone\n"
  3004. "DCC PCHAT <nick> - offer DCC CHAT using passive mode\n"
  3005. "DCC CLOSE <type> <nick> <file> example:\n"
  3006. " /dcc close send johnsmith file.tar.gz")},
  3007. {"DEBUG", cmd_debug, 0, 0, 1, 0},
  3008. {"DEHOP", cmd_dehop, 1, 1, 1,
  3009. N_("DEHOP <nick>, removes chanhalf-op status from the nick on the current channel (needs chanop)")},
  3010. {"DELBUTTON", cmd_delbutton, 0, 0, 1,
  3011. N_("DELBUTTON <name>, deletes a button from under the user-list")},
  3012. {"DEOP", cmd_deop, 1, 1, 1,
  3013. N_("DEOP <nick>, removes chanop status from the nick on the current channel (needs chanop)")},
  3014. {"DEVOICE", cmd_devoice, 1, 1, 1,
  3015. N_("DEVOICE <nick>, removes voice status from the nick on the current channel (needs chanop)")},
  3016. {"DISCON", cmd_discon, 0, 0, 1, N_("DISCON, Disconnects from server")},
  3017. {"DNS", cmd_dns, 0, 0, 1, N_("DNS <nick|host|ip>, Finds a users IP number")},
  3018. {"ECHO", cmd_echo, 0, 0, 1, N_("ECHO <text>, Prints text locally")},
  3019. #ifndef WIN32
  3020. {"EXEC", cmd_exec, 0, 0, 1,
  3021. N_("EXEC [-o] <command>, runs the command. If -o flag is used then output is sent to current channel, else is printed to current text box")},
  3022. #ifndef __EMX__
  3023. {"EXECCONT", cmd_execc, 0, 0, 1, N_("EXECCONT, sends the process SIGCONT")},
  3024. #endif
  3025. {"EXECKILL", cmd_execk, 0, 0, 1,
  3026. N_("EXECKILL [-9], kills a running exec in the current session. If -9 is given the process is SIGKILL'ed")},
  3027. #ifndef __EMX__
  3028. {"EXECSTOP", cmd_execs, 0, 0, 1, N_("EXECSTOP, sends the process SIGSTOP")},
  3029. {"EXECWRITE", cmd_execw, 0, 0, 1, N_("EXECWRITE, sends data to the processes stdin")},
  3030. #endif
  3031. #endif
  3032. {"FLUSHQ", cmd_flushq, 0, 0, 1,
  3033. N_("FLUSHQ, flushes the current server's send queue")},
  3034. {"GATE", cmd_gate, 0, 0, 1,
  3035. N_("GATE <host> [<port>], proxies through a host, port defaults to 23")},
  3036. {"GETFILE", cmd_getfile, 0, 0, 1, "GETFILE [-folder] [-multi] [-save] <command> <title> [<initial>]"},
  3037. {"GETINT", cmd_getint, 0, 0, 1, "GETINT <default> <command> <prompt>"},
  3038. {"GETSTR", cmd_getstr, 0, 0, 1, "GETSTR <default> <command> <prompt>"},
  3039. {"GHOST", cmd_ghost, 1, 0, 1, N_("GHOST <nick> [password], Kills a ghosted nickname")},
  3040. {"GUI", cmd_gui, 0, 0, 1, "GUI [APPLY|ATTACH|DETACH|SHOW|HIDE|FOCUS|FLASH|ICONIFY|COLOR <n>]\n"
  3041. " GUI [MSGBOX <text>|MENU TOGGLE]"},
  3042. {"HELP", cmd_help, 0, 0, 1, 0},
  3043. {"HOP", cmd_hop, 1, 1, 1,
  3044. N_("HOP <nick>, gives chanhalf-op status to the nick (needs chanop)")},
  3045. {"ID", cmd_id, 1, 0, 1, N_("ID <password>, identifies yourself to nickserv")},
  3046. {"IGNORE", cmd_ignore, 0, 0, 1,
  3047. N_("IGNORE <mask> <types..> <options..>\n"
  3048. " mask - host mask to ignore, eg: *!*@*.aol.com\n"
  3049. " types - types of data to ignore, one or all of:\n"
  3050. " PRIV, CHAN, NOTI, CTCP, DCC, INVI, ALL\n"
  3051. " options - NOSAVE, QUIET")},
  3052. {"INVITE", cmd_invite, 1, 0, 1,
  3053. N_("INVITE <nick> [<channel>], invites someone to a channel, by default the current channel (needs chanop)")},
  3054. {"JOIN", cmd_join, 1, 0, 0, N_("JOIN <channel>, joins the channel")},
  3055. {"KICK", cmd_kick, 1, 1, 1,
  3056. N_("KICK <nick>, kicks the nick from the current channel (needs chanop)")},
  3057. {"KICKBAN", cmd_kickban, 1, 1, 1,
  3058. N_("KICKBAN <nick>, bans then kicks the nick from the current channel (needs chanop)")},
  3059. {"KILLALL", cmd_killall, 0, 0, 1, "KILLALL, immediately exit"},
  3060. {"LAGCHECK", cmd_lagcheck, 0, 0, 1,
  3061. N_("LAGCHECK, forces a new lag check")},
  3062. {"LASTLOG", cmd_lastlog, 0, 0, 1,
  3063. N_("LASTLOG <string>, searches for a string in the buffer")},
  3064. {"LIST", cmd_list, 1, 0, 1, 0},
  3065. {"LOAD", cmd_load, 0, 0, 1, N_("LOAD [-e] <file>, loads a plugin or script")},
  3066. {"MDEHOP", cmd_mdehop, 1, 1, 1,
  3067. N_("MDEHOP, Mass deop's all chanhalf-ops in the current channel (needs chanop)")},
  3068. {"MDEOP", cmd_mdeop, 1, 1, 1,
  3069. N_("MDEOP, Mass deop's all chanops in the current channel (needs chanop)")},
  3070. {"ME", cmd_me, 0, 0, 1,
  3071. N_("ME <action>, sends the action to the current channel (actions are written in the 3rd person, like /me jumps)")},
  3072. {"MENU", cmd_menu, 0, 0, 1, "MENU [-eX] [-i<ICONFILE>] [-k<mod>,<key>] [-m] [-pX] [-r<X,group>] [-tX] {ADD|DEL} <path> [command] [unselect command]\n"
  3073. " See http://pchat-irc.com/documentation/ for more details."},
  3074. {"MKICK", cmd_mkick, 1, 1, 1,
  3075. N_("MKICK, Mass kicks everyone except you in the current channel (needs chanop)")},
  3076. {"MODE", cmd_mode, 1, 0, 1, 0},
  3077. {"MOP", cmd_mop, 1, 1, 1,
  3078. N_("MOP, Mass op's all users in the current channel (needs chanop)")},
  3079. {"MSG", cmd_msg, 0, 0, 1, N_("MSG <nick> <message>, sends a private message")},
  3080. {"NAMES", cmd_names, 1, 0, 1,
  3081. N_("NAMES, Lists the nicks on the current channel")},
  3082. {"NCTCP", cmd_nctcp, 1, 0, 1,
  3083. N_("NCTCP <nick> <message>, Sends a CTCP notice")},
  3084. {"NEWSERVER", cmd_newserver, 0, 0, 1, N_("NEWSERVER [-noconnect] <hostname> [<port>]")},
  3085. {"NICK", cmd_nick, 0, 0, 1, N_("NICK <nickname>, sets your nick")},
  3086. {"NOTICE", cmd_notice, 1, 0, 1,
  3087. N_("NOTICE <nick/channel> <message>, sends a notice. Notices are a type of message that should be auto reacted to")},
  3088. {"NOTIFY", cmd_notify, 0, 0, 1,
  3089. N_("NOTIFY [-n network1[,network2,...]] [<nick>], displays your notify list or adds someone to it")},
  3090. {"OP", cmd_op, 1, 1, 1,
  3091. N_("OP <nick>, gives chanop status to the nick (needs chanop)")},
  3092. {"PART", cmd_part, 1, 1, 0,
  3093. N_("PART [<channel>] [<reason>], leaves the channel, by default the current one")},
  3094. {"PING", cmd_ping, 1, 0, 1,
  3095. N_("PING <nick | channel>, CTCP pings nick or channel")},
  3096. {"QUERY", cmd_query, 0, 0, 1,
  3097. N_("QUERY [-nofocus] <nick>, opens up a new privmsg window to someone")},
  3098. {"QUIT", cmd_quit, 0, 0, 1,
  3099. N_("QUIT [<reason>], disconnects from the current server")},
  3100. {"QUOTE", cmd_quote, 1, 0, 1,
  3101. N_("QUOTE <text>, sends the text in raw form to the server")},
  3102. #ifdef USE_OPENSSL
  3103. {"RECONNECT", cmd_reconnect, 0, 0, 1,
  3104. N_("RECONNECT [-ssl] [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")},
  3105. #else
  3106. {"RECONNECT", cmd_reconnect, 0, 0, 1,
  3107. N_("RECONNECT [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")},
  3108. #endif
  3109. {"RECV", cmd_recv, 1, 0, 1, N_("RECV <text>, send raw data to PChat, as if it was received from the irc server")},
  3110. {"SAY", cmd_say, 0, 0, 1,
  3111. N_("SAY <text>, sends the text to the object in the current window")},
  3112. {"SEND", cmd_send, 0, 0, 1, N_("SEND <nick> [<file>]")},
  3113. #ifdef USE_OPENSSL
  3114. {"SERVCHAN", cmd_servchan, 0, 0, 1,
  3115. N_("SERVCHAN [-ssl] <host> <port> <channel>, connects and joins a channel")},
  3116. #else
  3117. {"SERVCHAN", cmd_servchan, 0, 0, 1,
  3118. N_("SERVCHAN <host> <port> <channel>, connects and joins a channel")},
  3119. #endif
  3120. #ifdef USE_OPENSSL
  3121. {"SERVER", cmd_server, 0, 0, 1,
  3122. N_("SERVER [-ssl] <host> [<port>] [<password>], connects to a server, the default port is 6667 for normal connections, and 9999 for ssl connections")},
  3123. #else
  3124. {"SERVER", cmd_server, 0, 0, 1,
  3125. N_("SERVER <host> [<port>] [<password>], connects to a server, the default port is 6667")},
  3126. #endif
  3127. {"SET", cmd_set, 0, 0, 1, N_("SET [-e] [-off|-on] [-quiet] <variable> [<value>]")},
  3128. {"SETCURSOR", cmd_setcursor, 0, 0, 1, N_("SETCURSOR [-|+]<position>")},
  3129. {"SETTAB", cmd_settab, 0, 0, 1, 0},
  3130. {"SETTEXT", cmd_settext, 0, 0, 1, 0},
  3131. {"SPLAY", cmd_splay, 0, 0, 1, "SPLAY <soundfile>"},
  3132. {"TOPIC", cmd_topic, 1, 1, 1,
  3133. N_("TOPIC [<topic>], sets the topic if one is given, else shows the current topic")},
  3134. {"TRAY", cmd_tray, 0, 0, 1,
  3135. N_("\nTRAY -f <timeout> <file1> [<file2>] Blink tray between two icons.\n"
  3136. "TRAY -f <filename> Set tray to a fixed icon.\n"
  3137. "TRAY -i <number> Blink tray with an internal icon.\n"
  3138. "TRAY -t <text> Set the tray tooltip.\n"
  3139. "TRAY -b <title> <text> Set the tray balloon."
  3140. )},
  3141. {"UNBAN", cmd_unban, 1, 1, 1,
  3142. N_("UNBAN <mask> [<mask>...], unbans the specified masks.")},
  3143. {"UNIGNORE", cmd_unignore, 0, 0, 1, N_("UNIGNORE <mask> [QUIET]")},
  3144. {"UNLOAD", cmd_unload, 0, 0, 1, N_("UNLOAD <name>, unloads a plugin or script")},
  3145. {"URL", cmd_url, 0, 0, 1, N_("URL <url>, opens a URL in your browser")},
  3146. {"USELECT", cmd_uselect, 0, 1, 0,
  3147. N_("USELECT [-a] [-s] <nick1> <nick2> etc, highlights nick(s) in channel userlist")},
  3148. {"USERLIST", cmd_userlist, 1, 1, 1, 0},
  3149. {"VOICE", cmd_voice, 1, 1, 1,
  3150. N_("VOICE <nick>, gives voice status to someone (needs chanop)")},
  3151. {"WALLCHAN", cmd_wallchan, 1, 1, 1,
  3152. N_("WALLCHAN <message>, writes the message to all channels")},
  3153. {"WALLCHOP", cmd_wallchop, 1, 1, 1,
  3154. N_("WALLCHOP <message>, sends the message to all chanops on the current channel")},
  3155. {0, 0, 0, 0, 0, 0}
  3156. };
  3157. static int command_compare(const void *a, const void *b)
  3158. {
  3159. return strcasecmp((const char*)a, ((commands*)b)->name);
  3160. }
  3161. static commands* find_internal_command(char *name)
  3162. {
  3163. // the "-1" is to skip the NULL terminator
  3164. return (commands*)bsearch(name, xc_cmds, (sizeof(xc_cmds) /
  3165. sizeof(xc_cmds[0])) - 1, sizeof(xc_cmds[0]), command_compare);
  3166. }
  3167. static void help(session *sess, char *tbuf, char *helpcmd, int quiet)
  3168. {
  3169. commands *cmd;
  3170. if (plugin_show_help(sess, helpcmd))
  3171. return;
  3172. cmd = find_internal_command(helpcmd);
  3173. if (cmd)
  3174. {
  3175. if (cmd->help)
  3176. {
  3177. snprintf(tbuf, TBUFSIZE, _("Usage: %s\n"), _(cmd->help));
  3178. PrintText(sess, tbuf);
  3179. }
  3180. else
  3181. {
  3182. if (!quiet)
  3183. PrintText(sess, _("\nNo help available on that command.\n"));
  3184. }
  3185. return;
  3186. }
  3187. if (!quiet)
  3188. PrintText(sess, _("No such command.\n"));
  3189. }
  3190. /* inserts %a, %c, %d etc into buffer. Also handles &x %x for word/word_eol. *
  3191. * returns 2 on buffer overflow *
  3192. * returns 1 on success *
  3193. * returns 0 on bad-args-for-user-command *
  3194. * - word/word_eol args might be NULL *
  3195. * - this beast is used for UserCommands, UserlistButtons and CTCP replies */
  3196. int auto_insert(char *dest, int destlen, unsigned char *src, char *word[],
  3197. char *word_eol[], char *a, char *c, char *d, char *e, char *h,
  3198. char *n, char *s)
  3199. {
  3200. int num;
  3201. char buf[32];
  3202. time_t now;
  3203. tm *tm_ptr;
  3204. char *utf;
  3205. size_t utf_len;
  3206. char *orig = dest;
  3207. destlen--;
  3208. while (src[0])
  3209. {
  3210. if (src[0] == '%' || src[0] == '&')
  3211. {
  3212. if (isdigit(src[1]))
  3213. {
  3214. if (isdigit(src[2]) && isdigit(src[3]))
  3215. {
  3216. buf[0] = src[1];
  3217. buf[1] = src[2];
  3218. buf[2] = src[3];
  3219. buf[3] = 0;
  3220. dest[0] = atoi(buf);
  3221. utf = g_locale_to_utf8(dest, 1, 0, (gsize*)&utf_len, 0);
  3222. if (utf)
  3223. {
  3224. if ((dest - orig) + utf_len >= destlen)
  3225. {
  3226. g_free(utf);
  3227. return 2;
  3228. }
  3229. memcpy(dest, utf, utf_len);
  3230. g_free(utf);
  3231. dest += utf_len;
  3232. }
  3233. src += 3;
  3234. }
  3235. else
  3236. {
  3237. if (word)
  3238. {
  3239. src++;
  3240. num = src[0] - '0'; // ascii to decimal
  3241. if (*word[num] == 0)
  3242. return 0;
  3243. if (src[-1] == '%')
  3244. utf = word[num];
  3245. else
  3246. utf = word_eol[num];
  3247. // avoid recusive usercommand overflow
  3248. if ((dest - orig) + strlen(utf) >= destlen)
  3249. return 2;
  3250. strcpy(dest, utf);
  3251. dest += strlen(dest);
  3252. }
  3253. }
  3254. }
  3255. else
  3256. {
  3257. if (src[0] == '&')
  3258. goto lamecode;
  3259. src++;
  3260. utf = NULL;
  3261. switch (src[0])
  3262. {
  3263. case '%':
  3264. if ((dest - orig) + 2 >= destlen)
  3265. return 2;
  3266. dest[0] = '%';
  3267. dest[1] = 0;
  3268. dest++;
  3269. break;
  3270. case 'a':
  3271. utf = a;
  3272. break;
  3273. case 'c':
  3274. utf = c;
  3275. break;
  3276. case 'd':
  3277. utf = d;
  3278. break;
  3279. case 'e':
  3280. utf = e;
  3281. break;
  3282. case 'h':
  3283. utf = h;
  3284. break;
  3285. case 'm':
  3286. utf = get_cpu_str();
  3287. break;
  3288. case 'n':
  3289. utf = n;
  3290. break;
  3291. case 's':
  3292. utf = s;
  3293. break;
  3294. case 't':
  3295. now = time(0);
  3296. utf = ctime(&now);
  3297. utf[19] = 0;
  3298. break;
  3299. case 'v':
  3300. utf = PACKAGE_VERSION;
  3301. break;
  3302. case 'y':
  3303. now = time(0);
  3304. tm_ptr = localtime(&now);
  3305. snprintf(buf, sizeof(buf), "%4d%02d%02d", 1900 +
  3306. tm_ptr->tm_year, 1 + tm_ptr->tm_mon, tm_ptr->tm_mday);
  3307. utf = buf;
  3308. break;
  3309. default:
  3310. src--;
  3311. goto lamecode;
  3312. }
  3313. if (utf)
  3314. {
  3315. if ((dest - orig) + strlen(utf) >= destlen)
  3316. return 2;
  3317. strcpy(dest, utf);
  3318. dest += strlen(dest);
  3319. }
  3320. }
  3321. src++;
  3322. }
  3323. else
  3324. {
  3325. utf_len = g_utf8_skip[src[0]];
  3326. if ((dest - orig) + utf_len >= destlen)
  3327. return 2;
  3328. if (utf_len == 1)
  3329. {
  3330. lamecode:
  3331. dest[0] = src[0];
  3332. dest++;
  3333. src++;
  3334. }
  3335. else
  3336. {
  3337. memcpy(dest, src, utf_len);
  3338. dest += utf_len;
  3339. src += utf_len;
  3340. }
  3341. }
  3342. }
  3343. dest[0] = 0;
  3344. return 1;
  3345. }
  3346. void check_special_chars(char *cmd, int do_ascii) // check for %X
  3347. {
  3348. int occur = 0;
  3349. int len = strlen(cmd);
  3350. char *buf, *utf;
  3351. char tbuf[4];
  3352. int i = 0, j = 0;
  3353. size_t utf_len;
  3354. if (!len)
  3355. return;
  3356. buf = (char*)malloc(len + 1);
  3357. if (buf)
  3358. {
  3359. while (cmd[j])
  3360. {
  3361. switch (cmd[j])
  3362. {
  3363. case '%':
  3364. occur++;
  3365. if (do_ascii && j + 3 < len && (isdigit((unsigned char)cmd[j + 1])
  3366. && isdigit((unsigned char)cmd[j + 2]) && isdigit((unsigned char)cmd[j + 3])))
  3367. {
  3368. tbuf[0] = cmd[j + 1];
  3369. tbuf[1] = cmd[j + 2];
  3370. tbuf[2] = cmd[j + 3];
  3371. tbuf[3] = 0;
  3372. buf[i] = atoi(tbuf);
  3373. utf = g_locale_to_utf8(buf + i, 1, 0, (gsize*)&utf_len, 0);
  3374. if (utf)
  3375. {
  3376. memcpy(buf + i, utf, utf_len);
  3377. g_free(utf);
  3378. i += (utf_len - 1);
  3379. }
  3380. j += 3;
  3381. }
  3382. else
  3383. {
  3384. switch (cmd[j + 1])
  3385. {
  3386. case 'R':
  3387. buf[i] = '\026';
  3388. break;
  3389. case 'U':
  3390. buf[i] = '\037';
  3391. break;
  3392. case 'B':
  3393. buf[i] = '\002';
  3394. break;
  3395. case 'C':
  3396. buf[i] = '\003';
  3397. break;
  3398. case 'O':
  3399. buf[i] = '\017';
  3400. break;
  3401. case 'H': // CL: invisible text code
  3402. buf[i] = HIDDEN_CHAR;
  3403. break;
  3404. case '%':
  3405. buf[i] = '%';
  3406. break;
  3407. default:
  3408. buf[i] = '%';
  3409. j--;
  3410. break;
  3411. }
  3412. j++;
  3413. break;
  3414. default:
  3415. buf[i] = cmd[j];
  3416. }
  3417. }
  3418. j++;
  3419. i++;
  3420. }
  3421. buf[i] = 0;
  3422. if (occur)
  3423. strcpy(cmd, buf);
  3424. free(buf);
  3425. }
  3426. }
  3427. typedef struct
  3428. {
  3429. char *nick;
  3430. int len;
  3431. User *best;
  3432. int bestlen;
  3433. char *space;
  3434. char *tbuf;
  3435. } nickdata;
  3436. static int nick_comp_cb(User *user, nickdata *data)
  3437. {
  3438. int lenu;
  3439. if (!rfc_ncasecmp(user->nick, data->nick, data->len))
  3440. {
  3441. lenu = strlen(user->nick);
  3442. if (lenu == data->len)
  3443. {
  3444. snprintf(data->tbuf, TBUFSIZE, "%s%s", user->nick, data->space);
  3445. data->len = -1;
  3446. return FALSE;
  3447. }
  3448. else if (lenu < data->bestlen)
  3449. {
  3450. data->bestlen = lenu;
  3451. data->best = user;
  3452. }
  3453. }
  3454. return TRUE;
  3455. }
  3456. static void perform_nick_completion(session *sess, char *cmd, char *tbuf)
  3457. {
  3458. int len;
  3459. char *space = strchr(cmd, ' ');
  3460. if (space && space != cmd)
  3461. {
  3462. if (space[-1] == prefs.nick_suffix[0] && space - 1 != cmd)
  3463. {
  3464. len = space - cmd - 1;
  3465. if (len < NICKLEN)
  3466. {
  3467. char nick[NICKLEN];
  3468. nickdata data;
  3469. memcpy(nick, cmd, len);
  3470. nick[len] = 0;
  3471. data.nick = nick;
  3472. data.len = len;
  3473. data.bestlen = INT_MAX;
  3474. data.best = NULL;
  3475. data.tbuf = tbuf;
  3476. data.space = space - 1;
  3477. tree_foreach((tree*)sess->usertree, (tree_traverse_func*)nick_comp_cb, &data);
  3478. if (data.len == -1)
  3479. return;
  3480. if (data.best)
  3481. {
  3482. snprintf(tbuf, TBUFSIZE, "%s%s", data.best->nick, space - 1);
  3483. return;
  3484. }
  3485. }
  3486. }
  3487. }
  3488. strcpy(tbuf, cmd);
  3489. }
  3490. static void user_command(session* sess, char *tbuf, char *cmd, char *word[], char *word_eol[])
  3491. {
  3492. if (!auto_insert(tbuf, 2048, (unsigned char*)cmd, word, word_eol, "", sess->channel, "",
  3493. server_get_network(sess->server, TRUE), "",
  3494. sess->server->nick, ""))
  3495. {
  3496. PrintText(sess, _("Bad arguments for user command.\n"));
  3497. return;
  3498. }
  3499. handle_command(sess, tbuf, TRUE);
  3500. }
  3501. // handle text entered without a CMDchar prefix
  3502. static void handle_say(session *sess, char *text, int check_spch)
  3503. {
  3504. DCC *dcc;
  3505. char *word[PDIWORDS+1];
  3506. char *word_eol[PDIWORDS+1];
  3507. char pdibuf_static[1024];
  3508. char newcmd_static[1024];
  3509. char *pdibuf = pdibuf_static;
  3510. char *newcmd = newcmd_static;
  3511. int len;
  3512. int newcmdlen = sizeof newcmd_static;
  3513. if (strcmp(sess->channel, "(lastlog)") == 0)
  3514. {
  3515. lastlog(sess->lastlog_sess, text, sess->lastlog_regexp);
  3516. return;
  3517. }
  3518. len = strlen(text);
  3519. if (len >= sizeof pdibuf_static)
  3520. pdibuf = (char*)malloc(len + 1);
  3521. if (len + NICKLEN >= newcmdlen)
  3522. newcmd = (char*)malloc(newcmdlen = len + NICKLEN + 1);
  3523. if (check_spch && prefs.perc_color)
  3524. check_special_chars(text, prefs.perc_ascii);
  3525. // Python relies on this
  3526. word[PDIWORDS] = NULL;
  3527. word_eol[PDIWORDS] = NULL;
  3528. // split the text into words and word_eol
  3529. process_data_init(pdibuf, text, word, word_eol, TRUE, FALSE);
  3530. // a command of "" can be hooked for non-commands
  3531. if (plugin_emit_command(sess, "", word, word_eol))
  3532. goto xit;
  3533. // incase a plugin did /close
  3534. if (!is_session(sess))
  3535. goto xit;
  3536. if (!sess->channel[0] || sess->type == SESS_SERVER || sess->type == SESS_NOTICES || sess->type == SESS_SNOTICES)
  3537. {
  3538. notj_msg(sess);
  3539. goto xit;
  3540. }
  3541. if (prefs.nickcompletion)
  3542. perform_nick_completion(sess, text, newcmd);
  3543. else
  3544. safe_strcpy(newcmd, text, newcmdlen);
  3545. text = newcmd;
  3546. if (sess->type == SESS_DIALOG)
  3547. {
  3548. // try it via dcc, if possible
  3549. dcc = dcc_write_chat(sess->channel, text);
  3550. if (dcc)
  3551. {
  3552. inbound_chanmsg(sess->server, NULL, sess->channel,
  3553. sess->server->nick, text, TRUE, FALSE);
  3554. set_topic(sess, net_ip(dcc->addr), net_ip(dcc->addr));
  3555. goto xit;
  3556. }
  3557. }
  3558. if (sess->server->connected)
  3559. {
  3560. unsigned int max;
  3561. unsigned char t = 0;
  3562. // maximum allowed message text
  3563. // :nickname!username@host.com PRIVMSG #channel :text\r\n
  3564. max = 512;
  3565. max -= 16; // :, !, @, " PRIVMSG ", " ", :, \r, \n
  3566. max -= strlen(sess->server->nick);
  3567. max -= strlen(sess->channel);
  3568. if (sess->me && sess->me->hostname)
  3569. max -= strlen(sess->me->hostname);
  3570. else
  3571. {
  3572. max -= 9; // username
  3573. max -= 65; // max possible hostname and '@'
  3574. }
  3575. if (strlen(text) > max)
  3576. {
  3577. int i = 0, size;
  3578. /* traverse the utf8 string and find the nearest cut point that
  3579. doesn't split 1 char in half */
  3580. while (1)
  3581. {
  3582. size = g_utf8_skip[((unsigned char*)text)[i]];
  3583. if ((i + size) >= max)
  3584. break;
  3585. i += size;
  3586. }
  3587. max = i;
  3588. t = text[max];
  3589. text[max] = 0; // insert a NULL terminator to shorten it
  3590. }
  3591. inbound_chanmsg(sess->server, sess, sess->channel, sess->server->nick,
  3592. text, TRUE, FALSE);
  3593. sess->server->p_message(sess->server, sess->channel, text);
  3594. if (t)
  3595. {
  3596. text[max] = t;
  3597. handle_say(sess, text + max, FALSE);
  3598. }
  3599. }
  3600. else
  3601. {
  3602. notc_msg(sess);
  3603. }
  3604. xit:
  3605. if (pdibuf != pdibuf_static)
  3606. free(pdibuf);
  3607. if (newcmd != newcmd_static)
  3608. free(newcmd);
  3609. }
  3610. // handle a command, without the '/' prefix
  3611. int handle_command(session *sess, char *cmd, int check_spch)
  3612. {
  3613. popup *pop;
  3614. int user_cmd = FALSE;
  3615. GSList *list;
  3616. char *word[PDIWORDS+1];
  3617. char *word_eol[PDIWORDS+1];
  3618. static int command_level = 0;
  3619. commands *int_cmd;
  3620. char pdibuf_static[1024];
  3621. char tbuf_static[TBUFSIZE];
  3622. char *pdibuf;
  3623. char *tbuf;
  3624. int len;
  3625. int ret = TRUE;
  3626. if (command_level > 99)
  3627. {
  3628. fe_message(_("Too many recursive usercommands, aborting."), FE_MSG_ERROR);
  3629. return TRUE;
  3630. }
  3631. command_level++;
  3632. // anything below MUST DEC command_level before returning
  3633. len = strlen(cmd);
  3634. if (len >= sizeof(pdibuf_static))
  3635. pdibuf = (char*)malloc(len + 1);
  3636. else
  3637. pdibuf = pdibuf_static;
  3638. if ((len * 2) >= sizeof(tbuf_static))
  3639. tbuf = (char*)malloc((len * 2) + 1);
  3640. else
  3641. tbuf = tbuf_static;
  3642. // split the text into words and word_eol
  3643. process_data_init(pdibuf, cmd, word, word_eol, TRUE, TRUE);
  3644. // ensure an empty string at index 32 for cmd_deop etc
  3645. // (internal use only, plugins can still only read 1-31).
  3646. word[PDIWORDS] = "\000\000";
  3647. word_eol[PDIWORDS] = "\000\000";
  3648. int_cmd = find_internal_command(word[1]);
  3649. // redo it without quotes processing, for some commands like /JOIN
  3650. if (int_cmd && !int_cmd->handle_quotes)
  3651. process_data_init(pdibuf, cmd, word, word_eol, FALSE, FALSE);
  3652. if (check_spch && prefs.perc_color)
  3653. check_special_chars(cmd, prefs.perc_ascii);
  3654. if (plugin_emit_command(sess, word[1], word, word_eol))
  3655. goto xit;
  3656. // incase a plugin did /close
  3657. if (!is_session(sess))
  3658. goto xit;
  3659. // first see if it's a userCommand
  3660. list = command_list;
  3661. while (list)
  3662. {
  3663. pop = (popup*)list->data;
  3664. if (!strcasecmp(pop->name, word[1]))
  3665. {
  3666. user_command(sess, tbuf, pop->cmd, word, word_eol);
  3667. user_cmd = TRUE;
  3668. }
  3669. list = list->next;
  3670. }
  3671. if (user_cmd)
  3672. goto xit;
  3673. // now check internal commands
  3674. int_cmd = find_internal_command(word[1]);
  3675. if (int_cmd)
  3676. {
  3677. if (int_cmd->needserver && !sess->server->connected)
  3678. {
  3679. notc_msg(sess);
  3680. }
  3681. else if (int_cmd->needchannel && !sess->channel[0])
  3682. {
  3683. notj_msg(sess);
  3684. }
  3685. else
  3686. {
  3687. switch (int_cmd->callback(sess, tbuf, word, word_eol))
  3688. {
  3689. case FALSE:
  3690. help(sess, tbuf, int_cmd->name, TRUE);
  3691. break;
  3692. case 2:
  3693. ret = FALSE;
  3694. goto xit;
  3695. }
  3696. }
  3697. }
  3698. else
  3699. {
  3700. // unknown command, just send it to the server and hope
  3701. if (!sess->server->connected)
  3702. PrintText(sess, _("Unknown Command. Try /help\n"));
  3703. else
  3704. sess->server->p_raw(sess->server, cmd);
  3705. }
  3706. xit:
  3707. command_level--;
  3708. if (pdibuf != pdibuf_static)
  3709. free(pdibuf);
  3710. if (tbuf != tbuf_static)
  3711. free(tbuf);
  3712. return ret;
  3713. }
  3714. // handle one line entered into the input box
  3715. static int handle_user_input(session *sess, char *text, int history, int nocommand)
  3716. {
  3717. if (*text == '\0')
  3718. return 1;
  3719. if (history)
  3720. history_add(&sess->history, text);
  3721. // is it NOT a command, just text?
  3722. if (nocommand || text[0] != prefs.cmdchar[0])
  3723. {
  3724. handle_say(sess, text, TRUE);
  3725. return 1;
  3726. }
  3727. // check for "//"
  3728. if (text[0] == prefs.cmdchar[0] && text[1] == prefs.cmdchar[0])
  3729. {
  3730. handle_say(sess, text + 1, TRUE);
  3731. return 1;
  3732. }
  3733. if (prefs.cmdchar[0] == '/')
  3734. {
  3735. int i;
  3736. const char *unix_dirs [] = {
  3737. "/bin/", "/boot/", "/dev/",
  3738. "/etc/", "/home/", "/lib/",
  3739. "/lost+found/", "/mnt/", "/opt/",
  3740. "/proc/", "/root/", "/sbin/",
  3741. "/tmp/", "/usr/", "/var/",
  3742. "/gnome/", NULL};
  3743. for (i = 0; unix_dirs[i] != NULL; i++)
  3744. if (strncmp(text, unix_dirs[i], strlen(unix_dirs[i]))==0)
  3745. {
  3746. handle_say(sess, text, TRUE);
  3747. return 1;
  3748. }
  3749. }
  3750. return handle_command(sess, text + 1, TRUE);
  3751. }
  3752. // changed by Steve Green. Macs sometimes paste with imbedded \r
  3753. void handle_multiline(session *sess, char *cmd, int history, int nocommand)
  3754. {
  3755. while (*cmd)
  3756. {
  3757. char *cr = cmd + strcspn(cmd, "\n\r");
  3758. int end_of_string = *cr == 0;
  3759. *cr = 0;
  3760. if (!handle_user_input(sess, cmd, history, nocommand))
  3761. return;
  3762. if (end_of_string)
  3763. break;
  3764. cmd = cr + 1;
  3765. }
  3766. }
  3767. /*void handle_multiline(session *sess, char *cmd, int history, int nocommand)
  3768. {
  3769. char *cr;
  3770. cr = strchr(cmd, '\n');
  3771. if (cr)
  3772. {
  3773. while (1)
  3774. {
  3775. if (cr)
  3776. *cr = 0;
  3777. if (!handle_user_input(sess, cmd, history, nocommand))
  3778. return;
  3779. if (!cr)
  3780. break;
  3781. cmd = cr + 1;
  3782. if (*cmd == 0)
  3783. break;
  3784. cr = strchr(cmd, '\n');
  3785. }
  3786. }
  3787. else
  3788. {
  3789. handle_user_input(sess, cmd, history, nocommand);
  3790. }
  3791. }*/