configure: add stuff for spell checking
[rofl0r-ixchat.git] / src / common / outbound.c
blob1c545b4125798e551beb28c9e9b864cac989cb86
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #define _GNU_SOURCE /* for memrchr */
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #include <limits.h>
25 #include <errno.h>
27 #define WANTSOCKET
28 #define WANTARPA
29 #include "inet.h"
31 #include <sys/wait.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <signal.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
39 #include "xchat.h"
40 #include "plugin.h"
41 #include "ignore.h"
42 #include "util.h"
43 #include "fe.h"
44 #include "cfgfiles.h" /* xchat_fopen_file() */
45 #include "network.h" /* net_ip() */
46 #include "modes.h"
47 #include "notify.h"
48 #include "inbound.h"
49 #include "text.h"
50 #include "xchatc.h"
51 #include "servlist.h"
52 #include "server.h"
53 #include "tree.h"
54 #include "outbound.h"
57 #ifdef USE_DEBUG
58 extern int current_mem_usage;
59 #endif
60 #define TBUFSIZE 4096
62 static void help (session *sess, char *tbuf, char *helpcmd, int quiet);
63 static int cmd_server (session *sess, char *tbuf, char *word[], char *word_eol[]);
64 static void handle_say (session *sess, char *text, int check_spch);
67 static void
68 notj_msg (struct session *sess)
70 PrintText (sess, _("No channel joined. Try /join #<channel>\n"));
73 void
74 notc_msg (struct session *sess)
76 PrintText (sess, _("Not connected. Try /server <host> [<port>]\n"));
79 static char *
80 random_line (char *file_name)
82 FILE *fh;
83 char buf[512];
84 int lines, ran;
86 if (!file_name[0])
87 goto nofile;
89 fh = xchat_fopen_file (file_name, "r", 0);
90 if (!fh)
92 nofile:
93 /* reason is not a file, an actual reason! */
94 return strdup (file_name);
97 /* count number of lines in file */
98 lines = 0;
99 while (fgets (buf, sizeof (buf), fh))
100 lines++;
102 if (lines < 1)
103 goto nofile;
105 /* go down a random number */
106 rewind (fh);
107 ran = RAND_INT (lines);
110 fgets (buf, sizeof (buf), fh);
111 lines--;
113 while (lines > ran);
114 fclose (fh);
115 buf[strlen (buf) - 1] = 0; /* remove the trailing '\n' */
116 return strdup (buf);
119 void
120 server_sendpart (server * serv, char *channel, char *reason)
122 if (!reason)
124 reason = random_line (prefs.partreason);
125 serv->p_part (serv, channel, reason);
126 free (reason);
127 } else
129 /* reason set by /quit, /close argument */
130 serv->p_part (serv, channel, reason);
134 void
135 server_sendquit (session * sess)
137 char *rea, *colrea;
139 if (!sess->quitreason)
141 colrea = strdup (prefs.quitreason);
142 check_special_chars (colrea, FALSE);
143 rea = random_line (colrea);
144 free (colrea);
145 sess->server->p_quit (sess->server, rea);
146 free (rea);
147 } else
149 /* reason set by /quit, /close argument */
150 sess->server->p_quit (sess->server, sess->quitreason);
154 void
155 process_data_init (char *buf, char *cmd, char *word[],
156 char *word_eol[], gboolean handle_quotes,
157 gboolean allow_escape_quotes)
159 int wordcount = 2;
160 int space = FALSE;
161 int quote = FALSE;
162 int j = 0;
163 int len;
165 word[0] = "\000\000";
166 word_eol[0] = "\000\000";
167 word[1] = (char *)buf;
168 word_eol[1] = (char *)cmd;
170 while (1)
172 switch (*cmd)
174 case 0:
175 buf[j] = 0;
176 for (j = wordcount; j < PDIWORDS; j++)
178 word[j] = "\000\000";
179 word_eol[j] = "\000\000";
181 return;
182 case '\042':
183 if (!handle_quotes)
184 goto def;
185 /* two quotes turn into 1 */
186 if (allow_escape_quotes && cmd[1] == '\042')
188 cmd++;
189 goto def;
191 if (quote)
193 quote = FALSE;
194 space = FALSE;
195 } else
196 quote = TRUE;
197 cmd++;
198 break;
199 case ' ':
200 if (!quote)
202 if (!space)
204 buf[j] = 0;
205 j++;
207 if (wordcount < PDIWORDS)
209 word[wordcount] = &buf[j];
210 word_eol[wordcount] = cmd + 1;
211 wordcount++;
214 space = TRUE;
216 cmd++;
217 break;
219 default:
220 def:
221 space = FALSE;
222 len = g_utf8_skip[((unsigned char *)cmd)[0]];
223 if (len == 1)
225 buf[j] = *cmd;
226 j++;
227 cmd++;
228 } else
230 /* skip past a multi-byte utf8 char */
231 memcpy (buf + j, cmd, len);
232 j += len;
233 cmd += len;
239 static int
240 cmd_addbutton (struct session *sess, char *tbuf, char *word[],
241 char *word_eol[])
243 if (*word[2] && *word_eol[3])
245 if (sess->type == SESS_DIALOG)
247 list_addentry (&dlgbutton_list, word_eol[3], word[2]);
248 fe_dlgbuttons_update (sess);
249 } else
251 list_addentry (&button_list, word_eol[3], word[2]);
252 fe_buttons_update (sess);
254 return TRUE;
256 return FALSE;
259 static int
260 cmd_allchannels (session *sess, char *tbuf, char *word[], char *word_eol[])
262 GSList *list = sess_list;
264 if (!*word_eol[2])
265 return FALSE;
267 while (list)
269 sess = list->data;
270 if (sess->type == SESS_CHANNEL && sess->channel[0] && sess->server->connected)
272 handle_command (sess, word_eol[2], FALSE);
274 list = list->next;
277 return TRUE;
280 static int
281 cmd_allchannelslocal (session *sess, char *tbuf, char *word[], char *word_eol[])
283 GSList *list = sess_list;
284 server *serv = sess->server;
286 if (!*word_eol[2])
287 return FALSE;
289 while (list)
291 sess = list->data;
292 if (sess->type == SESS_CHANNEL && sess->channel[0] &&
293 sess->server->connected && sess->server == serv)
295 handle_command (sess, word_eol[2], FALSE);
297 list = list->next;
300 return TRUE;
303 static int
304 cmd_allservers (struct session *sess, char *tbuf, char *word[],
305 char *word_eol[])
307 GSList *list;
308 server *serv;
310 if (!*word_eol[2])
311 return FALSE;
313 list = serv_list;
314 while (list)
316 serv = list->data;
317 if (serv->connected)
318 handle_command (serv->front_session, word_eol[2], FALSE);
319 list = list->next;
322 return TRUE;
325 static int
326 cmd_away (struct session *sess, char *tbuf, char *word[], char *word_eol[])
328 GSList *list;
329 char *reason = word_eol[2];
331 if (!(*reason))
333 if (sess->server->is_away)
335 if (sess->server->last_away_reason)
336 PrintTextf (sess, _("Already marked away: %s\n"), sess->server->last_away_reason);
337 return FALSE;
340 if (sess->server->reconnect_away)
341 reason = sess->server->last_away_reason;
342 else
343 /* must manage memory pointed to by random_line() */
344 reason = random_line (prefs.awayreason);
346 sess->server->p_set_away (sess->server, reason);
348 if (prefs.show_away_message)
350 snprintf (tbuf, TBUFSIZE, "me is away: %s", reason);
351 for (list = sess_list; list; list = list->next)
353 /* am I the right server and not a dialog box */
354 if (((struct session *) list->data)->server == sess->server
355 && ((struct session *) list->data)->type == SESS_CHANNEL
356 && ((struct session *) list->data)->channel[0])
358 handle_command ((session *) list->data, tbuf, TRUE);
363 if (sess->server->last_away_reason != reason)
365 if (sess->server->last_away_reason)
366 free (sess->server->last_away_reason);
368 if (reason == word_eol[2])
369 sess->server->last_away_reason = strdup (reason);
370 else
371 sess->server->last_away_reason = reason;
374 if (!sess->server->connected)
375 sess->server->reconnect_away = 1;
377 return TRUE;
380 static int
381 cmd_back (struct session *sess, char *tbuf, char *word[], char *word_eol[])
383 GSList *list;
384 unsigned int gone;
386 if (sess->server->is_away)
388 sess->server->p_set_back (sess->server);
390 if (prefs.show_away_message)
392 gone = time (NULL) - sess->server->away_time;
393 sprintf (tbuf, "me is back (gone %.2d:%.2d:%.2d)", gone / 3600,
394 (gone / 60) % 60, gone % 60);
395 for (list = sess_list; list; list = list->next)
397 /* am I the right server and not a dialog box */
398 if (((struct session *) list->data)->server == sess->server
399 && ((struct session *) list->data)->type == SESS_CHANNEL
400 && ((struct session *) list->data)->channel[0])
402 handle_command ((session *) list->data, tbuf, TRUE);
407 else
409 PrintText (sess, _("Already marked back.\n"));
412 if (sess->server->last_away_reason)
413 free (sess->server->last_away_reason);
414 sess->server->last_away_reason = NULL;
416 return TRUE;
419 static void
420 ban (session * sess, char *tbuf, char *mask, char *bantypestr, int deop)
422 int bantype;
423 struct User *user;
424 char *at, *dot, *lastdot;
425 char username[64], fullhost[128], domain[128], *mode, *p2;
426 server *serv = sess->server;
428 user = userlist_find (sess, mask);
429 if (user && user->hostname) /* it's a nickname, let's find a proper ban mask */
431 if (deop)
433 mode = "-o+b ";
434 p2 = user->nick;
435 } else
437 mode = "+b";
438 p2 = "";
441 mask = user->hostname;
443 at = strchr (mask, '@'); /* FIXME: utf8 */
444 if (!at)
445 return; /* can't happen? */
446 *at = 0;
448 if (mask[0] == '~' || mask[0] == '+' ||
449 mask[0] == '=' || mask[0] == '^' || mask[0] == '-')
451 /* the ident is prefixed with something, we replace that sign with an * */
452 safe_strcpy (username+1, mask+1, sizeof (username)-1);
453 username[0] = '*';
454 } else if (at - mask < USERNAMELEN)
456 /* we just add an * in the begining of the ident */
457 safe_strcpy (username+1, mask, sizeof (username)-1);
458 username[0] = '*';
459 } else
461 /* ident might be too long, we just ban what it gives and add an * in the end */
462 safe_strcpy (username, mask, sizeof (username));
464 *at = '@';
465 safe_strcpy (fullhost, at + 1, sizeof (fullhost));
467 dot = strchr (fullhost, '.');
468 if (dot)
470 safe_strcpy (domain, dot, sizeof (domain));
471 } else
473 safe_strcpy (domain, fullhost, sizeof (domain));
476 if (*bantypestr)
477 bantype = atoi (bantypestr);
478 else
479 bantype = prefs.bantype;
481 tbuf[0] = 0;
482 if (inet_addr (fullhost) != -1) /* "fullhost" is really a IP number */
484 lastdot = strrchr (fullhost, '.');
485 if (!lastdot)
486 return; /* can't happen? */
488 *lastdot = 0;
489 strcpy (domain, fullhost);
490 *lastdot = '.';
492 switch (bantype)
494 case 0:
495 snprintf (tbuf, TBUFSIZE, "%s%s *!*@%s.*", mode, p2, domain);
496 break;
498 case 1:
499 snprintf (tbuf, TBUFSIZE, "%s%s *!*@%s", mode, p2, fullhost);
500 break;
502 case 2:
503 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@%s.*", mode, p2, username, domain);
504 break;
506 case 3:
507 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@%s", mode, p2, username, fullhost);
508 break;
510 } else
512 switch (bantype)
514 case 0:
515 snprintf (tbuf, TBUFSIZE, "%s%s *!*@*%s", mode, p2, domain);
516 break;
518 case 1:
519 snprintf (tbuf, TBUFSIZE, "%s%s *!*@%s", mode, p2, fullhost);
520 break;
522 case 2:
523 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@*%s", mode, p2, username, domain);
524 break;
526 case 3:
527 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@%s", mode, p2, username, fullhost);
528 break;
532 } else
534 snprintf (tbuf, TBUFSIZE, "+b %s", mask);
536 serv->p_mode (serv, sess->channel, tbuf);
539 static int
540 cmd_ban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
542 char *mask = word[2];
544 if (*mask)
546 ban (sess, tbuf, mask, word[3], 0);
547 } else
549 sess->server->p_mode (sess->server, sess->channel, "+b"); /* banlist */
552 return TRUE;
555 static int
556 cmd_unban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
558 /* Allow more than one mask in /unban -- tvk */
559 int i = 2;
561 while (1)
563 if (!*word[i])
565 if (i == 2)
566 return FALSE;
567 send_channel_modes (sess, tbuf, word, 2, i, '-', 'b', 0);
568 return TRUE;
570 i++;
574 static int
575 cmd_chanopt (struct session *sess, char *tbuf, char *word[], char *word_eol[])
577 /* chanopt.c */
578 return chanopt_command (sess, tbuf, word, word_eol);
581 static int
582 cmd_charset (struct session *sess, char *tbuf, char *word[], char *word_eol[])
584 server *serv = sess->server;
585 const char *locale = NULL;
586 int offset = 0;
588 if (strcmp (word[2], "-quiet") == 0)
589 offset++;
591 if (!word[2 + offset][0])
593 g_get_charset (&locale);
594 PrintTextf (sess, "Current charset: %s\n",
595 serv->encoding ? serv->encoding : locale);
596 return TRUE;
599 if (servlist_check_encoding (word[2 + offset]))
601 server_set_encoding (serv, word[2 + offset]);
602 if (offset < 1)
603 PrintTextf (sess, "Charset changed to: %s\n", word[2 + offset]);
604 } else
606 PrintTextf (sess, "\0034Unknown charset:\017 %s\n", word[2 + offset]);
609 return TRUE;
612 static int
613 cmd_clear (struct session *sess, char *tbuf, char *word[], char *word_eol[])
615 GSList *list = sess_list;
616 char *reason = word_eol[2];
618 if (strcasecmp (reason, "HISTORY") == 0)
620 history_free (&sess->history);
621 return TRUE;
624 if (strncasecmp (reason, "all", 3) == 0)
626 while (list)
628 sess = list->data;
629 if (!sess->nick_said)
630 fe_text_clear (list->data, 0);
631 list = list->next;
633 return TRUE;
636 if (reason[0] != '-' && !isdigit (reason[0]) && reason[0] != 0)
637 return FALSE;
639 fe_text_clear (sess, atoi (reason));
640 return TRUE;
643 static int
644 cmd_close (struct session *sess, char *tbuf, char *word[], char *word_eol[])
646 GSList *list;
648 if (strcmp (word[2], "-m") == 0)
650 list = sess_list;
651 while (list)
653 sess = list->data;
654 list = list->next;
655 if (sess->type == SESS_DIALOG)
656 fe_close_window (sess);
658 } else
660 if (*word_eol[2])
661 sess->quitreason = word_eol[2];
662 fe_close_window (sess);
665 return TRUE;
668 static int
669 cmd_ctcp (struct session *sess, char *tbuf, char *word[], char *word_eol[])
671 int mbl;
672 char *to = word[2];
673 if (*to)
675 char *msg = word_eol[3];
676 if (*msg)
678 unsigned char *cmd = (unsigned char *)msg;
680 /* make the first word upper case (as per RFC) */
681 while (1)
683 if (*cmd == ' ' || *cmd == 0)
684 break;
685 mbl = g_utf8_skip[*cmd];
686 if (mbl == 1)
687 *cmd = toupper (*cmd);
688 cmd += mbl;
691 sess->server->p_ctcp (sess->server, to, msg);
693 EMIT_SIGNAL (XP_TE_CTCPSEND, sess, to, msg, NULL, NULL, 0);
695 return TRUE;
698 return FALSE;
701 static int
702 cmd_country (struct session *sess, char *tbuf, char *word[], char *word_eol[])
704 char *code = word[2];
705 if (*code)
707 /* search? */
708 if (strcmp (code, "-s") == 0)
710 country_search (word[3], sess, (void *)PrintTextf);
711 return TRUE;
714 /* search, but forgot the -s */
715 if (strchr (code, '*'))
717 country_search (code, sess, (void *)PrintTextf);
718 return TRUE;
721 sprintf (tbuf, "%s = %s\n", code, country (code));
722 PrintText (sess, tbuf);
723 return TRUE;
725 return FALSE;
728 static int
729 cmd_cycle (struct session *sess, char *tbuf, char *word[], char *word_eol[])
731 char *key = sess->channelkey;
732 char *chan = word[2];
733 if (!*chan)
734 chan = sess->channel;
735 if (*chan && sess->type == SESS_CHANNEL)
737 sess->server->p_cycle (sess->server, chan, key);
738 return TRUE;
740 return FALSE;
743 static int
744 cmd_dcc (struct session *sess, char *tbuf, char *word[], char *word_eol[])
746 int goodtype;
747 struct DCC *dcc = 0;
748 char *type = word[2];
749 if (*type)
751 if (!strcasecmp (type, "HELP"))
752 return FALSE;
753 if (!strcasecmp (type, "CLOSE"))
755 if (*word[3] && *word[4])
757 goodtype = 0;
758 if (!strcasecmp (word[3], "SEND"))
760 dcc = find_dcc (word[4], word[5], TYPE_SEND);
761 dcc_abort (sess, dcc);
762 goodtype = TRUE;
764 if (!strcasecmp (word[3], "GET"))
766 dcc = find_dcc (word[4], word[5], TYPE_RECV);
767 dcc_abort (sess, dcc);
768 goodtype = TRUE;
770 if (!strcasecmp (word[3], "CHAT"))
772 dcc = find_dcc (word[4], "", TYPE_CHATRECV);
773 if (!dcc)
774 dcc = find_dcc (word[4], "", TYPE_CHATSEND);
775 dcc_abort (sess, dcc);
776 goodtype = TRUE;
779 if (!goodtype)
780 return FALSE;
782 if (!dcc)
783 EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
785 return TRUE;
788 return FALSE;
790 if ((!strcasecmp (type, "CHAT")) || (!strcasecmp (type, "PCHAT")))
792 char *nick = word[3];
793 int passive = (!strcasecmp(type, "PCHAT")) ? 1 : 0;
794 if (*nick)
795 dcc_chat (sess, nick, passive);
796 return TRUE;
798 if (!strcasecmp (type, "LIST"))
800 dcc_show_list (sess);
801 return TRUE;
803 if (!strcasecmp (type, "GET"))
805 char *nick = word[3];
806 char *file = word[4];
807 if (!*file)
809 if (*nick)
810 dcc_get_nick (sess, nick);
811 } else
813 dcc = find_dcc (nick, file, TYPE_RECV);
814 if (dcc)
815 dcc_get (dcc);
816 else
817 EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
819 return TRUE;
821 if ((!strcasecmp (type, "SEND")) || (!strcasecmp (type, "PSEND")))
823 int i = 3, maxcps;
824 char *nick, *file;
825 int passive = (!strcasecmp(type, "PSEND")) ? 1 : 0;
827 nick = word[i];
828 if (!*nick)
829 return FALSE;
831 maxcps = prefs.dcc_max_send_cps;
832 if (!strncasecmp(nick, "-maxcps=", 8))
834 maxcps = atoi(nick + 8);
835 i++;
836 nick = word[i];
837 if (!*nick)
838 return FALSE;
841 i++;
843 file = word[i];
844 if (!*file)
846 fe_dcc_send_filereq (sess, nick, maxcps, passive);
847 return TRUE;
852 dcc_send (sess, nick, file, maxcps, passive);
853 i++;
854 file = word[i];
856 while (*file);
858 return TRUE;
861 return FALSE;
864 dcc_show_list (sess);
865 return TRUE;
868 static int
869 cmd_debug (struct session *sess, char *tbuf, char *word[], char *word_eol[])
871 struct session *s;
872 struct server *v;
873 GSList *list = sess_list;
875 PrintText (sess, "Session T Channel WaitChan WillChan Server\n");
876 while (list)
878 s = (struct session *) list->data;
879 sprintf (tbuf, "%p %1x %-10.10s %-10.10s %-10.10s %p\n",
880 s, s->type, s->channel, s->waitchannel,
881 s->willjoinchannel, s->server);
882 PrintText (sess, tbuf);
883 list = list->next;
886 list = serv_list;
887 PrintText (sess, "Server Sock Name\n");
888 while (list)
890 v = (struct server *) list->data;
891 sprintf (tbuf, "%p %-5d %s\n",
892 v, v->sok, v->servername);
893 PrintText (sess, tbuf);
894 list = list->next;
897 sprintf (tbuf,
898 "\nfront_session: %p\n"
899 "current_tab: %p\n\n",
900 sess->server->front_session, current_tab);
901 PrintText (sess, tbuf);
902 #ifdef USE_DEBUG
903 sprintf (tbuf, "current mem: %d\n\n", current_mem_usage);
904 PrintText (sess, tbuf);
905 #endif /* !MEMORY_DEBUG */
907 return TRUE;
910 static int
911 cmd_delbutton (struct session *sess, char *tbuf, char *word[],
912 char *word_eol[])
914 if (*word[2])
916 if (sess->type == SESS_DIALOG)
918 if (list_delentry (&dlgbutton_list, word[2]))
919 fe_dlgbuttons_update (sess);
920 } else
922 if (list_delentry (&button_list, word[2]))
923 fe_buttons_update (sess);
925 return TRUE;
927 return FALSE;
930 static int
931 cmd_dehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
933 int i = 2;
935 while (1)
937 if (!*word[i])
939 if (i == 2)
940 return FALSE;
941 send_channel_modes (sess, tbuf, word, 2, i, '-', 'h', 0);
942 return TRUE;
944 i++;
948 static int
949 cmd_deop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
951 int i = 2;
953 while (1)
955 if (!*word[i])
957 if (i == 2)
958 return FALSE;
959 send_channel_modes (sess, tbuf, word, 2, i, '-', 'o', 0);
960 return TRUE;
962 i++;
966 typedef struct
968 char **nicks;
969 int i;
970 session *sess;
971 char *reason;
972 char *tbuf;
973 } multidata;
975 static int
976 mdehop_cb (struct User *user, multidata *data)
978 if (user->hop && !user->me)
980 data->nicks[data->i] = user->nick;
981 data->i++;
983 return TRUE;
986 static int
987 cmd_mdehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
989 char **nicks = malloc (sizeof (char *) * sess->hops);
990 multidata data;
992 data.nicks = nicks;
993 data.i = 0;
994 tree_foreach (sess->usertree, (tree_traverse_func *)mdehop_cb, &data);
995 send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'h', 0);
996 free (nicks);
998 return TRUE;
1001 static int
1002 mdeop_cb (struct User *user, multidata *data)
1004 if (user->op && !user->me)
1006 data->nicks[data->i] = user->nick;
1007 data->i++;
1009 return TRUE;
1012 static int
1013 cmd_mdeop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1015 char **nicks = malloc (sizeof (char *) * sess->ops);
1016 multidata data;
1018 data.nicks = nicks;
1019 data.i = 0;
1020 tree_foreach (sess->usertree, (tree_traverse_func *)mdeop_cb, &data);
1021 send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'o', 0);
1022 free (nicks);
1024 return TRUE;
1027 GSList *menu_list = NULL;
1029 static void
1030 menu_free (menu_entry *me)
1032 free (me->path);
1033 if (me->label)
1034 free (me->label);
1035 if (me->cmd)
1036 free (me->cmd);
1037 if (me->ucmd)
1038 free (me->ucmd);
1039 if (me->group)
1040 free (me->group);
1041 if (me->icon)
1042 free (me->icon);
1043 free (me);
1046 /* strings equal? but ignore underscores */
1049 menu_streq (const char *s1, const char *s2, int def)
1051 /* for separators */
1052 if (s1 == NULL && s2 == NULL)
1053 return 0;
1054 if (s1 == NULL || s2 == NULL)
1055 return 1;
1056 while (*s1)
1058 if (*s1 == '_')
1059 s1++;
1060 if (*s2 == '_')
1061 s2++;
1062 if (*s1 != *s2)
1063 return 1;
1064 s1++;
1065 s2++;
1067 if (!*s2)
1068 return 0;
1069 return def;
1072 static menu_entry *
1073 menu_entry_find (char *path, char *label)
1075 GSList *list;
1076 menu_entry *me;
1078 list = menu_list;
1079 while (list)
1081 me = list->data;
1082 if (!strcmp (path, me->path))
1084 if (me->label && label && !strcmp (label, me->label))
1085 return me;
1087 list = list->next;
1089 return NULL;
1092 static void
1093 menu_del_children (char *path, char *label)
1095 GSList *list, *next;
1096 menu_entry *me;
1097 char buf[512];
1099 if (!label)
1100 label = "";
1101 if (path[0])
1102 snprintf (buf, sizeof (buf), "%s/%s", path, label);
1103 else
1104 snprintf (buf, sizeof (buf), "%s", label);
1106 list = menu_list;
1107 while (list)
1109 me = list->data;
1110 next = list->next;
1111 if (!menu_streq (buf, me->path, 0))
1113 menu_list = g_slist_remove (menu_list, me);
1114 menu_free (me);
1116 list = next;
1120 static int
1121 menu_del (char *path, char *label)
1123 GSList *list;
1124 menu_entry *me;
1126 list = menu_list;
1127 while (list)
1129 me = list->data;
1130 if (!menu_streq (me->label, label, 1) && !menu_streq (me->path, path, 1))
1132 menu_list = g_slist_remove (menu_list, me);
1133 fe_menu_del (me);
1134 menu_free (me);
1135 /* delete this item's children, if any */
1136 menu_del_children (path, label);
1137 return 1;
1139 list = list->next;
1142 return 0;
1145 static char
1146 menu_is_mainmenu_root (char *path, gint16 *offset)
1148 static const char *menus[] = {"\x4$TAB","\x5$TRAY","\x4$URL","\x5$NICK","\x5$CHAN"};
1149 int i;
1151 for (i = 0; i < 5; i++)
1153 if (!strncmp (path, menus[i] + 1, menus[i][0]))
1155 *offset = menus[i][0] + 1; /* number of bytes to offset the root */
1156 return 0; /* is not main menu */
1160 *offset = 0;
1161 return 1; /* is main menu */
1164 static void
1165 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)
1167 menu_entry *me;
1169 /* already exists? */
1170 me = menu_entry_find (path, label);
1171 if (me)
1173 /* update only */
1174 me->state = state;
1175 me->enable = enable;
1176 fe_menu_update (me);
1177 return;
1180 me = malloc (sizeof (menu_entry));
1181 me->pos = pos;
1182 me->modifier = mod;
1183 me->is_main = menu_is_mainmenu_root (path, &me->root_offset);
1184 me->state = state;
1185 me->markup = markup;
1186 me->enable = enable;
1187 me->key = key;
1188 me->path = strdup (path);
1189 me->label = NULL;
1190 me->cmd = NULL;
1191 me->ucmd = NULL;
1192 me->group = NULL;
1193 me->icon = NULL;
1195 if (label)
1196 me->label = strdup (label);
1197 if (cmd)
1198 me->cmd = strdup (cmd);
1199 if (ucmd)
1200 me->ucmd = strdup (ucmd);
1201 if (group)
1202 me->group = strdup (group);
1203 if (icon)
1204 me->icon = strdup (icon);
1206 menu_list = g_slist_append (menu_list, me);
1207 label = fe_menu_add (me);
1208 if (label)
1210 /* FE has given us a stripped label */
1211 free (me->label);
1212 me->label = strdup (label);
1213 g_free (label); /* this is from pango */
1217 static int
1218 cmd_menu (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1220 int idx = 2;
1221 int len;
1222 int pos = 0xffff;
1223 int state;
1224 int toggle = FALSE;
1225 int enable = TRUE;
1226 int markup = FALSE;
1227 int key = 0;
1228 int mod = 0;
1229 char *label;
1230 char *group = NULL;
1231 char *icon = NULL;
1233 if (!word[2][0] || !word[3][0])
1234 return FALSE;
1236 /* -eX enabled or not? */
1237 if (word[idx][0] == '-' && word[idx][1] == 'e')
1239 enable = atoi (word[idx] + 2);
1240 idx++;
1243 /* -i<ICONFILE> */
1244 if (word[idx][0] == '-' && word[idx][1] == 'i')
1246 icon = word[idx] + 2;
1247 idx++;
1250 /* -k<mod>,<key> key binding */
1251 if (word[idx][0] == '-' && word[idx][1] == 'k')
1253 char *comma = strchr (word[idx], ',');
1254 if (!comma)
1255 return FALSE;
1256 mod = atoi (word[idx] + 2);
1257 key = atoi (comma + 1);
1258 idx++;
1261 /* -m to specify PangoMarkup language */
1262 if (word[idx][0] == '-' && word[idx][1] == 'm')
1264 markup = TRUE;
1265 idx++;
1268 /* -pX to specify menu position */
1269 if (word[idx][0] == '-' && word[idx][1] == 'p')
1271 pos = atoi (word[idx] + 2);
1272 idx++;
1275 /* -rSTATE,GROUP to specify a radio item */
1276 if (word[idx][0] == '-' && word[idx][1] == 'r')
1278 state = atoi (word[idx] + 2);
1279 group = word[idx] + 4;
1280 idx++;
1283 /* -tX to specify toggle item with default state */
1284 if (word[idx][0] == '-' && word[idx][1] == 't')
1286 state = atoi (word[idx] + 2);
1287 idx++;
1288 toggle = TRUE;
1291 if (word[idx+1][0] == 0)
1292 return FALSE;
1294 /* the path */
1295 path_part (word[idx+1], tbuf, 512);
1296 len = strlen (tbuf);
1297 if (len)
1298 tbuf[len - 1] = 0;
1300 /* the name of the item */
1301 label = file_part (word[idx + 1]);
1302 if (label[0] == '-' && label[1] == 0)
1303 label = NULL; /* separator */
1305 if (markup)
1307 char *p; /* to force pango closing tags through */
1308 for (p = label; *p; p++)
1309 if (*p == 3)
1310 *p = '/';
1313 if (!strcasecmp (word[idx], "ADD"))
1315 if (toggle)
1317 menu_add (tbuf, label, word[idx + 2], word[idx + 3], pos, state, markup, enable, mod, key, NULL, NULL);
1318 } else
1320 if (word[idx + 2][0])
1321 menu_add (tbuf, label, word[idx + 2], NULL, pos, state, markup, enable, mod, key, group, icon);
1322 else
1323 menu_add (tbuf, label, NULL, NULL, pos, state, markup, enable, mod, key, group, icon);
1325 return TRUE;
1328 if (!strcasecmp (word[idx], "DEL"))
1330 menu_del (tbuf, label);
1331 return TRUE;
1334 return FALSE;
1337 static int
1338 mkick_cb (struct User *user, multidata *data)
1340 if (!user->op && !user->me)
1341 data->sess->server->p_kick (data->sess->server, data->sess->channel, user->nick, data->reason);
1342 return TRUE;
1345 static int
1346 mkickops_cb (struct User *user, multidata *data)
1348 if (user->op && !user->me)
1349 data->sess->server->p_kick (data->sess->server, data->sess->channel, user->nick, data->reason);
1350 return TRUE;
1353 static int
1354 cmd_mkick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1356 multidata data;
1358 data.sess = sess;
1359 data.reason = word_eol[2];
1360 tree_foreach (sess->usertree, (tree_traverse_func *)mkickops_cb, &data);
1361 tree_foreach (sess->usertree, (tree_traverse_func *)mkick_cb, &data);
1363 return TRUE;
1366 static int
1367 cmd_devoice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1369 int i = 2;
1371 while (1)
1373 if (!*word[i])
1375 if (i == 2)
1376 return FALSE;
1377 send_channel_modes (sess, tbuf, word, 2, i, '-', 'v', 0);
1378 return TRUE;
1380 i++;
1384 static int
1385 cmd_discon (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1387 sess->server->disconnect (sess, TRUE, -1);
1388 return TRUE;
1391 static int
1392 cmd_dns (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1394 #ifdef WIN32
1395 PrintText (sess, "DNS is not implemented in Windows.\n");
1396 return TRUE;
1397 #else
1398 char *nick = word[2];
1399 struct User *user;
1401 if (*nick)
1403 if (strchr (nick, '.') == NULL)
1405 user = userlist_find (sess, nick);
1406 if (user && user->hostname)
1408 do_dns (sess, user->nick, user->hostname);
1409 } else
1411 sess->server->p_get_ip (sess->server, nick);
1412 sess->server->doing_dns = TRUE;
1414 } else
1416 snprintf (tbuf, TBUFSIZE, "exec -d %s %s", prefs.dnsprogram, nick);
1417 handle_command (sess, tbuf, FALSE);
1419 return TRUE;
1421 return FALSE;
1422 #endif
1425 static int
1426 cmd_echo (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1428 PrintText (sess, word_eol[2]);
1429 return TRUE;
1432 #ifndef WIN32
1434 static void
1435 exec_check_process (struct session *sess)
1437 int val;
1439 if (sess->running_exec == NULL)
1440 return;
1441 val = waitpid (sess->running_exec->childpid, NULL, WNOHANG);
1442 if (val == -1 || val > 0)
1444 close (sess->running_exec->myfd);
1445 fe_input_remove (sess->running_exec->iotag);
1446 free (sess->running_exec);
1447 sess->running_exec = NULL;
1451 #ifndef __EMX__
1452 static int
1453 cmd_execs (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1455 int r;
1457 exec_check_process (sess);
1458 if (sess->running_exec == NULL)
1460 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1461 return FALSE;
1463 r = kill (sess->running_exec->childpid, SIGSTOP);
1464 if (r == -1)
1465 PrintText (sess, "Error in kill(2)\n");
1467 return TRUE;
1470 static int
1471 cmd_execc (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1473 int r;
1475 exec_check_process (sess);
1476 if (sess->running_exec == NULL)
1478 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1479 return FALSE;
1481 r = kill (sess->running_exec->childpid, SIGCONT);
1482 if (r == -1)
1483 PrintText (sess, "Error in kill(2)\n");
1485 return TRUE;
1488 static int
1489 cmd_execk (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1491 int r;
1493 exec_check_process (sess);
1494 if (sess->running_exec == NULL)
1496 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1497 return FALSE;
1499 if (strcmp (word[2], "-9") == 0)
1500 r = kill (sess->running_exec->childpid, SIGKILL);
1501 else
1502 r = kill (sess->running_exec->childpid, SIGTERM);
1503 if (r == -1)
1504 PrintText (sess, "Error in kill(2)\n");
1506 return TRUE;
1509 /* OS/2 Can't have the /EXECW command because it uses pipe(2) not socketpair
1510 and thus it is simplex --AGL */
1511 static int
1512 cmd_execw (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1514 int len;
1515 char *temp;
1516 exec_check_process (sess);
1517 if (sess->running_exec == NULL)
1519 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1520 return FALSE;
1522 len = strlen(word_eol[2]);
1523 temp = malloc(len + 2);
1524 sprintf(temp, "%s\n", word_eol[2]);
1525 PrintText(sess, temp);
1526 write(sess->running_exec->myfd, temp, len + 1);
1527 free(temp);
1529 return TRUE;
1531 #endif /* !__EMX__ */
1533 /* convert ANSI escape color codes to mIRC codes */
1535 static short escconv[] =
1536 /* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 */
1537 { 1,4,3,5,2,10,6,1, 1,7,9,8,12,11,13,1 };
1539 static void
1540 exec_handle_colors (char *buf, int len)
1542 char numb[16];
1543 char *nbuf;
1544 int i = 0, j = 0, k = 0, firstn = 0, col, colf = 0, colb = 0;
1545 int esc = FALSE, backc = FALSE, bold = FALSE;
1547 /* any escape codes in this text? */
1548 if (strchr (buf, 27) == 0)
1549 return;
1551 nbuf = malloc (len + 1);
1553 while (i < len)
1555 switch (buf[i])
1557 case '\r':
1558 break;
1559 case 27:
1560 esc = TRUE;
1561 break;
1562 case ';':
1563 if (!esc)
1564 goto norm;
1565 backc = TRUE;
1566 numb[k] = 0;
1567 firstn = atoi (numb);
1568 k = 0;
1569 break;
1570 case '[':
1571 if (!esc)
1572 goto norm;
1573 break;
1574 default:
1575 if (esc)
1577 if (buf[i] >= 'A' && buf[i] <= 'z')
1579 if (buf[i] == 'm')
1581 /* ^[[0m */
1582 if (k == 0 || (numb[0] == '0' && k == 1))
1584 nbuf[j] = '\017';
1585 j++;
1586 bold = FALSE;
1587 goto cont;
1590 numb[k] = 0;
1591 col = atoi (numb);
1592 backc = FALSE;
1594 if (firstn == 1)
1595 bold = TRUE;
1597 if (firstn >= 30 && firstn <= 37)
1598 colf = firstn - 30;
1600 if (col >= 40)
1602 colb = col - 40;
1603 backc = TRUE;
1606 if (col >= 30 && col <= 37)
1607 colf = col - 30;
1609 if (bold)
1610 colf += 8;
1612 if (backc)
1614 colb = escconv[colb % 14];
1615 colf = escconv[colf % 14];
1616 j += sprintf (&nbuf[j], "\003%d,%02d", colf, colb);
1617 } else
1619 colf = escconv[colf % 14];
1620 j += sprintf (&nbuf[j], "\003%02d", colf);
1623 cont: esc = FALSE;
1624 backc = FALSE;
1625 k = 0;
1626 } else
1628 if (isdigit ((unsigned char) buf[i]) && k < (sizeof (numb) - 1))
1630 numb[k] = buf[i];
1631 k++;
1634 } else
1636 norm: nbuf[j] = buf[i];
1637 j++;
1640 i++;
1643 nbuf[j] = 0;
1644 memcpy (buf, nbuf, j + 1);
1645 free (nbuf);
1648 static gboolean
1649 exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
1651 char *buf, *readpos, *rest;
1652 int rd, len;
1653 int sok = s->myfd;
1655 len = s->buffill;
1656 if (len) {
1657 /* append new data to buffered incomplete line */
1658 buf = malloc(len + 2050);
1659 memcpy(buf, s->linebuf, len);
1660 readpos = buf + len;
1661 free(s->linebuf);
1662 s->linebuf = NULL;
1664 else
1665 readpos = buf = malloc(2050);
1667 rd = read (sok, readpos, 2048);
1668 if (rd < 1)
1670 /* The process has died */
1671 kill(s->childpid, SIGKILL);
1672 if (len) {
1673 buf[len] = '\0';
1674 exec_handle_colors(buf, len);
1675 if (s->tochannel)
1677 /* must turn off auto-completion temporarily */
1678 unsigned int old = prefs.nickcompletion;
1679 prefs.nickcompletion = 0;
1680 handle_multiline (s->sess, buf, FALSE, TRUE);
1681 prefs.nickcompletion = old;
1683 else
1684 PrintText (s->sess, buf);
1686 free(buf);
1687 waitpid (s->childpid, NULL, 0);
1688 s->sess->running_exec = NULL;
1689 fe_input_remove (s->iotag);
1690 close (sok);
1691 free (s);
1692 return TRUE;
1694 len += rd;
1695 buf[len] = '\0';
1697 rest = memrchr(buf, '\n', len);
1698 if (rest)
1699 rest++;
1700 else
1701 rest = buf;
1702 if (*rest) {
1703 s->buffill = len - (rest - buf); /* = strlen(rest) */
1704 s->linebuf = malloc(s->buffill + 1);
1705 memcpy(s->linebuf, rest, s->buffill);
1706 *rest = '\0';
1707 len -= s->buffill; /* possibly 0 */
1709 else
1710 s->buffill = 0;
1712 if (len) {
1713 exec_handle_colors (buf, len);
1714 if (s->tochannel)
1715 handle_multiline (s->sess, buf, FALSE, TRUE);
1716 else
1717 PrintText (s->sess, buf);
1720 free(buf);
1721 return TRUE;
1724 static int
1725 cmd_exec (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1727 int tochannel = FALSE;
1728 char *cmd = word_eol[2];
1729 int fds[2], pid = 0;
1730 struct nbexec *s;
1731 int shell = TRUE;
1732 int fd;
1734 if (*cmd)
1736 exec_check_process (sess);
1737 if (sess->running_exec != NULL)
1739 EMIT_SIGNAL (XP_TE_ALREADYPROCESS, sess, NULL, NULL, NULL, NULL, 0);
1740 return TRUE;
1743 if (!strcmp (word[2], "-d"))
1745 if (!*word[3])
1746 return FALSE;
1747 cmd = word_eol[3];
1748 shell = FALSE;
1750 else if (!strcmp (word[2], "-o"))
1752 if (!*word[3])
1753 return FALSE;
1754 cmd = word_eol[3];
1755 tochannel = TRUE;
1758 if (shell)
1760 if (access ("/bin/sh", X_OK) != 0)
1762 fe_message (_("I need /bin/sh to run!\n"), FE_MSG_ERROR);
1763 return TRUE;
1767 #ifdef __EMX__ /* if os/2 */
1768 if (pipe (fds) < 0)
1770 PrintText (sess, "Pipe create error\n");
1771 return FALSE;
1773 setmode (fds[0], O_BINARY);
1774 setmode (fds[1], O_BINARY);
1775 #else
1776 if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1)
1778 PrintText (sess, "socketpair(2) failed\n");
1779 return FALSE;
1781 #endif
1782 s = (struct nbexec *) malloc (sizeof (struct nbexec));
1783 memset(s, 0, sizeof(*s));
1784 s->myfd = fds[0];
1785 s->tochannel = tochannel;
1786 s->sess = sess;
1788 pid = fork ();
1789 if (pid == 0)
1791 /* This is the child's context */
1792 close (0);
1793 close (1);
1794 close (2);
1795 /* Close parent's end of pipe */
1796 close(s->myfd);
1797 /* Copy the child end of the pipe to stdout and stderr */
1798 dup2 (fds[1], 1);
1799 dup2 (fds[1], 2);
1800 /* Also copy it to stdin so we can write to it */
1801 dup2 (fds[1], 0);
1802 /* Now close all open file descriptors except stdin, stdout and stderr */
1803 for (fd = 3; fd < 1024; fd++) close(fd);
1804 /* Now we call /bin/sh to run our cmd ; made it more friendly -DC1 */
1805 if (shell)
1807 execl ("/bin/sh", "sh", "-c", cmd, NULL);
1808 } else
1810 char **argv;
1811 int argc;
1813 my_poptParseArgvString (cmd, &argc, &argv);
1814 execvp (argv[0], argv);
1816 /* not reached unless error */
1817 /*printf("exec error\n");*/
1818 fflush (stdout);
1819 fflush (stdin);
1820 _exit (0);
1822 if (pid == -1)
1824 /* Parent context, fork() failed */
1826 PrintText (sess, "Error in fork(2)\n");
1827 close(fds[0]);
1828 close(fds[1]);
1829 } else
1831 /* Parent path */
1832 close(fds[1]);
1833 s->childpid = pid;
1834 s->iotag = fe_input_add (s->myfd, FIA_READ|FIA_EX, exec_data, s);
1835 sess->running_exec = s;
1836 return TRUE;
1839 return FALSE;
1842 #endif
1844 static int
1845 cmd_flushq (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1847 sprintf (tbuf, "Flushing server send queue, %d bytes.\n", sess->server->sendq_len);
1848 PrintText (sess, tbuf);
1849 sess->server->flush_queue (sess->server);
1850 return TRUE;
1853 static int
1854 cmd_quit (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1856 if (*word_eol[2])
1857 sess->quitreason = word_eol[2];
1858 sess->server->disconnect (sess, TRUE, -1);
1859 sess->quitreason = NULL;
1860 return 2;
1863 static int
1864 cmd_gate (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1866 char *server_name = word[2];
1867 server *serv = sess->server;
1868 if (*server_name)
1870 char *port = word[3];
1871 #ifdef USE_OPENSSL
1872 serv->use_ssl = FALSE;
1873 #endif
1874 server_fill_her_up (serv);
1875 if (*port)
1876 serv->connect (serv, server_name, atoi (port), TRUE);
1877 else
1878 serv->connect (serv, server_name, 23, TRUE);
1879 return TRUE;
1881 return FALSE;
1884 typedef struct
1886 char *cmd;
1887 session *sess;
1888 } getvalinfo;
1890 static void
1891 get_int_cb (int cancel, int val, getvalinfo *info)
1893 char buf[512];
1895 if (!cancel)
1897 snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
1898 if (is_session (info->sess))
1899 handle_command (info->sess, buf, FALSE);
1902 free (info->cmd);
1903 free (info);
1906 static int
1907 cmd_getint (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1909 getvalinfo *info;
1911 if (!word[4][0])
1912 return FALSE;
1914 info = malloc (sizeof (*info));
1915 info->cmd = strdup (word[3]);
1916 info->sess = sess;
1918 fe_get_int (word[4], atoi (word[2]), get_int_cb, info);
1920 return TRUE;
1923 static void
1924 get_file_cb (char *cmd, char *file)
1926 char buf[1024 + 128];
1928 /* execute the command once per file, then once more with
1929 no args */
1930 if (file)
1932 snprintf (buf, sizeof (buf), "%s %s", cmd, file);
1933 handle_command (current_sess, buf, FALSE);
1935 else
1937 handle_command (current_sess, cmd, FALSE);
1938 free (cmd);
1942 static int
1943 cmd_getfile (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1945 int idx = 2;
1946 int flags = 0;
1948 if (!word[3][0])
1949 return FALSE;
1951 if (!strcmp (word[2], "-folder"))
1953 flags |= FRF_CHOOSEFOLDER;
1954 idx++;
1957 if (!strcmp (word[idx], "-multi"))
1959 flags |= FRF_MULTIPLE;
1960 idx++;
1963 if (!strcmp (word[idx], "-save"))
1965 flags |= FRF_WRITE;
1966 idx++;
1969 fe_get_file (word[idx+1], word[idx+2], (void *)get_file_cb, strdup (word[idx]), flags);
1971 return TRUE;
1974 static void
1975 get_str_cb (int cancel, char *val, getvalinfo *info)
1977 char buf[512];
1979 if (!cancel)
1981 snprintf (buf, sizeof (buf), "%s %s", info->cmd, val);
1982 if (is_session (info->sess))
1983 handle_command (info->sess, buf, FALSE);
1986 free (info->cmd);
1987 free (info);
1990 static int
1991 cmd_getstr (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1993 getvalinfo *info;
1995 if (!word[4][0])
1996 return FALSE;
1998 info = malloc (sizeof (*info));
1999 info->cmd = strdup (word[3]);
2000 info->sess = sess;
2002 fe_get_str (word[4], word[2], get_str_cb, info);
2004 return TRUE;
2007 static int
2008 cmd_ghost (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2010 if (!word[2][0])
2011 return FALSE;
2013 sess->server->p_ns_ghost (sess->server, word[2], word[3]);
2014 return TRUE;
2017 static int
2018 cmd_gui (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2020 switch (str_ihash (word[2]))
2022 case 0x058b836e: fe_ctrl_gui (sess, 8, 0); break; /* APPLY */
2023 case 0xac1eee45: fe_ctrl_gui (sess, 7, 2); break; /* ATTACH */
2024 case 0x05a72f63: fe_ctrl_gui (sess, 4, atoi (word[3])); break; /* COLOR */
2025 case 0xb06a1793: fe_ctrl_gui (sess, 7, 1); break; /* DETACH */
2026 case 0x05cfeff0: fe_ctrl_gui (sess, 3, 0); break; /* FLASH */
2027 case 0x05d154d8: fe_ctrl_gui (sess, 2, 0); break; /* FOCUS */
2028 case 0x0030dd42: fe_ctrl_gui (sess, 0, 0); break; /* HIDE */
2029 case 0x61addbe3: fe_ctrl_gui (sess, 5, 0); break; /* ICONIFY */
2030 case 0xc0851aaa: fe_message (word[3], FE_MSG_INFO|FE_MSG_MARKUP); break; /* MSGBOX */
2031 case 0x0035dafd: fe_ctrl_gui (sess, 1, 0); break; /* SHOW */
2032 case 0x0033155f: /* MENU */
2033 if (!strcasecmp (word[3], "TOGGLE"))
2034 fe_ctrl_gui (sess, 6, 0);
2035 else
2036 return FALSE;
2037 break;
2038 default:
2039 return FALSE;
2042 return TRUE;
2045 typedef struct
2047 int longfmt;
2048 int i, t;
2049 char *buf;
2050 } help_list;
2052 static void
2053 show_help_line (session *sess, help_list *hl, char *name, char *usage)
2055 int j, len, max;
2056 char *p;
2058 if (name[0] == '.') /* hidden command? */
2059 return;
2061 if (hl->longfmt) /* long format for /HELP -l */
2063 if (!usage || usage[0] == 0)
2064 PrintTextf (sess, " \0034%s\003 :\n", name);
2065 else
2066 PrintTextf (sess, " \0034%s\003 : %s\n", name, _(usage));
2067 return;
2070 /* append the name into buffer, but convert to uppercase */
2071 len = strlen (hl->buf);
2072 p = name;
2073 while (*p)
2075 hl->buf[len] = toupper ((unsigned char) *p);
2076 len++;
2077 p++;
2079 hl->buf[len] = 0;
2081 hl->t++;
2082 if (hl->t == 5)
2084 hl->t = 0;
2085 strcat (hl->buf, "\n");
2086 PrintText (sess, hl->buf);
2087 hl->buf[0] = ' ';
2088 hl->buf[1] = ' ';
2089 hl->buf[2] = 0;
2090 } else
2092 /* append some spaces after the command name */
2093 max = strlen (name);
2094 if (max < 10)
2096 max = 10 - max;
2097 for (j = 0; j < max; j++)
2099 hl->buf[len] = ' ';
2100 len++;
2101 hl->buf[len] = 0;
2107 static int
2108 cmd_help (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2110 int i = 0, longfmt = 0;
2111 char *helpcmd = "";
2112 GSList *list;
2114 if (tbuf)
2115 helpcmd = word[2];
2116 if (*helpcmd && strcmp (helpcmd, "-l") == 0)
2117 longfmt = 1;
2119 if (*helpcmd && !longfmt)
2121 help (sess, tbuf, helpcmd, FALSE);
2122 } else
2124 struct popup *pop;
2125 char *buf = malloc (4096);
2126 help_list hl;
2128 hl.longfmt = longfmt;
2129 hl.buf = buf;
2131 PrintTextf (sess, "\n%s\n\n", _("Commands Available:"));
2132 buf[0] = ' ';
2133 buf[1] = ' ';
2134 buf[2] = 0;
2135 hl.t = 0;
2136 hl.i = 0;
2137 while (xc_cmds[i].name)
2139 show_help_line (sess, &hl, xc_cmds[i].name, xc_cmds[i].help);
2140 i++;
2142 strcat (buf, "\n");
2143 PrintText (sess, buf);
2145 PrintTextf (sess, "\n%s\n\n", _("User defined commands:"));
2146 buf[0] = ' ';
2147 buf[1] = ' ';
2148 buf[2] = 0;
2149 hl.t = 0;
2150 hl.i = 0;
2151 list = command_list;
2152 while (list)
2154 pop = list->data;
2155 show_help_line (sess, &hl, pop->name, pop->cmd);
2156 list = list->next;
2158 strcat (buf, "\n");
2159 PrintText (sess, buf);
2161 PrintTextf (sess, "\n%s\n\n", _("Plugin defined commands:"));
2162 buf[0] = ' ';
2163 buf[1] = ' ';
2164 buf[2] = 0;
2165 hl.t = 0;
2166 hl.i = 0;
2167 plugin_command_foreach (sess, &hl, (void *)show_help_line);
2168 strcat (buf, "\n");
2169 PrintText (sess, buf);
2170 free (buf);
2172 PrintTextf (sess, "\n%s\n\n", _("Type /HELP <command> for more information, or /HELP -l"));
2174 return TRUE;
2177 static int
2178 cmd_id (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2180 if (word[2][0])
2182 sess->server->p_ns_identify (sess->server, word[2]);
2183 return TRUE;
2186 return FALSE;
2189 static int
2190 cmd_ignore (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2192 int i;
2193 int type = 0;
2194 int quiet = 0;
2195 char *mask;
2197 if (!*word[2])
2199 ignore_showlist (sess);
2200 return TRUE;
2202 if (!*word[3])
2203 return FALSE;
2205 i = 3;
2206 while (1)
2208 if (!*word[i])
2210 if (type == 0)
2211 return FALSE;
2213 mask = word[2];
2214 if (strchr (mask, '?') == NULL &&
2215 strchr (mask, '*') == NULL)
2217 mask = tbuf;
2218 snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
2221 i = ignore_add (mask, type);
2222 if (quiet)
2223 return TRUE;
2224 switch (i)
2226 case 1:
2227 EMIT_SIGNAL (XP_TE_IGNOREADD, sess, mask, NULL, NULL, NULL, 0);
2228 break;
2229 case 2: /* old ignore changed */
2230 EMIT_SIGNAL (XP_TE_IGNORECHANGE, sess, mask, NULL, NULL, NULL, 0);
2232 return TRUE;
2234 if (!strcasecmp (word[i], "UNIGNORE"))
2235 type |= IG_UNIG;
2236 else if (!strcasecmp (word[i], "ALL"))
2237 type |= IG_PRIV | IG_NOTI | IG_CHAN | IG_CTCP | IG_INVI | IG_DCC;
2238 else if (!strcasecmp (word[i], "PRIV"))
2239 type |= IG_PRIV;
2240 else if (!strcasecmp (word[i], "NOTI"))
2241 type |= IG_NOTI;
2242 else if (!strcasecmp (word[i], "CHAN"))
2243 type |= IG_CHAN;
2244 else if (!strcasecmp (word[i], "CTCP"))
2245 type |= IG_CTCP;
2246 else if (!strcasecmp (word[i], "INVI"))
2247 type |= IG_INVI;
2248 else if (!strcasecmp (word[i], "QUIET"))
2249 quiet = 1;
2250 else if (!strcasecmp (word[i], "NOSAVE"))
2251 type |= IG_NOSAVE;
2252 else if (!strcasecmp (word[i], "DCC"))
2253 type |= IG_DCC;
2254 else
2256 sprintf (tbuf, _("Unknown arg '%s' ignored."), word[i]);
2257 PrintText (sess, tbuf);
2259 i++;
2263 static int
2264 cmd_invite (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2266 if (!*word[2])
2267 return FALSE;
2268 if (*word[3])
2269 sess->server->p_invite (sess->server, word[3], word[2]);
2270 else
2271 sess->server->p_invite (sess->server, sess->channel, word[2]);
2272 return TRUE;
2275 static int
2276 cmd_join (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2278 char *chan = word[2];
2279 if (*chan)
2281 char *po, *pass = word[3];
2282 sess->server->p_join (sess->server, chan, pass);
2283 if (sess->channel[0] == 0 && sess->waitchannel[0])
2285 po = strchr (chan, ',');
2286 if (po)
2287 *po = 0;
2288 safe_strcpy (sess->waitchannel, chan, CHANLEN);
2290 return TRUE;
2292 return FALSE;
2295 static int
2296 cmd_kick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2298 char *nick = word[2];
2299 char *reason = word_eol[3];
2300 if (*nick)
2302 sess->server->p_kick (sess->server, sess->channel, nick, reason);
2303 return TRUE;
2305 return FALSE;
2308 static int
2309 cmd_kickban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2311 char *nick = word[2];
2312 char *reason = word_eol[3];
2313 struct User *user;
2315 if (*nick)
2317 /* if the reason is a 1 digit number, treat it as a bantype */
2319 user = userlist_find (sess, nick);
2321 if (isdigit ((unsigned char) reason[0]) && reason[1] == 0)
2323 ban (sess, tbuf, nick, reason, (user && user->op));
2324 reason[0] = 0;
2325 } else
2326 ban (sess, tbuf, nick, "", (user && user->op));
2328 sess->server->p_kick (sess->server, sess->channel, nick, reason);
2330 return TRUE;
2332 return FALSE;
2335 static int
2336 cmd_killall (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2338 xchat_exit();
2339 return 2;
2342 static int
2343 cmd_lagcheck (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2345 lag_check ();
2346 return TRUE;
2349 static void
2350 lastlog (session *sess, char *search, gboolean regexp)
2352 session *lastlog_sess;
2354 if (!is_session (sess))
2355 return;
2357 lastlog_sess = find_dialog (sess->server, "(lastlog)");
2358 if (!lastlog_sess)
2359 lastlog_sess = new_ircwindow (sess->server, "(lastlog)", SESS_DIALOG, 0);
2361 lastlog_sess->lastlog_sess = sess;
2362 lastlog_sess->lastlog_regexp = regexp; /* remember the search type */
2364 fe_text_clear (lastlog_sess, 0);
2365 fe_lastlog (sess, lastlog_sess, search, regexp);
2368 static int
2369 cmd_lastlog (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2371 if (*word_eol[2])
2373 if (!strcmp (word[2], "-r"))
2374 lastlog (sess, word_eol[3], TRUE);
2375 else
2376 lastlog (sess, word_eol[2], FALSE);
2377 return TRUE;
2380 return FALSE;
2383 static int
2384 cmd_list (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2386 sess->server->p_list_channels (sess->server, word_eol[2], 1);
2388 return TRUE;
2391 gboolean
2392 load_perform_file (session *sess, char *file)
2394 char tbuf[1024 + 4];
2395 char *nl;
2396 FILE *fp;
2398 fp = xchat_fopen_file (file, "r", XOF_FULLPATH);
2399 if (!fp)
2400 return FALSE;
2402 tbuf[1024] = 0;
2403 while (fgets (tbuf, 1024, fp))
2405 nl = strchr (tbuf, '\n');
2406 if (nl == tbuf) /* skip empty commands */
2407 continue;
2408 if (nl)
2409 *nl = 0;
2410 if (tbuf[0] == prefs.cmdchar[0])
2411 handle_command (sess, tbuf + 1, TRUE);
2412 else
2413 handle_command (sess, tbuf, TRUE);
2415 fclose (fp);
2416 return TRUE;
2419 static int
2420 cmd_load (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2422 char *error, *arg, *file;
2423 int len;
2425 if (!word[2][0])
2426 return FALSE;
2428 if (strcmp (word[2], "-e") == 0)
2430 file = expand_homedir (word[3]);
2431 if (!load_perform_file (sess, file))
2433 PrintTextf (sess, _("Cannot access %s\n"), file);
2434 PrintText (sess, errorstring (errno));
2436 free (file);
2437 return TRUE;
2440 #ifdef USE_PLUGIN
2441 len = strlen (word[2]);
2442 #ifdef WIN32
2443 if (len > 4 && strcasecmp (".dll", word[2] + len - 4) == 0)
2444 #else
2445 #if defined(__hpux)
2446 if (len > 3 && strcasecmp (".sl", word[2] + len - 3) == 0)
2447 #else
2448 if (len > 3 && strcasecmp (".so", word[2] + len - 3) == 0)
2449 #endif
2450 #endif
2452 arg = NULL;
2453 if (word_eol[3][0])
2454 arg = word_eol[3];
2456 file = expand_homedir (word[2]);
2457 error = plugin_load (sess, file, arg);
2458 free (file);
2460 if (error)
2461 PrintText (sess, error);
2463 return TRUE;
2465 #endif
2467 sprintf (tbuf, "Unknown file type %s. Maybe you need to install the Perl or Python plugin?\n", word[2]);
2468 PrintText (sess, tbuf);
2470 return FALSE;
2473 static int
2474 cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2476 char *act = word_eol[2];
2478 if (!(*act))
2479 return FALSE;
2481 if (sess->type == SESS_SERVER)
2483 notj_msg (sess);
2484 return TRUE;
2487 snprintf (tbuf, TBUFSIZE, "\001ACTION %s\001\r", act);
2488 /* first try through DCC CHAT */
2489 if (dcc_write_chat (sess->channel, tbuf))
2491 /* print it to screen */
2492 inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
2493 } else
2495 /* DCC CHAT failed, try through server */
2496 if (sess->server->connected)
2498 sess->server->p_action (sess->server, sess->channel, act);
2499 /* print it to screen */
2500 inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
2501 } else
2503 notc_msg (sess);
2507 return TRUE;
2510 static int
2511 cmd_mode (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2513 /* +channel channels are dying, let those servers whine about modes.
2514 * return info about current channel if available and no info is given */
2515 if ((*word[2] == '+') || (*word[2] == 0) || (!is_channel(sess->server, word[2]) &&
2516 !(rfc_casecmp(sess->server->nick, word[2]) == 0)))
2518 if(sess->channel[0] == 0)
2519 return FALSE;
2520 sess->server->p_mode (sess->server, sess->channel, word_eol[2]);
2522 else
2523 sess->server->p_mode (sess->server, word[2], word_eol[3]);
2524 return TRUE;
2527 static int
2528 mop_cb (struct User *user, multidata *data)
2530 if (!user->op)
2532 data->nicks[data->i] = user->nick;
2533 data->i++;
2535 return TRUE;
2538 static int
2539 cmd_mop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2541 char **nicks = malloc (sizeof (char *) * (sess->total - sess->ops));
2542 multidata data;
2544 data.nicks = nicks;
2545 data.i = 0;
2546 tree_foreach (sess->usertree, (tree_traverse_func *)mop_cb, &data);
2547 send_channel_modes (sess, tbuf, nicks, 0, data.i, '+', 'o', 0);
2549 free (nicks);
2551 return TRUE;
2554 static int
2555 cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2557 char *nick = word[2];
2558 char *msg = word_eol[3];
2559 struct session *newsess;
2561 if (*nick)
2563 if (*msg)
2565 if (strcmp (nick, ".") == 0)
2566 { /* /msg the last nick /msg'ed */
2567 if (sess->lastnick[0])
2568 nick = sess->lastnick;
2569 } else
2571 safe_strcpy (sess->lastnick, nick, NICKLEN); /* prime the last nick memory */
2574 if (*nick == '=')
2576 nick++;
2577 if (!dcc_write_chat (nick, msg))
2579 EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
2580 return TRUE;
2582 } else
2584 if (!sess->server->connected)
2586 notc_msg (sess);
2587 return TRUE;
2589 sess->server->p_message (sess->server, nick, msg);
2591 newsess = find_dialog (sess->server, nick);
2592 if (!newsess)
2593 newsess = find_channel (sess->server, nick);
2594 if (newsess)
2595 inbound_chanmsg (newsess->server, NULL, newsess->channel,
2596 newsess->server->nick, msg, TRUE, FALSE);
2597 else
2599 /* mask out passwords */
2600 if (strcasecmp (nick, "nickserv") == 0 &&
2601 strncasecmp (msg, "identify ", 9) == 0)
2602 msg = "identify ****";
2603 EMIT_SIGNAL (XP_TE_MSGSEND, sess, nick, msg, NULL, NULL, 0);
2606 return TRUE;
2609 return FALSE;
2612 static int
2613 cmd_names (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2615 if (*word[2])
2616 sess->server->p_names (sess->server, word[2]);
2617 else
2618 sess->server->p_names (sess->server, sess->channel);
2619 return TRUE;
2622 static int
2623 cmd_nctcp (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2625 if (*word_eol[3])
2627 sess->server->p_nctcp (sess->server, word[2], word_eol[3]);
2628 return TRUE;
2630 return FALSE;
2633 static int
2634 cmd_newserver (struct session *sess, char *tbuf, char *word[],
2635 char *word_eol[])
2637 if (strcmp (word[2], "-noconnect") == 0)
2639 new_ircwindow (NULL, word[3], SESS_SERVER, 0);
2640 return TRUE;
2643 sess = new_ircwindow (NULL, NULL, SESS_SERVER, 0);
2644 cmd_server (sess, tbuf, word, word_eol);
2645 return TRUE;
2648 static int
2649 cmd_nick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2651 char *nick = word[2];
2652 if (*nick)
2654 if (sess->server->connected)
2655 sess->server->p_change_nick (sess->server, nick);
2656 else
2657 inbound_newnick (sess->server, sess->server->nick, nick, TRUE);
2658 return TRUE;
2660 return FALSE;
2663 static int
2664 cmd_notice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2666 if (*word[2] && *word_eol[3])
2668 sess->server->p_notice (sess->server, word[2], word_eol[3]);
2669 EMIT_SIGNAL (XP_TE_NOTICESEND, sess, word[2], word_eol[3], NULL, NULL, 0);
2670 return TRUE;
2672 return FALSE;
2675 static int
2676 cmd_notify (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2678 int i = 1;
2679 char *net = NULL;
2681 if (*word[2])
2683 if (strcmp (word[2], "-n") == 0) /* comma sep network list */
2685 net = word[3];
2686 i += 2;
2689 while (1)
2691 i++;
2692 if (!*word[i])
2693 break;
2694 if (notify_deluser (word[i]))
2696 EMIT_SIGNAL (XP_TE_DELNOTIFY, sess, word[i], NULL, NULL, NULL, 0);
2697 return TRUE;
2700 if (net && strcmp (net, "ASK") == 0)
2701 fe_notify_ask (word[i], NULL);
2702 else
2704 notify_adduser (word[i], net);
2705 EMIT_SIGNAL (XP_TE_ADDNOTIFY, sess, word[i], NULL, NULL, NULL, 0);
2708 } else
2709 notify_showlist (sess);
2710 return TRUE;
2713 static int
2714 cmd_op (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2716 int i = 2;
2718 while (1)
2720 if (!*word[i])
2722 if (i == 2)
2723 return FALSE;
2724 send_channel_modes (sess, tbuf, word, 2, i, '+', 'o', 0);
2725 return TRUE;
2727 i++;
2731 static int
2732 cmd_part (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2734 char *chan = word[2];
2735 char *reason = word_eol[3];
2736 if (!*chan)
2737 chan = sess->channel;
2738 if ((*chan) && is_channel (sess->server, chan))
2740 if (reason[0] == 0)
2741 reason = NULL;
2742 server_sendpart (sess->server, chan, reason);
2743 return TRUE;
2745 return FALSE;
2748 static int
2749 cmd_ping (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2751 char timestring[64];
2752 unsigned long tim;
2753 char *to = word[2];
2755 tim = make_ping_time ();
2757 snprintf (timestring, sizeof (timestring), "%lu", tim);
2758 sess->server->p_ping (sess->server, to, timestring);
2760 return TRUE;
2763 void
2764 open_query (server *serv, char *nick, gboolean focus_existing)
2766 session *sess;
2768 sess = find_dialog (serv, nick);
2769 if (!sess)
2770 new_ircwindow (serv, nick, SESS_DIALOG, 1);
2771 else if (focus_existing)
2772 fe_ctrl_gui (sess, 2, 0); /* bring-to-front */
2775 static int
2776 cmd_query (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2778 char *nick = word[2];
2779 gboolean focus = TRUE;
2781 if (strcmp (word[2], "-nofocus") == 0)
2783 nick = word[3];
2784 focus = FALSE;
2787 if (*nick && !is_channel (sess->server, nick))
2789 open_query (sess->server, nick, focus);
2790 return TRUE;
2792 return FALSE;
2795 static int
2796 cmd_quote (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2798 char *raw = word_eol[2];
2800 return sess->server->p_raw (sess->server, raw);
2803 static int
2804 cmd_reconnect (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2806 int tmp = prefs.recon_delay;
2807 GSList *list;
2808 server *serv = sess->server;
2810 prefs.recon_delay = 0;
2812 if (!strcasecmp (word[2], "ALL"))
2814 list = serv_list;
2815 while (list)
2817 serv = list->data;
2818 if (serv->connected)
2819 serv->auto_reconnect (serv, TRUE, -1);
2820 list = list->next;
2823 /* If it isn't "ALL" and there is something
2824 there it *should* be a server they are trying to connect to*/
2825 else if (*word[2])
2827 int offset = 0;
2828 #ifdef USE_OPENSSL
2829 int use_ssl = FALSE;
2831 if (strcmp (word[2], "-ssl") == 0)
2833 use_ssl = TRUE;
2834 offset++; /* args move up by 1 word */
2836 serv->use_ssl = use_ssl;
2837 serv->accept_invalid_cert = TRUE;
2838 #endif
2840 if (*word[4+offset])
2841 safe_strcpy (serv->password, word[4+offset], sizeof (serv->password));
2842 if (*word[3+offset])
2843 serv->port = atoi (word[3+offset]);
2844 safe_strcpy (serv->hostname, word[2+offset], sizeof (serv->hostname));
2845 serv->auto_reconnect (serv, TRUE, -1);
2847 else
2849 serv->auto_reconnect (serv, TRUE, -1);
2851 prefs.recon_delay = tmp;
2853 return TRUE;
2856 static int
2857 cmd_recv (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2859 if (*word_eol[2])
2861 sess->server->p_inline (sess->server, word_eol[2], strlen (word_eol[2]));
2862 return TRUE;
2865 return FALSE;
2868 static int
2869 cmd_say (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2871 char *speech = word_eol[2];
2872 if (*speech)
2874 handle_say (sess, speech, FALSE);
2875 return TRUE;
2877 return FALSE;
2880 static int
2881 cmd_send (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2883 guint32 addr;
2884 socklen_t len;
2885 struct sockaddr_in SAddr;
2887 if (!word[2][0])
2888 return FALSE;
2890 addr = dcc_get_my_address ();
2891 if (addr == 0)
2893 /* use the one from our connected server socket */
2894 memset (&SAddr, 0, sizeof (struct sockaddr_in));
2895 len = sizeof (SAddr);
2896 getsockname (sess->server->sok, (struct sockaddr *) &SAddr, &len);
2897 addr = SAddr.sin_addr.s_addr;
2899 addr = ntohl (addr);
2901 if ((addr & 0xffff0000) == 0xc0a80000 || /* 192.168.x.x */
2902 (addr & 0xff000000) == 0x0a000000) /* 10.x.x.x */
2903 /* we got a private net address, let's PSEND or it'll fail */
2904 snprintf (tbuf, 512, "DCC PSEND %s", word_eol[2]);
2905 else
2906 snprintf (tbuf, 512, "DCC SEND %s", word_eol[2]);
2908 handle_command (sess, tbuf, FALSE);
2910 return TRUE;
2913 static int
2914 cmd_setcursor (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2916 int delta = FALSE;
2918 if (*word[2])
2920 if (word[2][0] == '-' || word[2][0] == '+')
2921 delta = TRUE;
2922 fe_set_inputbox_cursor (sess, delta, atoi (word[2]));
2923 return TRUE;
2926 return FALSE;
2929 static int
2930 cmd_settab (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2932 if (*word_eol[2])
2934 strcpy (tbuf, sess->channel);
2935 safe_strcpy (sess->channel, word_eol[2], CHANLEN);
2936 fe_set_channel (sess);
2937 strcpy (sess->channel, tbuf);
2940 return TRUE;
2943 static int
2944 cmd_settext (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2946 fe_set_inputbox_contents (sess, word_eol[2]);
2947 return TRUE;
2950 static int
2951 cmd_splay (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2953 if (*word[2])
2955 sound_play (word[2], FALSE);
2956 return TRUE;
2959 return FALSE;
2962 static int
2963 parse_irc_url (char *url, char *server_name[], char *port[], char *channel[], int *use_ssl)
2965 char *co;
2966 #ifdef USE_OPENSSL
2967 if (strncasecmp ("ircs://", url, 7) == 0)
2969 *use_ssl = TRUE;
2970 *server_name = url + 7;
2971 goto urlserv;
2973 #endif
2975 if (strncasecmp ("irc://", url, 6) == 0)
2977 *server_name = url + 6;
2978 #ifdef USE_OPENSSL
2979 urlserv:
2980 #endif
2981 /* check for port */
2982 co = strchr (*server_name, ':');
2983 if (co)
2985 *port = co + 1;
2986 *co = 0;
2987 } else
2988 co = *server_name;
2989 /* check for channel - mirc style */
2990 co = strchr (co + 1, '/');
2991 if (co)
2993 *co = 0;
2994 co++;
2995 if (*co == '#')
2996 *channel = co+1;
2997 else
2998 *channel = co;
3001 return TRUE;
3003 return FALSE;
3006 static int
3007 cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3009 int offset = 0;
3010 char *server_name = NULL;
3011 char *port = NULL;
3012 char *pass = NULL;
3013 char *channel = NULL;
3014 int use_ssl = FALSE;
3015 int is_url = TRUE;
3016 server *serv = sess->server;
3018 #ifdef USE_OPENSSL
3019 /* BitchX uses -ssl, mIRC uses -e, let's support both */
3020 if (strcmp (word[2], "-ssl") == 0 || strcmp (word[2], "-e") == 0)
3022 use_ssl = TRUE;
3023 offset++; /* args move up by 1 word */
3025 #endif
3027 if (!parse_irc_url (word[2 + offset], &server_name, &port, &channel, &use_ssl))
3029 is_url = FALSE;
3030 server_name = word[2 + offset];
3032 if (port)
3033 pass = word[3 + offset];
3034 else
3036 port = word[3 + offset];
3037 pass = word[4 + offset];
3040 if (!(*server_name))
3041 return FALSE;
3043 sess->server->network = NULL;
3045 /* dont clear it for /servchan */
3046 if (strncasecmp (word_eol[1], "SERVCHAN ", 9))
3047 sess->willjoinchannel[0] = 0;
3049 if (channel)
3051 sess->willjoinchannel[0] = '#';
3052 safe_strcpy ((sess->willjoinchannel + 1), channel, (CHANLEN - 1));
3055 /* support +7000 style ports like mIRC */
3056 if (port[0] == '+')
3058 port++;
3059 #ifdef USE_OPENSSL
3060 use_ssl = TRUE;
3061 #endif
3064 if (*pass)
3066 safe_strcpy (serv->password, pass, sizeof (serv->password));
3068 #ifdef USE_OPENSSL
3069 serv->use_ssl = use_ssl;
3070 serv->accept_invalid_cert = TRUE;
3071 #endif
3073 /* try to connect by Network name */
3074 if (servlist_connect_by_netname (sess, server_name, !is_url))
3075 return TRUE;
3077 if (*port)
3079 serv->connect (serv, server_name, atoi (port), FALSE);
3080 } else
3082 /* -1 for default port */
3083 serv->connect (serv, server_name, -1, FALSE);
3086 /* try to associate this connection with a listed network */
3087 if (!serv->network)
3088 /* search for this hostname in the entire server list */
3089 serv->network = servlist_net_find_from_server (server_name);
3090 /* may return NULL, but that's OK */
3092 return TRUE;
3095 static int
3096 cmd_servchan (struct session *sess, char *tbuf, char *word[],
3097 char *word_eol[])
3099 int offset = 0;
3101 #ifdef USE_OPENSSL
3102 if (strcmp (word[2], "-ssl") == 0)
3103 offset++;
3104 #endif
3106 if (*word[4 + offset])
3108 safe_strcpy (sess->willjoinchannel, word[4 + offset], CHANLEN);
3109 return cmd_server (sess, tbuf, word, word_eol);
3112 return FALSE;
3115 static int
3116 cmd_topic (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3118 if (word[2][0] && is_channel (sess->server, word[2]))
3119 sess->server->p_topic (sess->server, word[2], word_eol[3]);
3120 else
3121 sess->server->p_topic (sess->server, sess->channel, word_eol[2]);
3122 return TRUE;
3125 static int
3126 cmd_tray (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3128 if (strcmp (word[2], "-b") == 0)
3130 fe_tray_set_balloon (word[3], word[4][0] ? word[4] : NULL);
3131 return TRUE;
3134 if (strcmp (word[2], "-t") == 0)
3136 fe_tray_set_tooltip (word[3][0] ? word[3] : NULL);
3137 return TRUE;
3140 if (strcmp (word[2], "-i") == 0)
3142 fe_tray_set_icon (atoi (word[3]));
3143 return TRUE;
3146 if (strcmp (word[2], "-f") != 0)
3147 return FALSE;
3149 if (!word[3][0])
3151 fe_tray_set_file (NULL); /* default xchat icon */
3152 return TRUE;
3155 if (!word[4][0])
3157 fe_tray_set_file (word[3]); /* fixed custom icon */
3158 return TRUE;
3161 /* flash between 2 icons */
3162 fe_tray_set_flash (word[4], word[5][0] ? word[5] : NULL, atoi (word[3]));
3163 return TRUE;
3166 static int
3167 cmd_unignore (struct session *sess, char *tbuf, char *word[],
3168 char *word_eol[])
3170 char *mask = word[2];
3171 char *arg = word[3];
3172 if (*mask)
3174 if (ignore_del (mask, NULL))
3176 if (strcasecmp (arg, "QUIET"))
3177 EMIT_SIGNAL (XP_TE_IGNOREREMOVE, sess, mask, NULL, NULL, NULL, 0);
3179 return TRUE;
3181 return FALSE;
3184 static int
3185 cmd_unload (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3187 #ifdef USE_PLUGIN
3188 int len, by_file = FALSE;
3190 len = strlen (word[2]);
3191 #ifdef WIN32
3192 if (len > 4 && strcasecmp (word[2] + len - 4, ".dll") == 0)
3193 #else
3194 #if defined(__hpux)
3195 if (len > 3 && strcasecmp (word[2] + len - 3, ".sl") == 0)
3196 #else
3197 if (len > 3 && strcasecmp (word[2] + len - 3, ".so") == 0)
3198 #endif
3199 #endif
3200 by_file = TRUE;
3202 switch (plugin_kill (word[2], by_file))
3204 case 0:
3205 PrintText (sess, _("No such plugin found.\n"));
3206 break;
3207 case 1:
3208 return TRUE;
3209 case 2:
3210 PrintText (sess, _("That plugin is refusing to unload.\n"));
3211 break;
3213 #endif
3215 return FALSE;
3218 static server *
3219 find_server_from_hostname (char *hostname)
3221 GSList *list = serv_list;
3222 server *serv;
3224 while (list)
3226 serv = list->data;
3227 if (!strcasecmp (hostname, serv->hostname) && serv->connected)
3228 return serv;
3229 list = list->next;
3232 return NULL;
3235 static server *
3236 find_server_from_net (void *net)
3238 GSList *list = serv_list;
3239 server *serv;
3241 while (list)
3243 serv = list->data;
3244 if (serv->network == net && serv->connected)
3245 return serv;
3246 list = list->next;
3249 return NULL;
3252 static void
3253 url_join_only (server *serv, char *tbuf, char *channel)
3255 /* already connected, JOIN only. FIXME: support keys? */
3256 tbuf[0] = '#';
3257 /* tbuf is 4kb */
3258 safe_strcpy ((tbuf + 1), channel, 256);
3259 serv->p_join (serv, tbuf, "");
3262 static int
3263 cmd_url (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3265 if (word[2][0])
3267 char *server_name = NULL;
3268 char *port = NULL;
3269 char *channel = NULL;
3270 char *url = g_strdup (word[2]);
3271 int use_ssl = FALSE;
3272 void *net;
3273 server *serv;
3275 if (parse_irc_url (url, &server_name, &port, &channel, &use_ssl))
3277 /* maybe we're already connected to this net */
3279 /* check for "FreeNode" */
3280 net = servlist_net_find (server_name, NULL, strcasecmp);
3281 /* check for "irc.eu.freenode.net" */
3282 if (!net)
3283 net = servlist_net_find_from_server (server_name);
3285 if (net)
3287 /* found the network, but are we connected? */
3288 serv = find_server_from_net (net);
3289 if (serv)
3291 url_join_only (serv, tbuf, channel);
3292 g_free (url);
3293 return TRUE;
3296 else
3298 /* an un-listed connection */
3299 serv = find_server_from_hostname (server_name);
3300 if (serv)
3302 url_join_only (serv, tbuf, channel);
3303 g_free (url);
3304 return TRUE;
3308 /* not connected to this net, open new window */
3309 cmd_newserver (sess, tbuf, word, word_eol);
3311 } else
3312 fe_open_url (word[2]);
3313 g_free (url);
3314 return TRUE;
3317 return FALSE;
3320 static int
3321 userlist_cb (struct User *user, session *sess)
3323 time_t lt;
3325 if (!user->lasttalk)
3326 lt = 0;
3327 else
3328 lt = time (0) - user->lasttalk;
3329 PrintTextf (sess,
3330 "\00306%s\t\00314[\00310%-38s\00314] \017ov\0033=\017%d%d away=%u lt\0033=\017%d\n",
3331 user->nick, user->hostname, user->op, user->voice, user->away, lt);
3333 return TRUE;
3336 static int
3337 cmd_uselect (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3339 int idx = 2;
3340 int clear = TRUE;
3341 int scroll = FALSE;
3343 if (strcmp (word[2], "-a") == 0) /* ADD (don't clear selections) */
3345 clear = FALSE;
3346 idx++;
3348 if (strcmp (word[idx], "-s") == 0) /* SCROLL TO */
3350 scroll = TRUE;
3351 idx++;
3353 /* always valid, no args means clear the selection list */
3354 fe_uselect (sess, word + idx, clear, scroll);
3355 return TRUE;
3358 static int
3359 cmd_userlist (struct session *sess, char *tbuf, char *word[],
3360 char *word_eol[])
3362 tree_foreach (sess->usertree, (tree_traverse_func *)userlist_cb, sess);
3363 return TRUE;
3366 static int
3367 wallchop_cb (struct User *user, multidata *data)
3369 if (user->op)
3371 if (data->i)
3372 strcat (data->tbuf, ",");
3373 strcat (data->tbuf, user->nick);
3374 data->i++;
3376 if (data->i == 5)
3378 data->i = 0;
3379 sprintf (data->tbuf + strlen (data->tbuf),
3380 " :[@%s] %s", data->sess->channel, data->reason);
3381 data->sess->server->p_raw (data->sess->server, data->tbuf);
3382 strcpy (data->tbuf, "NOTICE ");
3385 return TRUE;
3388 static int
3389 cmd_wallchop (struct session *sess, char *tbuf, char *word[],
3390 char *word_eol[])
3392 multidata data;
3394 if (!(*word_eol[2]))
3395 return FALSE;
3397 strcpy (tbuf, "NOTICE ");
3399 data.reason = word_eol[2];
3400 data.tbuf = tbuf;
3401 data.i = 0;
3402 data.sess = sess;
3403 tree_foreach (sess->usertree, (tree_traverse_func*)wallchop_cb, &data);
3405 if (data.i)
3407 sprintf (tbuf + strlen (tbuf),
3408 " :[@%s] %s", sess->channel, word_eol[2]);
3409 sess->server->p_raw (sess->server, tbuf);
3412 return TRUE;
3415 static int
3416 cmd_wallchan (struct session *sess, char *tbuf, char *word[],
3417 char *word_eol[])
3419 GSList *list;
3421 if (*word_eol[2])
3423 list = sess_list;
3424 while (list)
3426 sess = list->data;
3427 if (sess->type == SESS_CHANNEL)
3429 inbound_chanmsg (sess->server, NULL, sess->channel,
3430 sess->server->nick, word_eol[2], TRUE, FALSE);
3431 sess->server->p_message (sess->server, sess->channel, word_eol[2]);
3433 list = list->next;
3435 return TRUE;
3437 return FALSE;
3440 static int
3441 cmd_hop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3443 int i = 2;
3445 while (1)
3447 if (!*word[i])
3449 if (i == 2)
3450 return FALSE;
3451 send_channel_modes (sess, tbuf, word, 2, i, '+', 'h', 0);
3452 return TRUE;
3454 i++;
3458 static int
3459 cmd_voice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3461 int i = 2;
3463 while (1)
3465 if (!*word[i])
3467 if (i == 2)
3468 return FALSE;
3469 send_channel_modes (sess, tbuf, word, 2, i, '+', 'v', 0);
3470 return TRUE;
3472 i++;
3476 /* *MUST* be kept perfectly sorted for the bsearch to work */
3477 const struct commands xc_cmds[] = {
3478 {"ADDBUTTON", cmd_addbutton, 0, 0, 1,
3479 N_("ADDBUTTON <name> <action>, adds a button under the user-list")},
3480 {"ALLCHAN", cmd_allchannels, 0, 0, 1,
3481 N_("ALLCHAN <cmd>, sends a command to all channels you're in")},
3482 {"ALLCHANL", cmd_allchannelslocal, 0, 0, 1,
3483 N_("ALLCHANL <cmd>, sends a command to all channels on the current server")},
3484 {"ALLSERV", cmd_allservers, 0, 0, 1,
3485 N_("ALLSERV <cmd>, sends a command to all servers you're in")},
3486 {"AWAY", cmd_away, 1, 0, 1, N_("AWAY [<reason>], sets you away")},
3487 {"BACK", cmd_back, 1, 0, 1, N_("BACK, sets you back (not away)")},
3488 {"BAN", cmd_ban, 1, 1, 1,
3489 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)")},
3490 {"CHANOPT", cmd_chanopt, 0, 0, 1, N_("CHANOPT [-quiet] <variable> [<value>]")},
3491 {"CHARSET", cmd_charset, 0, 0, 1, N_("CHARSET [<encoding>], get or set the encoding used for the current connection")},
3492 {"CLEAR", cmd_clear, 0, 0, 1, N_("CLEAR [ALL|HISTORY], Clears the current text window or command history")},
3493 {"CLOSE", cmd_close, 0, 0, 1, N_("CLOSE, Closes the current window/tab")},
3495 {"COUNTRY", cmd_country, 0, 0, 1,
3496 N_("COUNTRY [-s] <code|wildcard>, finds a country code, eg: au = australia")},
3497 {"CTCP", cmd_ctcp, 1, 0, 1,
3498 N_("CTCP <nick> <message>, send the CTCP message to nick, common messages are VERSION and USERINFO")},
3499 {"CYCLE", cmd_cycle, 1, 1, 1,
3500 N_("CYCLE [<channel>], parts the current or given channel and immediately rejoins")},
3501 {"DCC", cmd_dcc, 0, 0, 1,
3502 N_("\n"
3503 "DCC GET <nick> - accept an offered file\n"
3504 "DCC SEND [-maxcps=#] <nick> [file] - send a file to someone\n"
3505 "DCC PSEND [-maxcps=#] <nick> [file] - send a file using passive mode\n"
3506 "DCC LIST - show DCC list\n"
3507 "DCC CHAT <nick> - offer DCC CHAT to someone\n"
3508 "DCC PCHAT <nick> - offer DCC CHAT using passive mode\n"
3509 "DCC CLOSE <type> <nick> <file> example:\n"
3510 " /dcc close send johnsmith file.tar.gz")},
3511 {"DEBUG", cmd_debug, 0, 0, 1, 0},
3513 {"DEHOP", cmd_dehop, 1, 1, 1,
3514 N_("DEHOP <nick>, removes chanhalf-op status from the nick on the current channel (needs chanop)")},
3515 {"DELBUTTON", cmd_delbutton, 0, 0, 1,
3516 N_("DELBUTTON <name>, deletes a button from under the user-list")},
3517 {"DEOP", cmd_deop, 1, 1, 1,
3518 N_("DEOP <nick>, removes chanop status from the nick on the current channel (needs chanop)")},
3519 {"DEVOICE", cmd_devoice, 1, 1, 1,
3520 N_("DEVOICE <nick>, removes voice status from the nick on the current channel (needs chanop)")},
3521 {"DISCON", cmd_discon, 0, 0, 1, N_("DISCON, Disconnects from server")},
3522 {"DNS", cmd_dns, 0, 0, 1, N_("DNS <nick|host|ip>, Finds a users IP number")},
3523 {"ECHO", cmd_echo, 0, 0, 1, N_("ECHO <text>, Prints text locally")},
3524 #ifndef WIN32
3525 {"EXEC", cmd_exec, 0, 0, 1,
3526 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")},
3527 #ifndef __EMX__
3528 {"EXECCONT", cmd_execc, 0, 0, 1, N_("EXECCONT, sends the process SIGCONT")},
3529 #endif
3530 {"EXECKILL", cmd_execk, 0, 0, 1,
3531 N_("EXECKILL [-9], kills a running exec in the current session. If -9 is given the process is SIGKILL'ed")},
3532 #ifndef __EMX__
3533 {"EXECSTOP", cmd_execs, 0, 0, 1, N_("EXECSTOP, sends the process SIGSTOP")},
3534 {"EXECWRITE", cmd_execw, 0, 0, 1, N_("EXECWRITE, sends data to the processes stdin")},
3535 #endif
3536 #endif
3537 {"FLUSHQ", cmd_flushq, 0, 0, 1,
3538 N_("FLUSHQ, flushes the current server's send queue")},
3539 {"GATE", cmd_gate, 0, 0, 1,
3540 N_("GATE <host> [<port>], proxies through a host, port defaults to 23")},
3541 {"GETFILE", cmd_getfile, 0, 0, 1, "GETFILE [-folder] [-multi] [-save] <command> <title> [<initial>]"},
3542 {"GETINT", cmd_getint, 0, 0, 1, "GETINT <default> <command> <prompt>"},
3543 {"GETSTR", cmd_getstr, 0, 0, 1, "GETSTR <default> <command> <prompt>"},
3544 {"GHOST", cmd_ghost, 1, 0, 1, N_("GHOST <nick> [password], Kills a ghosted nickname")},
3545 {"GUI", cmd_gui, 0, 0, 1, "GUI [APPLY|ATTACH|DETACH|SHOW|HIDE|FOCUS|FLASH|ICONIFY|COLOR <n>]\n"
3546 " GUI [MSGBOX <text>|MENU TOGGLE]"},
3547 {"HELP", cmd_help, 0, 0, 1, 0},
3548 {"HOP", cmd_hop, 1, 1, 1,
3549 N_("HOP <nick>, gives chanhalf-op status to the nick (needs chanop)")},
3550 {"ID", cmd_id, 1, 0, 1, N_("ID <password>, identifies yourself to nickserv")},
3551 {"IGNORE", cmd_ignore, 0, 0, 1,
3552 N_("IGNORE <mask> <types..> <options..>\n"
3553 " mask - host mask to ignore, eg: *!*@*.aol.com\n"
3554 " types - types of data to ignore, one or all of:\n"
3555 " PRIV, CHAN, NOTI, CTCP, DCC, INVI, ALL\n"
3556 " options - NOSAVE, QUIET")},
3558 {"INVITE", cmd_invite, 1, 0, 1,
3559 N_("INVITE <nick> [<channel>], invites someone to a channel, by default the current channel (needs chanop)")},
3560 {"JOIN", cmd_join, 1, 0, 0, N_("JOIN <channel>, joins the channel")},
3561 {"KICK", cmd_kick, 1, 1, 1,
3562 N_("KICK <nick>, kicks the nick from the current channel (needs chanop)")},
3563 {"KICKBAN", cmd_kickban, 1, 1, 1,
3564 N_("KICKBAN <nick>, bans then kicks the nick from the current channel (needs chanop)")},
3565 {"KILLALL", cmd_killall, 0, 0, 1, "KILLALL, immediately exit"},
3566 {"LAGCHECK", cmd_lagcheck, 0, 0, 1,
3567 N_("LAGCHECK, forces a new lag check")},
3568 {"LASTLOG", cmd_lastlog, 0, 0, 1,
3569 N_("LASTLOG <string>, searches for a string in the buffer")},
3570 {"LIST", cmd_list, 1, 0, 1, 0},
3571 {"LOAD", cmd_load, 0, 0, 1, N_("LOAD [-e] <file>, loads a plugin or script")},
3573 {"MDEHOP", cmd_mdehop, 1, 1, 1,
3574 N_("MDEHOP, Mass deop's all chanhalf-ops in the current channel (needs chanop)")},
3575 {"MDEOP", cmd_mdeop, 1, 1, 1,
3576 N_("MDEOP, Mass deop's all chanops in the current channel (needs chanop)")},
3577 {"ME", cmd_me, 0, 0, 1,
3578 N_("ME <action>, sends the action to the current channel (actions are written in the 3rd person, like /me jumps)")},
3579 {"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"
3580 " See http://xchat.org/docs/menu/ for more details."},
3581 {"MKICK", cmd_mkick, 1, 1, 1,
3582 N_("MKICK, Mass kicks everyone except you in the current channel (needs chanop)")},
3583 {"MODE", cmd_mode, 1, 0, 1, 0},
3584 {"MOP", cmd_mop, 1, 1, 1,
3585 N_("MOP, Mass op's all users in the current channel (needs chanop)")},
3586 {"MSG", cmd_msg, 0, 0, 1, N_("MSG <nick> <message>, sends a private message")},
3588 {"NAMES", cmd_names, 1, 0, 1,
3589 N_("NAMES, Lists the nicks on the current channel")},
3590 {"NCTCP", cmd_nctcp, 1, 0, 1,
3591 N_("NCTCP <nick> <message>, Sends a CTCP notice")},
3592 {"NEWSERVER", cmd_newserver, 0, 0, 1, N_("NEWSERVER [-noconnect] <hostname> [<port>]")},
3593 {"NICK", cmd_nick, 0, 0, 1, N_("NICK <nickname>, sets your nick")},
3595 {"NOTICE", cmd_notice, 1, 0, 1,
3596 N_("NOTICE <nick/channel> <message>, sends a notice. Notices are a type of message that should be auto reacted to")},
3597 {"NOTIFY", cmd_notify, 0, 0, 1,
3598 N_("NOTIFY [-n network1[,network2,...]] [<nick>], displays your notify list or adds someone to it")},
3599 {"OP", cmd_op, 1, 1, 1,
3600 N_("OP <nick>, gives chanop status to the nick (needs chanop)")},
3601 {"PART", cmd_part, 1, 1, 0,
3602 N_("PART [<channel>] [<reason>], leaves the channel, by default the current one")},
3603 {"PING", cmd_ping, 1, 0, 1,
3604 N_("PING <nick | channel>, CTCP pings nick or channel")},
3605 {"QUERY", cmd_query, 0, 0, 1,
3606 N_("QUERY [-nofocus] <nick>, opens up a new privmsg window to someone")},
3607 {"QUIT", cmd_quit, 0, 0, 1,
3608 N_("QUIT [<reason>], disconnects from the current server")},
3609 {"QUOTE", cmd_quote, 1, 0, 1,
3610 N_("QUOTE <text>, sends the text in raw form to the server")},
3611 #ifdef USE_OPENSSL
3612 {"RECONNECT", cmd_reconnect, 0, 0, 1,
3613 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")},
3614 #else
3615 {"RECONNECT", cmd_reconnect, 0, 0, 1,
3616 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")},
3617 #endif
3618 {"RECV", cmd_recv, 1, 0, 1, N_("RECV <text>, send raw data to xchat, as if it was received from the irc server")},
3620 {"SAY", cmd_say, 0, 0, 1,
3621 N_("SAY <text>, sends the text to the object in the current window")},
3622 {"SEND", cmd_send, 0, 0, 1, N_("SEND <nick> [<file>]")},
3623 #ifdef USE_OPENSSL
3624 {"SERVCHAN", cmd_servchan, 0, 0, 1,
3625 N_("SERVCHAN [-ssl] <host> <port> <channel>, connects and joins a channel")},
3626 #else
3627 {"SERVCHAN", cmd_servchan, 0, 0, 1,
3628 N_("SERVCHAN <host> <port> <channel>, connects and joins a channel")},
3629 #endif
3630 #ifdef USE_OPENSSL
3631 {"SERVER", cmd_server, 0, 0, 1,
3632 N_("SERVER [-ssl] <host> [<port>] [<password>], connects to a server, the default port is 6667 for normal connections, and 9999 for ssl connections")},
3633 #else
3634 {"SERVER", cmd_server, 0, 0, 1,
3635 N_("SERVER <host> [<port>] [<password>], connects to a server, the default port is 6667")},
3636 #endif
3637 {"SET", cmd_set, 0, 0, 1, N_("SET [-e] [-off|-on] [-quiet] <variable> [<value>]")},
3638 {"SETCURSOR", cmd_setcursor, 0, 0, 1, N_("SETCURSOR [-|+]<position>, reposition the cursor in the inputbox")},
3639 {"SETTAB", cmd_settab, 0, 0, 1, N_("SETTAB <new name>, change a tab's name, tab_trunc limit still applies")},
3640 {"SETTEXT", cmd_settext, 0, 0, 1, N_("SETTEXT <new text>, replace the text in the input box")},
3641 {"SPLAY", cmd_splay, 0, 0, 1, "SPLAY <soundfile>"},
3642 {"TOPIC", cmd_topic, 1, 1, 1,
3643 N_("TOPIC [<topic>], sets the topic if one is given, else shows the current topic")},
3644 {"TRAY", cmd_tray, 0, 0, 1,
3645 N_("\nTRAY -f <timeout> <file1> [<file2>] Blink tray between two icons.\n"
3646 "TRAY -f <filename> Set tray to a fixed icon.\n"
3647 "TRAY -i <number> Blink tray with an internal icon.\n"
3648 "TRAY -t <text> Set the tray tooltip.\n"
3649 "TRAY -b <title> <text> Set the tray balloon."
3651 {"UNBAN", cmd_unban, 1, 1, 1,
3652 N_("UNBAN <mask> [<mask>...], unbans the specified masks.")},
3653 {"UNIGNORE", cmd_unignore, 0, 0, 1, N_("UNIGNORE <mask> [QUIET]")},
3654 {"UNLOAD", cmd_unload, 0, 0, 1, N_("UNLOAD <name>, unloads a plugin or script")},
3655 {"URL", cmd_url, 0, 0, 1, N_("URL <url>, opens a URL in your browser")},
3656 {"USELECT", cmd_uselect, 0, 1, 0,
3657 N_("USELECT [-a] [-s] <nick1> <nick2> etc, highlights nick(s) in channel userlist")},
3658 {"USERLIST", cmd_userlist, 1, 1, 1, 0},
3659 {"VOICE", cmd_voice, 1, 1, 1,
3660 N_("VOICE <nick>, gives voice status to someone (needs chanop)")},
3661 {"WALLCHAN", cmd_wallchan, 1, 1, 1,
3662 N_("WALLCHAN <message>, writes the message to all channels")},
3663 {"WALLCHOP", cmd_wallchop, 1, 1, 1,
3664 N_("WALLCHOP <message>, sends the message to all chanops on the current channel")},
3665 {0, 0, 0, 0, 0, 0}
3669 static int
3670 command_compare (const void *a, const void *b)
3672 return strcasecmp (a, ((struct commands *)b)->name);
3675 static struct commands *
3676 find_internal_command (char *name)
3678 /* the "-1" is to skip the NULL terminator */
3679 return bsearch (name, xc_cmds, (sizeof (xc_cmds) /
3680 sizeof (xc_cmds[0])) - 1, sizeof (xc_cmds[0]), command_compare);
3683 static void
3684 help (session *sess, char *tbuf, char *helpcmd, int quiet)
3686 struct commands *cmd;
3688 if (plugin_show_help (sess, helpcmd))
3689 return;
3691 cmd = find_internal_command (helpcmd);
3693 if (cmd)
3695 if (cmd->help)
3697 snprintf (tbuf, TBUFSIZE, _("Usage: %s\n"), _(cmd->help));
3698 PrintText (sess, tbuf);
3699 } else
3701 if (!quiet)
3702 PrintText (sess, _("\nNo help available on that command.\n"));
3704 return;
3707 if (!quiet)
3708 PrintText (sess, _("No such command.\n"));
3711 /* inserts %a, %c, %d etc into buffer. Also handles &x %x for word/word_eol. *
3712 * returns 2 on buffer overflow
3713 * returns 1 on success *
3714 * returns 0 on bad-args-for-user-command *
3715 * - word/word_eol args might be NULL *
3716 * - this beast is used for UserCommands, UserlistButtons and CTCP replies */
3719 auto_insert (char *dest, int destlen, unsigned char *src, char *word[],
3720 char *word_eol[], char *a, char *c, char *d, char *e, char *h,
3721 char *n, char *s)
3723 int num;
3724 char buf[32];
3725 time_t now;
3726 struct tm *tm_ptr;
3727 char *utf;
3728 gsize utf_len;
3729 char *orig = dest;
3731 destlen--;
3733 while (src[0])
3735 if (src[0] == '%' || src[0] == '&')
3737 if (isdigit ((unsigned char) src[1]))
3739 if (isdigit ((unsigned char) src[2]) && isdigit ((unsigned char) src[3]))
3741 buf[0] = src[1];
3742 buf[1] = src[2];
3743 buf[2] = src[3];
3744 buf[3] = 0;
3745 dest[0] = atoi (buf);
3746 utf = g_locale_to_utf8 (dest, 1, 0, &utf_len, 0);
3747 if (utf)
3749 if ((dest - orig) + utf_len >= destlen)
3751 g_free (utf);
3752 return 2;
3755 memcpy (dest, utf, utf_len);
3756 g_free (utf);
3757 dest += utf_len;
3759 src += 3;
3760 } else
3762 if (word)
3764 src++;
3765 num = src[0] - '0'; /* ascii to decimal */
3766 if (*word[num] == 0)
3767 return 0;
3769 if (src[-1] == '%')
3770 utf = word[num];
3771 else
3772 utf = word_eol[num];
3774 /* avoid recusive usercommand overflow */
3775 if ((dest - orig) + strlen (utf) >= destlen)
3776 return 2;
3778 strcpy (dest, utf);
3779 dest += strlen (dest);
3782 } else
3784 if (src[0] == '&')
3785 goto lamecode;
3786 src++;
3787 utf = NULL;
3788 switch (src[0])
3790 case '%':
3791 if ((dest - orig) + 2 >= destlen)
3792 return 2;
3793 dest[0] = '%';
3794 dest[1] = 0;
3795 dest++;
3796 break;
3797 case 'a':
3798 utf = a; break;
3799 case 'c':
3800 utf = c; break;
3801 case 'd':
3802 utf = d; break;
3803 case 'e':
3804 utf = e; break;
3805 case 'h':
3806 utf = h; break;
3807 case 'm':
3808 utf = get_cpu_str (); break;
3809 case 'n':
3810 utf = n; break;
3811 case 's':
3812 utf = s; break;
3813 case 't':
3814 now = time (0);
3815 utf = ctime (&now);
3816 utf[19] = 0;
3817 break;
3818 case 'v':
3819 utf = PACKAGE_VERSION; break;
3820 break;
3821 case 'y':
3822 now = time (0);
3823 tm_ptr = localtime (&now);
3824 snprintf (buf, sizeof (buf), "%4d%02d%02d", 1900 +
3825 tm_ptr->tm_year, 1 + tm_ptr->tm_mon, tm_ptr->tm_mday);
3826 utf = buf;
3827 break;
3828 default:
3829 src--;
3830 goto lamecode;
3833 if (utf)
3835 if ((dest - orig) + strlen (utf) >= destlen)
3836 return 2;
3837 strcpy (dest, utf);
3838 dest += strlen (dest);
3842 src++;
3843 } else
3845 utf_len = g_utf8_skip[src[0]];
3847 if ((dest - orig) + utf_len >= destlen)
3848 return 2;
3850 if (utf_len == 1)
3852 lamecode:
3853 dest[0] = src[0];
3854 dest++;
3855 src++;
3856 } else
3858 memcpy (dest, src, utf_len);
3859 dest += utf_len;
3860 src += utf_len;
3865 dest[0] = 0;
3867 return 1;
3870 void
3871 check_special_chars (char *cmd, int do_ascii) /* check for %X */
3873 int occur = 0;
3874 int len = strlen (cmd);
3875 char *buf, *utf;
3876 char tbuf[4];
3877 int i = 0, j = 0;
3878 gsize utf_len;
3880 if (!len)
3881 return;
3883 buf = malloc (len + 1);
3885 if (buf)
3887 while (cmd[j])
3889 switch (cmd[j])
3891 case '%':
3892 occur++;
3893 if ( do_ascii &&
3894 j + 3 < len &&
3895 (isdigit ((unsigned char) cmd[j + 1]) && isdigit ((unsigned char) cmd[j + 2]) &&
3896 isdigit ((unsigned char) cmd[j + 3])))
3898 tbuf[0] = cmd[j + 1];
3899 tbuf[1] = cmd[j + 2];
3900 tbuf[2] = cmd[j + 3];
3901 tbuf[3] = 0;
3902 buf[i] = atoi (tbuf);
3903 utf = g_locale_to_utf8 (buf + i, 1, 0, &utf_len, 0);
3904 if (utf)
3906 memcpy (buf + i, utf, utf_len);
3907 g_free (utf);
3908 i += (utf_len - 1);
3910 j += 3;
3911 } else
3913 switch (cmd[j + 1])
3915 case 'R':
3916 buf[i] = '\026';
3917 break;
3918 case 'U':
3919 buf[i] = '\037';
3920 break;
3921 case 'B':
3922 buf[i] = '\002';
3923 break;
3924 case 'C':
3925 buf[i] = '\003';
3926 break;
3927 case 'O':
3928 buf[i] = '\017';
3929 break;
3930 case 'H': /* CL: invisible text code */
3931 buf[i] = HIDDEN_CHAR;
3932 break;
3933 case '%':
3934 buf[i] = '%';
3935 break;
3936 default:
3937 buf[i] = '%';
3938 j--;
3939 break;
3941 j++;
3942 break;
3943 default:
3944 buf[i] = cmd[j];
3947 j++;
3948 i++;
3950 buf[i] = 0;
3951 if (occur)
3952 strcpy (cmd, buf);
3953 free (buf);
3957 typedef struct
3959 char *nick;
3960 int len;
3961 struct User *best;
3962 int bestlen;
3963 char *space;
3964 char *tbuf;
3965 } nickdata;
3967 static int
3968 nick_comp_cb (struct User *user, nickdata *data)
3970 int lenu;
3972 if (!rfc_ncasecmp (user->nick, data->nick, data->len))
3974 lenu = strlen (user->nick);
3975 if (lenu == data->len)
3977 snprintf (data->tbuf, TBUFSIZE, "%s%s", user->nick, data->space);
3978 data->len = -1;
3979 return FALSE;
3980 } else if (lenu < data->bestlen)
3982 data->bestlen = lenu;
3983 data->best = user;
3987 return TRUE;
3990 static void
3991 perform_nick_completion (struct session *sess, char *cmd, char *tbuf)
3993 int len;
3994 char *space = strchr (cmd, ' ');
3995 if (space && space != cmd)
3997 if (space[-1] == prefs.nick_suffix[0] && space - 1 != cmd)
3999 len = space - cmd - 1;
4000 if (len < NICKLEN)
4002 char nick[NICKLEN];
4003 nickdata data;
4005 memcpy (nick, cmd, len);
4006 nick[len] = 0;
4008 data.nick = nick;
4009 data.len = len;
4010 data.bestlen = INT_MAX;
4011 data.best = NULL;
4012 data.tbuf = tbuf;
4013 data.space = space - 1;
4014 tree_foreach (sess->usertree, (tree_traverse_func *)nick_comp_cb, &data);
4016 if (data.len == -1)
4017 return;
4019 if (data.best)
4021 snprintf (tbuf, TBUFSIZE, "%s%s", data.best->nick, space - 1);
4022 return;
4028 strcpy (tbuf, cmd);
4031 static void
4032 user_command (session * sess, char *tbuf, char *cmd, char *word[],
4033 char *word_eol[])
4035 if (!auto_insert (tbuf, 2048, cmd, word, word_eol, "", sess->channel, "",
4036 server_get_network (sess->server, TRUE), "",
4037 sess->server->nick, ""))
4039 PrintText (sess, _("Bad arguments for user command.\n"));
4040 return;
4043 handle_command (sess, tbuf, TRUE);
4046 /* handle text entered without a CMDchar prefix */
4048 static void
4049 handle_say (session *sess, char *text, int check_spch)
4051 struct DCC *dcc;
4052 char *word[PDIWORDS+1];
4053 char *word_eol[PDIWORDS+1];
4054 char pdibuf_static[1024];
4055 char newcmd_static[1024];
4056 char *pdibuf = pdibuf_static;
4057 char *newcmd = newcmd_static;
4058 int len;
4059 int newcmdlen = sizeof newcmd_static;
4061 if (strcmp (sess->channel, "(lastlog)") == 0)
4063 lastlog (sess->lastlog_sess, text, sess->lastlog_regexp);
4064 return;
4067 len = strlen (text);
4068 if (len >= sizeof pdibuf_static)
4069 pdibuf = malloc (len + 1);
4071 if (len + NICKLEN >= newcmdlen)
4072 newcmd = malloc (newcmdlen = len + NICKLEN + 1);
4074 if (check_spch && prefs.perc_color)
4075 check_special_chars (text, prefs.perc_ascii);
4077 /* Python relies on this */
4078 word[PDIWORDS] = NULL;
4079 word_eol[PDIWORDS] = NULL;
4081 /* split the text into words and word_eol */
4082 process_data_init (pdibuf, text, word, word_eol, TRUE, FALSE);
4084 /* a command of "" can be hooked for non-commands */
4085 if (plugin_emit_command (sess, "", word, word_eol))
4086 goto xit;
4088 /* incase a plugin did /close */
4089 if (!is_session (sess))
4090 goto xit;
4092 if (!sess->channel[0] || sess->type == SESS_SERVER || sess->type == SESS_NOTICES || sess->type == SESS_SNOTICES)
4094 notj_msg (sess);
4095 goto xit;
4098 if (prefs.nickcompletion)
4099 perform_nick_completion (sess, text, newcmd);
4100 else
4101 safe_strcpy (newcmd, text, newcmdlen);
4103 text = newcmd;
4105 if (sess->type == SESS_DIALOG)
4107 /* try it via dcc, if possible */
4108 dcc = dcc_write_chat (sess->channel, text);
4109 if (dcc)
4111 inbound_chanmsg (sess->server, NULL, sess->channel,
4112 sess->server->nick, text, TRUE, FALSE);
4113 set_topic (sess, net_ip (dcc->addr), net_ip (dcc->addr));
4114 goto xit;
4118 if (sess->server->connected)
4120 unsigned int max;
4121 unsigned char t = 0;
4123 /* maximum allowed message text */
4124 /* :nickname!username@host.com PRIVMSG #channel :text\r\n */
4125 max = 512;
4126 max -= 16; /* :, !, @, " PRIVMSG ", " ", :, \r, \n */
4127 max -= strlen (sess->server->nick);
4128 max -= strlen (sess->channel);
4129 if (sess->me && sess->me->hostname)
4130 max -= strlen (sess->me->hostname);
4131 else
4133 max -= 9; /* username */
4134 max -= 65; /* max possible hostname and '@' */
4137 if (strlen (text) > max)
4139 int i = 0, size;
4141 /* traverse the utf8 string and find the nearest cut point that
4142 doesn't split 1 char in half */
4143 while (1)
4145 size = g_utf8_skip[((unsigned char *)text)[i]];
4146 if ((i + size) >= max)
4147 break;
4148 i += size;
4150 max = i;
4151 t = text[max];
4152 text[max] = 0; /* insert a NULL terminator to shorten it */
4155 inbound_chanmsg (sess->server, sess, sess->channel, sess->server->nick,
4156 text, TRUE, FALSE);
4157 sess->server->p_message (sess->server, sess->channel, text);
4159 if (t)
4161 text[max] = t;
4162 handle_say (sess, text + max, FALSE);
4165 } else
4167 notc_msg (sess);
4170 xit:
4171 if (pdibuf != pdibuf_static)
4172 free (pdibuf);
4174 if (newcmd != newcmd_static)
4175 free (newcmd);
4178 /* handle a command, without the '/' prefix */
4181 handle_command (session *sess, char *cmd, int check_spch)
4183 struct popup *pop;
4184 int user_cmd = FALSE;
4185 GSList *list;
4186 char *word[PDIWORDS+1];
4187 char *word_eol[PDIWORDS+1];
4188 static int command_level = 0;
4189 struct commands *int_cmd;
4190 char pdibuf_static[1024];
4191 char tbuf_static[TBUFSIZE];
4192 char *pdibuf;
4193 char *tbuf;
4194 int len;
4195 int ret = TRUE;
4197 if (command_level > 99)
4199 fe_message (_("Too many recursive usercommands, aborting."), FE_MSG_ERROR);
4200 return TRUE;
4202 command_level++;
4203 /* anything below MUST DEC command_level before returning */
4205 len = strlen (cmd);
4206 if (len >= sizeof (pdibuf_static))
4207 pdibuf = malloc (len + 1);
4208 else
4209 pdibuf = pdibuf_static;
4211 if ((len * 2) >= sizeof (tbuf_static))
4212 tbuf = malloc ((len * 2) + 1);
4213 else
4214 tbuf = tbuf_static;
4216 /* split the text into words and word_eol */
4217 process_data_init (pdibuf, cmd, word, word_eol, TRUE, TRUE);
4219 /* ensure an empty string at index 32 for cmd_deop etc */
4220 /* (internal use only, plugins can still only read 1-31). */
4221 word[PDIWORDS] = "\000\000";
4222 word_eol[PDIWORDS] = "\000\000";
4224 int_cmd = find_internal_command (word[1]);
4225 /* redo it without quotes processing, for some commands like /JOIN */
4226 if (int_cmd && !int_cmd->handle_quotes)
4227 process_data_init (pdibuf, cmd, word, word_eol, FALSE, FALSE);
4229 if (check_spch && prefs.perc_color)
4230 check_special_chars (cmd, prefs.perc_ascii);
4232 if (plugin_emit_command (sess, word[1], word, word_eol))
4233 goto xit;
4235 /* incase a plugin did /close */
4236 if (!is_session (sess))
4237 goto xit;
4239 /* first see if it's a userCommand */
4240 list = command_list;
4241 while (list)
4243 pop = (struct popup *) list->data;
4244 if (!strcasecmp (pop->name, word[1]))
4246 user_command (sess, tbuf, pop->cmd, word, word_eol);
4247 user_cmd = TRUE;
4249 list = list->next;
4252 if (user_cmd)
4253 goto xit;
4255 /* now check internal commands */
4256 int_cmd = find_internal_command (word[1]);
4258 if (int_cmd)
4260 if (int_cmd->needserver && !sess->server->connected)
4262 notc_msg (sess);
4263 } else if (int_cmd->needchannel && !sess->channel[0])
4265 notj_msg (sess);
4266 } else
4268 switch (int_cmd->callback (sess, tbuf, word, word_eol))
4270 case FALSE:
4271 help (sess, tbuf, int_cmd->name, TRUE);
4272 break;
4273 case 2:
4274 ret = FALSE;
4275 goto xit;
4278 } else
4280 /* unknown command, just send it to the server and hope */
4281 if (!sess->server->connected)
4282 PrintText (sess, _("Unknown Command. Try /help\n"));
4283 else
4284 sess->server->p_raw (sess->server, cmd);
4287 xit:
4288 command_level--;
4290 if (pdibuf != pdibuf_static)
4291 free (pdibuf);
4293 if (tbuf != tbuf_static)
4294 free (tbuf);
4296 return ret;
4299 /* handle one line entered into the input box */
4301 static int
4302 handle_user_input (session *sess, char *text, int history, int nocommand)
4304 if (*text == '\0')
4305 return 1;
4307 if (history)
4308 history_add (&sess->history, text);
4310 /* is it NOT a command, just text? */
4311 if (nocommand || text[0] != prefs.cmdchar[0])
4313 handle_say (sess, text, TRUE);
4314 return 1;
4317 /* check for // */
4318 if (text[0] == prefs.cmdchar[0] && text[1] == prefs.cmdchar[0])
4320 handle_say (sess, text + 1, TRUE);
4321 return 1;
4324 if (prefs.cmdchar[0] == '/')
4326 int i;
4327 const char *unix_dirs [] = {
4328 "/bin/", "/boot/", "/dev/",
4329 "/etc/", "/home/", "/lib/",
4330 "/lost+found/", "/mnt/", "/opt/",
4331 "/proc/", "/root/", "/sbin/",
4332 "/tmp/", "/usr/", "/var/",
4333 "/gnome/", NULL};
4334 for (i = 0; unix_dirs[i] != NULL; i++)
4335 if (strncmp (text, unix_dirs[i], strlen (unix_dirs[i]))==0)
4337 handle_say (sess, text, TRUE);
4338 return 1;
4342 return handle_command (sess, text + 1, TRUE);
4345 /* changed by Steve Green. Macs sometimes paste with imbedded \r */
4346 void
4347 handle_multiline (session *sess, char *cmd, int history, int nocommand)
4349 while (*cmd)
4351 char *cr = cmd + strcspn (cmd, "\n\r");
4352 int end_of_string = *cr == 0;
4353 *cr = 0;
4354 if (!handle_user_input (sess, cmd, history, nocommand))
4355 return;
4356 if (end_of_string)
4357 break;
4358 cmd = cr + 1;
4362 /*void
4363 handle_multiline (session *sess, char *cmd, int history, int nocommand)
4365 char *cr;
4367 cr = strchr (cmd, '\n');
4368 if (cr)
4370 while (1)
4372 if (cr)
4373 *cr = 0;
4374 if (!handle_user_input (sess, cmd, history, nocommand))
4375 return;
4376 if (!cr)
4377 break;
4378 cmd = cr + 1;
4379 if (*cmd == 0)
4380 break;
4381 cr = strchr (cmd, '\n');
4383 } else
4385 handle_user_input (sess, cmd, history, nocommand);