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 */
44 #include "cfgfiles.h" /* xchat_fopen_file() */
45 #include "network.h" /* net_ip() */
58 extern int current_mem_usage
;
62 static void help (session
*sess
, char *tbuf
, char *helpcmd
, int quiet
);
63 static int cmd_server (session
*sess
, char *tbuf
, char *word
[], char *word_eol
[]);
64 static void handle_say (session
*sess
, char *text
, int check_spch
);
68 notj_msg (struct session
*sess
)
70 PrintText (sess
, _("No channel joined. Try /join #<channel>\n"));
74 notc_msg (struct session
*sess
)
76 PrintText (sess
, _("Not connected. Try /server <host> [<port>]\n"));
80 random_line (char *file_name
)
89 fh
= xchat_fopen_file (file_name
, "r", 0);
93 /* reason is not a file, an actual reason! */
94 return strdup (file_name
);
97 /* count number of lines in file */
99 while (fgets (buf
, sizeof (buf
), fh
))
105 /* go down a random number */
107 ran
= RAND_INT (lines
);
110 fgets (buf
, sizeof (buf
), fh
);
115 buf
[strlen (buf
) - 1] = 0; /* remove the trailing '\n' */
120 server_sendpart (server
* serv
, char *channel
, char *reason
)
124 reason
= random_line (prefs
.partreason
);
125 serv
->p_part (serv
, channel
, reason
);
129 /* reason set by /quit, /close argument */
130 serv
->p_part (serv
, channel
, reason
);
135 server_sendquit (session
* sess
)
139 if (!sess
->quitreason
)
141 colrea
= strdup (prefs
.quitreason
);
142 check_special_chars (colrea
, FALSE
);
143 rea
= random_line (colrea
);
145 sess
->server
->p_quit (sess
->server
, rea
);
149 /* reason set by /quit, /close argument */
150 sess
->server
->p_quit (sess
->server
, sess
->quitreason
);
155 process_data_init (char *buf
, char *cmd
, char *word
[],
156 char *word_eol
[], gboolean handle_quotes
,
157 gboolean allow_escape_quotes
)
165 word
[0] = "\000\000";
166 word_eol
[0] = "\000\000";
167 word
[1] = (char *)buf
;
168 word_eol
[1] = (char *)cmd
;
176 for (j
= wordcount
; j
< PDIWORDS
; j
++)
178 word
[j
] = "\000\000";
179 word_eol
[j
] = "\000\000";
185 /* two quotes turn into 1 */
186 if (allow_escape_quotes
&& cmd
[1] == '\042')
207 if (wordcount
< PDIWORDS
)
209 word
[wordcount
] = &buf
[j
];
210 word_eol
[wordcount
] = cmd
+ 1;
222 len
= g_utf8_skip
[((unsigned char *)cmd
)[0]];
230 /* skip past a multi-byte utf8 char */
231 memcpy (buf
+ j
, cmd
, len
);
240 cmd_addbutton (struct session
*sess
, char *tbuf
, char *word
[],
243 if (*word
[2] && *word_eol
[3])
245 if (sess
->type
== SESS_DIALOG
)
247 list_addentry (&dlgbutton_list
, word_eol
[3], word
[2]);
248 fe_dlgbuttons_update (sess
);
251 list_addentry (&button_list
, word_eol
[3], word
[2]);
252 fe_buttons_update (sess
);
260 cmd_allchannels (session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
262 GSList
*list
= sess_list
;
270 if (sess
->type
== SESS_CHANNEL
&& sess
->channel
[0] && sess
->server
->connected
)
272 handle_command (sess
, word_eol
[2], FALSE
);
281 cmd_allchannelslocal (session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
283 GSList
*list
= sess_list
;
284 server
*serv
= sess
->server
;
292 if (sess
->type
== SESS_CHANNEL
&& sess
->channel
[0] &&
293 sess
->server
->connected
&& sess
->server
== serv
)
295 handle_command (sess
, word_eol
[2], FALSE
);
304 cmd_allservers (struct session
*sess
, char *tbuf
, char *word
[],
318 handle_command (serv
->front_session
, word_eol
[2], FALSE
);
326 cmd_away (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
329 char *reason
= word_eol
[2];
333 if (sess
->server
->is_away
)
335 if (sess
->server
->last_away_reason
)
336 PrintTextf (sess
, _("Already marked away: %s\n"), sess
->server
->last_away_reason
);
340 if (sess
->server
->reconnect_away
)
341 reason
= sess
->server
->last_away_reason
;
343 /* must manage memory pointed to by random_line() */
344 reason
= random_line (prefs
.awayreason
);
346 sess
->server
->p_set_away (sess
->server
, reason
);
348 if (prefs
.show_away_message
)
350 snprintf (tbuf
, TBUFSIZE
, "me is away: %s", reason
);
351 for (list
= sess_list
; list
; list
= list
->next
)
353 /* am I the right server and not a dialog box */
354 if (((struct session
*) list
->data
)->server
== sess
->server
355 && ((struct session
*) list
->data
)->type
== SESS_CHANNEL
356 && ((struct session
*) list
->data
)->channel
[0])
358 handle_command ((session
*) list
->data
, tbuf
, TRUE
);
363 if (sess
->server
->last_away_reason
!= reason
)
365 if (sess
->server
->last_away_reason
)
366 free (sess
->server
->last_away_reason
);
368 if (reason
== word_eol
[2])
369 sess
->server
->last_away_reason
= strdup (reason
);
371 sess
->server
->last_away_reason
= reason
;
374 if (!sess
->server
->connected
)
375 sess
->server
->reconnect_away
= 1;
381 cmd_back (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
386 if (sess
->server
->is_away
)
388 sess
->server
->p_set_back (sess
->server
);
390 if (prefs
.show_away_message
)
392 gone
= time (NULL
) - sess
->server
->away_time
;
393 sprintf (tbuf
, "me is back (gone %.2d:%.2d:%.2d)", gone
/ 3600,
394 (gone
/ 60) % 60, gone
% 60);
395 for (list
= sess_list
; list
; list
= list
->next
)
397 /* am I the right server and not a dialog box */
398 if (((struct session
*) list
->data
)->server
== sess
->server
399 && ((struct session
*) list
->data
)->type
== SESS_CHANNEL
400 && ((struct session
*) list
->data
)->channel
[0])
402 handle_command ((session
*) list
->data
, tbuf
, TRUE
);
409 PrintText (sess
, _("Already marked back.\n"));
412 if (sess
->server
->last_away_reason
)
413 free (sess
->server
->last_away_reason
);
414 sess
->server
->last_away_reason
= NULL
;
420 ban (session
* sess
, char *tbuf
, char *mask
, char *bantypestr
, int deop
)
424 char *at
, *dot
, *lastdot
;
425 char username
[64], fullhost
[128], domain
[128], *mode
, *p2
;
426 server
*serv
= sess
->server
;
428 user
= userlist_find (sess
, mask
);
429 if (user
&& user
->hostname
) /* it's a nickname, let's find a proper ban mask */
441 mask
= user
->hostname
;
443 at
= strchr (mask
, '@'); /* FIXME: utf8 */
445 return; /* can't happen? */
448 if (mask
[0] == '~' || mask
[0] == '+' ||
449 mask
[0] == '=' || mask
[0] == '^' || mask
[0] == '-')
451 /* the ident is prefixed with something, we replace that sign with an * */
452 safe_strcpy (username
+1, mask
+1, sizeof (username
)-1);
454 } else if (at
- mask
< USERNAMELEN
)
456 /* we just add an * in the begining of the ident */
457 safe_strcpy (username
+1, mask
, sizeof (username
)-1);
461 /* ident might be too long, we just ban what it gives and add an * in the end */
462 safe_strcpy (username
, mask
, sizeof (username
));
465 safe_strcpy (fullhost
, at
+ 1, sizeof (fullhost
));
467 dot
= strchr (fullhost
, '.');
470 safe_strcpy (domain
, dot
, sizeof (domain
));
473 safe_strcpy (domain
, fullhost
, sizeof (domain
));
477 bantype
= atoi (bantypestr
);
479 bantype
= prefs
.bantype
;
482 if (inet_addr (fullhost
) != -1) /* "fullhost" is really a IP number */
484 lastdot
= strrchr (fullhost
, '.');
486 return; /* can't happen? */
489 strcpy (domain
, fullhost
);
495 snprintf (tbuf
, TBUFSIZE
, "%s%s *!*@%s.*", mode
, p2
, domain
);
499 snprintf (tbuf
, TBUFSIZE
, "%s%s *!*@%s", mode
, p2
, fullhost
);
503 snprintf (tbuf
, TBUFSIZE
, "%s%s *!%s@%s.*", mode
, p2
, username
, domain
);
507 snprintf (tbuf
, TBUFSIZE
, "%s%s *!%s@%s", mode
, p2
, username
, fullhost
);
515 snprintf (tbuf
, TBUFSIZE
, "%s%s *!*@*%s", mode
, p2
, domain
);
519 snprintf (tbuf
, TBUFSIZE
, "%s%s *!*@%s", mode
, p2
, fullhost
);
523 snprintf (tbuf
, TBUFSIZE
, "%s%s *!%s@*%s", mode
, p2
, username
, domain
);
527 snprintf (tbuf
, TBUFSIZE
, "%s%s *!%s@%s", mode
, p2
, username
, fullhost
);
534 snprintf (tbuf
, TBUFSIZE
, "+b %s", mask
);
536 serv
->p_mode (serv
, sess
->channel
, tbuf
);
540 cmd_ban (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
542 char *mask
= word
[2];
546 ban (sess
, tbuf
, mask
, word
[3], 0);
549 sess
->server
->p_mode (sess
->server
, sess
->channel
, "+b"); /* banlist */
556 cmd_unban (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
558 /* Allow more than one mask in /unban -- tvk */
567 send_channel_modes (sess
, tbuf
, word
, 2, i
, '-', 'b', 0);
575 cmd_chanopt (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
578 return chanopt_command (sess
, tbuf
, word
, word_eol
);
582 cmd_charset (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
584 server
*serv
= sess
->server
;
585 const char *locale
= NULL
;
588 if (strcmp (word
[2], "-quiet") == 0)
591 if (!word
[2 + offset
][0])
593 g_get_charset (&locale
);
594 PrintTextf (sess
, "Current charset: %s\n",
595 serv
->encoding
? serv
->encoding
: locale
);
599 if (servlist_check_encoding (word
[2 + offset
]))
601 server_set_encoding (serv
, word
[2 + offset
]);
603 PrintTextf (sess
, "Charset changed to: %s\n", word
[2 + offset
]);
606 PrintTextf (sess
, "\0034Unknown charset:\017 %s\n", word
[2 + offset
]);
613 cmd_clear (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
615 GSList
*list
= sess_list
;
616 char *reason
= word_eol
[2];
618 if (strcasecmp (reason
, "HISTORY") == 0)
620 history_free (&sess
->history
);
624 if (strncasecmp (reason
, "all", 3) == 0)
629 if (!sess
->nick_said
)
630 fe_text_clear (list
->data
, 0);
636 if (reason
[0] != '-' && !isdigit (reason
[0]) && reason
[0] != 0)
639 fe_text_clear (sess
, atoi (reason
));
644 cmd_close (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
648 if (strcmp (word
[2], "-m") == 0)
655 if (sess
->type
== SESS_DIALOG
)
656 fe_close_window (sess
);
661 sess
->quitreason
= word_eol
[2];
662 fe_close_window (sess
);
669 cmd_ctcp (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
675 char *msg
= word_eol
[3];
678 unsigned char *cmd
= (unsigned char *)msg
;
680 /* make the first word upper case (as per RFC) */
683 if (*cmd
== ' ' || *cmd
== 0)
685 mbl
= g_utf8_skip
[*cmd
];
687 *cmd
= toupper (*cmd
);
691 sess
->server
->p_ctcp (sess
->server
, to
, msg
);
693 EMIT_SIGNAL (XP_TE_CTCPSEND
, sess
, to
, msg
, NULL
, NULL
, 0);
702 cmd_country (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
704 char *code
= word
[2];
708 if (strcmp (code
, "-s") == 0)
710 country_search (word
[3], sess
, (void *)PrintTextf
);
714 /* search, but forgot the -s */
715 if (strchr (code
, '*'))
717 country_search (code
, sess
, (void *)PrintTextf
);
721 sprintf (tbuf
, "%s = %s\n", code
, country (code
));
722 PrintText (sess
, tbuf
);
729 cmd_cycle (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
731 char *key
= sess
->channelkey
;
732 char *chan
= word
[2];
734 chan
= sess
->channel
;
735 if (*chan
&& sess
->type
== SESS_CHANNEL
)
737 sess
->server
->p_cycle (sess
->server
, chan
, key
);
744 cmd_dcc (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
748 char *type
= word
[2];
751 if (!strcasecmp (type
, "HELP"))
753 if (!strcasecmp (type
, "CLOSE"))
755 if (*word
[3] && *word
[4])
758 if (!strcasecmp (word
[3], "SEND"))
760 dcc
= find_dcc (word
[4], word
[5], TYPE_SEND
);
761 dcc_abort (sess
, dcc
);
764 if (!strcasecmp (word
[3], "GET"))
766 dcc
= find_dcc (word
[4], word
[5], TYPE_RECV
);
767 dcc_abort (sess
, dcc
);
770 if (!strcasecmp (word
[3], "CHAT"))
772 dcc
= find_dcc (word
[4], "", TYPE_CHATRECV
);
774 dcc
= find_dcc (word
[4], "", TYPE_CHATSEND
);
775 dcc_abort (sess
, dcc
);
783 EMIT_SIGNAL (XP_TE_NODCC
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
790 if ((!strcasecmp (type
, "CHAT")) || (!strcasecmp (type
, "PCHAT")))
792 char *nick
= word
[3];
793 int passive
= (!strcasecmp(type
, "PCHAT")) ? 1 : 0;
795 dcc_chat (sess
, nick
, passive
);
798 if (!strcasecmp (type
, "LIST"))
800 dcc_show_list (sess
);
803 if (!strcasecmp (type
, "GET"))
805 char *nick
= word
[3];
806 char *file
= word
[4];
810 dcc_get_nick (sess
, nick
);
813 dcc
= find_dcc (nick
, file
, TYPE_RECV
);
817 EMIT_SIGNAL (XP_TE_NODCC
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
821 if ((!strcasecmp (type
, "SEND")) || (!strcasecmp (type
, "PSEND")))
825 int passive
= (!strcasecmp(type
, "PSEND")) ? 1 : 0;
831 maxcps
= prefs
.dcc_max_send_cps
;
832 if (!strncasecmp(nick
, "-maxcps=", 8))
834 maxcps
= atoi(nick
+ 8);
846 fe_dcc_send_filereq (sess
, nick
, maxcps
, passive
);
852 dcc_send (sess
, nick
, file
, maxcps
, passive
);
864 dcc_show_list (sess
);
869 cmd_debug (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
873 GSList
*list
= sess_list
;
875 PrintText (sess
, "Session T Channel WaitChan WillChan Server\n");
878 s
= (struct session
*) list
->data
;
879 sprintf (tbuf
, "%p %1x %-10.10s %-10.10s %-10.10s %p\n",
880 s
, s
->type
, s
->channel
, s
->waitchannel
,
881 s
->willjoinchannel
, s
->server
);
882 PrintText (sess
, tbuf
);
887 PrintText (sess
, "Server Sock Name\n");
890 v
= (struct server
*) list
->data
;
891 sprintf (tbuf
, "%p %-5d %s\n",
892 v
, v
->sok
, v
->servername
);
893 PrintText (sess
, tbuf
);
898 "\nfront_session: %p\n"
899 "current_tab: %p\n\n",
900 sess
->server
->front_session
, current_tab
);
901 PrintText (sess
, tbuf
);
903 sprintf (tbuf
, "current mem: %d\n\n", current_mem_usage
);
904 PrintText (sess
, tbuf
);
905 #endif /* !MEMORY_DEBUG */
911 cmd_delbutton (struct session
*sess
, char *tbuf
, char *word
[],
916 if (sess
->type
== SESS_DIALOG
)
918 if (list_delentry (&dlgbutton_list
, word
[2]))
919 fe_dlgbuttons_update (sess
);
922 if (list_delentry (&button_list
, word
[2]))
923 fe_buttons_update (sess
);
931 cmd_dehop (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
941 send_channel_modes (sess
, tbuf
, word
, 2, i
, '-', 'h', 0);
949 cmd_deop (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
959 send_channel_modes (sess
, tbuf
, word
, 2, i
, '-', 'o', 0);
976 mdehop_cb (struct User
*user
, multidata
*data
)
978 if (user
->hop
&& !user
->me
)
980 data
->nicks
[data
->i
] = user
->nick
;
987 cmd_mdehop (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
989 char **nicks
= malloc (sizeof (char *) * sess
->hops
);
994 tree_foreach (sess
->usertree
, (tree_traverse_func
*)mdehop_cb
, &data
);
995 send_channel_modes (sess
, tbuf
, nicks
, 0, data
.i
, '-', 'h', 0);
1002 mdeop_cb (struct User
*user
, multidata
*data
)
1004 if (user
->op
&& !user
->me
)
1006 data
->nicks
[data
->i
] = user
->nick
;
1013 cmd_mdeop (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1015 char **nicks
= malloc (sizeof (char *) * sess
->ops
);
1020 tree_foreach (sess
->usertree
, (tree_traverse_func
*)mdeop_cb
, &data
);
1021 send_channel_modes (sess
, tbuf
, nicks
, 0, data
.i
, '-', 'o', 0);
1027 GSList
*menu_list
= NULL
;
1030 menu_free (menu_entry
*me
)
1046 /* strings equal? but ignore underscores */
1049 menu_streq (const char *s1
, const char *s2
, int def
)
1051 /* for separators */
1052 if (s1
== NULL
&& s2
== NULL
)
1054 if (s1
== NULL
|| s2
== NULL
)
1073 menu_entry_find (char *path
, char *label
)
1082 if (!strcmp (path
, me
->path
))
1084 if (me
->label
&& label
&& !strcmp (label
, me
->label
))
1093 menu_del_children (char *path
, char *label
)
1095 GSList
*list
, *next
;
1102 snprintf (buf
, sizeof (buf
), "%s/%s", path
, label
);
1104 snprintf (buf
, sizeof (buf
), "%s", label
);
1111 if (!menu_streq (buf
, me
->path
, 0))
1113 menu_list
= g_slist_remove (menu_list
, me
);
1121 menu_del (char *path
, char *label
)
1130 if (!menu_streq (me
->label
, label
, 1) && !menu_streq (me
->path
, path
, 1))
1132 menu_list
= g_slist_remove (menu_list
, me
);
1135 /* delete this item's children, if any */
1136 menu_del_children (path
, label
);
1146 menu_is_mainmenu_root (char *path
, gint16
*offset
)
1148 static const char *menus
[] = {"\x4$TAB","\x5$TRAY","\x4$URL","\x5$NICK","\x5$CHAN"};
1151 for (i
= 0; i
< 5; i
++)
1153 if (!strncmp (path
, menus
[i
] + 1, menus
[i
][0]))
1155 *offset
= menus
[i
][0] + 1; /* number of bytes to offset the root */
1156 return 0; /* is not main menu */
1161 return 1; /* is main menu */
1165 menu_add (char *path
, char *label
, char *cmd
, char *ucmd
, int pos
, int state
, int markup
, int enable
, int mod
, int key
, char *group
, char *icon
)
1169 /* already exists? */
1170 me
= menu_entry_find (path
, label
);
1175 me
->enable
= enable
;
1176 fe_menu_update (me
);
1180 me
= malloc (sizeof (menu_entry
));
1183 me
->is_main
= menu_is_mainmenu_root (path
, &me
->root_offset
);
1185 me
->markup
= markup
;
1186 me
->enable
= enable
;
1188 me
->path
= strdup (path
);
1196 me
->label
= strdup (label
);
1198 me
->cmd
= strdup (cmd
);
1200 me
->ucmd
= strdup (ucmd
);
1202 me
->group
= strdup (group
);
1204 me
->icon
= strdup (icon
);
1206 menu_list
= g_slist_append (menu_list
, me
);
1207 label
= fe_menu_add (me
);
1210 /* FE has given us a stripped label */
1212 me
->label
= strdup (label
);
1213 g_free (label
); /* this is from pango */
1218 cmd_menu (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1233 if (!word
[2][0] || !word
[3][0])
1236 /* -eX enabled or not? */
1237 if (word
[idx
][0] == '-' && word
[idx
][1] == 'e')
1239 enable
= atoi (word
[idx
] + 2);
1244 if (word
[idx
][0] == '-' && word
[idx
][1] == 'i')
1246 icon
= word
[idx
] + 2;
1250 /* -k<mod>,<key> key binding */
1251 if (word
[idx
][0] == '-' && word
[idx
][1] == 'k')
1253 char *comma
= strchr (word
[idx
], ',');
1256 mod
= atoi (word
[idx
] + 2);
1257 key
= atoi (comma
+ 1);
1261 /* -m to specify PangoMarkup language */
1262 if (word
[idx
][0] == '-' && word
[idx
][1] == 'm')
1268 /* -pX to specify menu position */
1269 if (word
[idx
][0] == '-' && word
[idx
][1] == 'p')
1271 pos
= atoi (word
[idx
] + 2);
1275 /* -rSTATE,GROUP to specify a radio item */
1276 if (word
[idx
][0] == '-' && word
[idx
][1] == 'r')
1278 state
= atoi (word
[idx
] + 2);
1279 group
= word
[idx
] + 4;
1283 /* -tX to specify toggle item with default state */
1284 if (word
[idx
][0] == '-' && word
[idx
][1] == 't')
1286 state
= atoi (word
[idx
] + 2);
1291 if (word
[idx
+1][0] == 0)
1295 path_part (word
[idx
+1], tbuf
, 512);
1296 len
= strlen (tbuf
);
1300 /* the name of the item */
1301 label
= file_part (word
[idx
+ 1]);
1302 if (label
[0] == '-' && label
[1] == 0)
1303 label
= NULL
; /* separator */
1307 char *p
; /* to force pango closing tags through */
1308 for (p
= label
; *p
; p
++)
1313 if (!strcasecmp (word
[idx
], "ADD"))
1317 menu_add (tbuf
, label
, word
[idx
+ 2], word
[idx
+ 3], pos
, state
, markup
, enable
, mod
, key
, NULL
, NULL
);
1320 if (word
[idx
+ 2][0])
1321 menu_add (tbuf
, label
, word
[idx
+ 2], NULL
, pos
, state
, markup
, enable
, mod
, key
, group
, icon
);
1323 menu_add (tbuf
, label
, NULL
, NULL
, pos
, state
, markup
, enable
, mod
, key
, group
, icon
);
1328 if (!strcasecmp (word
[idx
], "DEL"))
1330 menu_del (tbuf
, label
);
1338 mkick_cb (struct User
*user
, multidata
*data
)
1340 if (!user
->op
&& !user
->me
)
1341 data
->sess
->server
->p_kick (data
->sess
->server
, data
->sess
->channel
, user
->nick
, data
->reason
);
1346 mkickops_cb (struct User
*user
, multidata
*data
)
1348 if (user
->op
&& !user
->me
)
1349 data
->sess
->server
->p_kick (data
->sess
->server
, data
->sess
->channel
, user
->nick
, data
->reason
);
1354 cmd_mkick (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1359 data
.reason
= word_eol
[2];
1360 tree_foreach (sess
->usertree
, (tree_traverse_func
*)mkickops_cb
, &data
);
1361 tree_foreach (sess
->usertree
, (tree_traverse_func
*)mkick_cb
, &data
);
1367 cmd_devoice (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1377 send_channel_modes (sess
, tbuf
, word
, 2, i
, '-', 'v', 0);
1385 cmd_discon (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1387 sess
->server
->disconnect (sess
, TRUE
, -1);
1392 cmd_dns (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1395 PrintText (sess
, "DNS is not implemented in Windows.\n");
1398 char *nick
= word
[2];
1403 if (strchr (nick
, '.') == NULL
)
1405 user
= userlist_find (sess
, nick
);
1406 if (user
&& user
->hostname
)
1408 do_dns (sess
, user
->nick
, user
->hostname
);
1411 sess
->server
->p_get_ip (sess
->server
, nick
);
1412 sess
->server
->doing_dns
= TRUE
;
1416 snprintf (tbuf
, TBUFSIZE
, "exec -d %s %s", prefs
.dnsprogram
, nick
);
1417 handle_command (sess
, tbuf
, FALSE
);
1426 cmd_echo (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1428 PrintText (sess
, word_eol
[2]);
1435 exec_check_process (struct session
*sess
)
1439 if (sess
->running_exec
== NULL
)
1441 val
= waitpid (sess
->running_exec
->childpid
, NULL
, WNOHANG
);
1442 if (val
== -1 || val
> 0)
1444 close (sess
->running_exec
->myfd
);
1445 fe_input_remove (sess
->running_exec
->iotag
);
1446 free (sess
->running_exec
);
1447 sess
->running_exec
= NULL
;
1453 cmd_execs (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1457 exec_check_process (sess
);
1458 if (sess
->running_exec
== NULL
)
1460 EMIT_SIGNAL (XP_TE_NOCHILD
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
1463 r
= kill (sess
->running_exec
->childpid
, SIGSTOP
);
1465 PrintText (sess
, "Error in kill(2)\n");
1471 cmd_execc (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1475 exec_check_process (sess
);
1476 if (sess
->running_exec
== NULL
)
1478 EMIT_SIGNAL (XP_TE_NOCHILD
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
1481 r
= kill (sess
->running_exec
->childpid
, SIGCONT
);
1483 PrintText (sess
, "Error in kill(2)\n");
1489 cmd_execk (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1493 exec_check_process (sess
);
1494 if (sess
->running_exec
== NULL
)
1496 EMIT_SIGNAL (XP_TE_NOCHILD
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
1499 if (strcmp (word
[2], "-9") == 0)
1500 r
= kill (sess
->running_exec
->childpid
, SIGKILL
);
1502 r
= kill (sess
->running_exec
->childpid
, SIGTERM
);
1504 PrintText (sess
, "Error in kill(2)\n");
1509 /* OS/2 Can't have the /EXECW command because it uses pipe(2) not socketpair
1510 and thus it is simplex --AGL */
1512 cmd_execw (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1516 exec_check_process (sess
);
1517 if (sess
->running_exec
== NULL
)
1519 EMIT_SIGNAL (XP_TE_NOCHILD
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
1522 len
= strlen(word_eol
[2]);
1523 temp
= malloc(len
+ 2);
1524 sprintf(temp
, "%s\n", word_eol
[2]);
1525 PrintText(sess
, temp
);
1526 write(sess
->running_exec
->myfd
, temp
, len
+ 1);
1531 #endif /* !__EMX__ */
1533 /* convert ANSI escape color codes to mIRC codes */
1535 static short escconv
[] =
1536 /* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 */
1537 { 1,4,3,5,2,10,6,1, 1,7,9,8,12,11,13,1 };
1540 exec_handle_colors (char *buf
, int len
)
1544 int i
= 0, j
= 0, k
= 0, firstn
= 0, col
, colf
= 0, colb
= 0;
1545 int esc
= FALSE
, backc
= FALSE
, bold
= FALSE
;
1547 /* any escape codes in this text? */
1548 if (strchr (buf
, 27) == 0)
1551 nbuf
= malloc (len
+ 1);
1567 firstn
= atoi (numb
);
1577 if (buf
[i
] >= 'A' && buf
[i
] <= 'z')
1582 if (k
== 0 || (numb
[0] == '0' && k
== 1))
1597 if (firstn
>= 30 && firstn
<= 37)
1606 if (col
>= 30 && col
<= 37)
1614 colb
= escconv
[colb
% 14];
1615 colf
= escconv
[colf
% 14];
1616 j
+= sprintf (&nbuf
[j
], "\003%d,%02d", colf
, colb
);
1619 colf
= escconv
[colf
% 14];
1620 j
+= sprintf (&nbuf
[j
], "\003%02d", colf
);
1628 if (isdigit ((unsigned char) buf
[i
]) && k
< (sizeof (numb
) - 1))
1636 norm
: nbuf
[j
] = buf
[i
];
1644 memcpy (buf
, nbuf
, j
+ 1);
1649 exec_data (GIOChannel
*source
, GIOCondition condition
, struct nbexec
*s
)
1651 char *buf
, *readpos
, *rest
;
1657 /* append new data to buffered incomplete line */
1658 buf
= malloc(len
+ 2050);
1659 memcpy(buf
, s
->linebuf
, len
);
1660 readpos
= buf
+ len
;
1665 readpos
= buf
= malloc(2050);
1667 rd
= read (sok
, readpos
, 2048);
1670 /* The process has died */
1671 kill(s
->childpid
, SIGKILL
);
1674 exec_handle_colors(buf
, len
);
1677 /* must turn off auto-completion temporarily */
1678 unsigned int old
= prefs
.nickcompletion
;
1679 prefs
.nickcompletion
= 0;
1680 handle_multiline (s
->sess
, buf
, FALSE
, TRUE
);
1681 prefs
.nickcompletion
= old
;
1684 PrintText (s
->sess
, buf
);
1687 waitpid (s
->childpid
, NULL
, 0);
1688 s
->sess
->running_exec
= NULL
;
1689 fe_input_remove (s
->iotag
);
1697 rest
= memrchr(buf
, '\n', len
);
1703 s
->buffill
= len
- (rest
- buf
); /* = strlen(rest) */
1704 s
->linebuf
= malloc(s
->buffill
+ 1);
1705 memcpy(s
->linebuf
, rest
, s
->buffill
);
1707 len
-= s
->buffill
; /* possibly 0 */
1713 exec_handle_colors (buf
, len
);
1715 handle_multiline (s
->sess
, buf
, FALSE
, TRUE
);
1717 PrintText (s
->sess
, buf
);
1725 cmd_exec (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1727 int tochannel
= FALSE
;
1728 char *cmd
= word_eol
[2];
1729 int fds
[2], pid
= 0;
1736 exec_check_process (sess
);
1737 if (sess
->running_exec
!= NULL
)
1739 EMIT_SIGNAL (XP_TE_ALREADYPROCESS
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
1743 if (!strcmp (word
[2], "-d"))
1750 else if (!strcmp (word
[2], "-o"))
1760 if (access ("/bin/sh", X_OK
) != 0)
1762 fe_message (_("I need /bin/sh to run!\n"), FE_MSG_ERROR
);
1767 #ifdef __EMX__ /* if os/2 */
1770 PrintText (sess
, "Pipe create error\n");
1773 setmode (fds
[0], O_BINARY
);
1774 setmode (fds
[1], O_BINARY
);
1776 if (socketpair (PF_UNIX
, SOCK_STREAM
, 0, fds
) == -1)
1778 PrintText (sess
, "socketpair(2) failed\n");
1782 s
= (struct nbexec
*) malloc (sizeof (struct nbexec
));
1783 memset(s
, 0, sizeof(*s
));
1785 s
->tochannel
= tochannel
;
1791 /* This is the child's context */
1795 /* Close parent's end of pipe */
1797 /* Copy the child end of the pipe to stdout and stderr */
1800 /* Also copy it to stdin so we can write to it */
1802 /* Now close all open file descriptors except stdin, stdout and stderr */
1803 for (fd
= 3; fd
< 1024; fd
++) close(fd
);
1804 /* Now we call /bin/sh to run our cmd ; made it more friendly -DC1 */
1807 execl ("/bin/sh", "sh", "-c", cmd
, NULL
);
1813 my_poptParseArgvString (cmd
, &argc
, &argv
);
1814 execvp (argv
[0], argv
);
1816 /* not reached unless error */
1817 /*printf("exec error\n");*/
1824 /* Parent context, fork() failed */
1826 PrintText (sess
, "Error in fork(2)\n");
1834 s
->iotag
= fe_input_add (s
->myfd
, FIA_READ
|FIA_EX
, exec_data
, s
);
1835 sess
->running_exec
= s
;
1845 cmd_flushq (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1847 sprintf (tbuf
, "Flushing server send queue, %d bytes.\n", sess
->server
->sendq_len
);
1848 PrintText (sess
, tbuf
);
1849 sess
->server
->flush_queue (sess
->server
);
1854 cmd_quit (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1857 sess
->quitreason
= word_eol
[2];
1858 sess
->server
->disconnect (sess
, TRUE
, -1);
1859 sess
->quitreason
= NULL
;
1864 cmd_gate (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1866 char *server_name
= word
[2];
1867 server
*serv
= sess
->server
;
1870 char *port
= word
[3];
1872 serv
->use_ssl
= FALSE
;
1874 server_fill_her_up (serv
);
1876 serv
->connect (serv
, server_name
, atoi (port
), TRUE
);
1878 serv
->connect (serv
, server_name
, 23, TRUE
);
1891 get_int_cb (int cancel
, int val
, getvalinfo
*info
)
1897 snprintf (buf
, sizeof (buf
), "%s %d", info
->cmd
, val
);
1898 if (is_session (info
->sess
))
1899 handle_command (info
->sess
, buf
, FALSE
);
1907 cmd_getint (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1914 info
= malloc (sizeof (*info
));
1915 info
->cmd
= strdup (word
[3]);
1918 fe_get_int (word
[4], atoi (word
[2]), get_int_cb
, info
);
1924 get_file_cb (char *cmd
, char *file
)
1926 char buf
[1024 + 128];
1928 /* execute the command once per file, then once more with
1932 snprintf (buf
, sizeof (buf
), "%s %s", cmd
, file
);
1933 handle_command (current_sess
, buf
, FALSE
);
1937 handle_command (current_sess
, cmd
, FALSE
);
1943 cmd_getfile (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1951 if (!strcmp (word
[2], "-folder"))
1953 flags
|= FRF_CHOOSEFOLDER
;
1957 if (!strcmp (word
[idx
], "-multi"))
1959 flags
|= FRF_MULTIPLE
;
1963 if (!strcmp (word
[idx
], "-save"))
1969 fe_get_file (word
[idx
+1], word
[idx
+2], (void *)get_file_cb
, strdup (word
[idx
]), flags
);
1975 get_str_cb (int cancel
, char *val
, getvalinfo
*info
)
1981 snprintf (buf
, sizeof (buf
), "%s %s", info
->cmd
, val
);
1982 if (is_session (info
->sess
))
1983 handle_command (info
->sess
, buf
, FALSE
);
1991 cmd_getstr (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
1998 info
= malloc (sizeof (*info
));
1999 info
->cmd
= strdup (word
[3]);
2002 fe_get_str (word
[4], word
[2], get_str_cb
, info
);
2008 cmd_ghost (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2013 sess
->server
->p_ns_ghost (sess
->server
, word
[2], word
[3]);
2018 cmd_gui (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2020 switch (str_ihash (word
[2]))
2022 case 0x058b836e: fe_ctrl_gui (sess
, 8, 0); break; /* APPLY */
2023 case 0xac1eee45: fe_ctrl_gui (sess
, 7, 2); break; /* ATTACH */
2024 case 0x05a72f63: fe_ctrl_gui (sess
, 4, atoi (word
[3])); break; /* COLOR */
2025 case 0xb06a1793: fe_ctrl_gui (sess
, 7, 1); break; /* DETACH */
2026 case 0x05cfeff0: fe_ctrl_gui (sess
, 3, 0); break; /* FLASH */
2027 case 0x05d154d8: fe_ctrl_gui (sess
, 2, 0); break; /* FOCUS */
2028 case 0x0030dd42: fe_ctrl_gui (sess
, 0, 0); break; /* HIDE */
2029 case 0x61addbe3: fe_ctrl_gui (sess
, 5, 0); break; /* ICONIFY */
2030 case 0xc0851aaa: fe_message (word
[3], FE_MSG_INFO
|FE_MSG_MARKUP
); break; /* MSGBOX */
2031 case 0x0035dafd: fe_ctrl_gui (sess
, 1, 0); break; /* SHOW */
2032 case 0x0033155f: /* MENU */
2033 if (!strcasecmp (word
[3], "TOGGLE"))
2034 fe_ctrl_gui (sess
, 6, 0);
2053 show_help_line (session
*sess
, help_list
*hl
, char *name
, char *usage
)
2058 if (name
[0] == '.') /* hidden command? */
2061 if (hl
->longfmt
) /* long format for /HELP -l */
2063 if (!usage
|| usage
[0] == 0)
2064 PrintTextf (sess
, " \0034%s\003 :\n", name
);
2066 PrintTextf (sess
, " \0034%s\003 : %s\n", name
, _(usage
));
2070 /* append the name into buffer, but convert to uppercase */
2071 len
= strlen (hl
->buf
);
2075 hl
->buf
[len
] = toupper ((unsigned char) *p
);
2085 strcat (hl
->buf
, "\n");
2086 PrintText (sess
, hl
->buf
);
2092 /* append some spaces after the command name */
2093 max
= strlen (name
);
2097 for (j
= 0; j
< max
; j
++)
2108 cmd_help (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2110 int i
= 0, longfmt
= 0;
2116 if (*helpcmd
&& strcmp (helpcmd
, "-l") == 0)
2119 if (*helpcmd
&& !longfmt
)
2121 help (sess
, tbuf
, helpcmd
, FALSE
);
2125 char *buf
= malloc (4096);
2128 hl
.longfmt
= longfmt
;
2131 PrintTextf (sess
, "\n%s\n\n", _("Commands Available:"));
2137 while (xc_cmds
[i
].name
)
2139 show_help_line (sess
, &hl
, xc_cmds
[i
].name
, xc_cmds
[i
].help
);
2143 PrintText (sess
, buf
);
2145 PrintTextf (sess
, "\n%s\n\n", _("User defined commands:"));
2151 list
= command_list
;
2155 show_help_line (sess
, &hl
, pop
->name
, pop
->cmd
);
2159 PrintText (sess
, buf
);
2161 PrintTextf (sess
, "\n%s\n\n", _("Plugin defined commands:"));
2167 plugin_command_foreach (sess
, &hl
, (void *)show_help_line
);
2169 PrintText (sess
, buf
);
2172 PrintTextf (sess
, "\n%s\n\n", _("Type /HELP <command> for more information, or /HELP -l"));
2178 cmd_id (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2182 sess
->server
->p_ns_identify (sess
->server
, word
[2]);
2190 cmd_ignore (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2199 ignore_showlist (sess
);
2214 if (strchr (mask
, '?') == NULL
&&
2215 strchr (mask
, '*') == NULL
)
2218 snprintf (tbuf
, TBUFSIZE
, "%s!*@*", word
[2]);
2221 i
= ignore_add (mask
, type
);
2227 EMIT_SIGNAL (XP_TE_IGNOREADD
, sess
, mask
, NULL
, NULL
, NULL
, 0);
2229 case 2: /* old ignore changed */
2230 EMIT_SIGNAL (XP_TE_IGNORECHANGE
, sess
, mask
, NULL
, NULL
, NULL
, 0);
2234 if (!strcasecmp (word
[i
], "UNIGNORE"))
2236 else if (!strcasecmp (word
[i
], "ALL"))
2237 type
|= IG_PRIV
| IG_NOTI
| IG_CHAN
| IG_CTCP
| IG_INVI
| IG_DCC
;
2238 else if (!strcasecmp (word
[i
], "PRIV"))
2240 else if (!strcasecmp (word
[i
], "NOTI"))
2242 else if (!strcasecmp (word
[i
], "CHAN"))
2244 else if (!strcasecmp (word
[i
], "CTCP"))
2246 else if (!strcasecmp (word
[i
], "INVI"))
2248 else if (!strcasecmp (word
[i
], "QUIET"))
2250 else if (!strcasecmp (word
[i
], "NOSAVE"))
2252 else if (!strcasecmp (word
[i
], "DCC"))
2256 sprintf (tbuf
, _("Unknown arg '%s' ignored."), word
[i
]);
2257 PrintText (sess
, tbuf
);
2264 cmd_invite (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2269 sess
->server
->p_invite (sess
->server
, word
[3], word
[2]);
2271 sess
->server
->p_invite (sess
->server
, sess
->channel
, word
[2]);
2276 cmd_join (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2278 char *chan
= word
[2];
2281 char *po
, *pass
= word
[3];
2282 sess
->server
->p_join (sess
->server
, chan
, pass
);
2283 if (sess
->channel
[0] == 0 && sess
->waitchannel
[0])
2285 po
= strchr (chan
, ',');
2288 safe_strcpy (sess
->waitchannel
, chan
, CHANLEN
);
2296 cmd_kick (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2298 char *nick
= word
[2];
2299 char *reason
= word_eol
[3];
2302 sess
->server
->p_kick (sess
->server
, sess
->channel
, nick
, reason
);
2309 cmd_kickban (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2311 char *nick
= word
[2];
2312 char *reason
= word_eol
[3];
2317 /* if the reason is a 1 digit number, treat it as a bantype */
2319 user
= userlist_find (sess
, nick
);
2321 if (isdigit ((unsigned char) reason
[0]) && reason
[1] == 0)
2323 ban (sess
, tbuf
, nick
, reason
, (user
&& user
->op
));
2326 ban (sess
, tbuf
, nick
, "", (user
&& user
->op
));
2328 sess
->server
->p_kick (sess
->server
, sess
->channel
, nick
, reason
);
2336 cmd_killall (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2343 cmd_lagcheck (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2350 lastlog (session
*sess
, char *search
, gboolean regexp
)
2352 session
*lastlog_sess
;
2354 if (!is_session (sess
))
2357 lastlog_sess
= find_dialog (sess
->server
, "(lastlog)");
2359 lastlog_sess
= new_ircwindow (sess
->server
, "(lastlog)", SESS_DIALOG
, 0);
2361 lastlog_sess
->lastlog_sess
= sess
;
2362 lastlog_sess
->lastlog_regexp
= regexp
; /* remember the search type */
2364 fe_text_clear (lastlog_sess
, 0);
2365 fe_lastlog (sess
, lastlog_sess
, search
, regexp
);
2369 cmd_lastlog (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2373 if (!strcmp (word
[2], "-r"))
2374 lastlog (sess
, word_eol
[3], TRUE
);
2376 lastlog (sess
, word_eol
[2], FALSE
);
2384 cmd_list (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2386 sess
->server
->p_list_channels (sess
->server
, word_eol
[2], 1);
2392 load_perform_file (session
*sess
, char *file
)
2394 char tbuf
[1024 + 4];
2398 fp
= xchat_fopen_file (file
, "r", XOF_FULLPATH
);
2403 while (fgets (tbuf
, 1024, fp
))
2405 nl
= strchr (tbuf
, '\n');
2406 if (nl
== tbuf
) /* skip empty commands */
2410 if (tbuf
[0] == prefs
.cmdchar
[0])
2411 handle_command (sess
, tbuf
+ 1, TRUE
);
2413 handle_command (sess
, tbuf
, TRUE
);
2420 cmd_load (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2422 char *error
, *arg
, *file
;
2428 if (strcmp (word
[2], "-e") == 0)
2430 file
= expand_homedir (word
[3]);
2431 if (!load_perform_file (sess
, file
))
2433 PrintTextf (sess
, _("Cannot access %s\n"), file
);
2434 PrintText (sess
, errorstring (errno
));
2441 len
= strlen (word
[2]);
2443 if (len
> 4 && strcasecmp (".dll", word
[2] + len
- 4) == 0)
2446 if (len
> 3 && strcasecmp (".sl", word
[2] + len
- 3) == 0)
2448 if (len
> 3 && strcasecmp (".so", word
[2] + len
- 3) == 0)
2456 file
= expand_homedir (word
[2]);
2457 error
= plugin_load (sess
, file
, arg
);
2461 PrintText (sess
, error
);
2467 sprintf (tbuf
, "Unknown file type %s. Maybe you need to install the Perl or Python plugin?\n", word
[2]);
2468 PrintText (sess
, tbuf
);
2474 cmd_me (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2476 char *act
= word_eol
[2];
2481 if (sess
->type
== SESS_SERVER
)
2487 snprintf (tbuf
, TBUFSIZE
, "\001ACTION %s\001\r", act
);
2488 /* first try through DCC CHAT */
2489 if (dcc_write_chat (sess
->channel
, tbuf
))
2491 /* print it to screen */
2492 inbound_action (sess
, sess
->channel
, sess
->server
->nick
, "", act
, TRUE
, FALSE
);
2495 /* DCC CHAT failed, try through server */
2496 if (sess
->server
->connected
)
2498 sess
->server
->p_action (sess
->server
, sess
->channel
, act
);
2499 /* print it to screen */
2500 inbound_action (sess
, sess
->channel
, sess
->server
->nick
, "", act
, TRUE
, FALSE
);
2511 cmd_mode (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2513 /* +channel channels are dying, let those servers whine about modes.
2514 * return info about current channel if available and no info is given */
2515 if ((*word
[2] == '+') || (*word
[2] == 0) || (!is_channel(sess
->server
, word
[2]) &&
2516 !(rfc_casecmp(sess
->server
->nick
, word
[2]) == 0)))
2518 if(sess
->channel
[0] == 0)
2520 sess
->server
->p_mode (sess
->server
, sess
->channel
, word_eol
[2]);
2523 sess
->server
->p_mode (sess
->server
, word
[2], word_eol
[3]);
2528 mop_cb (struct User
*user
, multidata
*data
)
2532 data
->nicks
[data
->i
] = user
->nick
;
2539 cmd_mop (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2541 char **nicks
= malloc (sizeof (char *) * (sess
->total
- sess
->ops
));
2546 tree_foreach (sess
->usertree
, (tree_traverse_func
*)mop_cb
, &data
);
2547 send_channel_modes (sess
, tbuf
, nicks
, 0, data
.i
, '+', 'o', 0);
2555 cmd_msg (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2557 char *nick
= word
[2];
2558 char *msg
= word_eol
[3];
2559 struct session
*newsess
;
2565 if (strcmp (nick
, ".") == 0)
2566 { /* /msg the last nick /msg'ed */
2567 if (sess
->lastnick
[0])
2568 nick
= sess
->lastnick
;
2571 safe_strcpy (sess
->lastnick
, nick
, NICKLEN
); /* prime the last nick memory */
2577 if (!dcc_write_chat (nick
, msg
))
2579 EMIT_SIGNAL (XP_TE_NODCC
, sess
, NULL
, NULL
, NULL
, NULL
, 0);
2584 if (!sess
->server
->connected
)
2589 sess
->server
->p_message (sess
->server
, nick
, msg
);
2591 newsess
= find_dialog (sess
->server
, nick
);
2593 newsess
= find_channel (sess
->server
, nick
);
2595 inbound_chanmsg (newsess
->server
, NULL
, newsess
->channel
,
2596 newsess
->server
->nick
, msg
, TRUE
, FALSE
);
2599 /* mask out passwords */
2600 if (strcasecmp (nick
, "nickserv") == 0 &&
2601 strncasecmp (msg
, "identify ", 9) == 0)
2602 msg
= "identify ****";
2603 EMIT_SIGNAL (XP_TE_MSGSEND
, sess
, nick
, msg
, NULL
, NULL
, 0);
2613 cmd_names (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2616 sess
->server
->p_names (sess
->server
, word
[2]);
2618 sess
->server
->p_names (sess
->server
, sess
->channel
);
2623 cmd_nctcp (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2627 sess
->server
->p_nctcp (sess
->server
, word
[2], word_eol
[3]);
2634 cmd_newserver (struct session
*sess
, char *tbuf
, char *word
[],
2637 if (strcmp (word
[2], "-noconnect") == 0)
2639 new_ircwindow (NULL
, word
[3], SESS_SERVER
, 0);
2643 sess
= new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
2644 cmd_server (sess
, tbuf
, word
, word_eol
);
2649 cmd_nick (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2651 char *nick
= word
[2];
2654 if (sess
->server
->connected
)
2655 sess
->server
->p_change_nick (sess
->server
, nick
);
2657 inbound_newnick (sess
->server
, sess
->server
->nick
, nick
, TRUE
);
2664 cmd_notice (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2666 if (*word
[2] && *word_eol
[3])
2668 sess
->server
->p_notice (sess
->server
, word
[2], word_eol
[3]);
2669 EMIT_SIGNAL (XP_TE_NOTICESEND
, sess
, word
[2], word_eol
[3], NULL
, NULL
, 0);
2676 cmd_notify (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2683 if (strcmp (word
[2], "-n") == 0) /* comma sep network list */
2694 if (notify_deluser (word
[i
]))
2696 EMIT_SIGNAL (XP_TE_DELNOTIFY
, sess
, word
[i
], NULL
, NULL
, NULL
, 0);
2700 if (net
&& strcmp (net
, "ASK") == 0)
2701 fe_notify_ask (word
[i
], NULL
);
2704 notify_adduser (word
[i
], net
);
2705 EMIT_SIGNAL (XP_TE_ADDNOTIFY
, sess
, word
[i
], NULL
, NULL
, NULL
, 0);
2709 notify_showlist (sess
);
2714 cmd_op (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2724 send_channel_modes (sess
, tbuf
, word
, 2, i
, '+', 'o', 0);
2732 cmd_part (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2734 char *chan
= word
[2];
2735 char *reason
= word_eol
[3];
2737 chan
= sess
->channel
;
2738 if ((*chan
) && is_channel (sess
->server
, chan
))
2742 server_sendpart (sess
->server
, chan
, reason
);
2749 cmd_ping (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2751 char timestring
[64];
2755 tim
= make_ping_time ();
2757 snprintf (timestring
, sizeof (timestring
), "%lu", tim
);
2758 sess
->server
->p_ping (sess
->server
, to
, timestring
);
2764 open_query (server
*serv
, char *nick
, gboolean focus_existing
)
2768 sess
= find_dialog (serv
, nick
);
2770 new_ircwindow (serv
, nick
, SESS_DIALOG
, 1);
2771 else if (focus_existing
)
2772 fe_ctrl_gui (sess
, 2, 0); /* bring-to-front */
2776 cmd_query (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2778 char *nick
= word
[2];
2779 gboolean focus
= TRUE
;
2781 if (strcmp (word
[2], "-nofocus") == 0)
2787 if (*nick
&& !is_channel (sess
->server
, nick
))
2789 open_query (sess
->server
, nick
, focus
);
2796 cmd_quote (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2798 char *raw
= word_eol
[2];
2800 return sess
->server
->p_raw (sess
->server
, raw
);
2804 cmd_reconnect (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2806 int tmp
= prefs
.recon_delay
;
2808 server
*serv
= sess
->server
;
2810 prefs
.recon_delay
= 0;
2812 if (!strcasecmp (word
[2], "ALL"))
2818 if (serv
->connected
)
2819 serv
->auto_reconnect (serv
, TRUE
, -1);
2823 /* If it isn't "ALL" and there is something
2824 there it *should* be a server they are trying to connect to*/
2829 int use_ssl
= FALSE
;
2831 if (strcmp (word
[2], "-ssl") == 0)
2834 offset
++; /* args move up by 1 word */
2836 serv
->use_ssl
= use_ssl
;
2837 serv
->accept_invalid_cert
= TRUE
;
2840 if (*word
[4+offset
])
2841 safe_strcpy (serv
->password
, word
[4+offset
], sizeof (serv
->password
));
2842 if (*word
[3+offset
])
2843 serv
->port
= atoi (word
[3+offset
]);
2844 safe_strcpy (serv
->hostname
, word
[2+offset
], sizeof (serv
->hostname
));
2845 serv
->auto_reconnect (serv
, TRUE
, -1);
2849 serv
->auto_reconnect (serv
, TRUE
, -1);
2851 prefs
.recon_delay
= tmp
;
2857 cmd_recv (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2861 sess
->server
->p_inline (sess
->server
, word_eol
[2], strlen (word_eol
[2]));
2869 cmd_say (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2871 char *speech
= word_eol
[2];
2874 handle_say (sess
, speech
, FALSE
);
2881 cmd_send (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2885 struct sockaddr_in SAddr
;
2890 addr
= dcc_get_my_address ();
2893 /* use the one from our connected server socket */
2894 memset (&SAddr
, 0, sizeof (struct sockaddr_in
));
2895 len
= sizeof (SAddr
);
2896 getsockname (sess
->server
->sok
, (struct sockaddr
*) &SAddr
, &len
);
2897 addr
= SAddr
.sin_addr
.s_addr
;
2899 addr
= ntohl (addr
);
2901 if ((addr
& 0xffff0000) == 0xc0a80000 || /* 192.168.x.x */
2902 (addr
& 0xff000000) == 0x0a000000) /* 10.x.x.x */
2903 /* we got a private net address, let's PSEND or it'll fail */
2904 snprintf (tbuf
, 512, "DCC PSEND %s", word_eol
[2]);
2906 snprintf (tbuf
, 512, "DCC SEND %s", word_eol
[2]);
2908 handle_command (sess
, tbuf
, FALSE
);
2914 cmd_setcursor (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2920 if (word
[2][0] == '-' || word
[2][0] == '+')
2922 fe_set_inputbox_cursor (sess
, delta
, atoi (word
[2]));
2930 cmd_settab (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2934 strcpy (tbuf
, sess
->channel
);
2935 safe_strcpy (sess
->channel
, word_eol
[2], CHANLEN
);
2936 fe_set_channel (sess
);
2937 strcpy (sess
->channel
, tbuf
);
2944 cmd_settext (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2946 fe_set_inputbox_contents (sess
, word_eol
[2]);
2951 cmd_splay (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
2955 sound_play (word
[2], FALSE
);
2963 parse_irc_url (char *url
, char *server_name
[], char *port
[], char *channel
[], int *use_ssl
)
2967 if (strncasecmp ("ircs://", url
, 7) == 0)
2970 *server_name
= url
+ 7;
2975 if (strncasecmp ("irc://", url
, 6) == 0)
2977 *server_name
= url
+ 6;
2981 /* check for port */
2982 co
= strchr (*server_name
, ':');
2989 /* check for channel - mirc style */
2990 co
= strchr (co
+ 1, '/');
3007 cmd_server (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3010 char *server_name
= NULL
;
3013 char *channel
= NULL
;
3014 int use_ssl
= FALSE
;
3016 server
*serv
= sess
->server
;
3019 /* BitchX uses -ssl, mIRC uses -e, let's support both */
3020 if (strcmp (word
[2], "-ssl") == 0 || strcmp (word
[2], "-e") == 0)
3023 offset
++; /* args move up by 1 word */
3027 if (!parse_irc_url (word
[2 + offset
], &server_name
, &port
, &channel
, &use_ssl
))
3030 server_name
= word
[2 + offset
];
3033 pass
= word
[3 + offset
];
3036 port
= word
[3 + offset
];
3037 pass
= word
[4 + offset
];
3040 if (!(*server_name
))
3043 sess
->server
->network
= NULL
;
3045 /* dont clear it for /servchan */
3046 if (strncasecmp (word_eol
[1], "SERVCHAN ", 9))
3047 sess
->willjoinchannel
[0] = 0;
3051 sess
->willjoinchannel
[0] = '#';
3052 safe_strcpy ((sess
->willjoinchannel
+ 1), channel
, (CHANLEN
- 1));
3055 /* support +7000 style ports like mIRC */
3066 safe_strcpy (serv
->password
, pass
, sizeof (serv
->password
));
3069 serv
->use_ssl
= use_ssl
;
3070 serv
->accept_invalid_cert
= TRUE
;
3073 /* try to connect by Network name */
3074 if (servlist_connect_by_netname (sess
, server_name
, !is_url
))
3079 serv
->connect (serv
, server_name
, atoi (port
), FALSE
);
3082 /* -1 for default port */
3083 serv
->connect (serv
, server_name
, -1, FALSE
);
3086 /* try to associate this connection with a listed network */
3088 /* search for this hostname in the entire server list */
3089 serv
->network
= servlist_net_find_from_server (server_name
);
3090 /* may return NULL, but that's OK */
3096 cmd_servchan (struct session
*sess
, char *tbuf
, char *word
[],
3102 if (strcmp (word
[2], "-ssl") == 0)
3106 if (*word
[4 + offset
])
3108 safe_strcpy (sess
->willjoinchannel
, word
[4 + offset
], CHANLEN
);
3109 return cmd_server (sess
, tbuf
, word
, word_eol
);
3116 cmd_topic (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3118 if (word
[2][0] && is_channel (sess
->server
, word
[2]))
3119 sess
->server
->p_topic (sess
->server
, word
[2], word_eol
[3]);
3121 sess
->server
->p_topic (sess
->server
, sess
->channel
, word_eol
[2]);
3126 cmd_tray (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3128 if (strcmp (word
[2], "-b") == 0)
3130 fe_tray_set_balloon (word
[3], word
[4][0] ? word
[4] : NULL
);
3134 if (strcmp (word
[2], "-t") == 0)
3136 fe_tray_set_tooltip (word
[3][0] ? word
[3] : NULL
);
3140 if (strcmp (word
[2], "-i") == 0)
3142 fe_tray_set_icon (atoi (word
[3]));
3146 if (strcmp (word
[2], "-f") != 0)
3151 fe_tray_set_file (NULL
); /* default xchat icon */
3157 fe_tray_set_file (word
[3]); /* fixed custom icon */
3161 /* flash between 2 icons */
3162 fe_tray_set_flash (word
[4], word
[5][0] ? word
[5] : NULL
, atoi (word
[3]));
3167 cmd_unignore (struct session
*sess
, char *tbuf
, char *word
[],
3170 char *mask
= word
[2];
3171 char *arg
= word
[3];
3174 if (ignore_del (mask
, NULL
))
3176 if (strcasecmp (arg
, "QUIET"))
3177 EMIT_SIGNAL (XP_TE_IGNOREREMOVE
, sess
, mask
, NULL
, NULL
, NULL
, 0);
3185 cmd_unload (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3188 int len
, by_file
= FALSE
;
3190 len
= strlen (word
[2]);
3192 if (len
> 4 && strcasecmp (word
[2] + len
- 4, ".dll") == 0)
3195 if (len
> 3 && strcasecmp (word
[2] + len
- 3, ".sl") == 0)
3197 if (len
> 3 && strcasecmp (word
[2] + len
- 3, ".so") == 0)
3202 switch (plugin_kill (word
[2], by_file
))
3205 PrintText (sess
, _("No such plugin found.\n"));
3210 PrintText (sess
, _("That plugin is refusing to unload.\n"));
3219 find_server_from_hostname (char *hostname
)
3221 GSList
*list
= serv_list
;
3227 if (!strcasecmp (hostname
, serv
->hostname
) && serv
->connected
)
3236 find_server_from_net (void *net
)
3238 GSList
*list
= serv_list
;
3244 if (serv
->network
== net
&& serv
->connected
)
3253 url_join_only (server
*serv
, char *tbuf
, char *channel
)
3255 /* already connected, JOIN only. FIXME: support keys? */
3258 safe_strcpy ((tbuf
+ 1), channel
, 256);
3259 serv
->p_join (serv
, tbuf
, "");
3263 cmd_url (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3267 char *server_name
= NULL
;
3269 char *channel
= NULL
;
3270 char *url
= g_strdup (word
[2]);
3271 int use_ssl
= FALSE
;
3275 if (parse_irc_url (url
, &server_name
, &port
, &channel
, &use_ssl
))
3277 /* maybe we're already connected to this net */
3279 /* check for "FreeNode" */
3280 net
= servlist_net_find (server_name
, NULL
, strcasecmp
);
3281 /* check for "irc.eu.freenode.net" */
3283 net
= servlist_net_find_from_server (server_name
);
3287 /* found the network, but are we connected? */
3288 serv
= find_server_from_net (net
);
3291 url_join_only (serv
, tbuf
, channel
);
3298 /* an un-listed connection */
3299 serv
= find_server_from_hostname (server_name
);
3302 url_join_only (serv
, tbuf
, channel
);
3308 /* not connected to this net, open new window */
3309 cmd_newserver (sess
, tbuf
, word
, word_eol
);
3312 fe_open_url (word
[2]);
3321 userlist_cb (struct User
*user
, session
*sess
)
3325 if (!user
->lasttalk
)
3328 lt
= time (0) - user
->lasttalk
;
3330 "\00306%s\t\00314[\00310%-38s\00314] \017ov\0033=\017%d%d away=%u lt\0033=\017%d\n",
3331 user
->nick
, user
->hostname
, user
->op
, user
->voice
, user
->away
, lt
);
3337 cmd_uselect (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3343 if (strcmp (word
[2], "-a") == 0) /* ADD (don't clear selections) */
3348 if (strcmp (word
[idx
], "-s") == 0) /* SCROLL TO */
3353 /* always valid, no args means clear the selection list */
3354 fe_uselect (sess
, word
+ idx
, clear
, scroll
);
3359 cmd_userlist (struct session
*sess
, char *tbuf
, char *word
[],
3362 tree_foreach (sess
->usertree
, (tree_traverse_func
*)userlist_cb
, sess
);
3367 wallchop_cb (struct User
*user
, multidata
*data
)
3372 strcat (data
->tbuf
, ",");
3373 strcat (data
->tbuf
, user
->nick
);
3379 sprintf (data
->tbuf
+ strlen (data
->tbuf
),
3380 " :[@%s] %s", data
->sess
->channel
, data
->reason
);
3381 data
->sess
->server
->p_raw (data
->sess
->server
, data
->tbuf
);
3382 strcpy (data
->tbuf
, "NOTICE ");
3389 cmd_wallchop (struct session
*sess
, char *tbuf
, char *word
[],
3394 if (!(*word_eol
[2]))
3397 strcpy (tbuf
, "NOTICE ");
3399 data
.reason
= word_eol
[2];
3403 tree_foreach (sess
->usertree
, (tree_traverse_func
*)wallchop_cb
, &data
);
3407 sprintf (tbuf
+ strlen (tbuf
),
3408 " :[@%s] %s", sess
->channel
, word_eol
[2]);
3409 sess
->server
->p_raw (sess
->server
, tbuf
);
3416 cmd_wallchan (struct session
*sess
, char *tbuf
, char *word
[],
3427 if (sess
->type
== SESS_CHANNEL
)
3429 inbound_chanmsg (sess
->server
, NULL
, sess
->channel
,
3430 sess
->server
->nick
, word_eol
[2], TRUE
, FALSE
);
3431 sess
->server
->p_message (sess
->server
, sess
->channel
, word_eol
[2]);
3441 cmd_hop (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3451 send_channel_modes (sess
, tbuf
, word
, 2, i
, '+', 'h', 0);
3459 cmd_voice (struct session
*sess
, char *tbuf
, char *word
[], char *word_eol
[])
3469 send_channel_modes (sess
, tbuf
, word
, 2, i
, '+', 'v', 0);
3476 /* *MUST* be kept perfectly sorted for the bsearch to work */
3477 const struct commands xc_cmds
[] = {
3478 {"ADDBUTTON", cmd_addbutton
, 0, 0, 1,
3479 N_("ADDBUTTON <name> <action>, adds a button under the user-list")},
3480 {"ALLCHAN", cmd_allchannels
, 0, 0, 1,
3481 N_("ALLCHAN <cmd>, sends a command to all channels you're in")},
3482 {"ALLCHANL", cmd_allchannelslocal
, 0, 0, 1,
3483 N_("ALLCHANL <cmd>, sends a command to all channels on the current server")},
3484 {"ALLSERV", cmd_allservers
, 0, 0, 1,
3485 N_("ALLSERV <cmd>, sends a command to all servers you're in")},
3486 {"AWAY", cmd_away
, 1, 0, 1, N_("AWAY [<reason>], sets you away")},
3487 {"BACK", cmd_back
, 1, 0, 1, N_("BACK, sets you back (not away)")},
3488 {"BAN", cmd_ban
, 1, 1, 1,
3489 N_("BAN <mask> [<bantype>], bans everyone matching the mask from the current channel. If they are already on the channel this doesn't kick them (needs chanop)")},
3490 {"CHANOPT", cmd_chanopt
, 0, 0, 1, N_("CHANOPT [-quiet] <variable> [<value>]")},
3491 {"CHARSET", cmd_charset
, 0, 0, 1, N_("CHARSET [<encoding>], get or set the encoding used for the current connection")},
3492 {"CLEAR", cmd_clear
, 0, 0, 1, N_("CLEAR [ALL|HISTORY], Clears the current text window or command history")},
3493 {"CLOSE", cmd_close
, 0, 0, 1, N_("CLOSE, Closes the current window/tab")},
3495 {"COUNTRY", cmd_country
, 0, 0, 1,
3496 N_("COUNTRY [-s] <code|wildcard>, finds a country code, eg: au = australia")},
3497 {"CTCP", cmd_ctcp
, 1, 0, 1,
3498 N_("CTCP <nick> <message>, send the CTCP message to nick, common messages are VERSION and USERINFO")},
3499 {"CYCLE", cmd_cycle
, 1, 1, 1,
3500 N_("CYCLE [<channel>], parts the current or given channel and immediately rejoins")},
3501 {"DCC", cmd_dcc
, 0, 0, 1,
3503 "DCC GET <nick> - accept an offered file\n"
3504 "DCC SEND [-maxcps=#] <nick> [file] - send a file to someone\n"
3505 "DCC PSEND [-maxcps=#] <nick> [file] - send a file using passive mode\n"
3506 "DCC LIST - show DCC list\n"
3507 "DCC CHAT <nick> - offer DCC CHAT to someone\n"
3508 "DCC PCHAT <nick> - offer DCC CHAT using passive mode\n"
3509 "DCC CLOSE <type> <nick> <file> example:\n"
3510 " /dcc close send johnsmith file.tar.gz")},
3511 {"DEBUG", cmd_debug
, 0, 0, 1, 0},
3513 {"DEHOP", cmd_dehop
, 1, 1, 1,
3514 N_("DEHOP <nick>, removes chanhalf-op status from the nick on the current channel (needs chanop)")},
3515 {"DELBUTTON", cmd_delbutton
, 0, 0, 1,
3516 N_("DELBUTTON <name>, deletes a button from under the user-list")},
3517 {"DEOP", cmd_deop
, 1, 1, 1,
3518 N_("DEOP <nick>, removes chanop status from the nick on the current channel (needs chanop)")},
3519 {"DEVOICE", cmd_devoice
, 1, 1, 1,
3520 N_("DEVOICE <nick>, removes voice status from the nick on the current channel (needs chanop)")},
3521 {"DISCON", cmd_discon
, 0, 0, 1, N_("DISCON, Disconnects from server")},
3522 {"DNS", cmd_dns
, 0, 0, 1, N_("DNS <nick|host|ip>, Finds a users IP number")},
3523 {"ECHO", cmd_echo
, 0, 0, 1, N_("ECHO <text>, Prints text locally")},
3525 {"EXEC", cmd_exec
, 0, 0, 1,
3526 N_("EXEC [-o] <command>, runs the command. If -o flag is used then output is sent to current channel, else is printed to current text box")},
3528 {"EXECCONT", cmd_execc
, 0, 0, 1, N_("EXECCONT, sends the process SIGCONT")},
3530 {"EXECKILL", cmd_execk
, 0, 0, 1,
3531 N_("EXECKILL [-9], kills a running exec in the current session. If -9 is given the process is SIGKILL'ed")},
3533 {"EXECSTOP", cmd_execs
, 0, 0, 1, N_("EXECSTOP, sends the process SIGSTOP")},
3534 {"EXECWRITE", cmd_execw
, 0, 0, 1, N_("EXECWRITE, sends data to the processes stdin")},
3537 {"FLUSHQ", cmd_flushq
, 0, 0, 1,
3538 N_("FLUSHQ, flushes the current server's send queue")},
3539 {"GATE", cmd_gate
, 0, 0, 1,
3540 N_("GATE <host> [<port>], proxies through a host, port defaults to 23")},
3541 {"GETFILE", cmd_getfile
, 0, 0, 1, "GETFILE [-folder] [-multi] [-save] <command> <title> [<initial>]"},
3542 {"GETINT", cmd_getint
, 0, 0, 1, "GETINT <default> <command> <prompt>"},
3543 {"GETSTR", cmd_getstr
, 0, 0, 1, "GETSTR <default> <command> <prompt>"},
3544 {"GHOST", cmd_ghost
, 1, 0, 1, N_("GHOST <nick> [password], Kills a ghosted nickname")},
3545 {"GUI", cmd_gui
, 0, 0, 1, "GUI [APPLY|ATTACH|DETACH|SHOW|HIDE|FOCUS|FLASH|ICONIFY|COLOR <n>]\n"
3546 " GUI [MSGBOX <text>|MENU TOGGLE]"},
3547 {"HELP", cmd_help
, 0, 0, 1, 0},
3548 {"HOP", cmd_hop
, 1, 1, 1,
3549 N_("HOP <nick>, gives chanhalf-op status to the nick (needs chanop)")},
3550 {"ID", cmd_id
, 1, 0, 1, N_("ID <password>, identifies yourself to nickserv")},
3551 {"IGNORE", cmd_ignore
, 0, 0, 1,
3552 N_("IGNORE <mask> <types..> <options..>\n"
3553 " mask - host mask to ignore, eg: *!*@*.aol.com\n"
3554 " types - types of data to ignore, one or all of:\n"
3555 " PRIV, CHAN, NOTI, CTCP, DCC, INVI, ALL\n"
3556 " options - NOSAVE, QUIET")},
3558 {"INVITE", cmd_invite
, 1, 0, 1,
3559 N_("INVITE <nick> [<channel>], invites someone to a channel, by default the current channel (needs chanop)")},
3560 {"JOIN", cmd_join
, 1, 0, 0, N_("JOIN <channel>, joins the channel")},
3561 {"KICK", cmd_kick
, 1, 1, 1,
3562 N_("KICK <nick>, kicks the nick from the current channel (needs chanop)")},
3563 {"KICKBAN", cmd_kickban
, 1, 1, 1,
3564 N_("KICKBAN <nick>, bans then kicks the nick from the current channel (needs chanop)")},
3565 {"KILLALL", cmd_killall
, 0, 0, 1, "KILLALL, immediately exit"},
3566 {"LAGCHECK", cmd_lagcheck
, 0, 0, 1,
3567 N_("LAGCHECK, forces a new lag check")},
3568 {"LASTLOG", cmd_lastlog
, 0, 0, 1,
3569 N_("LASTLOG <string>, searches for a string in the buffer")},
3570 {"LIST", cmd_list
, 1, 0, 1, 0},
3571 {"LOAD", cmd_load
, 0, 0, 1, N_("LOAD [-e] <file>, loads a plugin or script")},
3573 {"MDEHOP", cmd_mdehop
, 1, 1, 1,
3574 N_("MDEHOP, Mass deop's all chanhalf-ops in the current channel (needs chanop)")},
3575 {"MDEOP", cmd_mdeop
, 1, 1, 1,
3576 N_("MDEOP, Mass deop's all chanops in the current channel (needs chanop)")},
3577 {"ME", cmd_me
, 0, 0, 1,
3578 N_("ME <action>, sends the action to the current channel (actions are written in the 3rd person, like /me jumps)")},
3579 {"MENU", cmd_menu
, 0, 0, 1, "MENU [-eX] [-i<ICONFILE>] [-k<mod>,<key>] [-m] [-pX] [-r<X,group>] [-tX] {ADD|DEL} <path> [command] [unselect command]\n"
3580 " See http://xchat.org/docs/menu/ for more details."},
3581 {"MKICK", cmd_mkick
, 1, 1, 1,
3582 N_("MKICK, Mass kicks everyone except you in the current channel (needs chanop)")},
3583 {"MODE", cmd_mode
, 1, 0, 1, 0},
3584 {"MOP", cmd_mop
, 1, 1, 1,
3585 N_("MOP, Mass op's all users in the current channel (needs chanop)")},
3586 {"MSG", cmd_msg
, 0, 0, 1, N_("MSG <nick> <message>, sends a private message")},
3588 {"NAMES", cmd_names
, 1, 0, 1,
3589 N_("NAMES, Lists the nicks on the current channel")},
3590 {"NCTCP", cmd_nctcp
, 1, 0, 1,
3591 N_("NCTCP <nick> <message>, Sends a CTCP notice")},
3592 {"NEWSERVER", cmd_newserver
, 0, 0, 1, N_("NEWSERVER [-noconnect] <hostname> [<port>]")},
3593 {"NICK", cmd_nick
, 0, 0, 1, N_("NICK <nickname>, sets your nick")},
3595 {"NOTICE", cmd_notice
, 1, 0, 1,
3596 N_("NOTICE <nick/channel> <message>, sends a notice. Notices are a type of message that should be auto reacted to")},
3597 {"NOTIFY", cmd_notify
, 0, 0, 1,
3598 N_("NOTIFY [-n network1[,network2,...]] [<nick>], displays your notify list or adds someone to it")},
3599 {"OP", cmd_op
, 1, 1, 1,
3600 N_("OP <nick>, gives chanop status to the nick (needs chanop)")},
3601 {"PART", cmd_part
, 1, 1, 0,
3602 N_("PART [<channel>] [<reason>], leaves the channel, by default the current one")},
3603 {"PING", cmd_ping
, 1, 0, 1,
3604 N_("PING <nick | channel>, CTCP pings nick or channel")},
3605 {"QUERY", cmd_query
, 0, 0, 1,
3606 N_("QUERY [-nofocus] <nick>, opens up a new privmsg window to someone")},
3607 {"QUIT", cmd_quit
, 0, 0, 1,
3608 N_("QUIT [<reason>], disconnects from the current server")},
3609 {"QUOTE", cmd_quote
, 1, 0, 1,
3610 N_("QUOTE <text>, sends the text in raw form to the server")},
3612 {"RECONNECT", cmd_reconnect
, 0, 0, 1,
3613 N_("RECONNECT [-ssl] [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")},
3615 {"RECONNECT", cmd_reconnect
, 0, 0, 1,
3616 N_("RECONNECT [<host>] [<port>] [<password>], Can be called just as /RECONNECT to reconnect to the current server or with /RECONNECT ALL to reconnect to all the open servers")},
3618 {"RECV", cmd_recv
, 1, 0, 1, N_("RECV <text>, send raw data to xchat, as if it was received from the irc server")},
3620 {"SAY", cmd_say
, 0, 0, 1,
3621 N_("SAY <text>, sends the text to the object in the current window")},
3622 {"SEND", cmd_send
, 0, 0, 1, N_("SEND <nick> [<file>]")},
3624 {"SERVCHAN", cmd_servchan
, 0, 0, 1,
3625 N_("SERVCHAN [-ssl] <host> <port> <channel>, connects and joins a channel")},
3627 {"SERVCHAN", cmd_servchan
, 0, 0, 1,
3628 N_("SERVCHAN <host> <port> <channel>, connects and joins a channel")},
3631 {"SERVER", cmd_server
, 0, 0, 1,
3632 N_("SERVER [-ssl] <host> [<port>] [<password>], connects to a server, the default port is 6667 for normal connections, and 9999 for ssl connections")},
3634 {"SERVER", cmd_server
, 0, 0, 1,
3635 N_("SERVER <host> [<port>] [<password>], connects to a server, the default port is 6667")},
3637 {"SET", cmd_set
, 0, 0, 1, N_("SET [-e] [-off|-on] [-quiet] <variable> [<value>]")},
3638 {"SETCURSOR", cmd_setcursor
, 0, 0, 1, N_("SETCURSOR [-|+]<position>, reposition the cursor in the inputbox")},
3639 {"SETTAB", cmd_settab
, 0, 0, 1, N_("SETTAB <new name>, change a tab's name, tab_trunc limit still applies")},
3640 {"SETTEXT", cmd_settext
, 0, 0, 1, N_("SETTEXT <new text>, replace the text in the input box")},
3641 {"SPLAY", cmd_splay
, 0, 0, 1, "SPLAY <soundfile>"},
3642 {"TOPIC", cmd_topic
, 1, 1, 1,
3643 N_("TOPIC [<topic>], sets the topic if one is given, else shows the current topic")},
3644 {"TRAY", cmd_tray
, 0, 0, 1,
3645 N_("\nTRAY -f <timeout> <file1> [<file2>] Blink tray between two icons.\n"
3646 "TRAY -f <filename> Set tray to a fixed icon.\n"
3647 "TRAY -i <number> Blink tray with an internal icon.\n"
3648 "TRAY -t <text> Set the tray tooltip.\n"
3649 "TRAY -b <title> <text> Set the tray balloon."
3651 {"UNBAN", cmd_unban
, 1, 1, 1,
3652 N_("UNBAN <mask> [<mask>...], unbans the specified masks.")},
3653 {"UNIGNORE", cmd_unignore
, 0, 0, 1, N_("UNIGNORE <mask> [QUIET]")},
3654 {"UNLOAD", cmd_unload
, 0, 0, 1, N_("UNLOAD <name>, unloads a plugin or script")},
3655 {"URL", cmd_url
, 0, 0, 1, N_("URL <url>, opens a URL in your browser")},
3656 {"USELECT", cmd_uselect
, 0, 1, 0,
3657 N_("USELECT [-a] [-s] <nick1> <nick2> etc, highlights nick(s) in channel userlist")},
3658 {"USERLIST", cmd_userlist
, 1, 1, 1, 0},
3659 {"VOICE", cmd_voice
, 1, 1, 1,
3660 N_("VOICE <nick>, gives voice status to someone (needs chanop)")},
3661 {"WALLCHAN", cmd_wallchan
, 1, 1, 1,
3662 N_("WALLCHAN <message>, writes the message to all channels")},
3663 {"WALLCHOP", cmd_wallchop
, 1, 1, 1,
3664 N_("WALLCHOP <message>, sends the message to all chanops on the current channel")},
3670 command_compare (const void *a
, const void *b
)
3672 return strcasecmp (a
, ((struct commands
*)b
)->name
);
3675 static struct commands
*
3676 find_internal_command (char *name
)
3678 /* the "-1" is to skip the NULL terminator */
3679 return bsearch (name
, xc_cmds
, (sizeof (xc_cmds
) /
3680 sizeof (xc_cmds
[0])) - 1, sizeof (xc_cmds
[0]), command_compare
);
3684 help (session
*sess
, char *tbuf
, char *helpcmd
, int quiet
)
3686 struct commands
*cmd
;
3688 if (plugin_show_help (sess
, helpcmd
))
3691 cmd
= find_internal_command (helpcmd
);
3697 snprintf (tbuf
, TBUFSIZE
, _("Usage: %s\n"), _(cmd
->help
));
3698 PrintText (sess
, tbuf
);
3702 PrintText (sess
, _("\nNo help available on that command.\n"));
3708 PrintText (sess
, _("No such command.\n"));
3711 /* inserts %a, %c, %d etc into buffer. Also handles &x %x for word/word_eol. *
3712 * returns 2 on buffer overflow
3713 * returns 1 on success *
3714 * returns 0 on bad-args-for-user-command *
3715 * - word/word_eol args might be NULL *
3716 * - this beast is used for UserCommands, UserlistButtons and CTCP replies */
3719 auto_insert (char *dest
, int destlen
, unsigned char *src
, char *word
[],
3720 char *word_eol
[], char *a
, char *c
, char *d
, char *e
, char *h
,
3735 if (src
[0] == '%' || src
[0] == '&')
3737 if (isdigit ((unsigned char) src
[1]))
3739 if (isdigit ((unsigned char) src
[2]) && isdigit ((unsigned char) src
[3]))
3745 dest
[0] = atoi (buf
);
3746 utf
= g_locale_to_utf8 (dest
, 1, 0, &utf_len
, 0);
3749 if ((dest
- orig
) + utf_len
>= destlen
)
3755 memcpy (dest
, utf
, utf_len
);
3765 num
= src
[0] - '0'; /* ascii to decimal */
3766 if (*word
[num
] == 0)
3772 utf
= word_eol
[num
];
3774 /* avoid recusive usercommand overflow */
3775 if ((dest
- orig
) + strlen (utf
) >= destlen
)
3779 dest
+= strlen (dest
);
3791 if ((dest
- orig
) + 2 >= destlen
)
3808 utf
= get_cpu_str (); break;
3819 utf
= PACKAGE_VERSION
; break;
3823 tm_ptr
= localtime (&now
);
3824 snprintf (buf
, sizeof (buf
), "%4d%02d%02d", 1900 +
3825 tm_ptr
->tm_year
, 1 + tm_ptr
->tm_mon
, tm_ptr
->tm_mday
);
3835 if ((dest
- orig
) + strlen (utf
) >= destlen
)
3838 dest
+= strlen (dest
);
3845 utf_len
= g_utf8_skip
[src
[0]];
3847 if ((dest
- orig
) + utf_len
>= destlen
)
3858 memcpy (dest
, src
, utf_len
);
3871 check_special_chars (char *cmd
, int do_ascii
) /* check for %X */
3874 int len
= strlen (cmd
);
3883 buf
= malloc (len
+ 1);
3895 (isdigit ((unsigned char) cmd
[j
+ 1]) && isdigit ((unsigned char) cmd
[j
+ 2]) &&
3896 isdigit ((unsigned char) cmd
[j
+ 3])))
3898 tbuf
[0] = cmd
[j
+ 1];
3899 tbuf
[1] = cmd
[j
+ 2];
3900 tbuf
[2] = cmd
[j
+ 3];
3902 buf
[i
] = atoi (tbuf
);
3903 utf
= g_locale_to_utf8 (buf
+ i
, 1, 0, &utf_len
, 0);
3906 memcpy (buf
+ i
, utf
, utf_len
);
3930 case 'H': /* CL: invisible text code */
3931 buf
[i
] = HIDDEN_CHAR
;
3968 nick_comp_cb (struct User
*user
, nickdata
*data
)
3972 if (!rfc_ncasecmp (user
->nick
, data
->nick
, data
->len
))
3974 lenu
= strlen (user
->nick
);
3975 if (lenu
== data
->len
)
3977 snprintf (data
->tbuf
, TBUFSIZE
, "%s%s", user
->nick
, data
->space
);
3980 } else if (lenu
< data
->bestlen
)
3982 data
->bestlen
= lenu
;
3991 perform_nick_completion (struct session
*sess
, char *cmd
, char *tbuf
)
3994 char *space
= strchr (cmd
, ' ');
3995 if (space
&& space
!= cmd
)
3997 if (space
[-1] == prefs
.nick_suffix
[0] && space
- 1 != cmd
)
3999 len
= space
- cmd
- 1;
4005 memcpy (nick
, cmd
, len
);
4010 data
.bestlen
= INT_MAX
;
4013 data
.space
= space
- 1;
4014 tree_foreach (sess
->usertree
, (tree_traverse_func
*)nick_comp_cb
, &data
);
4021 snprintf (tbuf
, TBUFSIZE
, "%s%s", data
.best
->nick
, space
- 1);
4032 user_command (session
* sess
, char *tbuf
, char *cmd
, char *word
[],
4035 if (!auto_insert (tbuf
, 2048, cmd
, word
, word_eol
, "", sess
->channel
, "",
4036 server_get_network (sess
->server
, TRUE
), "",
4037 sess
->server
->nick
, ""))
4039 PrintText (sess
, _("Bad arguments for user command.\n"));
4043 handle_command (sess
, tbuf
, TRUE
);
4046 /* handle text entered without a CMDchar prefix */
4049 handle_say (session
*sess
, char *text
, int check_spch
)
4052 char *word
[PDIWORDS
+1];
4053 char *word_eol
[PDIWORDS
+1];
4054 char pdibuf_static
[1024];
4055 char newcmd_static
[1024];
4056 char *pdibuf
= pdibuf_static
;
4057 char *newcmd
= newcmd_static
;
4059 int newcmdlen
= sizeof newcmd_static
;
4061 if (strcmp (sess
->channel
, "(lastlog)") == 0)
4063 lastlog (sess
->lastlog_sess
, text
, sess
->lastlog_regexp
);
4067 len
= strlen (text
);
4068 if (len
>= sizeof pdibuf_static
)
4069 pdibuf
= malloc (len
+ 1);
4071 if (len
+ NICKLEN
>= newcmdlen
)
4072 newcmd
= malloc (newcmdlen
= len
+ NICKLEN
+ 1);
4074 if (check_spch
&& prefs
.perc_color
)
4075 check_special_chars (text
, prefs
.perc_ascii
);
4077 /* Python relies on this */
4078 word
[PDIWORDS
] = NULL
;
4079 word_eol
[PDIWORDS
] = NULL
;
4081 /* split the text into words and word_eol */
4082 process_data_init (pdibuf
, text
, word
, word_eol
, TRUE
, FALSE
);
4084 /* a command of "" can be hooked for non-commands */
4085 if (plugin_emit_command (sess
, "", word
, word_eol
))
4088 /* incase a plugin did /close */
4089 if (!is_session (sess
))
4092 if (!sess
->channel
[0] || sess
->type
== SESS_SERVER
|| sess
->type
== SESS_NOTICES
|| sess
->type
== SESS_SNOTICES
)
4098 if (prefs
.nickcompletion
)
4099 perform_nick_completion (sess
, text
, newcmd
);
4101 safe_strcpy (newcmd
, text
, newcmdlen
);
4105 if (sess
->type
== SESS_DIALOG
)
4107 /* try it via dcc, if possible */
4108 dcc
= dcc_write_chat (sess
->channel
, text
);
4111 inbound_chanmsg (sess
->server
, NULL
, sess
->channel
,
4112 sess
->server
->nick
, text
, TRUE
, FALSE
);
4113 set_topic (sess
, net_ip (dcc
->addr
), net_ip (dcc
->addr
));
4118 if (sess
->server
->connected
)
4121 unsigned char t
= 0;
4123 /* maximum allowed message text */
4124 /* :nickname!username@host.com PRIVMSG #channel :text\r\n */
4126 max
-= 16; /* :, !, @, " PRIVMSG ", " ", :, \r, \n */
4127 max
-= strlen (sess
->server
->nick
);
4128 max
-= strlen (sess
->channel
);
4129 if (sess
->me
&& sess
->me
->hostname
)
4130 max
-= strlen (sess
->me
->hostname
);
4133 max
-= 9; /* username */
4134 max
-= 65; /* max possible hostname and '@' */
4137 if (strlen (text
) > max
)
4141 /* traverse the utf8 string and find the nearest cut point that
4142 doesn't split 1 char in half */
4145 size
= g_utf8_skip
[((unsigned char *)text
)[i
]];
4146 if ((i
+ size
) >= max
)
4152 text
[max
] = 0; /* insert a NULL terminator to shorten it */
4155 inbound_chanmsg (sess
->server
, sess
, sess
->channel
, sess
->server
->nick
,
4157 sess
->server
->p_message (sess
->server
, sess
->channel
, text
);
4162 handle_say (sess
, text
+ max
, FALSE
);
4171 if (pdibuf
!= pdibuf_static
)
4174 if (newcmd
!= newcmd_static
)
4178 /* handle a command, without the '/' prefix */
4181 handle_command (session
*sess
, char *cmd
, int check_spch
)
4184 int user_cmd
= FALSE
;
4186 char *word
[PDIWORDS
+1];
4187 char *word_eol
[PDIWORDS
+1];
4188 static int command_level
= 0;
4189 struct commands
*int_cmd
;
4190 char pdibuf_static
[1024];
4191 char tbuf_static
[TBUFSIZE
];
4197 if (command_level
> 99)
4199 fe_message (_("Too many recursive usercommands, aborting."), FE_MSG_ERROR
);
4203 /* anything below MUST DEC command_level before returning */
4206 if (len
>= sizeof (pdibuf_static
))
4207 pdibuf
= malloc (len
+ 1);
4209 pdibuf
= pdibuf_static
;
4211 if ((len
* 2) >= sizeof (tbuf_static
))
4212 tbuf
= malloc ((len
* 2) + 1);
4216 /* split the text into words and word_eol */
4217 process_data_init (pdibuf
, cmd
, word
, word_eol
, TRUE
, TRUE
);
4219 /* ensure an empty string at index 32 for cmd_deop etc */
4220 /* (internal use only, plugins can still only read 1-31). */
4221 word
[PDIWORDS
] = "\000\000";
4222 word_eol
[PDIWORDS
] = "\000\000";
4224 int_cmd
= find_internal_command (word
[1]);
4225 /* redo it without quotes processing, for some commands like /JOIN */
4226 if (int_cmd
&& !int_cmd
->handle_quotes
)
4227 process_data_init (pdibuf
, cmd
, word
, word_eol
, FALSE
, FALSE
);
4229 if (check_spch
&& prefs
.perc_color
)
4230 check_special_chars (cmd
, prefs
.perc_ascii
);
4232 if (plugin_emit_command (sess
, word
[1], word
, word_eol
))
4235 /* incase a plugin did /close */
4236 if (!is_session (sess
))
4239 /* first see if it's a userCommand */
4240 list
= command_list
;
4243 pop
= (struct popup
*) list
->data
;
4244 if (!strcasecmp (pop
->name
, word
[1]))
4246 user_command (sess
, tbuf
, pop
->cmd
, word
, word_eol
);
4255 /* now check internal commands */
4256 int_cmd
= find_internal_command (word
[1]);
4260 if (int_cmd
->needserver
&& !sess
->server
->connected
)
4263 } else if (int_cmd
->needchannel
&& !sess
->channel
[0])
4268 switch (int_cmd
->callback (sess
, tbuf
, word
, word_eol
))
4271 help (sess
, tbuf
, int_cmd
->name
, TRUE
);
4280 /* unknown command, just send it to the server and hope */
4281 if (!sess
->server
->connected
)
4282 PrintText (sess
, _("Unknown Command. Try /help\n"));
4284 sess
->server
->p_raw (sess
->server
, cmd
);
4290 if (pdibuf
!= pdibuf_static
)
4293 if (tbuf
!= tbuf_static
)
4299 /* handle one line entered into the input box */
4302 handle_user_input (session
*sess
, char *text
, int history
, int nocommand
)
4308 history_add (&sess
->history
, text
);
4310 /* is it NOT a command, just text? */
4311 if (nocommand
|| text
[0] != prefs
.cmdchar
[0])
4313 handle_say (sess
, text
, TRUE
);
4318 if (text
[0] == prefs
.cmdchar
[0] && text
[1] == prefs
.cmdchar
[0])
4320 handle_say (sess
, text
+ 1, TRUE
);
4324 if (prefs
.cmdchar
[0] == '/')
4327 const char *unix_dirs
[] = {
4328 "/bin/", "/boot/", "/dev/",
4329 "/etc/", "/home/", "/lib/",
4330 "/lost+found/", "/mnt/", "/opt/",
4331 "/proc/", "/root/", "/sbin/",
4332 "/tmp/", "/usr/", "/var/",
4334 for (i
= 0; unix_dirs
[i
] != NULL
; i
++)
4335 if (strncmp (text
, unix_dirs
[i
], strlen (unix_dirs
[i
]))==0)
4337 handle_say (sess
, text
, TRUE
);
4342 return handle_command (sess
, text
+ 1, TRUE
);
4345 /* changed by Steve Green. Macs sometimes paste with imbedded \r */
4347 handle_multiline (session
*sess
, char *cmd
, int history
, int nocommand
)
4351 char *cr
= cmd
+ strcspn (cmd
, "\n\r");
4352 int end_of_string
= *cr
== 0;
4354 if (!handle_user_input (sess
, cmd
, history
, nocommand
))
4363 handle_multiline (session *sess, char *cmd, int history, int nocommand)
4367 cr = strchr (cmd, '\n');
4374 if (!handle_user_input (sess, cmd, history, nocommand))
4381 cr = strchr (cmd, '\n');
4385 handle_user_input (sess, cmd, history, nocommand);