fix implicit declarations
[rofl0r-ixchat.git] / src / common / outbound.c
blobddcc6b9910138cc8b4b535e693ff5cd4384d2199
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"
55 #include "chanopt.h"
58 #ifdef USE_DEBUG
59 extern int current_mem_usage;
60 #endif
61 #define TBUFSIZE 4096
63 static void help (session *sess, char *tbuf, char *helpcmd, int quiet);
64 static int cmd_server (session *sess, char *tbuf, char *word[], char *word_eol[]);
65 static void handle_say (session *sess, char *text, int check_spch);
68 static void
69 notj_msg (struct session *sess)
71 PrintText (sess, _("No channel joined. Try /join #<channel>\n"));
74 void
75 notc_msg (struct session *sess)
77 PrintText (sess, _("Not connected. Try /server <host> [<port>]\n"));
80 static char *
81 random_line (char *file_name)
83 FILE *fh;
84 char buf[512];
85 int lines, ran;
87 if (!file_name[0])
88 goto nofile;
90 fh = xchat_fopen_file (file_name, "r", 0);
91 if (!fh)
93 nofile:
94 /* reason is not a file, an actual reason! */
95 return strdup (file_name);
98 /* count number of lines in file */
99 lines = 0;
100 while (fgets (buf, sizeof (buf), fh))
101 lines++;
103 if (lines < 1)
104 goto nofile;
106 /* go down a random number */
107 rewind (fh);
108 ran = RAND_INT (lines);
111 fgets (buf, sizeof (buf), fh);
112 lines--;
114 while (lines > ran);
115 fclose (fh);
116 buf[strlen (buf) - 1] = 0; /* remove the trailing '\n' */
117 return strdup (buf);
120 void
121 server_sendpart (server * serv, char *channel, char *reason)
123 if (!reason)
125 reason = random_line (prefs.partreason);
126 serv->p_part (serv, channel, reason);
127 free (reason);
128 } else
130 /* reason set by /quit, /close argument */
131 serv->p_part (serv, channel, reason);
135 void
136 server_sendquit (session * sess)
138 char *rea, *colrea;
140 if (!sess->quitreason)
142 colrea = strdup (prefs.quitreason);
143 check_special_chars (colrea, FALSE);
144 rea = random_line (colrea);
145 free (colrea);
146 sess->server->p_quit (sess->server, rea);
147 free (rea);
148 } else
150 /* reason set by /quit, /close argument */
151 sess->server->p_quit (sess->server, sess->quitreason);
155 void
156 process_data_init (char *buf, char *cmd, char *word[],
157 char *word_eol[], gboolean handle_quotes,
158 gboolean allow_escape_quotes)
160 int wordcount = 2;
161 int space = FALSE;
162 int quote = FALSE;
163 int j = 0;
164 int len;
166 word[0] = "\000\000";
167 word_eol[0] = "\000\000";
168 word[1] = (char *)buf;
169 word_eol[1] = (char *)cmd;
171 while (1)
173 switch (*cmd)
175 case 0:
176 buf[j] = 0;
177 for (j = wordcount; j < PDIWORDS; j++)
179 word[j] = "\000\000";
180 word_eol[j] = "\000\000";
182 return;
183 case '\042':
184 if (!handle_quotes)
185 goto def;
186 /* two quotes turn into 1 */
187 if (allow_escape_quotes && cmd[1] == '\042')
189 cmd++;
190 goto def;
192 if (quote)
194 quote = FALSE;
195 space = FALSE;
196 } else
197 quote = TRUE;
198 cmd++;
199 break;
200 case ' ':
201 if (!quote)
203 if (!space)
205 buf[j] = 0;
206 j++;
208 if (wordcount < PDIWORDS)
210 word[wordcount] = &buf[j];
211 word_eol[wordcount] = cmd + 1;
212 wordcount++;
215 space = TRUE;
217 cmd++;
218 break;
220 default:
221 def:
222 space = FALSE;
223 len = g_utf8_skip[((unsigned char *)cmd)[0]];
224 if (len == 1)
226 buf[j] = *cmd;
227 j++;
228 cmd++;
229 } else
231 /* skip past a multi-byte utf8 char */
232 memcpy (buf + j, cmd, len);
233 j += len;
234 cmd += len;
240 static int
241 cmd_addbutton (struct session *sess, char *tbuf, char *word[],
242 char *word_eol[])
244 if (*word[2] && *word_eol[3])
246 if (sess->type == SESS_DIALOG)
248 list_addentry (&dlgbutton_list, word_eol[3], word[2]);
249 fe_dlgbuttons_update (sess);
250 } else
252 list_addentry (&button_list, word_eol[3], word[2]);
253 fe_buttons_update (sess);
255 return TRUE;
257 return FALSE;
260 static int
261 cmd_allchannels (session *sess, char *tbuf, char *word[], char *word_eol[])
263 GSList *list = sess_list;
265 if (!*word_eol[2])
266 return FALSE;
268 while (list)
270 sess = list->data;
271 if (sess->type == SESS_CHANNEL && sess->channel[0] && sess->server->connected)
273 handle_command (sess, word_eol[2], FALSE);
275 list = list->next;
278 return TRUE;
281 static int
282 cmd_allchannelslocal (session *sess, char *tbuf, char *word[], char *word_eol[])
284 GSList *list = sess_list;
285 server *serv = sess->server;
287 if (!*word_eol[2])
288 return FALSE;
290 while (list)
292 sess = list->data;
293 if (sess->type == SESS_CHANNEL && sess->channel[0] &&
294 sess->server->connected && sess->server == serv)
296 handle_command (sess, word_eol[2], FALSE);
298 list = list->next;
301 return TRUE;
304 static int
305 cmd_allservers (struct session *sess, char *tbuf, char *word[],
306 char *word_eol[])
308 GSList *list;
309 server *serv;
311 if (!*word_eol[2])
312 return FALSE;
314 list = serv_list;
315 while (list)
317 serv = list->data;
318 if (serv->connected)
319 handle_command (serv->front_session, word_eol[2], FALSE);
320 list = list->next;
323 return TRUE;
326 static int
327 cmd_away (struct session *sess, char *tbuf, char *word[], char *word_eol[])
329 GSList *list;
330 char *reason = word_eol[2];
332 if (!(*reason))
334 if (sess->server->is_away)
336 if (sess->server->last_away_reason)
337 PrintTextf (sess, _("Already marked away: %s\n"), sess->server->last_away_reason);
338 return FALSE;
341 if (sess->server->reconnect_away)
342 reason = sess->server->last_away_reason;
343 else
344 /* must manage memory pointed to by random_line() */
345 reason = random_line (prefs.awayreason);
347 sess->server->p_set_away (sess->server, reason);
349 if (prefs.show_away_message)
351 snprintf (tbuf, TBUFSIZE, "me is away: %s", reason);
352 for (list = sess_list; list; list = list->next)
354 /* am I the right server and not a dialog box */
355 if (((struct session *) list->data)->server == sess->server
356 && ((struct session *) list->data)->type == SESS_CHANNEL
357 && ((struct session *) list->data)->channel[0])
359 handle_command ((session *) list->data, tbuf, TRUE);
364 if (sess->server->last_away_reason != reason)
366 if (sess->server->last_away_reason)
367 free (sess->server->last_away_reason);
369 if (reason == word_eol[2])
370 sess->server->last_away_reason = strdup (reason);
371 else
372 sess->server->last_away_reason = reason;
375 if (!sess->server->connected)
376 sess->server->reconnect_away = 1;
378 return TRUE;
381 static int
382 cmd_back (struct session *sess, char *tbuf, char *word[], char *word_eol[])
384 GSList *list;
385 unsigned int gone;
387 if (sess->server->is_away)
389 sess->server->p_set_back (sess->server);
391 if (prefs.show_away_message)
393 gone = time (NULL) - sess->server->away_time;
394 sprintf (tbuf, "me is back (gone %.2d:%.2d:%.2d)", gone / 3600,
395 (gone / 60) % 60, gone % 60);
396 for (list = sess_list; list; list = list->next)
398 /* am I the right server and not a dialog box */
399 if (((struct session *) list->data)->server == sess->server
400 && ((struct session *) list->data)->type == SESS_CHANNEL
401 && ((struct session *) list->data)->channel[0])
403 handle_command ((session *) list->data, tbuf, TRUE);
408 else
410 PrintText (sess, _("Already marked back.\n"));
413 if (sess->server->last_away_reason)
414 free (sess->server->last_away_reason);
415 sess->server->last_away_reason = NULL;
417 return TRUE;
420 static void
421 ban (session * sess, char *tbuf, char *mask, char *bantypestr, int deop)
423 int bantype;
424 struct User *user;
425 char *at, *dot, *lastdot;
426 char username[64], fullhost[128], domain[128], *mode, *p2;
427 server *serv = sess->server;
429 user = userlist_find (sess, mask);
430 if (user && user->hostname) /* it's a nickname, let's find a proper ban mask */
432 if (deop)
434 mode = "-o+b ";
435 p2 = user->nick;
436 } else
438 mode = "+b";
439 p2 = "";
442 mask = user->hostname;
444 at = strchr (mask, '@'); /* FIXME: utf8 */
445 if (!at)
446 return; /* can't happen? */
447 *at = 0;
449 if (mask[0] == '~' || mask[0] == '+' ||
450 mask[0] == '=' || mask[0] == '^' || mask[0] == '-')
452 /* the ident is prefixed with something, we replace that sign with an * */
453 safe_strcpy (username+1, mask+1, sizeof (username)-1);
454 username[0] = '*';
455 } else if (at - mask < USERNAMELEN)
457 /* we just add an * in the begining of the ident */
458 safe_strcpy (username+1, mask, sizeof (username)-1);
459 username[0] = '*';
460 } else
462 /* ident might be too long, we just ban what it gives and add an * in the end */
463 safe_strcpy (username, mask, sizeof (username));
465 *at = '@';
466 safe_strcpy (fullhost, at + 1, sizeof (fullhost));
468 dot = strchr (fullhost, '.');
469 if (dot)
471 safe_strcpy (domain, dot, sizeof (domain));
472 } else
474 safe_strcpy (domain, fullhost, sizeof (domain));
477 if (*bantypestr)
478 bantype = atoi (bantypestr);
479 else
480 bantype = prefs.bantype;
482 tbuf[0] = 0;
483 if (inet_addr (fullhost) != -1) /* "fullhost" is really a IP number */
485 lastdot = strrchr (fullhost, '.');
486 if (!lastdot)
487 return; /* can't happen? */
489 *lastdot = 0;
490 strcpy (domain, fullhost);
491 *lastdot = '.';
493 switch (bantype)
495 case 0:
496 snprintf (tbuf, TBUFSIZE, "%s%s *!*@%s.*", mode, p2, domain);
497 break;
499 case 1:
500 snprintf (tbuf, TBUFSIZE, "%s%s *!*@%s", mode, p2, fullhost);
501 break;
503 case 2:
504 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@%s.*", mode, p2, username, domain);
505 break;
507 case 3:
508 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@%s", mode, p2, username, fullhost);
509 break;
511 } else
513 switch (bantype)
515 case 0:
516 snprintf (tbuf, TBUFSIZE, "%s%s *!*@*%s", mode, p2, domain);
517 break;
519 case 1:
520 snprintf (tbuf, TBUFSIZE, "%s%s *!*@%s", mode, p2, fullhost);
521 break;
523 case 2:
524 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@*%s", mode, p2, username, domain);
525 break;
527 case 3:
528 snprintf (tbuf, TBUFSIZE, "%s%s *!%s@%s", mode, p2, username, fullhost);
529 break;
533 } else
535 snprintf (tbuf, TBUFSIZE, "+b %s", mask);
537 serv->p_mode (serv, sess->channel, tbuf);
540 static int
541 cmd_ban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
543 char *mask = word[2];
545 if (*mask)
547 ban (sess, tbuf, mask, word[3], 0);
548 } else
550 sess->server->p_mode (sess->server, sess->channel, "+b"); /* banlist */
553 return TRUE;
556 static int
557 cmd_unban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
559 /* Allow more than one mask in /unban -- tvk */
560 int i = 2;
562 while (1)
564 if (!*word[i])
566 if (i == 2)
567 return FALSE;
568 send_channel_modes (sess, tbuf, word, 2, i, '-', 'b', 0);
569 return TRUE;
571 i++;
575 static int
576 cmd_chanopt (struct session *sess, char *tbuf, char *word[], char *word_eol[])
578 /* chanopt.c */
579 return chanopt_command (sess, tbuf, word, word_eol);
582 static int
583 cmd_charset (struct session *sess, char *tbuf, char *word[], char *word_eol[])
585 server *serv = sess->server;
586 const char *locale = NULL;
587 int offset = 0;
589 if (strcmp (word[2], "-quiet") == 0)
590 offset++;
592 if (!word[2 + offset][0])
594 g_get_charset (&locale);
595 PrintTextf (sess, "Current charset: %s\n",
596 serv->encoding ? serv->encoding : locale);
597 return TRUE;
600 if (servlist_check_encoding (word[2 + offset]))
602 server_set_encoding (serv, word[2 + offset]);
603 if (offset < 1)
604 PrintTextf (sess, "Charset changed to: %s\n", word[2 + offset]);
605 } else
607 PrintTextf (sess, "\0034Unknown charset:\017 %s\n", word[2 + offset]);
610 return TRUE;
613 static int
614 cmd_clear (struct session *sess, char *tbuf, char *word[], char *word_eol[])
616 GSList *list = sess_list;
617 char *reason = word_eol[2];
619 if (strcasecmp (reason, "HISTORY") == 0)
621 history_free (&sess->history);
622 return TRUE;
625 if (strncasecmp (reason, "all", 3) == 0)
627 while (list)
629 sess = list->data;
630 if (!sess->nick_said)
631 fe_text_clear (list->data, 0);
632 list = list->next;
634 return TRUE;
637 if (reason[0] != '-' && !isdigit (reason[0]) && reason[0] != 0)
638 return FALSE;
640 fe_text_clear (sess, atoi (reason));
641 return TRUE;
644 static int
645 cmd_close (struct session *sess, char *tbuf, char *word[], char *word_eol[])
647 GSList *list;
649 if (strcmp (word[2], "-m") == 0)
651 list = sess_list;
652 while (list)
654 sess = list->data;
655 list = list->next;
656 if (sess->type == SESS_DIALOG)
657 fe_close_window (sess);
659 } else
661 if (*word_eol[2])
662 sess->quitreason = word_eol[2];
663 fe_close_window (sess);
666 return TRUE;
669 static int
670 cmd_ctcp (struct session *sess, char *tbuf, char *word[], char *word_eol[])
672 int mbl;
673 char *to = word[2];
674 if (*to)
676 char *msg = word_eol[3];
677 if (*msg)
679 unsigned char *cmd = (unsigned char *)msg;
681 /* make the first word upper case (as per RFC) */
682 while (1)
684 if (*cmd == ' ' || *cmd == 0)
685 break;
686 mbl = g_utf8_skip[*cmd];
687 if (mbl == 1)
688 *cmd = toupper (*cmd);
689 cmd += mbl;
692 sess->server->p_ctcp (sess->server, to, msg);
694 EMIT_SIGNAL (XP_TE_CTCPSEND, sess, to, msg, NULL, NULL, 0);
696 return TRUE;
699 return FALSE;
702 static int
703 cmd_country (struct session *sess, char *tbuf, char *word[], char *word_eol[])
705 char *code = word[2];
706 if (*code)
708 /* search? */
709 if (strcmp (code, "-s") == 0)
711 country_search (word[3], sess, (void *)PrintTextf);
712 return TRUE;
715 /* search, but forgot the -s */
716 if (strchr (code, '*'))
718 country_search (code, sess, (void *)PrintTextf);
719 return TRUE;
722 sprintf (tbuf, "%s = %s\n", code, country (code));
723 PrintText (sess, tbuf);
724 return TRUE;
726 return FALSE;
729 static int
730 cmd_cycle (struct session *sess, char *tbuf, char *word[], char *word_eol[])
732 char *key = sess->channelkey;
733 char *chan = word[2];
734 if (!*chan)
735 chan = sess->channel;
736 if (*chan && sess->type == SESS_CHANNEL)
738 sess->server->p_cycle (sess->server, chan, key);
739 return TRUE;
741 return FALSE;
744 static int
745 cmd_dcc (struct session *sess, char *tbuf, char *word[], char *word_eol[])
747 int goodtype;
748 struct DCC *dcc = 0;
749 char *type = word[2];
750 if (*type)
752 if (!strcasecmp (type, "HELP"))
753 return FALSE;
754 if (!strcasecmp (type, "CLOSE"))
756 if (*word[3] && *word[4])
758 goodtype = 0;
759 if (!strcasecmp (word[3], "SEND"))
761 dcc = find_dcc (word[4], word[5], TYPE_SEND);
762 dcc_abort (sess, dcc);
763 goodtype = TRUE;
765 if (!strcasecmp (word[3], "GET"))
767 dcc = find_dcc (word[4], word[5], TYPE_RECV);
768 dcc_abort (sess, dcc);
769 goodtype = TRUE;
771 if (!strcasecmp (word[3], "CHAT"))
773 dcc = find_dcc (word[4], "", TYPE_CHATRECV);
774 if (!dcc)
775 dcc = find_dcc (word[4], "", TYPE_CHATSEND);
776 dcc_abort (sess, dcc);
777 goodtype = TRUE;
780 if (!goodtype)
781 return FALSE;
783 if (!dcc)
784 EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
786 return TRUE;
789 return FALSE;
791 if ((!strcasecmp (type, "CHAT")) || (!strcasecmp (type, "PCHAT")))
793 char *nick = word[3];
794 int passive = (!strcasecmp(type, "PCHAT")) ? 1 : 0;
795 if (*nick)
796 dcc_chat (sess, nick, passive);
797 return TRUE;
799 if (!strcasecmp (type, "LIST"))
801 dcc_show_list (sess);
802 return TRUE;
804 if (!strcasecmp (type, "GET"))
806 char *nick = word[3];
807 char *file = word[4];
808 if (!*file)
810 if (*nick)
811 dcc_get_nick (sess, nick);
812 } else
814 dcc = find_dcc (nick, file, TYPE_RECV);
815 if (dcc)
816 dcc_get (dcc);
817 else
818 EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
820 return TRUE;
822 if ((!strcasecmp (type, "SEND")) || (!strcasecmp (type, "PSEND")))
824 int i = 3, maxcps;
825 char *nick, *file;
826 int passive = (!strcasecmp(type, "PSEND")) ? 1 : 0;
828 nick = word[i];
829 if (!*nick)
830 return FALSE;
832 maxcps = prefs.dcc_max_send_cps;
833 if (!strncasecmp(nick, "-maxcps=", 8))
835 maxcps = atoi(nick + 8);
836 i++;
837 nick = word[i];
838 if (!*nick)
839 return FALSE;
842 i++;
844 file = word[i];
845 if (!*file)
847 fe_dcc_send_filereq (sess, nick, maxcps, passive);
848 return TRUE;
853 dcc_send (sess, nick, file, maxcps, passive);
854 i++;
855 file = word[i];
857 while (*file);
859 return TRUE;
862 return FALSE;
865 dcc_show_list (sess);
866 return TRUE;
869 static int
870 cmd_debug (struct session *sess, char *tbuf, char *word[], char *word_eol[])
872 struct session *s;
873 struct server *v;
874 GSList *list = sess_list;
876 PrintText (sess, "Session T Channel WaitChan WillChan Server\n");
877 while (list)
879 s = (struct session *) list->data;
880 sprintf (tbuf, "%p %1x %-10.10s %-10.10s %-10.10s %p\n",
881 s, s->type, s->channel, s->waitchannel,
882 s->willjoinchannel, s->server);
883 PrintText (sess, tbuf);
884 list = list->next;
887 list = serv_list;
888 PrintText (sess, "Server Sock Name\n");
889 while (list)
891 v = (struct server *) list->data;
892 sprintf (tbuf, "%p %-5d %s\n",
893 v, v->sok, v->servername);
894 PrintText (sess, tbuf);
895 list = list->next;
898 sprintf (tbuf,
899 "\nfront_session: %p\n"
900 "current_tab: %p\n\n",
901 sess->server->front_session, current_tab);
902 PrintText (sess, tbuf);
903 #ifdef USE_DEBUG
904 sprintf (tbuf, "current mem: %d\n\n", current_mem_usage);
905 PrintText (sess, tbuf);
906 #endif /* !MEMORY_DEBUG */
908 return TRUE;
911 static int
912 cmd_delbutton (struct session *sess, char *tbuf, char *word[],
913 char *word_eol[])
915 if (*word[2])
917 if (sess->type == SESS_DIALOG)
919 if (list_delentry (&dlgbutton_list, word[2]))
920 fe_dlgbuttons_update (sess);
921 } else
923 if (list_delentry (&button_list, word[2]))
924 fe_buttons_update (sess);
926 return TRUE;
928 return FALSE;
931 static int
932 cmd_dehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
934 int i = 2;
936 while (1)
938 if (!*word[i])
940 if (i == 2)
941 return FALSE;
942 send_channel_modes (sess, tbuf, word, 2, i, '-', 'h', 0);
943 return TRUE;
945 i++;
949 static int
950 cmd_deop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
952 int i = 2;
954 while (1)
956 if (!*word[i])
958 if (i == 2)
959 return FALSE;
960 send_channel_modes (sess, tbuf, word, 2, i, '-', 'o', 0);
961 return TRUE;
963 i++;
967 typedef struct
969 char **nicks;
970 int i;
971 session *sess;
972 char *reason;
973 char *tbuf;
974 } multidata;
976 static int
977 mdehop_cb (struct User *user, multidata *data)
979 if (user->hop && !user->me)
981 data->nicks[data->i] = user->nick;
982 data->i++;
984 return TRUE;
987 static int
988 cmd_mdehop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
990 char **nicks = malloc (sizeof (char *) * sess->hops);
991 multidata data;
993 data.nicks = nicks;
994 data.i = 0;
995 tree_foreach (sess->usertree, (tree_traverse_func *)mdehop_cb, &data);
996 send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'h', 0);
997 free (nicks);
999 return TRUE;
1002 static int
1003 mdeop_cb (struct User *user, multidata *data)
1005 if (user->op && !user->me)
1007 data->nicks[data->i] = user->nick;
1008 data->i++;
1010 return TRUE;
1013 static int
1014 cmd_mdeop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1016 char **nicks = malloc (sizeof (char *) * sess->ops);
1017 multidata data;
1019 data.nicks = nicks;
1020 data.i = 0;
1021 tree_foreach (sess->usertree, (tree_traverse_func *)mdeop_cb, &data);
1022 send_channel_modes (sess, tbuf, nicks, 0, data.i, '-', 'o', 0);
1023 free (nicks);
1025 return TRUE;
1028 GSList *menu_list = NULL;
1030 static void
1031 menu_free (menu_entry *me)
1033 free (me->path);
1034 if (me->label)
1035 free (me->label);
1036 if (me->cmd)
1037 free (me->cmd);
1038 if (me->ucmd)
1039 free (me->ucmd);
1040 if (me->group)
1041 free (me->group);
1042 if (me->icon)
1043 free (me->icon);
1044 free (me);
1047 /* strings equal? but ignore underscores */
1050 menu_streq (const char *s1, const char *s2, int def)
1052 /* for separators */
1053 if (s1 == NULL && s2 == NULL)
1054 return 0;
1055 if (s1 == NULL || s2 == NULL)
1056 return 1;
1057 while (*s1)
1059 if (*s1 == '_')
1060 s1++;
1061 if (*s2 == '_')
1062 s2++;
1063 if (*s1 != *s2)
1064 return 1;
1065 s1++;
1066 s2++;
1068 if (!*s2)
1069 return 0;
1070 return def;
1073 static menu_entry *
1074 menu_entry_find (char *path, char *label)
1076 GSList *list;
1077 menu_entry *me;
1079 list = menu_list;
1080 while (list)
1082 me = list->data;
1083 if (!strcmp (path, me->path))
1085 if (me->label && label && !strcmp (label, me->label))
1086 return me;
1088 list = list->next;
1090 return NULL;
1093 static void
1094 menu_del_children (char *path, char *label)
1096 GSList *list, *next;
1097 menu_entry *me;
1098 char buf[512];
1100 if (!label)
1101 label = "";
1102 if (path[0])
1103 snprintf (buf, sizeof (buf), "%s/%s", path, label);
1104 else
1105 snprintf (buf, sizeof (buf), "%s", label);
1107 list = menu_list;
1108 while (list)
1110 me = list->data;
1111 next = list->next;
1112 if (!menu_streq (buf, me->path, 0))
1114 menu_list = g_slist_remove (menu_list, me);
1115 menu_free (me);
1117 list = next;
1121 static int
1122 menu_del (char *path, char *label)
1124 GSList *list;
1125 menu_entry *me;
1127 list = menu_list;
1128 while (list)
1130 me = list->data;
1131 if (!menu_streq (me->label, label, 1) && !menu_streq (me->path, path, 1))
1133 menu_list = g_slist_remove (menu_list, me);
1134 fe_menu_del (me);
1135 menu_free (me);
1136 /* delete this item's children, if any */
1137 menu_del_children (path, label);
1138 return 1;
1140 list = list->next;
1143 return 0;
1146 static char
1147 menu_is_mainmenu_root (char *path, gint16 *offset)
1149 static const char *menus[] = {"\x4$TAB","\x5$TRAY","\x4$URL","\x5$NICK","\x5$CHAN"};
1150 int i;
1152 for (i = 0; i < 5; i++)
1154 if (!strncmp (path, menus[i] + 1, menus[i][0]))
1156 *offset = menus[i][0] + 1; /* number of bytes to offset the root */
1157 return 0; /* is not main menu */
1161 *offset = 0;
1162 return 1; /* is main menu */
1165 static void
1166 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)
1168 menu_entry *me;
1170 /* already exists? */
1171 me = menu_entry_find (path, label);
1172 if (me)
1174 /* update only */
1175 me->state = state;
1176 me->enable = enable;
1177 fe_menu_update (me);
1178 return;
1181 me = malloc (sizeof (menu_entry));
1182 me->pos = pos;
1183 me->modifier = mod;
1184 me->is_main = menu_is_mainmenu_root (path, &me->root_offset);
1185 me->state = state;
1186 me->markup = markup;
1187 me->enable = enable;
1188 me->key = key;
1189 me->path = strdup (path);
1190 me->label = NULL;
1191 me->cmd = NULL;
1192 me->ucmd = NULL;
1193 me->group = NULL;
1194 me->icon = NULL;
1196 if (label)
1197 me->label = strdup (label);
1198 if (cmd)
1199 me->cmd = strdup (cmd);
1200 if (ucmd)
1201 me->ucmd = strdup (ucmd);
1202 if (group)
1203 me->group = strdup (group);
1204 if (icon)
1205 me->icon = strdup (icon);
1207 menu_list = g_slist_append (menu_list, me);
1208 label = fe_menu_add (me);
1209 if (label)
1211 /* FE has given us a stripped label */
1212 free (me->label);
1213 me->label = strdup (label);
1214 g_free (label); /* this is from pango */
1218 static int
1219 cmd_menu (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1221 int idx = 2;
1222 int len;
1223 int pos = 0xffff;
1224 int state;
1225 int toggle = FALSE;
1226 int enable = TRUE;
1227 int markup = FALSE;
1228 int key = 0;
1229 int mod = 0;
1230 char *label;
1231 char *group = NULL;
1232 char *icon = NULL;
1234 if (!word[2][0] || !word[3][0])
1235 return FALSE;
1237 /* -eX enabled or not? */
1238 if (word[idx][0] == '-' && word[idx][1] == 'e')
1240 enable = atoi (word[idx] + 2);
1241 idx++;
1244 /* -i<ICONFILE> */
1245 if (word[idx][0] == '-' && word[idx][1] == 'i')
1247 icon = word[idx] + 2;
1248 idx++;
1251 /* -k<mod>,<key> key binding */
1252 if (word[idx][0] == '-' && word[idx][1] == 'k')
1254 char *comma = strchr (word[idx], ',');
1255 if (!comma)
1256 return FALSE;
1257 mod = atoi (word[idx] + 2);
1258 key = atoi (comma + 1);
1259 idx++;
1262 /* -m to specify PangoMarkup language */
1263 if (word[idx][0] == '-' && word[idx][1] == 'm')
1265 markup = TRUE;
1266 idx++;
1269 /* -pX to specify menu position */
1270 if (word[idx][0] == '-' && word[idx][1] == 'p')
1272 pos = atoi (word[idx] + 2);
1273 idx++;
1276 /* -rSTATE,GROUP to specify a radio item */
1277 if (word[idx][0] == '-' && word[idx][1] == 'r')
1279 state = atoi (word[idx] + 2);
1280 group = word[idx] + 4;
1281 idx++;
1284 /* -tX to specify toggle item with default state */
1285 if (word[idx][0] == '-' && word[idx][1] == 't')
1287 state = atoi (word[idx] + 2);
1288 idx++;
1289 toggle = TRUE;
1292 if (word[idx+1][0] == 0)
1293 return FALSE;
1295 /* the path */
1296 path_part (word[idx+1], tbuf, 512);
1297 len = strlen (tbuf);
1298 if (len)
1299 tbuf[len - 1] = 0;
1301 /* the name of the item */
1302 label = file_part (word[idx + 1]);
1303 if (label[0] == '-' && label[1] == 0)
1304 label = NULL; /* separator */
1306 if (markup)
1308 char *p; /* to force pango closing tags through */
1309 for (p = label; *p; p++)
1310 if (*p == 3)
1311 *p = '/';
1314 if (!strcasecmp (word[idx], "ADD"))
1316 if (toggle)
1318 menu_add (tbuf, label, word[idx + 2], word[idx + 3], pos, state, markup, enable, mod, key, NULL, NULL);
1319 } else
1321 if (word[idx + 2][0])
1322 menu_add (tbuf, label, word[idx + 2], NULL, pos, state, markup, enable, mod, key, group, icon);
1323 else
1324 menu_add (tbuf, label, NULL, NULL, pos, state, markup, enable, mod, key, group, icon);
1326 return TRUE;
1329 if (!strcasecmp (word[idx], "DEL"))
1331 menu_del (tbuf, label);
1332 return TRUE;
1335 return FALSE;
1338 static int
1339 mkick_cb (struct User *user, multidata *data)
1341 if (!user->op && !user->me)
1342 data->sess->server->p_kick (data->sess->server, data->sess->channel, user->nick, data->reason);
1343 return TRUE;
1346 static int
1347 mkickops_cb (struct User *user, multidata *data)
1349 if (user->op && !user->me)
1350 data->sess->server->p_kick (data->sess->server, data->sess->channel, user->nick, data->reason);
1351 return TRUE;
1354 static int
1355 cmd_mkick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1357 multidata data;
1359 data.sess = sess;
1360 data.reason = word_eol[2];
1361 tree_foreach (sess->usertree, (tree_traverse_func *)mkickops_cb, &data);
1362 tree_foreach (sess->usertree, (tree_traverse_func *)mkick_cb, &data);
1364 return TRUE;
1367 static int
1368 cmd_devoice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1370 int i = 2;
1372 while (1)
1374 if (!*word[i])
1376 if (i == 2)
1377 return FALSE;
1378 send_channel_modes (sess, tbuf, word, 2, i, '-', 'v', 0);
1379 return TRUE;
1381 i++;
1385 static int
1386 cmd_discon (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1388 sess->server->disconnect (sess, TRUE, -1);
1389 return TRUE;
1392 static int
1393 cmd_dns (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1395 char *nick = word[2];
1396 struct User *user;
1398 if (*nick)
1400 if (strchr (nick, '.') == NULL)
1402 user = userlist_find (sess, nick);
1403 if (user && user->hostname)
1405 do_dns (sess, user->nick, user->hostname);
1406 } else
1408 sess->server->p_get_ip (sess->server, nick);
1409 sess->server->doing_dns = TRUE;
1411 } else
1413 snprintf (tbuf, TBUFSIZE, "exec -d %s %s", prefs.dnsprogram, nick);
1414 handle_command (sess, tbuf, FALSE);
1416 return TRUE;
1418 return FALSE;
1421 static int
1422 cmd_echo (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1424 PrintText (sess, word_eol[2]);
1425 return TRUE;
1428 static void
1429 exec_check_process (struct session *sess)
1431 int val;
1433 if (sess->running_exec == NULL)
1434 return;
1435 val = waitpid (sess->running_exec->childpid, NULL, WNOHANG);
1436 if (val == -1 || val > 0)
1438 close (sess->running_exec->myfd);
1439 fe_input_remove (sess->running_exec->iotag);
1440 free (sess->running_exec);
1441 sess->running_exec = NULL;
1445 static int
1446 cmd_execs (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1448 int r;
1450 exec_check_process (sess);
1451 if (sess->running_exec == NULL)
1453 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1454 return FALSE;
1456 r = kill (sess->running_exec->childpid, SIGSTOP);
1457 if (r == -1)
1458 PrintText (sess, "Error in kill(2)\n");
1460 return TRUE;
1463 static int
1464 cmd_execc (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1466 int r;
1468 exec_check_process (sess);
1469 if (sess->running_exec == NULL)
1471 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1472 return FALSE;
1474 r = kill (sess->running_exec->childpid, SIGCONT);
1475 if (r == -1)
1476 PrintText (sess, "Error in kill(2)\n");
1478 return TRUE;
1481 static int
1482 cmd_execk (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1484 int r;
1486 exec_check_process (sess);
1487 if (sess->running_exec == NULL)
1489 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1490 return FALSE;
1492 if (strcmp (word[2], "-9") == 0)
1493 r = kill (sess->running_exec->childpid, SIGKILL);
1494 else
1495 r = kill (sess->running_exec->childpid, SIGTERM);
1496 if (r == -1)
1497 PrintText (sess, "Error in kill(2)\n");
1499 return TRUE;
1502 /* OS/2 Can't have the /EXECW command because it uses pipe(2) not socketpair
1503 and thus it is simplex --AGL */
1504 static int
1505 cmd_execw (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1507 int len;
1508 char *temp;
1509 exec_check_process (sess);
1510 if (sess->running_exec == NULL)
1512 EMIT_SIGNAL (XP_TE_NOCHILD, sess, NULL, NULL, NULL, NULL, 0);
1513 return FALSE;
1515 len = strlen(word_eol[2]);
1516 temp = malloc(len + 2);
1517 sprintf(temp, "%s\n", word_eol[2]);
1518 PrintText(sess, temp);
1519 write(sess->running_exec->myfd, temp, len + 1);
1520 free(temp);
1522 return TRUE;
1525 /* convert ANSI escape color codes to mIRC codes */
1527 static short escconv[] =
1528 /* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 */
1529 { 1,4,3,5,2,10,6,1, 1,7,9,8,12,11,13,1 };
1531 static void
1532 exec_handle_colors (char *buf, int len)
1534 char numb[16];
1535 char *nbuf;
1536 int i = 0, j = 0, k = 0, firstn = 0, col, colf = 0, colb = 0;
1537 int esc = FALSE, backc = FALSE, bold = FALSE;
1539 /* any escape codes in this text? */
1540 if (strchr (buf, 27) == 0)
1541 return;
1543 nbuf = malloc (len + 1);
1545 while (i < len)
1547 switch (buf[i])
1549 case '\r':
1550 break;
1551 case 27:
1552 esc = TRUE;
1553 break;
1554 case ';':
1555 if (!esc)
1556 goto norm;
1557 backc = TRUE;
1558 numb[k] = 0;
1559 firstn = atoi (numb);
1560 k = 0;
1561 break;
1562 case '[':
1563 if (!esc)
1564 goto norm;
1565 break;
1566 default:
1567 if (esc)
1569 if (buf[i] >= 'A' && buf[i] <= 'z')
1571 if (buf[i] == 'm')
1573 /* ^[[0m */
1574 if (k == 0 || (numb[0] == '0' && k == 1))
1576 nbuf[j] = '\017';
1577 j++;
1578 bold = FALSE;
1579 goto cont;
1582 numb[k] = 0;
1583 col = atoi (numb);
1584 backc = FALSE;
1586 if (firstn == 1)
1587 bold = TRUE;
1589 if (firstn >= 30 && firstn <= 37)
1590 colf = firstn - 30;
1592 if (col >= 40)
1594 colb = col - 40;
1595 backc = TRUE;
1598 if (col >= 30 && col <= 37)
1599 colf = col - 30;
1601 if (bold)
1602 colf += 8;
1604 if (backc)
1606 colb = escconv[colb % 14];
1607 colf = escconv[colf % 14];
1608 j += sprintf (&nbuf[j], "\003%d,%02d", colf, colb);
1609 } else
1611 colf = escconv[colf % 14];
1612 j += sprintf (&nbuf[j], "\003%02d", colf);
1615 cont: esc = FALSE;
1616 backc = FALSE;
1617 k = 0;
1618 } else
1620 if (isdigit ((unsigned char) buf[i]) && k < (sizeof (numb) - 1))
1622 numb[k] = buf[i];
1623 k++;
1626 } else
1628 norm: nbuf[j] = buf[i];
1629 j++;
1632 i++;
1635 nbuf[j] = 0;
1636 memcpy (buf, nbuf, j + 1);
1637 free (nbuf);
1640 static gboolean
1641 exec_data (GIOChannel *source, GIOCondition condition, struct nbexec *s)
1643 char *buf, *readpos, *rest;
1644 int rd, len;
1645 int sok = s->myfd;
1647 len = s->buffill;
1648 if (len) {
1649 /* append new data to buffered incomplete line */
1650 buf = malloc(len + 2050);
1651 memcpy(buf, s->linebuf, len);
1652 readpos = buf + len;
1653 free(s->linebuf);
1654 s->linebuf = NULL;
1656 else
1657 readpos = buf = malloc(2050);
1659 rd = read (sok, readpos, 2048);
1660 if (rd < 1)
1662 /* The process has died */
1663 kill(s->childpid, SIGKILL);
1664 if (len) {
1665 buf[len] = '\0';
1666 exec_handle_colors(buf, len);
1667 if (s->tochannel)
1669 /* must turn off auto-completion temporarily */
1670 unsigned int old = prefs.nickcompletion;
1671 prefs.nickcompletion = 0;
1672 handle_multiline (s->sess, buf, FALSE, TRUE);
1673 prefs.nickcompletion = old;
1675 else
1676 PrintText (s->sess, buf);
1678 free(buf);
1679 waitpid (s->childpid, NULL, 0);
1680 s->sess->running_exec = NULL;
1681 fe_input_remove (s->iotag);
1682 close (sok);
1683 free (s);
1684 return TRUE;
1686 len += rd;
1687 buf[len] = '\0';
1689 rest = memrchr(buf, '\n', len);
1690 if (rest)
1691 rest++;
1692 else
1693 rest = buf;
1694 if (*rest) {
1695 s->buffill = len - (rest - buf); /* = strlen(rest) */
1696 s->linebuf = malloc(s->buffill + 1);
1697 memcpy(s->linebuf, rest, s->buffill);
1698 *rest = '\0';
1699 len -= s->buffill; /* possibly 0 */
1701 else
1702 s->buffill = 0;
1704 if (len) {
1705 exec_handle_colors (buf, len);
1706 if (s->tochannel)
1707 handle_multiline (s->sess, buf, FALSE, TRUE);
1708 else
1709 PrintText (s->sess, buf);
1712 free(buf);
1713 return TRUE;
1716 static int
1717 cmd_exec (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1719 int tochannel = FALSE;
1720 char *cmd = word_eol[2];
1721 int fds[2], pid = 0;
1722 struct nbexec *s;
1723 int shell = TRUE;
1724 int fd;
1726 if (*cmd)
1728 exec_check_process (sess);
1729 if (sess->running_exec != NULL)
1731 EMIT_SIGNAL (XP_TE_ALREADYPROCESS, sess, NULL, NULL, NULL, NULL, 0);
1732 return TRUE;
1735 if (!strcmp (word[2], "-d"))
1737 if (!*word[3])
1738 return FALSE;
1739 cmd = word_eol[3];
1740 shell = FALSE;
1742 else if (!strcmp (word[2], "-o"))
1744 if (!*word[3])
1745 return FALSE;
1746 cmd = word_eol[3];
1747 tochannel = TRUE;
1750 if (shell)
1752 if (access ("/bin/sh", X_OK) != 0)
1754 fe_message (_("I need /bin/sh to run!\n"), FE_MSG_ERROR);
1755 return TRUE;
1759 if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) == -1)
1761 PrintText (sess, "socketpair(2) failed\n");
1762 return FALSE;
1764 s = (struct nbexec *) malloc (sizeof (struct nbexec));
1765 memset(s, 0, sizeof(*s));
1766 s->myfd = fds[0];
1767 s->tochannel = tochannel;
1768 s->sess = sess;
1770 pid = fork ();
1771 if (pid == 0)
1773 /* This is the child's context */
1774 close (0);
1775 close (1);
1776 close (2);
1777 /* Close parent's end of pipe */
1778 close(s->myfd);
1779 /* Copy the child end of the pipe to stdout and stderr */
1780 dup2 (fds[1], 1);
1781 dup2 (fds[1], 2);
1782 /* Also copy it to stdin so we can write to it */
1783 dup2 (fds[1], 0);
1784 /* Now close all open file descriptors except stdin, stdout and stderr */
1785 for (fd = 3; fd < 1024; fd++) close(fd);
1786 /* Now we call /bin/sh to run our cmd ; made it more friendly -DC1 */
1787 if (shell)
1789 execl ("/bin/sh", "sh", "-c", cmd, NULL);
1790 } else
1792 char **argv;
1793 int argc;
1795 my_poptParseArgvString (cmd, &argc, &argv);
1796 execvp (argv[0], argv);
1798 /* not reached unless error */
1799 /*printf("exec error\n");*/
1800 fflush (stdout);
1801 fflush (stdin);
1802 _exit (0);
1804 if (pid == -1)
1806 /* Parent context, fork() failed */
1808 PrintText (sess, "Error in fork(2)\n");
1809 close(fds[0]);
1810 close(fds[1]);
1811 } else
1813 /* Parent path */
1814 close(fds[1]);
1815 s->childpid = pid;
1816 s->iotag = fe_input_add (s->myfd, FIA_READ|FIA_EX, exec_data, s);
1817 sess->running_exec = s;
1818 return TRUE;
1821 return FALSE;
1824 static int
1825 cmd_flushq (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1827 sprintf (tbuf, "Flushing server send queue, %d bytes.\n", sess->server->sendq_len);
1828 PrintText (sess, tbuf);
1829 sess->server->flush_queue (sess->server);
1830 return TRUE;
1833 static int
1834 cmd_quit (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1836 if (*word_eol[2])
1837 sess->quitreason = word_eol[2];
1838 sess->server->disconnect (sess, TRUE, -1);
1839 sess->quitreason = NULL;
1840 return 2;
1843 static int
1844 cmd_gate (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1846 char *server_name = word[2];
1847 server *serv = sess->server;
1848 if (*server_name)
1850 char *port = word[3];
1851 #ifdef USE_OPENSSL
1852 serv->use_ssl = FALSE;
1853 #endif
1854 server_fill_her_up (serv);
1855 if (*port)
1856 serv->connect (serv, server_name, atoi (port), TRUE);
1857 else
1858 serv->connect (serv, server_name, 23, TRUE);
1859 return TRUE;
1861 return FALSE;
1864 typedef struct
1866 char *cmd;
1867 session *sess;
1868 } getvalinfo;
1870 static void
1871 get_int_cb (int cancel, int val, getvalinfo *info)
1873 char buf[512];
1875 if (!cancel)
1877 snprintf (buf, sizeof (buf), "%s %d", info->cmd, val);
1878 if (is_session (info->sess))
1879 handle_command (info->sess, buf, FALSE);
1882 free (info->cmd);
1883 free (info);
1886 static int
1887 cmd_getint (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1889 getvalinfo *info;
1891 if (!word[4][0])
1892 return FALSE;
1894 info = malloc (sizeof (*info));
1895 info->cmd = strdup (word[3]);
1896 info->sess = sess;
1898 fe_get_int (word[4], atoi (word[2]), get_int_cb, info);
1900 return TRUE;
1903 static void
1904 get_file_cb (char *cmd, char *file)
1906 char buf[1024 + 128];
1908 /* execute the command once per file, then once more with
1909 no args */
1910 if (file)
1912 snprintf (buf, sizeof (buf), "%s %s", cmd, file);
1913 handle_command (current_sess, buf, FALSE);
1915 else
1917 handle_command (current_sess, cmd, FALSE);
1918 free (cmd);
1922 static int
1923 cmd_getfile (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1925 int idx = 2;
1926 int flags = 0;
1928 if (!word[3][0])
1929 return FALSE;
1931 if (!strcmp (word[2], "-folder"))
1933 flags |= FRF_CHOOSEFOLDER;
1934 idx++;
1937 if (!strcmp (word[idx], "-multi"))
1939 flags |= FRF_MULTIPLE;
1940 idx++;
1943 if (!strcmp (word[idx], "-save"))
1945 flags |= FRF_WRITE;
1946 idx++;
1949 fe_get_file (word[idx+1], word[idx+2], (void *)get_file_cb, strdup (word[idx]), flags);
1951 return TRUE;
1954 static void
1955 get_str_cb (int cancel, char *val, getvalinfo *info)
1957 char buf[512];
1959 if (!cancel)
1961 snprintf (buf, sizeof (buf), "%s %s", info->cmd, val);
1962 if (is_session (info->sess))
1963 handle_command (info->sess, buf, FALSE);
1966 free (info->cmd);
1967 free (info);
1970 static int
1971 cmd_getstr (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1973 getvalinfo *info;
1975 if (!word[4][0])
1976 return FALSE;
1978 info = malloc (sizeof (*info));
1979 info->cmd = strdup (word[3]);
1980 info->sess = sess;
1982 fe_get_str (word[4], word[2], get_str_cb, info);
1984 return TRUE;
1987 static int
1988 cmd_ghost (struct session *sess, char *tbuf, char *word[], char *word_eol[])
1990 if (!word[2][0])
1991 return FALSE;
1993 sess->server->p_ns_ghost (sess->server, word[2], word[3]);
1994 return TRUE;
1997 static int
1998 cmd_gui (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2000 switch (str_ihash (word[2]))
2002 case 0x058b836e: fe_ctrl_gui (sess, 8, 0); break; /* APPLY */
2003 case 0xac1eee45: fe_ctrl_gui (sess, 7, 2); break; /* ATTACH */
2004 case 0x05a72f63: fe_ctrl_gui (sess, 4, atoi (word[3])); break; /* COLOR */
2005 case 0xb06a1793: fe_ctrl_gui (sess, 7, 1); break; /* DETACH */
2006 case 0x05cfeff0: fe_ctrl_gui (sess, 3, 0); break; /* FLASH */
2007 case 0x05d154d8: fe_ctrl_gui (sess, 2, 0); break; /* FOCUS */
2008 case 0x0030dd42: fe_ctrl_gui (sess, 0, 0); break; /* HIDE */
2009 case 0x61addbe3: fe_ctrl_gui (sess, 5, 0); break; /* ICONIFY */
2010 case 0xc0851aaa: fe_message (word[3], FE_MSG_INFO|FE_MSG_MARKUP); break; /* MSGBOX */
2011 case 0x0035dafd: fe_ctrl_gui (sess, 1, 0); break; /* SHOW */
2012 case 0x0033155f: /* MENU */
2013 if (!strcasecmp (word[3], "TOGGLE"))
2014 fe_ctrl_gui (sess, 6, 0);
2015 else
2016 return FALSE;
2017 break;
2018 default:
2019 return FALSE;
2022 return TRUE;
2025 typedef struct
2027 int longfmt;
2028 int i, t;
2029 char *buf;
2030 } help_list;
2032 static void
2033 show_help_line (session *sess, help_list *hl, char *name, char *usage)
2035 int j, len, max;
2036 char *p;
2038 if (name[0] == '.') /* hidden command? */
2039 return;
2041 if (hl->longfmt) /* long format for /HELP -l */
2043 if (!usage || usage[0] == 0)
2044 PrintTextf (sess, " \0034%s\003 :\n", name);
2045 else
2046 PrintTextf (sess, " \0034%s\003 : %s\n", name, _(usage));
2047 return;
2050 /* append the name into buffer, but convert to uppercase */
2051 len = strlen (hl->buf);
2052 p = name;
2053 while (*p)
2055 hl->buf[len] = toupper ((unsigned char) *p);
2056 len++;
2057 p++;
2059 hl->buf[len] = 0;
2061 hl->t++;
2062 if (hl->t == 5)
2064 hl->t = 0;
2065 strcat (hl->buf, "\n");
2066 PrintText (sess, hl->buf);
2067 hl->buf[0] = ' ';
2068 hl->buf[1] = ' ';
2069 hl->buf[2] = 0;
2070 } else
2072 /* append some spaces after the command name */
2073 max = strlen (name);
2074 if (max < 10)
2076 max = 10 - max;
2077 for (j = 0; j < max; j++)
2079 hl->buf[len] = ' ';
2080 len++;
2081 hl->buf[len] = 0;
2087 static int
2088 cmd_help (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2090 int i = 0, longfmt = 0;
2091 char *helpcmd = "";
2092 GSList *list;
2094 if (tbuf)
2095 helpcmd = word[2];
2096 if (*helpcmd && strcmp (helpcmd, "-l") == 0)
2097 longfmt = 1;
2099 if (*helpcmd && !longfmt)
2101 help (sess, tbuf, helpcmd, FALSE);
2102 } else
2104 struct popup *pop;
2105 char *buf = malloc (4096);
2106 help_list hl;
2108 hl.longfmt = longfmt;
2109 hl.buf = buf;
2111 PrintTextf (sess, "\n%s\n\n", _("Commands Available:"));
2112 buf[0] = ' ';
2113 buf[1] = ' ';
2114 buf[2] = 0;
2115 hl.t = 0;
2116 hl.i = 0;
2117 while (xc_cmds[i].name)
2119 show_help_line (sess, &hl, xc_cmds[i].name, xc_cmds[i].help);
2120 i++;
2122 strcat (buf, "\n");
2123 PrintText (sess, buf);
2125 PrintTextf (sess, "\n%s\n\n", _("User defined commands:"));
2126 buf[0] = ' ';
2127 buf[1] = ' ';
2128 buf[2] = 0;
2129 hl.t = 0;
2130 hl.i = 0;
2131 list = command_list;
2132 while (list)
2134 pop = list->data;
2135 show_help_line (sess, &hl, pop->name, pop->cmd);
2136 list = list->next;
2138 strcat (buf, "\n");
2139 PrintText (sess, buf);
2141 PrintTextf (sess, "\n%s\n\n", _("Plugin defined commands:"));
2142 buf[0] = ' ';
2143 buf[1] = ' ';
2144 buf[2] = 0;
2145 hl.t = 0;
2146 hl.i = 0;
2147 plugin_command_foreach (sess, &hl, (void *)show_help_line);
2148 strcat (buf, "\n");
2149 PrintText (sess, buf);
2150 free (buf);
2152 PrintTextf (sess, "\n%s\n\n", _("Type /HELP <command> for more information, or /HELP -l"));
2154 return TRUE;
2157 static int
2158 cmd_id (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2160 if (word[2][0])
2162 sess->server->p_ns_identify (sess->server, word[2]);
2163 return TRUE;
2166 return FALSE;
2169 static int
2170 cmd_ignore (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2172 int i;
2173 int type = 0;
2174 int quiet = 0;
2175 char *mask;
2177 if (!*word[2])
2179 ignore_showlist (sess);
2180 return TRUE;
2182 if (!*word[3])
2183 return FALSE;
2185 i = 3;
2186 while (1)
2188 if (!*word[i])
2190 if (type == 0)
2191 return FALSE;
2193 mask = word[2];
2194 if (strchr (mask, '?') == NULL &&
2195 strchr (mask, '*') == NULL)
2197 mask = tbuf;
2198 snprintf (tbuf, TBUFSIZE, "%s!*@*", word[2]);
2201 i = ignore_add (mask, type);
2202 if (quiet)
2203 return TRUE;
2204 switch (i)
2206 case 1:
2207 EMIT_SIGNAL (XP_TE_IGNOREADD, sess, mask, NULL, NULL, NULL, 0);
2208 break;
2209 case 2: /* old ignore changed */
2210 EMIT_SIGNAL (XP_TE_IGNORECHANGE, sess, mask, NULL, NULL, NULL, 0);
2212 return TRUE;
2214 if (!strcasecmp (word[i], "UNIGNORE"))
2215 type |= IG_UNIG;
2216 else if (!strcasecmp (word[i], "ALL"))
2217 type |= IG_PRIV | IG_NOTI | IG_CHAN | IG_CTCP | IG_INVI | IG_DCC;
2218 else if (!strcasecmp (word[i], "PRIV"))
2219 type |= IG_PRIV;
2220 else if (!strcasecmp (word[i], "NOTI"))
2221 type |= IG_NOTI;
2222 else if (!strcasecmp (word[i], "CHAN"))
2223 type |= IG_CHAN;
2224 else if (!strcasecmp (word[i], "CTCP"))
2225 type |= IG_CTCP;
2226 else if (!strcasecmp (word[i], "INVI"))
2227 type |= IG_INVI;
2228 else if (!strcasecmp (word[i], "QUIET"))
2229 quiet = 1;
2230 else if (!strcasecmp (word[i], "NOSAVE"))
2231 type |= IG_NOSAVE;
2232 else if (!strcasecmp (word[i], "DCC"))
2233 type |= IG_DCC;
2234 else
2236 sprintf (tbuf, _("Unknown arg '%s' ignored."), word[i]);
2237 PrintText (sess, tbuf);
2239 i++;
2243 static int
2244 cmd_invite (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2246 if (!*word[2])
2247 return FALSE;
2248 if (*word[3])
2249 sess->server->p_invite (sess->server, word[3], word[2]);
2250 else
2251 sess->server->p_invite (sess->server, sess->channel, word[2]);
2252 return TRUE;
2255 static int
2256 cmd_join (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2258 char *chan = word[2];
2259 if (*chan)
2261 char *po, *pass = word[3];
2262 sess->server->p_join (sess->server, chan, pass);
2263 if (sess->channel[0] == 0 && sess->waitchannel[0])
2265 po = strchr (chan, ',');
2266 if (po)
2267 *po = 0;
2268 safe_strcpy (sess->waitchannel, chan, CHANLEN);
2270 return TRUE;
2272 return FALSE;
2275 static int
2276 cmd_kick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2278 char *nick = word[2];
2279 char *reason = word_eol[3];
2280 if (*nick)
2282 sess->server->p_kick (sess->server, sess->channel, nick, reason);
2283 return TRUE;
2285 return FALSE;
2288 static int
2289 cmd_kickban (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2291 char *nick = word[2];
2292 char *reason = word_eol[3];
2293 struct User *user;
2295 if (*nick)
2297 /* if the reason is a 1 digit number, treat it as a bantype */
2299 user = userlist_find (sess, nick);
2301 if (isdigit ((unsigned char) reason[0]) && reason[1] == 0)
2303 ban (sess, tbuf, nick, reason, (user && user->op));
2304 reason[0] = 0;
2305 } else
2306 ban (sess, tbuf, nick, "", (user && user->op));
2308 sess->server->p_kick (sess->server, sess->channel, nick, reason);
2310 return TRUE;
2312 return FALSE;
2315 static int
2316 cmd_killall (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2318 xchat_exit();
2319 return 2;
2322 static int
2323 cmd_lagcheck (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2325 lag_check ();
2326 return TRUE;
2329 static void
2330 lastlog (session *sess, char *search, gboolean regexp)
2332 session *lastlog_sess;
2334 if (!is_session (sess))
2335 return;
2337 lastlog_sess = find_dialog (sess->server, "(lastlog)");
2338 if (!lastlog_sess)
2339 lastlog_sess = new_ircwindow (sess->server, "(lastlog)", SESS_DIALOG, 0);
2341 lastlog_sess->lastlog_sess = sess;
2342 lastlog_sess->lastlog_regexp = regexp; /* remember the search type */
2344 fe_text_clear (lastlog_sess, 0);
2345 fe_lastlog (sess, lastlog_sess, search, regexp);
2348 static int
2349 cmd_lastlog (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2351 if (*word_eol[2])
2353 if (!strcmp (word[2], "-r"))
2354 lastlog (sess, word_eol[3], TRUE);
2355 else
2356 lastlog (sess, word_eol[2], FALSE);
2357 return TRUE;
2360 return FALSE;
2363 static int
2364 cmd_list (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2366 sess->server->p_list_channels (sess->server, word_eol[2], 1);
2368 return TRUE;
2371 gboolean
2372 load_perform_file (session *sess, char *file)
2374 char tbuf[1024 + 4];
2375 char *nl;
2376 FILE *fp;
2378 fp = xchat_fopen_file (file, "r", XOF_FULLPATH);
2379 if (!fp)
2380 return FALSE;
2382 tbuf[1024] = 0;
2383 while (fgets (tbuf, 1024, fp))
2385 nl = strchr (tbuf, '\n');
2386 if (nl == tbuf) /* skip empty commands */
2387 continue;
2388 if (nl)
2389 *nl = 0;
2390 if (tbuf[0] == prefs.cmdchar[0])
2391 handle_command (sess, tbuf + 1, TRUE);
2392 else
2393 handle_command (sess, tbuf, TRUE);
2395 fclose (fp);
2396 return TRUE;
2399 static int
2400 cmd_load (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2402 char *error, *arg, *file;
2403 int len;
2405 if (!word[2][0])
2406 return FALSE;
2408 if (strcmp (word[2], "-e") == 0)
2410 file = expand_homedir (word[3]);
2411 if (!load_perform_file (sess, file))
2413 PrintTextf (sess, _("Cannot access %s\n"), file);
2414 PrintText (sess, errorstring (errno));
2416 free (file);
2417 return TRUE;
2420 #ifdef USE_PLUGIN
2421 len = strlen (word[2]);
2422 if (len > 3 && strcasecmp (".so", word[2] + len - 3) == 0)
2424 arg = NULL;
2425 if (word_eol[3][0])
2426 arg = word_eol[3];
2428 file = expand_homedir (word[2]);
2429 error = plugin_load (sess, file, arg);
2430 free (file);
2432 if (error)
2433 PrintText (sess, error);
2435 return TRUE;
2437 #endif
2439 sprintf (tbuf, "Unknown file type %s. Maybe you need to install the Perl or Python plugin?\n", word[2]);
2440 PrintText (sess, tbuf);
2442 return FALSE;
2445 static int
2446 cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2448 char *act = word_eol[2];
2450 if (!(*act))
2451 return FALSE;
2453 if (sess->type == SESS_SERVER)
2455 notj_msg (sess);
2456 return TRUE;
2459 snprintf (tbuf, TBUFSIZE, "\001ACTION %s\001\r", act);
2460 /* first try through DCC CHAT */
2461 if (dcc_write_chat (sess->channel, tbuf))
2463 /* print it to screen */
2464 inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
2465 } else
2467 /* DCC CHAT failed, try through server */
2468 if (sess->server->connected)
2470 sess->server->p_action (sess->server, sess->channel, act);
2471 /* print it to screen */
2472 inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
2473 } else
2475 notc_msg (sess);
2479 return TRUE;
2482 static int
2483 cmd_mode (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2485 /* +channel channels are dying, let those servers whine about modes.
2486 * return info about current channel if available and no info is given */
2487 if ((*word[2] == '+') || (*word[2] == 0) || (!is_channel(sess->server, word[2]) &&
2488 !(rfc_casecmp(sess->server->nick, word[2]) == 0)))
2490 if(sess->channel[0] == 0)
2491 return FALSE;
2492 sess->server->p_mode (sess->server, sess->channel, word_eol[2]);
2494 else
2495 sess->server->p_mode (sess->server, word[2], word_eol[3]);
2496 return TRUE;
2499 static int
2500 mop_cb (struct User *user, multidata *data)
2502 if (!user->op)
2504 data->nicks[data->i] = user->nick;
2505 data->i++;
2507 return TRUE;
2510 static int
2511 cmd_mop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2513 char **nicks = malloc (sizeof (char *) * (sess->total - sess->ops));
2514 multidata data;
2516 data.nicks = nicks;
2517 data.i = 0;
2518 tree_foreach (sess->usertree, (tree_traverse_func *)mop_cb, &data);
2519 send_channel_modes (sess, tbuf, nicks, 0, data.i, '+', 'o', 0);
2521 free (nicks);
2523 return TRUE;
2526 static int
2527 cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2529 char *nick = word[2];
2530 char *msg = word_eol[3];
2531 struct session *newsess;
2533 if (*nick)
2535 if (*msg)
2537 if (strcmp (nick, ".") == 0)
2538 { /* /msg the last nick /msg'ed */
2539 if (sess->lastnick[0])
2540 nick = sess->lastnick;
2541 } else
2543 safe_strcpy (sess->lastnick, nick, NICKLEN); /* prime the last nick memory */
2546 if (*nick == '=')
2548 nick++;
2549 if (!dcc_write_chat (nick, msg))
2551 EMIT_SIGNAL (XP_TE_NODCC, sess, NULL, NULL, NULL, NULL, 0);
2552 return TRUE;
2554 } else
2556 if (!sess->server->connected)
2558 notc_msg (sess);
2559 return TRUE;
2561 sess->server->p_message (sess->server, nick, msg);
2563 newsess = find_dialog (sess->server, nick);
2564 if (!newsess)
2565 newsess = find_channel (sess->server, nick);
2566 if (newsess)
2567 inbound_chanmsg (newsess->server, NULL, newsess->channel,
2568 newsess->server->nick, msg, TRUE, FALSE);
2569 else
2571 /* mask out passwords */
2572 if (strcasecmp (nick, "nickserv") == 0 &&
2573 strncasecmp (msg, "identify ", 9) == 0)
2574 msg = "identify ****";
2575 EMIT_SIGNAL (XP_TE_MSGSEND, sess, nick, msg, NULL, NULL, 0);
2578 return TRUE;
2581 return FALSE;
2584 static int
2585 cmd_names (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2587 if (*word[2])
2588 sess->server->p_names (sess->server, word[2]);
2589 else
2590 sess->server->p_names (sess->server, sess->channel);
2591 return TRUE;
2594 static int
2595 cmd_nctcp (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2597 if (*word_eol[3])
2599 sess->server->p_nctcp (sess->server, word[2], word_eol[3]);
2600 return TRUE;
2602 return FALSE;
2605 static int
2606 cmd_newserver (struct session *sess, char *tbuf, char *word[],
2607 char *word_eol[])
2609 if (strcmp (word[2], "-noconnect") == 0)
2611 new_ircwindow (NULL, word[3], SESS_SERVER, 0);
2612 return TRUE;
2615 sess = new_ircwindow (NULL, NULL, SESS_SERVER, 0);
2616 cmd_server (sess, tbuf, word, word_eol);
2617 return TRUE;
2620 static int
2621 cmd_nick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2623 char *nick = word[2];
2624 if (*nick)
2626 if (sess->server->connected)
2627 sess->server->p_change_nick (sess->server, nick);
2628 else
2629 inbound_newnick (sess->server, sess->server->nick, nick, TRUE);
2630 return TRUE;
2632 return FALSE;
2635 static int
2636 cmd_notice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2638 if (*word[2] && *word_eol[3])
2640 sess->server->p_notice (sess->server, word[2], word_eol[3]);
2641 EMIT_SIGNAL (XP_TE_NOTICESEND, sess, word[2], word_eol[3], NULL, NULL, 0);
2642 return TRUE;
2644 return FALSE;
2647 static int
2648 cmd_notify (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2650 int i = 1;
2651 char *net = NULL;
2653 if (*word[2])
2655 if (strcmp (word[2], "-n") == 0) /* comma sep network list */
2657 net = word[3];
2658 i += 2;
2661 while (1)
2663 i++;
2664 if (!*word[i])
2665 break;
2666 if (notify_deluser (word[i]))
2668 EMIT_SIGNAL (XP_TE_DELNOTIFY, sess, word[i], NULL, NULL, NULL, 0);
2669 return TRUE;
2672 if (net && strcmp (net, "ASK") == 0)
2673 fe_notify_ask (word[i], NULL);
2674 else
2676 notify_adduser (word[i], net);
2677 EMIT_SIGNAL (XP_TE_ADDNOTIFY, sess, word[i], NULL, NULL, NULL, 0);
2680 } else
2681 notify_showlist (sess);
2682 return TRUE;
2685 static int
2686 cmd_op (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2688 int i = 2;
2690 while (1)
2692 if (!*word[i])
2694 if (i == 2)
2695 return FALSE;
2696 send_channel_modes (sess, tbuf, word, 2, i, '+', 'o', 0);
2697 return TRUE;
2699 i++;
2703 static int
2704 cmd_part (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2706 char *chan = word[2];
2707 char *reason = word_eol[3];
2708 if (!*chan)
2709 chan = sess->channel;
2710 if ((*chan) && is_channel (sess->server, chan))
2712 if (reason[0] == 0)
2713 reason = NULL;
2714 server_sendpart (sess->server, chan, reason);
2715 return TRUE;
2717 return FALSE;
2720 static int
2721 cmd_ping (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2723 char timestring[64];
2724 unsigned long tim;
2725 char *to = word[2];
2727 tim = make_ping_time ();
2729 snprintf (timestring, sizeof (timestring), "%lu", tim);
2730 sess->server->p_ping (sess->server, to, timestring);
2732 return TRUE;
2735 void
2736 open_query (server *serv, char *nick, gboolean focus_existing)
2738 session *sess;
2740 sess = find_dialog (serv, nick);
2741 if (!sess)
2742 new_ircwindow (serv, nick, SESS_DIALOG, 1);
2743 else if (focus_existing)
2744 fe_ctrl_gui (sess, 2, 0); /* bring-to-front */
2747 static int
2748 cmd_query (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2750 char *nick = word[2];
2751 gboolean focus = TRUE;
2753 if (strcmp (word[2], "-nofocus") == 0)
2755 nick = word[3];
2756 focus = FALSE;
2759 if (*nick && !is_channel (sess->server, nick))
2761 open_query (sess->server, nick, focus);
2762 return TRUE;
2764 return FALSE;
2767 static int
2768 cmd_quote (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2770 char *raw = word_eol[2];
2772 return sess->server->p_raw (sess->server, raw);
2775 static int
2776 cmd_reconnect (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2778 int tmp = prefs.recon_delay;
2779 GSList *list;
2780 server *serv = sess->server;
2782 prefs.recon_delay = 0;
2784 if (!strcasecmp (word[2], "ALL"))
2786 list = serv_list;
2787 while (list)
2789 serv = list->data;
2790 if (serv->connected)
2791 serv->auto_reconnect (serv, TRUE, -1);
2792 list = list->next;
2795 /* If it isn't "ALL" and there is something
2796 there it *should* be a server they are trying to connect to*/
2797 else if (*word[2])
2799 int offset = 0;
2800 #ifdef USE_OPENSSL
2801 int use_ssl = FALSE;
2803 if (strcmp (word[2], "-ssl") == 0)
2805 use_ssl = TRUE;
2806 offset++; /* args move up by 1 word */
2808 serv->use_ssl = use_ssl;
2809 serv->accept_invalid_cert = TRUE;
2810 #endif
2812 if (*word[4+offset])
2813 safe_strcpy (serv->password, word[4+offset], sizeof (serv->password));
2814 if (*word[3+offset])
2815 serv->port = atoi (word[3+offset]);
2816 safe_strcpy (serv->hostname, word[2+offset], sizeof (serv->hostname));
2817 serv->auto_reconnect (serv, TRUE, -1);
2819 else
2821 serv->auto_reconnect (serv, TRUE, -1);
2823 prefs.recon_delay = tmp;
2825 return TRUE;
2828 static int
2829 cmd_recv (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2831 if (*word_eol[2])
2833 sess->server->p_inline (sess->server, word_eol[2], strlen (word_eol[2]));
2834 return TRUE;
2837 return FALSE;
2840 static int
2841 cmd_say (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2843 char *speech = word_eol[2];
2844 if (*speech)
2846 handle_say (sess, speech, FALSE);
2847 return TRUE;
2849 return FALSE;
2852 static int
2853 cmd_send (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2855 guint32 addr;
2856 socklen_t len;
2857 struct sockaddr_in SAddr;
2859 if (!word[2][0])
2860 return FALSE;
2862 addr = dcc_get_my_address ();
2863 if (addr == 0)
2865 /* use the one from our connected server socket */
2866 memset (&SAddr, 0, sizeof (struct sockaddr_in));
2867 len = sizeof (SAddr);
2868 getsockname (sess->server->sok, (struct sockaddr *) &SAddr, &len);
2869 addr = SAddr.sin_addr.s_addr;
2871 addr = ntohl (addr);
2873 if ((addr & 0xffff0000) == 0xc0a80000 || /* 192.168.x.x */
2874 (addr & 0xff000000) == 0x0a000000) /* 10.x.x.x */
2875 /* we got a private net address, let's PSEND or it'll fail */
2876 snprintf (tbuf, 512, "DCC PSEND %s", word_eol[2]);
2877 else
2878 snprintf (tbuf, 512, "DCC SEND %s", word_eol[2]);
2880 handle_command (sess, tbuf, FALSE);
2882 return TRUE;
2885 static int
2886 cmd_setcursor (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2888 int delta = FALSE;
2890 if (*word[2])
2892 if (word[2][0] == '-' || word[2][0] == '+')
2893 delta = TRUE;
2894 fe_set_inputbox_cursor (sess, delta, atoi (word[2]));
2895 return TRUE;
2898 return FALSE;
2901 static int
2902 cmd_settab (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2904 if (*word_eol[2])
2906 strcpy (tbuf, sess->channel);
2907 safe_strcpy (sess->channel, word_eol[2], CHANLEN);
2908 fe_set_channel (sess);
2909 strcpy (sess->channel, tbuf);
2912 return TRUE;
2915 static int
2916 cmd_settext (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2918 fe_set_inputbox_contents (sess, word_eol[2]);
2919 return TRUE;
2922 static int
2923 cmd_splay (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2925 if (*word[2])
2927 sound_play (word[2], FALSE);
2928 return TRUE;
2931 return FALSE;
2934 static int
2935 parse_irc_url (char *url, char *server_name[], char *port[], char *channel[], int *use_ssl)
2937 char *co;
2938 #ifdef USE_OPENSSL
2939 if (strncasecmp ("ircs://", url, 7) == 0)
2941 *use_ssl = TRUE;
2942 *server_name = url + 7;
2943 goto urlserv;
2945 #endif
2947 if (strncasecmp ("irc://", url, 6) == 0)
2949 *server_name = url + 6;
2950 #ifdef USE_OPENSSL
2951 urlserv:
2952 #endif
2953 /* check for port */
2954 co = strchr (*server_name, ':');
2955 if (co)
2957 *port = co + 1;
2958 *co = 0;
2959 } else
2960 co = *server_name;
2961 /* check for channel - mirc style */
2962 co = strchr (co + 1, '/');
2963 if (co)
2965 *co = 0;
2966 co++;
2967 if (*co == '#')
2968 *channel = co+1;
2969 else
2970 *channel = co;
2973 return TRUE;
2975 return FALSE;
2978 static int
2979 cmd_server (struct session *sess, char *tbuf, char *word[], char *word_eol[])
2981 int offset = 0;
2982 char *server_name = NULL;
2983 char *port = NULL;
2984 char *pass = NULL;
2985 char *channel = NULL;
2986 int use_ssl = FALSE;
2987 int is_url = TRUE;
2988 server *serv = sess->server;
2990 #ifdef USE_OPENSSL
2991 /* BitchX uses -ssl, mIRC uses -e, let's support both */
2992 if (strcmp (word[2], "-ssl") == 0 || strcmp (word[2], "-e") == 0)
2994 use_ssl = TRUE;
2995 offset++; /* args move up by 1 word */
2997 #endif
2999 if (!parse_irc_url (word[2 + offset], &server_name, &port, &channel, &use_ssl))
3001 is_url = FALSE;
3002 server_name = word[2 + offset];
3004 if (port)
3005 pass = word[3 + offset];
3006 else
3008 port = word[3 + offset];
3009 pass = word[4 + offset];
3012 if (!(*server_name))
3013 return FALSE;
3015 sess->server->network = NULL;
3017 /* dont clear it for /servchan */
3018 if (strncasecmp (word_eol[1], "SERVCHAN ", 9))
3019 sess->willjoinchannel[0] = 0;
3021 if (channel)
3023 sess->willjoinchannel[0] = '#';
3024 safe_strcpy ((sess->willjoinchannel + 1), channel, (CHANLEN - 1));
3027 /* support +7000 style ports like mIRC */
3028 if (port[0] == '+')
3030 port++;
3031 #ifdef USE_OPENSSL
3032 use_ssl = TRUE;
3033 #endif
3036 if (*pass)
3038 safe_strcpy (serv->password, pass, sizeof (serv->password));
3039 serv->loginmethod = LOGIN_PASS;
3041 else
3043 serv->password[0] = 0;
3046 #ifdef USE_OPENSSL
3047 serv->use_ssl = use_ssl;
3048 serv->accept_invalid_cert = TRUE;
3049 #endif
3051 /* try to connect by Network name */
3052 if (servlist_connect_by_netname (sess, server_name, !is_url))
3053 return TRUE;
3055 if (*port)
3057 serv->connect (serv, server_name, atoi (port), FALSE);
3058 } else
3060 /* -1 for default port */
3061 serv->connect (serv, server_name, -1, FALSE);
3064 /* try to associate this connection with a listed network */
3065 if (!serv->network)
3066 /* search for this hostname in the entire server list */
3067 serv->network = servlist_net_find_from_server (server_name);
3068 /* may return NULL, but that's OK */
3070 return TRUE;
3073 static int
3074 cmd_servchan (struct session *sess, char *tbuf, char *word[],
3075 char *word_eol[])
3077 int offset = 0;
3079 #ifdef USE_OPENSSL
3080 if (strcmp (word[2], "-ssl") == 0)
3081 offset++;
3082 #endif
3084 if (*word[4 + offset])
3086 safe_strcpy (sess->willjoinchannel, word[4 + offset], CHANLEN);
3087 return cmd_server (sess, tbuf, word, word_eol);
3090 return FALSE;
3093 static int
3094 cmd_topic (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3096 if (word[2][0] && is_channel (sess->server, word[2]))
3097 sess->server->p_topic (sess->server, word[2], word_eol[3]);
3098 else
3099 sess->server->p_topic (sess->server, sess->channel, word_eol[2]);
3100 return TRUE;
3103 static int
3104 cmd_tray (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3106 if (strcmp (word[2], "-b") == 0)
3108 fe_tray_set_balloon (word[3], word[4][0] ? word[4] : NULL);
3109 return TRUE;
3112 if (strcmp (word[2], "-t") == 0)
3114 fe_tray_set_tooltip (word[3][0] ? word[3] : NULL);
3115 return TRUE;
3118 if (strcmp (word[2], "-i") == 0)
3120 fe_tray_set_icon (atoi (word[3]));
3121 return TRUE;
3124 if (strcmp (word[2], "-f") != 0)
3125 return FALSE;
3127 if (!word[3][0])
3129 fe_tray_set_file (NULL); /* default xchat icon */
3130 return TRUE;
3133 if (!word[4][0])
3135 fe_tray_set_file (word[3]); /* fixed custom icon */
3136 return TRUE;
3139 /* flash between 2 icons */
3140 fe_tray_set_flash (word[4], word[5][0] ? word[5] : NULL, atoi (word[3]));
3141 return TRUE;
3144 static int
3145 cmd_unignore (struct session *sess, char *tbuf, char *word[],
3146 char *word_eol[])
3148 char *mask = word[2];
3149 char *arg = word[3];
3150 if (*mask)
3152 if (ignore_del (mask, NULL))
3154 if (strcasecmp (arg, "QUIET"))
3155 EMIT_SIGNAL (XP_TE_IGNOREREMOVE, sess, mask, NULL, NULL, NULL, 0);
3157 return TRUE;
3159 return FALSE;
3162 static int
3163 cmd_unload (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3165 #ifdef USE_PLUGIN
3166 int len, by_file = FALSE;
3168 len = strlen (word[2]);
3169 if (len > 3 && strcasecmp (word[2] + len - 3, ".so") == 0)
3170 by_file = TRUE;
3172 switch (plugin_kill (word[2], by_file))
3174 case 0:
3175 PrintText (sess, _("No such plugin found.\n"));
3176 break;
3177 case 1:
3178 return TRUE;
3179 case 2:
3180 PrintText (sess, _("That plugin is refusing to unload.\n"));
3181 break;
3183 #endif
3185 return FALSE;
3188 static server *
3189 find_server_from_hostname (char *hostname)
3191 GSList *list = serv_list;
3192 server *serv;
3194 while (list)
3196 serv = list->data;
3197 if (!strcasecmp (hostname, serv->hostname) && serv->connected)
3198 return serv;
3199 list = list->next;
3202 return NULL;
3205 static server *
3206 find_server_from_net (void *net)
3208 GSList *list = serv_list;
3209 server *serv;
3211 while (list)
3213 serv = list->data;
3214 if (serv->network == net && serv->connected)
3215 return serv;
3216 list = list->next;
3219 return NULL;
3222 static void
3223 url_join_only (server *serv, char *tbuf, char *channel)
3225 /* already connected, JOIN only. FIXME: support keys? */
3226 tbuf[0] = '#';
3227 /* tbuf is 4kb */
3228 safe_strcpy ((tbuf + 1), channel, 256);
3229 serv->p_join (serv, tbuf, "");
3232 static int
3233 cmd_url (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3235 if (word[2][0])
3237 char *server_name = NULL;
3238 char *port = NULL;
3239 char *channel = NULL;
3240 char *url = g_strdup (word[2]);
3241 int use_ssl = FALSE;
3242 void *net;
3243 server *serv;
3245 if (parse_irc_url (url, &server_name, &port, &channel, &use_ssl))
3247 /* maybe we're already connected to this net */
3249 /* check for "FreeNode" */
3250 net = servlist_net_find (server_name, NULL, strcasecmp);
3251 /* check for "irc.eu.freenode.net" */
3252 if (!net)
3253 net = servlist_net_find_from_server (server_name);
3255 if (net)
3257 /* found the network, but are we connected? */
3258 serv = find_server_from_net (net);
3259 if (serv)
3261 url_join_only (serv, tbuf, channel);
3262 g_free (url);
3263 return TRUE;
3266 else
3268 /* an un-listed connection */
3269 serv = find_server_from_hostname (server_name);
3270 if (serv)
3272 url_join_only (serv, tbuf, channel);
3273 g_free (url);
3274 return TRUE;
3278 /* not connected to this net, open new window */
3279 cmd_newserver (sess, tbuf, word, word_eol);
3281 } else
3282 fe_open_url (word[2]);
3283 g_free (url);
3284 return TRUE;
3287 return FALSE;
3290 static int
3291 userlist_cb (struct User *user, session *sess)
3293 time_t lt;
3295 if (!user->lasttalk)
3296 lt = 0;
3297 else
3298 lt = time (0) - user->lasttalk;
3299 PrintTextf (sess,
3300 "\00306%s\t\00314[\00310%-38s\00314] \017ov\0033=\017%d%d away=%u lt\0033=\017%d\n",
3301 user->nick, user->hostname, user->op, user->voice, user->away, lt);
3303 return TRUE;
3306 static int
3307 cmd_uselect (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3309 int idx = 2;
3310 int clear = TRUE;
3311 int scroll = FALSE;
3313 if (strcmp (word[2], "-a") == 0) /* ADD (don't clear selections) */
3315 clear = FALSE;
3316 idx++;
3318 if (strcmp (word[idx], "-s") == 0) /* SCROLL TO */
3320 scroll = TRUE;
3321 idx++;
3323 /* always valid, no args means clear the selection list */
3324 fe_uselect (sess, word + idx, clear, scroll);
3325 return TRUE;
3328 static int
3329 cmd_userlist (struct session *sess, char *tbuf, char *word[],
3330 char *word_eol[])
3332 tree_foreach (sess->usertree, (tree_traverse_func *)userlist_cb, sess);
3333 return TRUE;
3336 static int
3337 wallchop_cb (struct User *user, multidata *data)
3339 if (user->op)
3341 if (data->i)
3342 strcat (data->tbuf, ",");
3343 strcat (data->tbuf, user->nick);
3344 data->i++;
3346 if (data->i == 5)
3348 data->i = 0;
3349 sprintf (data->tbuf + strlen (data->tbuf),
3350 " :[@%s] %s", data->sess->channel, data->reason);
3351 data->sess->server->p_raw (data->sess->server, data->tbuf);
3352 strcpy (data->tbuf, "NOTICE ");
3355 return TRUE;
3358 static int
3359 cmd_wallchop (struct session *sess, char *tbuf, char *word[],
3360 char *word_eol[])
3362 multidata data;
3364 if (!(*word_eol[2]))
3365 return FALSE;
3367 strcpy (tbuf, "NOTICE ");
3369 data.reason = word_eol[2];
3370 data.tbuf = tbuf;
3371 data.i = 0;
3372 data.sess = sess;
3373 tree_foreach (sess->usertree, (tree_traverse_func*)wallchop_cb, &data);
3375 if (data.i)
3377 sprintf (tbuf + strlen (tbuf),
3378 " :[@%s] %s", sess->channel, word_eol[2]);
3379 sess->server->p_raw (sess->server, tbuf);
3382 return TRUE;
3385 static int
3386 cmd_wallchan (struct session *sess, char *tbuf, char *word[],
3387 char *word_eol[])
3389 GSList *list;
3391 if (*word_eol[2])
3393 list = sess_list;
3394 while (list)
3396 sess = list->data;
3397 if (sess->type == SESS_CHANNEL)
3399 inbound_chanmsg (sess->server, NULL, sess->channel,
3400 sess->server->nick, word_eol[2], TRUE, FALSE);
3401 sess->server->p_message (sess->server, sess->channel, word_eol[2]);
3403 list = list->next;
3405 return TRUE;
3407 return FALSE;
3410 static int
3411 cmd_hop (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3413 int i = 2;
3415 while (1)
3417 if (!*word[i])
3419 if (i == 2)
3420 return FALSE;
3421 send_channel_modes (sess, tbuf, word, 2, i, '+', 'h', 0);
3422 return TRUE;
3424 i++;
3428 static int
3429 cmd_voice (struct session *sess, char *tbuf, char *word[], char *word_eol[])
3431 int i = 2;
3433 while (1)
3435 if (!*word[i])
3437 if (i == 2)
3438 return FALSE;
3439 send_channel_modes (sess, tbuf, word, 2, i, '+', 'v', 0);
3440 return TRUE;
3442 i++;
3446 /* *MUST* be kept perfectly sorted for the bsearch to work */
3447 const struct commands xc_cmds[] = {
3448 {"ADDBUTTON", cmd_addbutton, 0, 0, 1,
3449 N_("ADDBUTTON <name> <action>, adds a button under the user-list")},
3450 {"ALLCHAN", cmd_allchannels, 0, 0, 1,
3451 N_("ALLCHAN <cmd>, sends a command to all channels you're in")},
3452 {"ALLCHANL", cmd_allchannelslocal, 0, 0, 1,
3453 N_("ALLCHANL <cmd>, sends a command to all channels on the current server")},
3454 {"ALLSERV", cmd_allservers, 0, 0, 1,
3455 N_("ALLSERV <cmd>, sends a command to all servers you're in")},
3456 {"AWAY", cmd_away, 1, 0, 1, N_("AWAY [<reason>], sets you away")},
3457 {"BACK", cmd_back, 1, 0, 1, N_("BACK, sets you back (not away)")},
3458 {"BAN", cmd_ban, 1, 1, 1,
3459 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)")},
3460 {"CHANOPT", cmd_chanopt, 0, 0, 1, N_("CHANOPT [-quiet] <variable> [<value>]")},
3461 {"CHARSET", cmd_charset, 0, 0, 1, N_("CHARSET [<encoding>], get or set the encoding used for the current connection")},
3462 {"CLEAR", cmd_clear, 0, 0, 1, N_("CLEAR [ALL|HISTORY], Clears the current text window or command history")},
3463 {"CLOSE", cmd_close, 0, 0, 1, N_("CLOSE, Closes the current window/tab")},
3465 {"COUNTRY", cmd_country, 0, 0, 1,
3466 N_("COUNTRY [-s] <code|wildcard>, finds a country code, eg: au = australia")},
3467 {"CTCP", cmd_ctcp, 1, 0, 1,
3468 N_("CTCP <nick> <message>, send the CTCP message to nick, common messages are VERSION and USERINFO")},
3469 {"CYCLE", cmd_cycle, 1, 1, 1,
3470 N_("CYCLE [<channel>], parts the current or given channel and immediately rejoins")},
3471 {"DCC", cmd_dcc, 0, 0, 1,
3472 N_("\n"
3473 "DCC GET <nick> - accept an offered file\n"
3474 "DCC SEND [-maxcps=#] <nick> [file] - send a file to someone\n"
3475 "DCC PSEND [-maxcps=#] <nick> [file] - send a file using passive mode\n"
3476 "DCC LIST - show DCC list\n"
3477 "DCC CHAT <nick> - offer DCC CHAT to someone\n"
3478 "DCC PCHAT <nick> - offer DCC CHAT using passive mode\n"
3479 "DCC CLOSE <type> <nick> <file> example:\n"
3480 " /dcc close send johnsmith file.tar.gz")},
3481 {"DEBUG", cmd_debug, 0, 0, 1, 0},
3483 {"DEHOP", cmd_dehop, 1, 1, 1,
3484 N_("DEHOP <nick>, removes chanhalf-op status from the nick on the current channel (needs chanop)")},
3485 {"DELBUTTON", cmd_delbutton, 0, 0, 1,
3486 N_("DELBUTTON <name>, deletes a button from under the user-list")},
3487 {"DEOP", cmd_deop, 1, 1, 1,
3488 N_("DEOP <nick>, removes chanop status from the nick on the current channel (needs chanop)")},
3489 {"DEVOICE", cmd_devoice, 1, 1, 1,
3490 N_("DEVOICE <nick>, removes voice status from the nick on the current channel (needs chanop)")},
3491 {"DISCON", cmd_discon, 0, 0, 1, N_("DISCON, Disconnects from server")},
3492 {"DNS", cmd_dns, 0, 0, 1, N_("DNS <nick|host|ip>, Finds a users IP number")},
3493 {"ECHO", cmd_echo, 0, 0, 1, N_("ECHO <text>, Prints text locally")},
3494 {"EXEC", cmd_exec, 0, 0, 1,
3495 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")},
3496 {"EXECCONT", cmd_execc, 0, 0, 1, N_("EXECCONT, sends the process SIGCONT")},
3497 {"EXECKILL", cmd_execk, 0, 0, 1,
3498 N_("EXECKILL [-9], kills a running exec in the current session. If -9 is given the process is SIGKILL'ed")},
3499 {"EXECSTOP", cmd_execs, 0, 0, 1, N_("EXECSTOP, sends the process SIGSTOP")},
3500 {"EXECWRITE", cmd_execw, 0, 0, 1, N_("EXECWRITE, sends data to the processes stdin")},
3501 {"FLUSHQ", cmd_flushq, 0, 0, 1,
3502 N_("FLUSHQ, flushes the current server's send queue")},
3503 {"GATE", cmd_gate, 0, 0, 1,
3504 N_("GATE <host> [<port>], proxies through a host, port defaults to 23")},
3505 {"GETFILE", cmd_getfile, 0, 0, 1, "GETFILE [-folder] [-multi] [-save] <command> <title> [<initial>]"},
3506 {"GETINT", cmd_getint, 0, 0, 1, "GETINT <default> <command> <prompt>"},
3507 {"GETSTR", cmd_getstr, 0, 0, 1, "GETSTR <default> <command> <prompt>"},
3508 {"GHOST", cmd_ghost, 1, 0, 1, N_("GHOST <nick> [password], Kills a ghosted nickname")},
3509 {"GUI", cmd_gui, 0, 0, 1, "GUI [APPLY|ATTACH|DETACH|SHOW|HIDE|FOCUS|FLASH|ICONIFY|COLOR <n>]\n"
3510 " GUI [MSGBOX <text>|MENU TOGGLE]"},
3511 {"HELP", cmd_help, 0, 0, 1, 0},
3512 {"HOP", cmd_hop, 1, 1, 1,
3513 N_("HOP <nick>, gives chanhalf-op status to the nick (needs chanop)")},
3514 {"ID", cmd_id, 1, 0, 1, N_("ID <password>, identifies yourself to nickserv")},
3515 {"IGNORE", cmd_ignore, 0, 0, 1,
3516 N_("IGNORE <mask> <types..> <options..>\n"
3517 " mask - host mask to ignore, eg: *!*@*.aol.com\n"
3518 " types - types of data to ignore, one or all of:\n"
3519 " PRIV, CHAN, NOTI, CTCP, DCC, INVI, ALL\n"
3520 " options - NOSAVE, QUIET")},
3522 {"INVITE", cmd_invite, 1, 0, 1,
3523 N_("INVITE <nick> [<channel>], invites someone to a channel, by default the current channel (needs chanop)")},
3524 {"JOIN", cmd_join, 1, 0, 0, N_("JOIN <channel>, joins the channel")},
3525 {"KICK", cmd_kick, 1, 1, 1,
3526 N_("KICK <nick>, kicks the nick from the current channel (needs chanop)")},
3527 {"KICKBAN", cmd_kickban, 1, 1, 1,
3528 N_("KICKBAN <nick>, bans then kicks the nick from the current channel (needs chanop)")},
3529 {"KILLALL", cmd_killall, 0, 0, 1, "KILLALL, immediately exit"},
3530 {"LAGCHECK", cmd_lagcheck, 0, 0, 1,
3531 N_("LAGCHECK, forces a new lag check")},
3532 {"LASTLOG", cmd_lastlog, 0, 0, 1,
3533 N_("LASTLOG <string>, searches for a string in the buffer")},
3534 {"LIST", cmd_list, 1, 0, 1, 0},
3535 {"LOAD", cmd_load, 0, 0, 1, N_("LOAD [-e] <file>, loads a plugin or script")},
3537 {"MDEHOP", cmd_mdehop, 1, 1, 1,
3538 N_("MDEHOP, Mass deop's all chanhalf-ops in the current channel (needs chanop)")},
3539 {"MDEOP", cmd_mdeop, 1, 1, 1,
3540 N_("MDEOP, Mass deop's all chanops in the current channel (needs chanop)")},
3541 {"ME", cmd_me, 0, 0, 1,
3542 N_("ME <action>, sends the action to the current channel (actions are written in the 3rd person, like /me jumps)")},
3543 {"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"
3544 " See http://xchat.org/docs/menu/ for more details."},
3545 {"MKICK", cmd_mkick, 1, 1, 1,
3546 N_("MKICK, Mass kicks everyone except you in the current channel (needs chanop)")},
3547 {"MODE", cmd_mode, 1, 0, 1, 0},
3548 {"MOP", cmd_mop, 1, 1, 1,
3549 N_("MOP, Mass op's all users in the current channel (needs chanop)")},
3550 {"MSG", cmd_msg, 0, 0, 1, N_("MSG <nick> <message>, sends a private message")},
3552 {"NAMES", cmd_names, 1, 0, 1,
3553 N_("NAMES, Lists the nicks on the current channel")},
3554 {"NCTCP", cmd_nctcp, 1, 0, 1,
3555 N_("NCTCP <nick> <message>, Sends a CTCP notice")},
3556 {"NEWSERVER", cmd_newserver, 0, 0, 1, N_("NEWSERVER [-noconnect] <hostname> [<port>]")},
3557 {"NICK", cmd_nick, 0, 0, 1, N_("NICK <nickname>, sets your nick")},
3559 {"NOTICE", cmd_notice, 1, 0, 1,
3560 N_("NOTICE <nick/channel> <message>, sends a notice. Notices are a type of message that should be auto reacted to")},
3561 {"NOTIFY", cmd_notify, 0, 0, 1,
3562 N_("NOTIFY [-n network1[,network2,...]] [<nick>], displays your notify list or adds someone to it")},
3563 {"OP", cmd_op, 1, 1, 1,
3564 N_("OP <nick>, gives chanop status to the nick (needs chanop)")},
3565 {"PART", cmd_part, 1, 1, 0,
3566 N_("PART [<channel>] [<reason>], leaves the channel, by default the current one")},
3567 {"PING", cmd_ping, 1, 0, 1,
3568 N_("PING <nick | channel>, CTCP pings nick or channel")},
3569 {"QUERY", cmd_query, 0, 0, 1,
3570 N_("QUERY [-nofocus] <nick>, opens up a new privmsg window to someone")},
3571 {"QUIT", cmd_quit, 0, 0, 1,
3572 N_("QUIT [<reason>], disconnects from the current server")},
3573 {"QUOTE", cmd_quote, 1, 0, 1,
3574 N_("QUOTE <text>, sends the text in raw form to the server")},
3575 #ifdef USE_OPENSSL
3576 {"RECONNECT", cmd_reconnect, 0, 0, 1,
3577 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")},
3578 #else
3579 {"RECONNECT", cmd_reconnect, 0, 0, 1,
3580 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")},
3581 #endif
3582 {"RECV", cmd_recv, 1, 0, 1, N_("RECV <text>, send raw data to xchat, as if it was received from the irc server")},
3584 {"SAY", cmd_say, 0, 0, 1,
3585 N_("SAY <text>, sends the text to the object in the current window")},
3586 {"SEND", cmd_send, 0, 0, 1, N_("SEND <nick> [<file>]")},
3587 #ifdef USE_OPENSSL
3588 {"SERVCHAN", cmd_servchan, 0, 0, 1,
3589 N_("SERVCHAN [-ssl] <host> <port> <channel>, connects and joins a channel")},
3590 #else
3591 {"SERVCHAN", cmd_servchan, 0, 0, 1,
3592 N_("SERVCHAN <host> <port> <channel>, connects and joins a channel")},
3593 #endif
3594 #ifdef USE_OPENSSL
3595 {"SERVER", cmd_server, 0, 0, 1,
3596 N_("SERVER [-ssl] <host> [<port>] [<password>], connects to a server, the default port is 6667 for normal connections, and 9999 for ssl connections")},
3597 #else
3598 {"SERVER", cmd_server, 0, 0, 1,
3599 N_("SERVER <host> [<port>] [<password>], connects to a server, the default port is 6667")},
3600 #endif
3601 {"SET", cmd_set, 0, 0, 1, N_("SET [-e] [-off|-on] [-quiet] <variable> [<value>]")},
3602 {"SETCURSOR", cmd_setcursor, 0, 0, 1, N_("SETCURSOR [-|+]<position>, reposition the cursor in the inputbox")},
3603 {"SETTAB", cmd_settab, 0, 0, 1, N_("SETTAB <new name>, change a tab's name, tab_trunc limit still applies")},
3604 {"SETTEXT", cmd_settext, 0, 0, 1, N_("SETTEXT <new text>, replace the text in the input box")},
3605 {"SPLAY", cmd_splay, 0, 0, 1, "SPLAY <soundfile>"},
3606 {"TOPIC", cmd_topic, 1, 1, 1,
3607 N_("TOPIC [<topic>], sets the topic if one is given, else shows the current topic")},
3608 {"TRAY", cmd_tray, 0, 0, 1,
3609 N_("\nTRAY -f <timeout> <file1> [<file2>] Blink tray between two icons.\n"
3610 "TRAY -f <filename> Set tray to a fixed icon.\n"
3611 "TRAY -i <number> Blink tray with an internal icon.\n"
3612 "TRAY -t <text> Set the tray tooltip.\n"
3613 "TRAY -b <title> <text> Set the tray balloon."
3615 {"UNBAN", cmd_unban, 1, 1, 1,
3616 N_("UNBAN <mask> [<mask>...], unbans the specified masks.")},
3617 {"UNIGNORE", cmd_unignore, 0, 0, 1, N_("UNIGNORE <mask> [QUIET]")},
3618 {"UNLOAD", cmd_unload, 0, 0, 1, N_("UNLOAD <name>, unloads a plugin or script")},
3619 {"URL", cmd_url, 0, 0, 1, N_("URL <url>, opens a URL in your browser")},
3620 {"USELECT", cmd_uselect, 0, 1, 0,
3621 N_("USELECT [-a] [-s] <nick1> <nick2> etc, highlights nick(s) in channel userlist")},
3622 {"USERLIST", cmd_userlist, 1, 1, 1, 0},
3623 {"VOICE", cmd_voice, 1, 1, 1,
3624 N_("VOICE <nick>, gives voice status to someone (needs chanop)")},
3625 {"WALLCHAN", cmd_wallchan, 1, 1, 1,
3626 N_("WALLCHAN <message>, writes the message to all channels")},
3627 {"WALLCHOP", cmd_wallchop, 1, 1, 1,
3628 N_("WALLCHOP <message>, sends the message to all chanops on the current channel")},
3629 {0, 0, 0, 0, 0, 0}
3633 static int
3634 command_compare (const void *a, const void *b)
3636 return strcasecmp (a, ((struct commands *)b)->name);
3639 static struct commands *
3640 find_internal_command (char *name)
3642 /* the "-1" is to skip the NULL terminator */
3643 return bsearch (name, xc_cmds, (sizeof (xc_cmds) /
3644 sizeof (xc_cmds[0])) - 1, sizeof (xc_cmds[0]), command_compare);
3647 static void
3648 help (session *sess, char *tbuf, char *helpcmd, int quiet)
3650 struct commands *cmd;
3652 if (plugin_show_help (sess, helpcmd))
3653 return;
3655 cmd = find_internal_command (helpcmd);
3657 if (cmd)
3659 if (cmd->help)
3661 snprintf (tbuf, TBUFSIZE, _("Usage: %s\n"), _(cmd->help));
3662 PrintText (sess, tbuf);
3663 } else
3665 if (!quiet)
3666 PrintText (sess, _("\nNo help available on that command.\n"));
3668 return;
3671 if (!quiet)
3672 PrintText (sess, _("No such command.\n"));
3675 /* inserts %a, %c, %d etc into buffer. Also handles &x %x for word/word_eol. *
3676 * returns 2 on buffer overflow
3677 * returns 1 on success *
3678 * returns 0 on bad-args-for-user-command *
3679 * - word/word_eol args might be NULL *
3680 * - this beast is used for UserCommands, UserlistButtons and CTCP replies */
3683 auto_insert (char *dest, int destlen, unsigned char *src, char *word[],
3684 char *word_eol[], char *a, char *c, char *d, char *e, char *h,
3685 char *n, char *s)
3687 int num;
3688 char buf[32];
3689 time_t now;
3690 struct tm *tm_ptr;
3691 char *utf;
3692 gsize utf_len;
3693 char *orig = dest;
3695 destlen--;
3697 while (src[0])
3699 if (src[0] == '%' || src[0] == '&')
3701 if (isdigit ((unsigned char) src[1]))
3703 if (isdigit ((unsigned char) src[2]) && isdigit ((unsigned char) src[3]))
3705 buf[0] = src[1];
3706 buf[1] = src[2];
3707 buf[2] = src[3];
3708 buf[3] = 0;
3709 dest[0] = atoi (buf);
3710 utf = g_locale_to_utf8 (dest, 1, 0, &utf_len, 0);
3711 if (utf)
3713 if ((dest - orig) + utf_len >= destlen)
3715 g_free (utf);
3716 return 2;
3719 memcpy (dest, utf, utf_len);
3720 g_free (utf);
3721 dest += utf_len;
3723 src += 3;
3724 } else
3726 if (word)
3728 src++;
3729 num = src[0] - '0'; /* ascii to decimal */
3730 if (*word[num] == 0)
3731 return 0;
3733 if (src[-1] == '%')
3734 utf = word[num];
3735 else
3736 utf = word_eol[num];
3738 /* avoid recusive usercommand overflow */
3739 if ((dest - orig) + strlen (utf) >= destlen)
3740 return 2;
3742 strcpy (dest, utf);
3743 dest += strlen (dest);
3746 } else
3748 if (src[0] == '&')
3749 goto lamecode;
3750 src++;
3751 utf = NULL;
3752 switch (src[0])
3754 case '%':
3755 if ((dest - orig) + 2 >= destlen)
3756 return 2;
3757 dest[0] = '%';
3758 dest[1] = 0;
3759 dest++;
3760 break;
3761 case 'a':
3762 utf = a; break;
3763 case 'c':
3764 utf = c; break;
3765 case 'd':
3766 utf = d; break;
3767 case 'e':
3768 utf = e; break;
3769 case 'h':
3770 utf = h; break;
3771 case 'm':
3772 utf = get_cpu_str (); break;
3773 case 'n':
3774 utf = n; break;
3775 case 's':
3776 utf = s; break;
3777 case 't':
3778 now = time (0);
3779 utf = ctime (&now);
3780 utf[19] = 0;
3781 break;
3782 case 'v':
3783 utf = PACKAGE_VERSION; break;
3784 break;
3785 case 'y':
3786 now = time (0);
3787 tm_ptr = localtime (&now);
3788 snprintf (buf, sizeof (buf), "%4d%02d%02d", 1900 +
3789 tm_ptr->tm_year, 1 + tm_ptr->tm_mon, tm_ptr->tm_mday);
3790 utf = buf;
3791 break;
3792 default:
3793 src--;
3794 goto lamecode;
3797 if (utf)
3799 if ((dest - orig) + strlen (utf) >= destlen)
3800 return 2;
3801 strcpy (dest, utf);
3802 dest += strlen (dest);
3806 src++;
3807 } else
3809 utf_len = g_utf8_skip[src[0]];
3811 if ((dest - orig) + utf_len >= destlen)
3812 return 2;
3814 if (utf_len == 1)
3816 lamecode:
3817 dest[0] = src[0];
3818 dest++;
3819 src++;
3820 } else
3822 memcpy (dest, src, utf_len);
3823 dest += utf_len;
3824 src += utf_len;
3829 dest[0] = 0;
3831 return 1;
3834 void
3835 check_special_chars (char *cmd, int do_ascii) /* check for %X */
3837 int occur = 0;
3838 int len = strlen (cmd);
3839 char *buf, *utf;
3840 char tbuf[4];
3841 int i = 0, j = 0;
3842 gsize utf_len;
3844 if (!len)
3845 return;
3847 buf = malloc (len + 1);
3849 if (buf)
3851 while (cmd[j])
3853 switch (cmd[j])
3855 case '%':
3856 occur++;
3857 if ( do_ascii &&
3858 j + 3 < len &&
3859 (isdigit ((unsigned char) cmd[j + 1]) && isdigit ((unsigned char) cmd[j + 2]) &&
3860 isdigit ((unsigned char) cmd[j + 3])))
3862 tbuf[0] = cmd[j + 1];
3863 tbuf[1] = cmd[j + 2];
3864 tbuf[2] = cmd[j + 3];
3865 tbuf[3] = 0;
3866 buf[i] = atoi (tbuf);
3867 utf = g_locale_to_utf8 (buf + i, 1, 0, &utf_len, 0);
3868 if (utf)
3870 memcpy (buf + i, utf, utf_len);
3871 g_free (utf);
3872 i += (utf_len - 1);
3874 j += 3;
3875 } else
3877 switch (cmd[j + 1])
3879 case 'R':
3880 buf[i] = '\026';
3881 break;
3882 case 'U':
3883 buf[i] = '\037';
3884 break;
3885 case 'B':
3886 buf[i] = '\002';
3887 break;
3888 case 'C':
3889 buf[i] = '\003';
3890 break;
3891 case 'O':
3892 buf[i] = '\017';
3893 break;
3894 case 'H': /* CL: invisible text code */
3895 buf[i] = HIDDEN_CHAR;
3896 break;
3897 case '%':
3898 buf[i] = '%';
3899 break;
3900 default:
3901 buf[i] = '%';
3902 j--;
3903 break;
3905 j++;
3906 break;
3907 default:
3908 buf[i] = cmd[j];
3911 j++;
3912 i++;
3914 buf[i] = 0;
3915 if (occur)
3916 strcpy (cmd, buf);
3917 free (buf);
3921 typedef struct
3923 char *nick;
3924 int len;
3925 struct User *best;
3926 int bestlen;
3927 char *space;
3928 char *tbuf;
3929 } nickdata;
3931 static int
3932 nick_comp_cb (struct User *user, nickdata *data)
3934 int lenu;
3936 if (!rfc_ncasecmp (user->nick, data->nick, data->len))
3938 lenu = strlen (user->nick);
3939 if (lenu == data->len)
3941 snprintf (data->tbuf, TBUFSIZE, "%s%s", user->nick, data->space);
3942 data->len = -1;
3943 return FALSE;
3944 } else if (lenu < data->bestlen)
3946 data->bestlen = lenu;
3947 data->best = user;
3951 return TRUE;
3954 static void
3955 perform_nick_completion (struct session *sess, char *cmd, char *tbuf)
3957 int len;
3958 char *space = strchr (cmd, ' ');
3959 if (space && space != cmd)
3961 if (space[-1] == prefs.nick_suffix[0] && space - 1 != cmd)
3963 len = space - cmd - 1;
3964 if (len < NICKLEN)
3966 char nick[NICKLEN];
3967 nickdata data;
3969 memcpy (nick, cmd, len);
3970 nick[len] = 0;
3972 data.nick = nick;
3973 data.len = len;
3974 data.bestlen = INT_MAX;
3975 data.best = NULL;
3976 data.tbuf = tbuf;
3977 data.space = space - 1;
3978 tree_foreach (sess->usertree, (tree_traverse_func *)nick_comp_cb, &data);
3980 if (data.len == -1)
3981 return;
3983 if (data.best)
3985 snprintf (tbuf, TBUFSIZE, "%s%s", data.best->nick, space - 1);
3986 return;
3992 strcpy (tbuf, cmd);
3995 static void
3996 user_command (session * sess, char *tbuf, char *cmd, char *word[],
3997 char *word_eol[])
3999 if (!auto_insert (tbuf, 2048, cmd, word, word_eol, "", sess->channel, "",
4000 server_get_network (sess->server, TRUE), "",
4001 sess->server->nick, ""))
4003 PrintText (sess, _("Bad arguments for user command.\n"));
4004 return;
4007 handle_command (sess, tbuf, TRUE);
4010 /* handle text entered without a CMDchar prefix */
4012 static void
4013 handle_say (session *sess, char *text, int check_spch)
4015 struct DCC *dcc;
4016 char *word[PDIWORDS+1];
4017 char *word_eol[PDIWORDS+1];
4018 char pdibuf_static[1024];
4019 char newcmd_static[1024];
4020 char *pdibuf = pdibuf_static;
4021 char *newcmd = newcmd_static;
4022 int len;
4023 int newcmdlen = sizeof newcmd_static;
4025 if (strcmp (sess->channel, "(lastlog)") == 0)
4027 lastlog (sess->lastlog_sess, text, sess->lastlog_regexp);
4028 return;
4031 len = strlen (text);
4032 if (len >= sizeof pdibuf_static)
4033 pdibuf = malloc (len + 1);
4035 if (len + NICKLEN >= newcmdlen)
4036 newcmd = malloc (newcmdlen = len + NICKLEN + 1);
4038 if (check_spch && prefs.perc_color)
4039 check_special_chars (text, prefs.perc_ascii);
4041 /* Python relies on this */
4042 word[PDIWORDS] = NULL;
4043 word_eol[PDIWORDS] = NULL;
4045 /* split the text into words and word_eol */
4046 process_data_init (pdibuf, text, word, word_eol, TRUE, FALSE);
4048 /* a command of "" can be hooked for non-commands */
4049 if (plugin_emit_command (sess, "", word, word_eol))
4050 goto xit;
4052 /* incase a plugin did /close */
4053 if (!is_session (sess))
4054 goto xit;
4056 if (!sess->channel[0] || sess->type == SESS_SERVER || sess->type == SESS_NOTICES || sess->type == SESS_SNOTICES)
4058 notj_msg (sess);
4059 goto xit;
4062 if (prefs.nickcompletion)
4063 perform_nick_completion (sess, text, newcmd);
4064 else
4065 safe_strcpy (newcmd, text, newcmdlen);
4067 text = newcmd;
4069 if (sess->type == SESS_DIALOG)
4071 /* try it via dcc, if possible */
4072 dcc = dcc_write_chat (sess->channel, text);
4073 if (dcc)
4075 inbound_chanmsg (sess->server, NULL, sess->channel,
4076 sess->server->nick, text, TRUE, FALSE);
4077 set_topic (sess, net_ip (dcc->addr), net_ip (dcc->addr));
4078 goto xit;
4082 if (sess->server->connected)
4084 unsigned int max;
4085 unsigned char t = 0;
4087 /* maximum allowed message text */
4088 /* :nickname!username@host.com PRIVMSG #channel :text\r\n */
4089 max = 512;
4090 max -= 16; /* :, !, @, " PRIVMSG ", " ", :, \r, \n */
4091 max -= strlen (sess->server->nick);
4092 max -= strlen (sess->channel);
4093 if (sess->me && sess->me->hostname)
4094 max -= strlen (sess->me->hostname);
4095 else
4097 max -= 9; /* username */
4098 max -= 65; /* max possible hostname and '@' */
4101 if (strlen (text) > max)
4103 int i = 0, size;
4105 /* traverse the utf8 string and find the nearest cut point that
4106 doesn't split 1 char in half */
4107 while (1)
4109 size = g_utf8_skip[((unsigned char *)text)[i]];
4110 if ((i + size) >= max)
4111 break;
4112 i += size;
4114 max = i;
4115 t = text[max];
4116 text[max] = 0; /* insert a NULL terminator to shorten it */
4119 inbound_chanmsg (sess->server, sess, sess->channel, sess->server->nick,
4120 text, TRUE, FALSE);
4121 sess->server->p_message (sess->server, sess->channel, text);
4123 if (t)
4125 text[max] = t;
4126 handle_say (sess, text + max, FALSE);
4129 } else
4131 notc_msg (sess);
4134 xit:
4135 if (pdibuf != pdibuf_static)
4136 free (pdibuf);
4138 if (newcmd != newcmd_static)
4139 free (newcmd);
4142 /* handle a command, without the '/' prefix */
4145 handle_command (session *sess, char *cmd, int check_spch)
4147 struct popup *pop;
4148 int user_cmd = FALSE;
4149 GSList *list;
4150 char *word[PDIWORDS+1];
4151 char *word_eol[PDIWORDS+1];
4152 static int command_level = 0;
4153 struct commands *int_cmd;
4154 char pdibuf_static[1024];
4155 char tbuf_static[TBUFSIZE];
4156 char *pdibuf;
4157 char *tbuf;
4158 int len;
4159 int ret = TRUE;
4161 if (command_level > 99)
4163 fe_message (_("Too many recursive usercommands, aborting."), FE_MSG_ERROR);
4164 return TRUE;
4166 command_level++;
4167 /* anything below MUST DEC command_level before returning */
4169 len = strlen (cmd);
4170 if (len >= sizeof (pdibuf_static))
4171 pdibuf = malloc (len + 1);
4172 else
4173 pdibuf = pdibuf_static;
4175 if ((len * 2) >= sizeof (tbuf_static))
4176 tbuf = malloc ((len * 2) + 1);
4177 else
4178 tbuf = tbuf_static;
4180 /* split the text into words and word_eol */
4181 process_data_init (pdibuf, cmd, word, word_eol, TRUE, TRUE);
4183 /* ensure an empty string at index 32 for cmd_deop etc */
4184 /* (internal use only, plugins can still only read 1-31). */
4185 word[PDIWORDS] = "\000\000";
4186 word_eol[PDIWORDS] = "\000\000";
4188 int_cmd = find_internal_command (word[1]);
4189 /* redo it without quotes processing, for some commands like /JOIN */
4190 if (int_cmd && !int_cmd->handle_quotes)
4191 process_data_init (pdibuf, cmd, word, word_eol, FALSE, FALSE);
4193 if (check_spch && prefs.perc_color)
4194 check_special_chars (cmd, prefs.perc_ascii);
4196 if (plugin_emit_command (sess, word[1], word, word_eol))
4197 goto xit;
4199 /* incase a plugin did /close */
4200 if (!is_session (sess))
4201 goto xit;
4203 /* first see if it's a userCommand */
4204 list = command_list;
4205 while (list)
4207 pop = (struct popup *) list->data;
4208 if (!strcasecmp (pop->name, word[1]))
4210 user_command (sess, tbuf, pop->cmd, word, word_eol);
4211 user_cmd = TRUE;
4213 list = list->next;
4216 if (user_cmd)
4217 goto xit;
4219 /* now check internal commands */
4220 int_cmd = find_internal_command (word[1]);
4222 if (int_cmd)
4224 if (int_cmd->needserver && !sess->server->connected)
4226 notc_msg (sess);
4227 } else if (int_cmd->needchannel && !sess->channel[0])
4229 notj_msg (sess);
4230 } else
4232 switch (int_cmd->callback (sess, tbuf, word, word_eol))
4234 case FALSE:
4235 help (sess, tbuf, int_cmd->name, TRUE);
4236 break;
4237 case 2:
4238 ret = FALSE;
4239 goto xit;
4242 } else
4244 /* unknown command, just send it to the server and hope */
4245 if (!sess->server->connected)
4246 PrintText (sess, _("Unknown Command. Try /help\n"));
4247 else
4248 sess->server->p_raw (sess->server, cmd);
4251 xit:
4252 command_level--;
4254 if (pdibuf != pdibuf_static)
4255 free (pdibuf);
4257 if (tbuf != tbuf_static)
4258 free (tbuf);
4260 return ret;
4263 /* handle one line entered into the input box */
4265 static int
4266 handle_user_input (session *sess, char *text, int history, int nocommand)
4268 if (*text == '\0')
4269 return 1;
4271 if (history)
4272 history_add (&sess->history, text);
4274 /* is it NOT a command, just text? */
4275 if (nocommand || text[0] != prefs.cmdchar[0])
4277 handle_say (sess, text, TRUE);
4278 return 1;
4281 /* check for // */
4282 if (text[0] == prefs.cmdchar[0] && text[1] == prefs.cmdchar[0])
4284 handle_say (sess, text + 1, TRUE);
4285 return 1;
4288 if (prefs.cmdchar[0] == '/')
4290 int i;
4291 const char *unix_dirs [] = {
4292 "/bin/", "/boot/", "/dev/",
4293 "/etc/", "/home/", "/lib/",
4294 "/lost+found/", "/mnt/", "/opt/",
4295 "/proc/", "/root/", "/sbin/",
4296 "/tmp/", "/usr/", "/var/",
4297 "/gnome/", NULL};
4298 for (i = 0; unix_dirs[i] != NULL; i++)
4299 if (strncmp (text, unix_dirs[i], strlen (unix_dirs[i]))==0)
4301 handle_say (sess, text, TRUE);
4302 return 1;
4306 return handle_command (sess, text + 1, TRUE);
4309 /* changed by Steve Green. Macs sometimes paste with imbedded \r */
4310 void
4311 handle_multiline (session *sess, char *cmd, int history, int nocommand)
4313 while (*cmd)
4315 char *cr = cmd + strcspn (cmd, "\n\r");
4316 int end_of_string = *cr == 0;
4317 *cr = 0;
4318 if (!handle_user_input (sess, cmd, history, nocommand))
4319 return;
4320 if (end_of_string)
4321 break;
4322 cmd = cr + 1;
4326 /*void
4327 handle_multiline (session *sess, char *cmd, int history, int nocommand)
4329 char *cr;
4331 cr = strchr (cmd, '\n');
4332 if (cr)
4334 while (1)
4336 if (cr)
4337 *cr = 0;
4338 if (!handle_user_input (sess, cmd, history, nocommand))
4339 return;
4340 if (!cr)
4341 break;
4342 cmd = cr + 1;
4343 if (*cmd == 0)
4344 break;
4345 cr = strchr (cmd, '\n');
4347 } else
4349 handle_user_input (sess, cmd, history, nocommand);