1 /*
2  * tcldcc.c -- handles:
3  *   Tcl stubs for the dcc commands
4  */
5 /*
6  * Copyright (C) 1997 Robey Pointer
7  * Copyright (C) 1999 - 2021 Eggheads Development Team
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23 
24 #include "main.h"
25 #include "tandem.h"
26 #include "modules.h"
27 #include <errno.h>
28 #include <signal.h>
29 
30 extern Tcl_Interp *interp;
31 extern tcl_timer_t *timer, *utimer;
32 extern struct dcc_t *dcc;
33 extern char botnetnick[], listen_ip[];
34 extern int dcc_total, backgrd, parties, make_userfile, remote_boots, max_dcc,
35            conmask;
36 #ifdef IPV6
37 extern int pref_af;
38 #endif
39 extern volatile sig_atomic_t do_restart;
40 #ifdef TLS
41 extern int tls_vfydcc;
42 extern sock_list *socklist;
43 #endif
44 extern party_t *party;
45 extern tand_t *tandbot;
46 extern time_t now;
47 extern unsigned long otraffic_irc, otraffic_irc_today, itraffic_irc,
48                      itraffic_irc_today, otraffic_bn, otraffic_bn_today,
49                      itraffic_bn, itraffic_bn_today, otraffic_dcc,
50                      otraffic_dcc_today, itraffic_dcc, itraffic_dcc_today,
51                      otraffic_trans, otraffic_trans_today, itraffic_trans,
52                      itraffic_trans_today, otraffic_unknown, itraffic_unknown,
53                      otraffic_unknown_today, itraffic_unknown_today;
54 static struct portmap *root = NULL;
55 
56 
expmem_tcldcc(void)57 int expmem_tcldcc(void)
58 {
59   int tot = 0;
60   struct portmap *pmap;
61 
62   for (pmap = root; pmap; pmap = pmap->next)
63     tot += sizeof(struct portmap);
64 
65   return tot;
66 }
67 
68 static int tcl_putdcc STDVAR
69 {
70   int j;
71 
72   BADARGS(3, 4, " idx text ?options?");
73 
74   if ((argc == 4) && strcasecmp(argv[3], "-raw")) {
75     Tcl_AppendResult(irp, "unknown putdcc option: should be ",
76                      "-raw", NULL);
77     return TCL_ERROR;
78   }
79 
80   j = findidx(atoi(argv[1]));
81   if (j < 0) {
82     Tcl_AppendResult(irp, "invalid idx", NULL);
83     return TCL_ERROR;
84   }
85   if (argc == 4)
86     tputs(dcc[j].sock, argv[2], strlen(argv[2]));
87   else
88     dumplots(j, "", argv[2]);
89 
90   return TCL_OK;
91 }
92 
93 /* Allows tcl scripts to send out raw data. Can be used for fast server write.
94  * The server idx is 0.
95  *
96  * Usage: putdccraw <idx> <size> <rawdata>
97  *
98  * Example: putdccraw 6 13 "eggdrop rulz\n"
99  *
100  * Added by <drummer@sophia.jpte.hu>.
101  */
102 static int tcl_putdccraw STDVAR
103 {
104 #if 0
105   int i, j = 0, z;
106 
107   BADARGS(4, 4, " idx size text");
108 
109   z = atoi(argv[1]);
110   for (i = 0; i < dcc_total; i++) {
111     if (!z && !strcmp(dcc[i].nick, "(server)")) {
112       j = dcc[i].sock;
113       break;
114     } else if (dcc[i].sock == z) {
115       j = dcc[i].sock;
116       break;
117     }
118   }
119   if (i == dcc_total) {
120     Tcl_AppendResult(irp, "invalid idx", NULL);
121     return TCL_ERROR;
122   }
123   tputs(j, argv[3], atoi(argv[2]));
124   return TCL_OK;
125 #endif
126 
127   Tcl_AppendResult(irp, "putdccraw is deprecated. "
128                    "Please use putdcc/putnow instead.", NULL);
129   return TCL_ERROR;
130 }
131 
132 static int tcl_dccsimul STDVAR
133 {
134   int idx;
135 
136   BADARGS(3, 3, " idx command");
137 
138   idx = findidx(atoi(argv[1]));
139   if (idx >= 0 && (dcc[idx].type->flags & DCT_SIMUL)) {
140     int l = strlen(argv[2]);
141 
142     if (l > 510) {
143       l = 510;
144       argv[2][510] = 0;        /* Restrict length of cmd */
145     }
146     if (dcc[idx].type && dcc[idx].type->activity) {
147       dcc[idx].type->activity(idx, argv[2], l);
148       return TCL_OK;
149     }
150   } else {
151     Tcl_AppendResult(irp, "invalid idx", NULL);
152   }
153   return TCL_ERROR;
154 }
155 
156 
157 static int tcl_dccbroadcast STDVAR
158 {
159   char msg[401];
160 
161   BADARGS(2, 2, " message");
162 
163   strlcpy(msg, argv[1], sizeof msg);
164   chatout("*** %s\n", msg);
165   botnet_send_chat(-1, botnetnick, msg);
166   check_tcl_bcst(botnetnick, -1, msg);
167   return TCL_OK;
168 }
169 
170 static int tcl_hand2idx STDVAR
171 {
172   int i;
173   char s[11];
174 
175   BADARGS(2, 2, " handle");
176 
177   for (i = 0; i < dcc_total; i++)
178     if ((dcc[i].type->flags & (DCT_SIMUL | DCT_BOT)) &&
179         !strcasecmp(argv[1], dcc[i].nick)) {
180       egg_snprintf(s, sizeof s, "%ld", dcc[i].sock);
181       Tcl_AppendResult(irp, s, NULL);
182       return TCL_OK;
183     }
184   Tcl_AppendResult(irp, "-1", NULL);
185   return TCL_OK;
186 }
187 
188 static int tcl_getchan STDVAR
189 {
190   char s[7];
191   int idx;
192 
193   BADARGS(2, 2, " idx");
194 
195   idx = findidx(atoi(argv[1]));
196   if (idx < 0 || (dcc[idx].type != &DCC_CHAT &&
197       dcc[idx].type != &DCC_SCRIPT)) {
198     Tcl_AppendResult(irp, "invalid idx", NULL);
199     return TCL_ERROR;
200   }
201 
202   if (dcc[idx].type == &DCC_SCRIPT)
203     egg_snprintf(s, sizeof s, "%d", dcc[idx].u.script->u.chat->channel);
204   else
205     egg_snprintf(s, sizeof s, "%d", dcc[idx].u.chat->channel);
206 
207   Tcl_AppendResult(irp, s, NULL);
208   return TCL_OK;
209 }
210 
211 static int tcl_setchan STDVAR
212 {
213   int idx, chan;
214   module_entry *me;
215 
216   BADARGS(3, 3, " idx channel");
217 
218   idx = findidx(atoi(argv[1]));
219   if (idx < 0 || (dcc[idx].type != &DCC_CHAT &&
220       dcc[idx].type != &DCC_SCRIPT)) {
221     Tcl_AppendResult(irp, "invalid idx", NULL);
222     return TCL_ERROR;
223   }
224   if (argv[2][0] < '0' || argv[2][0] > '9') {
225     if (!strcmp(argv[2], "-1") || !strcasecmp(argv[2], "off"))
226       chan = -1;
227     else {
228       Tcl_SetVar(irp, "_chan", argv[2], 0);
229       if (Tcl_VarEval(irp, "assoc ", "$_chan", NULL) != TCL_OK ||
230           tcl_resultempty()) {
231         Tcl_AppendResult(irp, "channel name is invalid", NULL);
232         return TCL_ERROR;
233       }
234       chan = tcl_resultint();
235     }
236   } else
237     chan = atoi(argv[2]);
238   if ((chan < -1) || (chan > 199999)) {
239     Tcl_AppendResult(irp, "channel out of range; must be -1 through 199999",
240                      NULL);
241     return TCL_ERROR;
242   }
243   if (dcc[idx].type == &DCC_SCRIPT)
244     dcc[idx].u.script->u.chat->channel = chan;
245   else {
246     int oldchan = dcc[idx].u.chat->channel;
247 
248     if (dcc[idx].u.chat->channel >= 0) {
249       if ((chan >= GLOBAL_CHANS) && (oldchan < GLOBAL_CHANS))
250         botnet_send_part_idx(idx, "*script*");
251       check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock,
252                      dcc[idx].u.chat->channel);
253     }
254     dcc[idx].u.chat->channel = chan;
255     if (chan < GLOBAL_CHANS)
256       botnet_send_join_idx(idx, oldchan);
257     check_tcl_chjn(botnetnick, dcc[idx].nick, chan, geticon(idx),
258                    dcc[idx].sock, dcc[idx].host);
259   }
260   /* Console autosave. */
261   if ((me = module_find("console", 1, 1))) {
262     Function *func = me->funcs;
263 
264     (func[CONSOLE_DOSTORE]) (idx);
265   }
266   return TCL_OK;
267 }
268 
269 static int tcl_dccputchan STDVAR
270 {
271   int chan;
272   char msg[401];
273 
274   BADARGS(3, 3, " channel message");
275 
276   chan = atoi(argv[1]);
277   if ((chan < 0) || (chan > 199999)) {
278     Tcl_AppendResult(irp, "channel out of range; must be 0 through 199999", NULL);
279     return TCL_ERROR;
280   }
281   strlcpy(msg, argv[2], sizeof msg);
282 
283   chanout_but(-1, chan, "*** %s\n", argv[2]);
284   botnet_send_chan(-1, botnetnick, NULL, chan, argv[2]);
285   check_tcl_bcst(botnetnick, chan, argv[2]);
286   return TCL_OK;
287 }
288 
tcl_do_console(Tcl_Interp * irp,ClientData cd,int argc,char ** argv,int reset)289 static int tcl_do_console(Tcl_Interp *irp, ClientData cd, int argc,
290     char **argv, int reset)
291 {
292   int i, j, pls, arg;
293   module_entry *me;
294 
295   i = findidx(atoi(argv[1]));
296   if (i < 0 || dcc[i].type != &DCC_CHAT) {
297     Tcl_AppendResult(irp, "invalid idx", NULL);
298     return TCL_ERROR;
299   }
300   pls = 1;
301 
302   for (arg = 2; arg < argc; arg++) {
303     if (argv[arg][0] && !reset && ((strchr(CHANMETA, argv[arg][0])
304         != NULL) || (argv[arg][0] == '*'))) {
305       if ((argv[arg][0] != '*') && (!findchan_by_dname(argv[arg]))) {
306         /* If we don't find the channel, and it starts with a +, assume it
307          * should be the console flags to set. */
308         if (argv[arg][0] == '+')
309           goto do_console_flags;
310         Tcl_AppendResult(irp, "invalid channel", NULL);
311         return TCL_ERROR;
312       }
313       strlcpy(dcc[i].u.chat->con_chan, argv[arg], sizeof dcc[i].u.chat->con_chan);
314     } else {
315       if (!reset && (argv[arg][0] != '+') && (argv[arg][0] != '-'))
316         dcc[i].u.chat->con_flags = 0;
317     do_console_flags:
318       if (reset) {
319         dcc[i].u.chat->con_flags =
320             (dcc[i].user && (dcc[i].user->flags & USER_MASTER) ? conmask : 0);
321       } else {
322         for (j = 0; j < strlen(argv[arg]); j++) {
323           if (argv[arg][j] == '+')
324             pls = 1;
325           else if (argv[arg][j] == '-')
326             pls = -1;
327           else {
328             char s[2];
329 
330             s[0] = argv[arg][j];
331             s[1] = 0;
332             if (pls == 1)
333               dcc[i].u.chat->con_flags |= logmodes(s);
334             else
335               dcc[i].u.chat->con_flags &= ~logmodes(s);
336           }
337         }
338       }
339     }
340   }
341   Tcl_AppendElement(irp, dcc[i].u.chat->con_chan);
342   Tcl_AppendElement(irp, masktype(dcc[i].u.chat->con_flags));
343   /* Console autosave. */
344   if (argc > 2 && (me = module_find("console", 1, 1))) {
345     Function *func = me->funcs;
346 
347     (func[CONSOLE_DOSTORE]) (i);
348   }
349   return TCL_OK;
350 }
351 
352 static int tcl_console STDVAR
353 {
354   BADARGS(2, 4, " idx ?channel? ?console-modes?");
355   return tcl_do_console(irp, cd, argc, argv, 0);
356 }
357 
358 static int tcl_resetconsole STDVAR
359 {
360   BADARGS(2, 2, " idx");
361   return tcl_do_console(irp, cd, argc, argv, 1);
362 }
363 
364 
365 static int tcl_strip STDVAR
366 {
367   int i, j, pls, arg;
368   module_entry *me;
369 
370   BADARGS(2, 4, " idx ?strip-flags?");
371 
372   i = findidx(atoi(argv[1]));
373   if (i < 0 || dcc[i].type != &DCC_CHAT) {
374     Tcl_AppendResult(irp, "invalid idx", NULL);
375     return TCL_ERROR;
376   }
377   pls = 1;
378 
379   for (arg = 2; arg < argc; arg++) {
380     if ((argv[arg][0] != '+') && (argv[arg][0] != '-'))
381       dcc[i].u.chat->strip_flags = 0;
382     for (j = 0; j < strlen(argv[arg]); j++) {
383       if (argv[arg][j] == '+')
384         pls = 1;
385       else if (argv[arg][j] == '-')
386         pls = -1;
387       else {
388         char s[2];
389 
390         s[0] = argv[arg][j];
391         s[1] = 0;
392         if (pls == 1)
393           dcc[i].u.chat->strip_flags |= stripmodes(s);
394         else
395           dcc[i].u.chat->strip_flags &= ~stripmodes(s);
396       }
397     }
398   }
399   Tcl_AppendElement(irp, stripmasktype(dcc[i].u.chat->strip_flags));
400   /* Console autosave. */
401   if (argc > 2 && (me = module_find("console", 1, 1))) {
402     Function *func = me->funcs;
403 
404     (func[CONSOLE_DOSTORE]) (i);
405   }
406   return TCL_OK;
407 }
408 
409 static int tcl_echo STDVAR
410 {
411   int i;
412   module_entry *me;
413 
414   BADARGS(2, 3, " idx ?status?");
415 
416   i = findidx(atoi(argv[1]));
417   if (i < 0 || dcc[i].type != &DCC_CHAT) {
418     Tcl_AppendResult(irp, "invalid idx", NULL);
419     return TCL_ERROR;
420   }
421   if (argc == 3) {
422     if (atoi(argv[2]))
423       dcc[i].status |= STAT_ECHO;
424     else
425       dcc[i].status &= ~STAT_ECHO;
426   }
427   if (dcc[i].status & STAT_ECHO)
428     Tcl_AppendResult(irp, "1", NULL);
429   else
430     Tcl_AppendResult(irp, "0", NULL);
431   /* Console autosave. */
432   if (argc > 2 && (me = module_find("console", 1, 1))) {
433     Function *func = me->funcs;
434 
435     (func[CONSOLE_DOSTORE]) (i);
436   }
437   return TCL_OK;
438 }
439 
440 static int tcl_page STDVAR
441 {
442   int i;
443   char x[20];
444   module_entry *me;
445 
446   BADARGS(2, 3, " idx ?status?");
447 
448   i = findidx(atoi(argv[1]));
449   if (i < 0 || dcc[i].type != &DCC_CHAT) {
450     Tcl_AppendResult(irp, "invalid idx", NULL);
451     return TCL_ERROR;
452   }
453   if (argc == 3) {
454     int l = atoi(argv[2]);
455 
456     if (!l)
457       dcc[i].status &= ~STAT_PAGE;
458     else {
459       dcc[i].status |= STAT_PAGE;
460       dcc[i].u.chat->max_line = l;
461     }
462   }
463   if (dcc[i].status & STAT_PAGE) {
464     egg_snprintf(x, sizeof x, "%d", dcc[i].u.chat->max_line);
465     Tcl_AppendResult(irp, x, NULL);
466   } else
467     Tcl_AppendResult(irp, "0", NULL);
468   /* Console autosave. */
469   if ((argc > 2) && (me = module_find("console", 1, 1))) {
470     Function *func = me->funcs;
471 
472     (func[CONSOLE_DOSTORE]) (i);
473   }
474   return TCL_OK;
475 }
476 
477 static int tcl_control STDVAR
478 {
479   int idx;
480   void *hold;
481 
482   BADARGS(3, 3, " idx command");
483 
484   idx = findidx(atoi(argv[1]));
485   if (idx < 0) {
486     Tcl_AppendResult(irp, "invalid idx", NULL);
487     return TCL_ERROR;
488   }
489   if (dcc[idx].type->flags & DCT_CHAT) {
490     if (dcc[idx].u.chat->channel >= 0) {
491       chanout_but(idx, dcc[idx].u.chat->channel, "*** %s has gone.\n",
492                   dcc[idx].nick);
493       check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock,
494                      dcc[idx].u.chat->channel);
495       botnet_send_part_idx(idx, "gone");
496     }
497     check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
498   }
499   hold = dcc[idx].u.other;
500   dcc[idx].u.script = get_data_ptr(sizeof(struct script_info));
501   dcc[idx].u.script->u.other = hold;
502   dcc[idx].u.script->type = dcc[idx].type;
503   dcc[idx].type = &DCC_SCRIPT;
504   /* Do not buffer data anymore. All received and stored data is passed
505    * over to the dcc functions from now on.  */
506   sockoptions(dcc[idx].sock, EGG_OPTION_UNSET, SOCK_BUFFER);
507   strlcpy(dcc[idx].u.script->command, argv[2], 120);
508   return TCL_OK;
509 }
510 
511 static int tcl_valididx STDVAR
512 {
513   int idx;
514 
515   BADARGS(2, 2, " idx");
516 
517   idx = findidx(atoi(argv[1]));
518   if (idx < 0 || !(dcc[idx].type->flags & DCT_VALIDIDX))
519     Tcl_AppendResult(irp, "0", NULL);
520   else
521     Tcl_AppendResult(irp, "1", NULL);
522    return TCL_OK;
523 }
524 
525 static int tcl_killdcc STDVAR
526 {
527   int idx;
528 
529   BADARGS(2, 3, " idx ?reason?");
530 
531   idx = findidx(atoi(argv[1]));
532   if (idx < 0) {
533     Tcl_AppendResult(irp, "invalid idx", NULL);
534     return TCL_ERROR;
535   }
536 
537   if ((dcc[idx].sock == STDOUT) && !backgrd) /* Don't kill terminal socket */
538     return TCL_OK;
539 
540 
541   if (dcc[idx].type->flags & DCT_CHAT) { /* Make sure 'whom' info is updated */
542     chanout_but(idx, dcc[idx].u.chat->channel, "*** %s has left the %s%s%s\n",
543                 dcc[idx].nick, dcc[idx].u.chat ? "channel" : "partyline",
544                 argc == 3 ? ": " : "", argc == 3 ? argv[2] : "");
545     botnet_send_part_idx(idx, argc == 3 ? argv[2] : "");
546     if ((dcc[idx].u.chat->channel >= 0) &&
547         (dcc[idx].u.chat->channel < GLOBAL_CHANS))
548       check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock,
549                      dcc[idx].u.chat->channel);
550     check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
551   }
552   killsock(dcc[idx].sock);
553   killtransfer(idx);
554   lostdcc(idx);
555   return TCL_OK;
556 }
557 
558 static int tcl_putbot STDVAR
559 {
560   int i;
561   char msg[401];
562 
563   BADARGS(3, 3, " botnick message");
564 
565   i = nextbot(argv[1]);
566   if (i < 0) {
567     Tcl_AppendResult(irp, "bot is not on the botnet", NULL);
568     return TCL_ERROR;
569   }
570   strlcpy(msg, argv[2], sizeof msg);
571 
572   botnet_send_zapf(i, botnetnick, argv[1], msg);
573   return TCL_OK;
574 }
575 
576 static int tcl_putallbots STDVAR
577 {
578   char msg[401];
579 
580   BADARGS(2, 2, " message");
581 
582   strlcpy(msg, argv[1], sizeof msg);
583   botnet_send_zapf_broad(-1, botnetnick, NULL, msg);
584   return TCL_OK;
585 }
586 
587 static int tcl_idx2hand STDVAR
588 {
589   int idx;
590 
591   BADARGS(2, 2, " idx");
592 
593   idx = findidx(atoi(argv[1]));
594   if (idx < 0) {
595     Tcl_AppendResult(irp, "invalid idx", NULL);
596     return TCL_ERROR;
597   }
598 
599   Tcl_AppendResult(irp, dcc[idx].nick, NULL);
600   return TCL_OK;
601 }
602 
603 static int tcl_islinked STDVAR
604 {
605   int i;
606 
607   BADARGS(2, 2, " bot");
608 
609   i = nextbot(argv[1]);
610   if (i < 0)
611     Tcl_AppendResult(irp, "0", NULL);
612   else
613     Tcl_AppendResult(irp, "1", NULL);
614   return TCL_OK;
615 }
616 
617 static int tcl_bots STDVAR
618 {
619   tand_t *bot;
620 
621   BADARGS(1, 1, "");
622 
623   for (bot = tandbot; bot; bot = bot->next)
624     Tcl_AppendElement(irp, bot->bot);
625   return TCL_OK;
626 }
627 
628 static int tcl_botlist STDVAR
629 {
630   char *p, sh[2], string[20];
631   EGG_CONST char *list[4];
632   tand_t *bot;
633 
634   BADARGS(1, 1, "");
635 
636   sh[1] = 0;
637   list[3] = sh;
638   list[2] = string;
639   for (bot = tandbot; bot; bot = bot->next) {
640     list[0] = bot->bot;
641     list[1] = (bot->uplink == (tand_t *) 1) ? botnetnick : bot->uplink->bot;
642     strlcpy(string, int_to_base10(bot->ver), sizeof string);
643     sh[0] = bot->share;
644     p = Tcl_Merge(4, list);
645     Tcl_AppendElement(irp, p);
646     Tcl_Free((char *) p);
647   }
648   return TCL_OK;
649 }
650 
build_dcc_list(Tcl_Interp * irp,char * idxstr,char * nick,char * host,char * portstring,char * type,char * other,char * timestamp)651 static void build_dcc_list(Tcl_Interp *irp, char *idxstr, char *nick, char *host,
652             char *portstring, char *type, char *other, char *timestamp) {
653   char *p;
654   EGG_CONST char *list[7];
655 
656   list[0] = idxstr;
657   list[1] = nick;
658   list[2] = host;
659   list[3] = portstring;
660   list[4] = type;
661   list[5] = other;
662   list[6] = timestamp;
663   p = Tcl_Merge(7, list);
664   Tcl_AppendElement(irp, p);
665   Tcl_Free((char *) p);
666 }
667 
668 /* Build and return a list of lists of all sockets, in dict-readable format */
build_sock_list(Tcl_Interp * irp,Tcl_Obj * masterlist,char * idxstr,char * nick,char * host,char * ip,int port,int secure,char * type,char * other,char * timestamp)669 static void build_sock_list(Tcl_Interp *irp, Tcl_Obj *masterlist, char *idxstr,
670             char *nick, char *host, char *ip, int port, int secure,
671             char *type, char *other, char *timestamp) {
672   EGG_CONST char *val[] = {"idx", "handle", "host", "ip", "port", "secure",
673                            "type", "info", "time"};
674   Tcl_Obj *thelist;
675   char securestr[2], portstr[6];
676 
677   egg_snprintf(securestr, sizeof securestr, "%d", secure);
678   egg_snprintf(portstr, sizeof portstr, "%d", port);
679   thelist = Tcl_NewListObj(0, NULL);
680   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[0], -1));
681   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(idxstr, -1));
682   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[1], -1));
683   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(nick, -1));
684   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[2], -1));
685   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(host, -1));
686   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[3], -1));
687   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(ip, -1));
688   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[4], -1));
689   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(portstr, -1));
690   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[5], -1));
691   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(securestr, -1));
692   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[6], -1));
693   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(type, -1));
694   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[7], -1));
695   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(other, -1));
696   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(val[8], -1));
697   Tcl_ListObjAppendElement(irp, thelist, Tcl_NewStringObj(timestamp, -1));
698   Tcl_ListObjAppendElement(irp, masterlist, thelist);
699   Tcl_SetObjResult(irp, masterlist);
700 }
701 
702 /* Gather information for dcclist or socklist */
dccsocklist(Tcl_Interp * irp,int argc,char * type,int src)703 static void dccsocklist(Tcl_Interp *irp, int argc, char *type, int src) {
704   int i;
705   char idxstr[10], timestamp[11], other[160];
706   char portstring[7]; /* ssl + portmax + NULL */
707   long tv;
708 #ifdef IPV6
709   char s[INET6_ADDRSTRLEN];
710 #else
711   char s[INET_ADDRSTRLEN];
712 #endif
713   socklen_t namelen;
714   struct sockaddr_storage ss;
715   Tcl_Obj *masterlist = NULL; /* initialize to NULL to make old gcc versions
716                                * happy */
717 
718   if (src) {
719     masterlist = Tcl_NewListObj(0, NULL);
720   }
721   for (i = 0; i < dcc_total; i++) {
722     if (argc == 1 || ((argc == 2) && (dcc[i].type &&
723         !strcasecmp(dcc[i].type->name, type)))) {
724       egg_snprintf(idxstr, sizeof idxstr, "%ld", dcc[i].sock);
725       tv = dcc[i].timeval;
726       egg_snprintf(timestamp, sizeof timestamp, "%ld", tv);
727       if (dcc[i].type && dcc[i].type->display)
728         dcc[i].type->display(i, other);
729       else {
730         egg_snprintf(other, sizeof other, "?:%lX  !! ERROR !!",
731                      (long) dcc[i].type);
732         break;
733       }
734 #ifdef TLS
735       egg_snprintf(portstring, sizeof portstring, "%s%d", dcc[i].ssl ? "+" : "", dcc[i].port);
736 #else
737       egg_snprintf(portstring, sizeof portstring, "%d", dcc[i].port);
738 #endif
739       /* If this came from dcclist... */
740       if (!src) {
741         build_dcc_list(irp, idxstr, dcc[i].nick,
742             (dcc[i].host[0] == '\0') ? iptostr(&dcc[i].sockname.addr.sa) : dcc[i].host,
743             portstring, dcc[i].type ? dcc[i].type->name : "*UNKNOWN*", other,
744             timestamp);
745       /* If this came from socklist... */
746       } else {
747         /* Update dcc table socket information, needed for getting local IP */
748         namelen = sizeof ss;
749         getsockname(dcc[i].sock, (struct sockaddr *) &ss, &namelen);
750         if (ss.ss_family == AF_INET) {
751           struct sockaddr_in *saddr = (struct sockaddr_in *)&ss;
752           inet_ntop(AF_INET, &(saddr->sin_addr), s, INET_ADDRSTRLEN);
753 #ifdef IPV6
754         } else if (ss.ss_family == AF_INET6) {
755           struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)&ss;
756             inet_ntop(AF_INET6, &(saddr->sin6_addr), s, INET6_ADDRSTRLEN);
757 #endif
758         }
759         build_sock_list(irp, masterlist, idxstr, dcc[i].nick,
760             (dcc[i].host[0] == '\0') ? iptostr(&dcc[i].sockname.addr.sa) : dcc[i].host,
761             s, dcc[i].port,
762 #ifdef TLS
763             dcc[i].ssl,
764 #else
765             0,
766 #endif
767             dcc[i].type ? dcc[i].type->name : "*UNKNOWN*", other,
768             timestamp);
769       }
770     }
771   }
772 }
773 
774 static int tcl_socklist STDVAR
775 {
776 
777   BADARGS(1, 2, " ?type?");
778   dccsocklist(irp, argc, (argc == 2) ? argv[1] : NULL, 1);
779   return TCL_OK;
780 }
781 
782 static int tcl_dcclist STDVAR
783 {
784   BADARGS(1, 2, " ?type?");
785   dccsocklist(irp, argc, (argc == 2) ? argv[1] : NULL, 0);
786   return TCL_OK;
787 }
788 
789 static int tcl_whom STDVAR
790 {
791   int chan, i;
792   char c[2], idle[32], work[20], *p;
793   long tv = 0;
794   EGG_CONST char *list[7];
795 
796   BADARGS(2, 2, " chan");
797 
798   if (argv[1][0] == '*')
799     chan = -1;
800   else {
801     if ((argv[1][0] < '0') || (argv[1][0] > '9')) {
802       Tcl_SetVar(interp, "_chan", argv[1], 0);
803       if ((Tcl_VarEval(interp, "assoc ", "$_chan", NULL) != TCL_OK) ||
804           tcl_resultempty()) {
805         Tcl_AppendResult(irp, "channel name is invalid", NULL);
806         return TCL_ERROR;
807       }
808       chan = tcl_resultint();
809     } else
810       chan = atoi(argv[1]);
811     if ((chan < 0) || (chan > 199999)) {
812       Tcl_AppendResult(irp, "channel out of range; must be 0 through 199999",
813                        NULL);
814       return TCL_ERROR;
815     }
816   }
817   for (i = 0; i < dcc_total; i++)
818     if (dcc[i].type == &DCC_CHAT) {
819       if (dcc[i].u.chat->channel == chan || chan == -1) {
820         c[0] = geticon(i);
821         c[1] = 0;
822         tv = (now - dcc[i].timeval) / 60;
823         egg_snprintf(idle, sizeof idle, "%li", tv);
824         list[0] = dcc[i].nick;
825         list[1] = botnetnick;
826         list[2] = dcc[i].host;
827         list[3] = c;
828         list[4] = idle;
829         list[5] = dcc[i].u.chat->away ? dcc[i].u.chat->away : "";
830         if (chan == -1) {
831           egg_snprintf(work, sizeof work, "%d", dcc[i].u.chat->channel);
832           list[6] = work;
833         }
834         p = Tcl_Merge((chan == -1) ? 7 : 6, list);
835         Tcl_AppendElement(irp, p);
836         Tcl_Free((char *) p);
837       }
838     }
839   for (i = 0; i < parties; i++) {
840     if (party[i].chan == chan || chan == -1) {
841       c[0] = party[i].flag;
842       c[1] = 0;
843       if (party[i].timer == 0L)
844         strcpy(idle, "0");
845       else {
846         tv = (now - party[i].timer) / 60;
847         egg_snprintf(idle, sizeof idle, "%li", tv);
848       }
849       list[0] = party[i].nick;
850       list[1] = party[i].bot;
851       list[2] = party[i].from ? party[i].from : "";
852       list[3] = c;
853       list[4] = idle;
854       list[5] = party[i].status & PLSTAT_AWAY ? party[i].away : "";
855       if (chan == -1) {
856         egg_snprintf(work, sizeof work, "%d", party[i].chan);
857         list[6] = work;
858       }
859       p = Tcl_Merge((chan == -1) ? 7 : 6, list);
860       Tcl_AppendElement(irp, p);
861       Tcl_Free((char *) p);
862     }
863   }
864   return TCL_OK;
865 }
866 
867 static int tcl_dccused STDVAR
868 {
869   char s[20];
870 
871   BADARGS(1, 1, "");
872 
873   egg_snprintf(s, sizeof s, "%d", dcc_total);
874   Tcl_AppendResult(irp, s, NULL);
875   return TCL_OK;
876 }
877 
878 static int tcl_getdccidle STDVAR
879 {
880   int x, idx;
881   char s[21];
882 
883   BADARGS(2, 2, " idx");
884 
885   idx = findidx(atoi(argv[1]));
886   if (idx < 0) {
887     Tcl_AppendResult(irp, "invalid idx", NULL);
888     return TCL_ERROR;
889   }
890   x = (now - dcc[idx].timeval);
891 
892   egg_snprintf(s, sizeof s, "%d", x);
893   Tcl_AppendElement(irp, s);
894   return TCL_OK;
895 }
896 
897 static int tcl_getdccaway STDVAR
898 {
899   int idx;
900 
901   BADARGS(2, 2, " idx");
902 
903   idx = findidx(atol(argv[1]));
904   if (idx < 0 || dcc[idx].type != &DCC_CHAT) {
905     Tcl_AppendResult(irp, "invalid idx", NULL);
906     return TCL_ERROR;
907   }
908   if (dcc[idx].u.chat->away == NULL)
909     return TCL_OK;
910 
911   Tcl_AppendResult(irp, dcc[idx].u.chat->away, NULL);
912   return TCL_OK;
913 }
914 
915 static int tcl_setdccaway STDVAR
916 {
917   int idx;
918 
919   BADARGS(3, 3, " idx message");
920 
921   idx = findidx(atol(argv[1]));
922   if (idx < 0 || dcc[idx].type != &DCC_CHAT) {
923     Tcl_AppendResult(irp, "invalid idx", NULL);
924     return TCL_ERROR;
925   }
926   if (!argv[2][0]) {
927     if (dcc[idx].u.chat->away != NULL)
928       not_away(idx);
929     return TCL_OK;
930   }
931   set_away(idx, argv[2]);
932   return TCL_OK;
933 }
934 
935 static int tcl_link STDVAR
936 {
937   int x, i;
938   char bot[HANDLEN + 1], bot2[HANDLEN + 1];
939 
940   BADARGS(2, 3, " ?via-bot? bot");
941 
942   strlcpy(bot, argv[1], sizeof bot);
943   if (argc == 3) {
944     x = 1;
945     strlcpy(bot2, argv[2], sizeof bot2);
946     i = nextbot(bot);
947     if (i < 0)
948       x = 0;
949     else
950       botnet_send_link(i, botnetnick, bot, bot2);
951   } else
952     x = botlink("", -2, bot);
953 
954   egg_snprintf(bot, sizeof bot, "%d", x);
955   Tcl_AppendResult(irp, bot, NULL);
956   return TCL_OK;
957 }
958 
959 static int tcl_unlink STDVAR
960 {
961   int i, x;
962   char bot[HANDLEN + 1];
963 
964   BADARGS(2, 3, " bot ?comment?");
965 
966   strlcpy(bot, argv[1], sizeof bot);
967   i = nextbot(bot);
968   if (i < 0)
969     x = 0;
970   else {
971     x = 1;
972     if (!strcasecmp(bot, dcc[i].nick))
973       x = botunlink(-2, bot, argv[2], botnetnick);
974     else
975       botnet_send_unlink(i, botnetnick, lastbot(bot), bot, argv[2]);
976   }
977   egg_snprintf(bot, sizeof bot, "%d", x);
978 
979   Tcl_AppendResult(irp, bot, NULL);
980   return TCL_OK;
981 }
982 
983 static int tcl_connect STDVAR
984 {
985   int i, sock;
986   char s[81];
987 
988   BADARGS(3, 3, " hostname port");
989 
990   if (dcc_total == max_dcc && increase_socks_max()) {
991     Tcl_AppendResult(irp, "out of dcc table space", NULL);
992     return TCL_ERROR;
993   }
994 
995   i = new_dcc(&DCC_SOCKET, 0);
996   if (i < 0) {
997     Tcl_AppendResult(irp, "Could not allocate socket.", NULL);
998     return TCL_ERROR;
999   }
1000   sock = open_telnet(i, argv[1], atoi(argv[2]));
1001   if (sock < 0) {
1002     switch (sock) {
1003       case -3:
1004         Tcl_AppendResult(irp, MISC_NOFREESOCK, NULL);
1005         break;
1006       case -2:
1007         Tcl_AppendResult(irp, "DNS lookup failed", NULL);
1008         break;
1009       default:
1010         Tcl_AppendResult(irp, strerror(errno), NULL);
1011     }
1012     lostdcc(i);
1013     return TCL_ERROR;
1014   }
1015 #ifdef TLS
1016   if (*argv[2] == '+') {
1017     if (ssl_handshake(sock, TLS_CONNECT, 0, LOG_MISC, NULL, NULL)) {
1018       killsock(sock);
1019       lostdcc(i);
1020       strlcpy(s, "Failed to establish a TLS session", sizeof s);
1021       Tcl_AppendResult(irp, s, NULL);
1022       return TCL_ERROR;
1023     } else
1024       dcc[i].ssl = 1;
1025   }
1026 #endif
1027   strcpy(dcc[i].nick, "*");
1028   strlcpy(dcc[i].host, argv[1], UHOSTMAX);
1029   egg_snprintf(s, sizeof s, "%d", sock);
1030   Tcl_AppendResult(irp, s, NULL);
1031   return TCL_OK;
1032 }
1033 
setlisten(Tcl_Interp * irp,char * ip,char * portp,char * type,char * maskproc,char * flag)1034 static int setlisten(Tcl_Interp *irp, char *ip, char *portp, char *type, char *maskproc, char *flag) {
1035   int i, idx = -1, port, realport, found=0, ipv4=1;
1036   char s[11], msg[256], newip[INET6_ADDRSTRLEN];
1037   struct portmap *pmap = NULL, *pold = NULL;
1038   sockname_t name;
1039   struct in_addr ipaddr4;
1040   struct addrinfo hint, *ipaddr = NULL;
1041   int ret;
1042 #ifdef IPV6
1043   struct in6_addr ipaddr6;
1044 #endif
1045 
1046   memset(&hint, '\0', sizeof hint);
1047   hint.ai_family = PF_UNSPEC;
1048   hint.ai_flags = AI_NUMERICHOST;
1049   if (!ip[0]) {
1050 #ifdef IPV6
1051     if (pref_af) {
1052       strlcpy(newip, "::", sizeof newip);
1053     } else {
1054 #endif
1055       strlcpy(newip, "0.0.0.0", sizeof newip);
1056 #ifdef IPV6
1057     }
1058 #endif
1059   } else {
1060     strlcpy(newip, ip, sizeof newip);
1061   }
1062   /* Return addrinfo struct ipaddr containing family... */
1063   ret = getaddrinfo(newip, NULL, &hint, &ipaddr);
1064   if (!ret) {
1065   /* Load network address to in(6)_addr struct for later byte comparisons */
1066     if (ipaddr->ai_family == AF_INET) {
1067       inet_pton(AF_INET, newip, &ipaddr4);
1068     }
1069 #ifdef IPV6
1070     else if (ipaddr->ai_family == AF_INET6) {
1071       inet_pton(AF_INET6, newip, &ipaddr6);
1072       ipv4 = 0;
1073     }
1074 #endif
1075   }
1076   freeaddrinfo(ipaddr);
1077   port = realport = atoi(portp);
1078   for (pmap = root; pmap; pold = pmap, pmap = pmap->next) {
1079     if (pmap->realport == port) {
1080       port = pmap->mappedto;
1081       break;
1082     }
1083   }
1084   for (i = 0; i < dcc_total; i++) {
1085     if ((dcc[i].type == &DCC_TELNET) && (dcc[i].port == port)) {
1086       idx = i;
1087       found = 1;
1088 
1089       /* Check if this is an exact match and skip these checks (ie, rehash) */
1090       if (ipv4) {
1091         if (ipaddr4.s_addr == dcc[idx].sockname.addr.s4.sin_addr.s_addr) {
1092           break;
1093         }
1094       }
1095 #ifdef IPV6
1096       else if (ipaddr6.s6_addr == dcc[idx].sockname.addr.s6.sin6_addr.s6_addr) {
1097         break;
1098       }
1099 
1100       /* Check if the bound IP is IPvX, and the new IP is IPvY */
1101       if (((ipv4) && (dcc[idx].sockname.addr.sa.sa_family != AF_INET)) ||
1102          ((!ipv4) && (dcc[idx].sockname.addr.sa.sa_family != AF_INET6))) {
1103         found = 0;
1104         break;
1105       }
1106 #endif
1107 
1108       /* Check if IP is specific, but the already-bound IP is all-interfaces */
1109       if (ipv4) {
1110         if ((ipaddr4.s_addr != 0) && (dcc[idx].sockname.addr.s4.sin_addr.s_addr == 0)) {
1111           Tcl_AppendResult(irp, "this port is already bound to 0.0.0.0 on this "
1112                 "machine, remove it before trying to bind to this IP", NULL);
1113           return TCL_ERROR;
1114         }
1115       }
1116 #ifdef IPV6
1117       else if ((!IN6_IS_ADDR_UNSPECIFIED(&ipaddr6)) &&
1118                 (IN6_IS_ADDR_UNSPECIFIED(&dcc[idx].sockname.addr.s6.sin6_addr))) {
1119           Tcl_AppendResult(irp, "this port is already bound to :: on this "
1120                 "machine, remove it before trying to bind to this IP", NULL);
1121           return TCL_ERROR;
1122       }
1123 #endif
1124 
1125       /* Check if IP is all-interfaces, but the already-bound IP is specific */
1126       if (ipv4) {
1127         if ((ipaddr4.s_addr == 0) && (dcc[idx].sockname.addr.s4.sin_addr.s_addr != 0)) {
1128           Tcl_AppendResult(irp, "this port is already bound to a specific IP "
1129                 "on this machine, remove it before trying to bind to all "
1130                 "interfaces", NULL);
1131           return TCL_ERROR;
1132         }
1133       }
1134 #ifdef IPV6
1135       else if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr6) &&
1136                 (!IN6_IS_ADDR_UNSPECIFIED(&dcc[idx].sockname.addr.s6.sin6_addr))) {
1137           Tcl_AppendResult(irp, "this port is already bound to a specific IP "
1138                 "on this machine, remove it before trying to bind to this all "
1139                 "interfaces", NULL);
1140           return TCL_ERROR;
1141       }
1142 #endif
1143     }
1144   }
1145   if (!strcasecmp(type, "off")) {
1146     if (pmap) {
1147       if (pold)
1148         pold->next = pmap->next;
1149       else
1150         root = pmap->next;
1151       nfree(pmap);
1152     }
1153     /* Remove */
1154     if (idx < 0) {
1155       Tcl_AppendResult(irp, "no such listen port is open", NULL);
1156       return TCL_ERROR;
1157     }
1158     killsock(dcc[idx].sock);
1159     lostdcc(idx);
1160     return TCL_OK;
1161   }
1162   /* If there isn't already something listening on that port, or there is but
1163    * it is something that may allow us to try a different IP for that port
1164    */
1165   if ((idx < 0) || (!found)) {
1166     /* Make new one */
1167     if (dcc_total >= max_dcc && increase_socks_max()) {
1168       Tcl_AppendResult(irp, "No more DCC slots available.", NULL);
1169       return TCL_ERROR;
1170     }
1171     /* We used to try up to 20 ports here, but have scientifically concluded
1172      * that is just silly.
1173      */
1174     /* If we didn't find a listening ip/port, or we did but it isn't all
1175      * interfaces
1176      */
1177     if (strlen(newip)) {
1178       setsockname(&name, newip, port, 1);
1179       i = open_address_listen(&name);
1180     } else {
1181       i = open_listen(&port);
1182     }
1183     if (i < 0) {
1184       egg_snprintf(msg, sizeof msg, "Couldn't listen on port '%d' on the given "
1185                  "address: %s. Please check that the port is not already in use",
1186                   realport, strerror(errno));
1187       Tcl_AppendResult(irp, msg, NULL);
1188       return TCL_ERROR;
1189     }
1190     idx = new_dcc(&DCC_TELNET, 0);
1191     dcc[idx].sockname.addrlen = sizeof(dcc[idx].sockname.addr);
1192     getsockname(i, &dcc[idx].sockname.addr.sa, &dcc[idx].sockname.addrlen);
1193     dcc[idx].sockname.family = dcc[idx].sockname.addr.sa.sa_family;
1194     dcc[idx].port = port;
1195     dcc[idx].sock = i;
1196     dcc[idx].timeval = now;
1197   }
1198 #ifdef TLS
1199   if (portp[0] == '+')
1200     dcc[idx].ssl = 1;
1201   else
1202     dcc[idx].ssl = 0;
1203 #endif
1204   /* script? */
1205   if (!strcmp(type, "script")) {
1206     strcpy(dcc[idx].nick, "(script)");
1207     if (flag) {
1208       dcc[idx].status = LSTN_PUBLIC;
1209     }
1210     strlcpy(dcc[idx].host, maskproc, UHOSTMAX);
1211     egg_snprintf(s, sizeof s, "%d", port);
1212     Tcl_AppendResult(irp, s, NULL);
1213     return TCL_OK;
1214   }
1215   /* bots/users/all */
1216   if (!strcmp(type, "bots"))
1217     strcpy(dcc[idx].nick, "(bots)");
1218   else if (!strcmp(type, "users"))
1219     strcpy(dcc[idx].nick, "(users)");
1220   else if (!strcmp(type, "all"))
1221     strcpy(dcc[idx].nick, "(telnet)");
1222   if (maskproc[0])
1223     strlcpy(dcc[idx].host, maskproc, UHOSTMAX);
1224   else
1225     strcpy(dcc[idx].host, "*");
1226   egg_snprintf(s, sizeof s, "%d", port);
1227   Tcl_AppendResult(irp, s, NULL);
1228   if (!pmap) {
1229     pmap = nmalloc(sizeof(struct portmap));
1230     pmap->next = root;
1231     root = pmap;
1232   }
1233   pmap->realport = realport;
1234   pmap->mappedto = port;
1235 
1236 #ifdef TLS
1237   putlog(LOG_MISC, "*", "Listening for telnet connections on %s port %s%d (%s).",
1238         iptostr(&dcc[idx].sockname.addr.sa), dcc[idx].ssl ? "+" : "", port, type);
1239 #else
1240   putlog(LOG_MISC, "*", "Listening for telnet connections on %s port %d (%s).",
1241         iptostr(&dcc[idx].sockname.addr.sa), port, type);
1242 #endif
1243 
1244   return TCL_OK;
1245 }
1246 
1247 /* Create a new listening port (or destroy one)
1248  *
1249  * listen [ip] <port> bots/all/users [mask]
1250  * listen [ip] <port> script <proc> <flag>
1251  * listen [ip] <port> off
1252  */
1253 static int tcl_listen STDVAR
1254 {
1255   char ip[121], maskproc[UHOSTMAX] = "";
1256   char port[7], type[7], flag[4], *endptr;
1257   unsigned char buf[sizeof(struct in6_addr)];
1258   int i = 1;
1259 
1260 /* People like to add comments to this command for some reason, and it can cause
1261  * errors that are difficult to figure out. Let's instead throw a more helpful
1262  * error for this case to get around BADARGS, and handle other cases further
1263  * down in the code
1264  *
1265  * Check if extra args are config comments
1266  */
1267   if (argc > 6) {
1268     if (argv[6][0] == '#') {
1269       fatal(DCC_BADLISTEN, 0);
1270     }
1271   }
1272 
1273   BADARGS(3, 6, " ?ip? port type ?mask?/?proc flag?");
1274 
1275 /* default listen-addr if not specified */
1276   strlcpy(ip, listen_ip, sizeof(ip));
1277 
1278 /* Check if IP exists, set to NULL if not */
1279   strtol(argv[1], &endptr, 10);
1280   if (*endptr != '\0') {
1281     if (inet_pton(AF_INET, argv[1], buf)
1282 #ifdef IPV6
1283         || inet_pton(AF_INET6, argv[1], buf)
1284 #endif
1285       ) {
1286       strlcpy(ip, argv[1], sizeof(ip));
1287       i++;
1288     } else {
1289       Tcl_AppendResult(irp, "invalid ip address", NULL);
1290       return TCL_ERROR;
1291     }
1292   }
1293 /* Check for port */
1294   if ((atoi(argv[i]) > 65535) || (atoi(argv[i]) < 1)) {
1295     Tcl_AppendResult(irp, "invalid listen port", NULL);
1296     return TCL_ERROR;
1297   }
1298   strlcpy(port, argv[i], sizeof(port));
1299   i++;
1300 /* Check for listen type */
1301   if (!argv[i]) {
1302     Tcl_AppendResult(irp, "missing listen type", NULL);
1303     return TCL_ERROR;
1304   }
1305   if ((strcmp(argv[i], "bots")) && (strcmp(argv[i], "users"))
1306         && (strcmp(argv[i], "all")) && (strcmp(argv[i], "off"))
1307         && (strcmp(argv[i], "script"))) {
1308     Tcl_AppendResult(irp, "invalid listen type: must be one of ",
1309           "bots, users, all, off, script", NULL);
1310     return TCL_ERROR;
1311   }
1312   strlcpy(type, argv[i], sizeof(type));
1313 /* Check if mask or proc exists */
1314   if ((((argc>3) && !ip[0]) || ((argc >4) && ip[0])) &&
1315         (argv[i+1][0] != '#')) { /* Ignore config comments! */
1316     i++;
1317     strlcpy(maskproc, argv[i], sizeof(maskproc));
1318   }
1319 /* If script, check for proc and flag */
1320   if (!strcmp(type, "script")) {
1321     if (!maskproc[0]) {
1322       Tcl_AppendResult(irp, "a proc name must be specified for a script listen", NULL);
1323       return TCL_ERROR;
1324     }
1325     if ((!ip[0] && (argc==4)) || (ip[0] && argc==5)) {
1326       Tcl_AppendResult(irp, "missing flag. allowed flags: pub", NULL);
1327       return TCL_ERROR;
1328     }
1329     if ((!ip[0] && (argc==5)) || (argc == 6)) {
1330       i++;
1331       if (strcmp(argv[i], "pub")) {
1332         Tcl_AppendResult(irp, "unknown flag: ", flag, ". allowed flags: pub",
1333               NULL);
1334         return TCL_ERROR;
1335       }
1336       strlcpy(flag, argv[i], sizeof flag);
1337     }
1338   }
1339   return setlisten(irp, ip, port, type, maskproc, flag);
1340 }
1341 
1342 static int tcl_boot STDVAR
1343 {
1344   char who[NOTENAMELEN + 1];
1345   int i, ok = 0;
1346 
1347   BADARGS(2, 3, " user@bot ?reason?");
1348 
1349   strlcpy(who, argv[1], sizeof who);
1350 
1351   if (strchr(who, '@') != NULL) {
1352     char whonick[HANDLEN + 1];
1353 
1354     splitc(whonick, who, '@');
1355     whonick[HANDLEN] = 0;
1356     if (!strcasecmp(who, botnetnick))
1357       strlcpy(who, whonick, sizeof who);
1358     else if (remote_boots > 0) {
1359       i = nextbot(who);
1360       if (i < 0)
1361         return TCL_OK;
1362       botnet_send_reject(i, botnetnick, NULL, whonick, who,
1363                          argc >= 3 && argv[2] ? argv[2] : "");
1364     } else
1365       return TCL_OK;
1366   }
1367   for (i = 0; i < dcc_total; i++)
1368     if (!ok && (dcc[i].type->flags & DCT_CANBOOT) &&
1369         !strcasecmp(dcc[i].nick, who)) {
1370       do_boot(i, botnetnick, argc >= 3 && argv[2] ? argv[2] : "");
1371       ok = 1;
1372     }
1373   return TCL_OK;
1374 }
1375 
1376 static int tcl_rehash STDVAR
1377 {
1378   BADARGS(1, 1, "");
1379 
1380   if (make_userfile) {
1381     putlog(LOG_MISC, "*", USERF_NONEEDNEW);
1382     make_userfile = 0;
1383   }
1384   write_userfile(-1);
1385 
1386   putlog(LOG_MISC, "*", USERF_REHASHING);
1387   do_restart = -2;
1388   return TCL_OK;
1389 }
1390 
1391 static int tcl_restart STDVAR
1392 {
1393   BADARGS(1, 1, "");
1394 
1395   if (!backgrd) {
1396     Tcl_AppendResult(interp, "You can't restart a -n bot", NULL);
1397     return TCL_ERROR;
1398   }
1399   if (make_userfile) {
1400     putlog(LOG_MISC, "*", USERF_NONEEDNEW);
1401     make_userfile = 0;
1402   }
1403   write_userfile(-1);
1404   putlog(LOG_MISC, "*", MISC_RESTARTING);
1405   wipe_timers(interp, &utimer);
1406   wipe_timers(interp, &timer);
1407   do_restart = -1;
1408   return TCL_OK;
1409 }
1410 
1411 static int tcl_traffic STDVAR
1412 {
1413   char buf[1024];
1414   unsigned long out_total_today, out_total;
1415   unsigned long in_total_today, in_total;
1416 
1417   /* IRC traffic */
1418   sprintf(buf, "irc %lu %lu %lu %lu", itraffic_irc_today, itraffic_irc +
1419           itraffic_irc_today, otraffic_irc_today,
1420           otraffic_irc + otraffic_irc_today);
1421   Tcl_AppendElement(irp, buf);
1422 
1423   /* Botnet traffic */
1424   sprintf(buf, "botnet %lu %lu %lu %lu", itraffic_bn_today, itraffic_bn +
1425           itraffic_bn_today, otraffic_bn_today,
1426           otraffic_bn + otraffic_bn_today);
1427   Tcl_AppendElement(irp, buf);
1428 
1429   /* Partyline */
1430   sprintf(buf, "partyline %lu %lu %lu %lu", itraffic_dcc_today, itraffic_dcc +
1431           itraffic_dcc_today, otraffic_dcc_today,
1432           otraffic_dcc + otraffic_dcc_today);
1433   Tcl_AppendElement(irp, buf);
1434 
1435   /* Transfer */
1436   sprintf(buf, "transfer %lu %lu %lu %lu", itraffic_trans_today,
1437           itraffic_trans + itraffic_trans_today, otraffic_trans_today,
1438           otraffic_trans + otraffic_trans_today);
1439   Tcl_AppendElement(irp, buf);
1440 
1441   /* Misc traffic */
1442   sprintf(buf, "misc %lu %lu %lu %lu", itraffic_unknown_today,
1443           itraffic_unknown + itraffic_unknown_today, otraffic_unknown_today,
1444           otraffic_unknown + otraffic_unknown_today);
1445   Tcl_AppendElement(irp, buf);
1446 
1447   /* Totals */
1448   in_total_today = itraffic_irc_today + itraffic_bn_today +
1449                    itraffic_dcc_today + itraffic_trans_today +
1450                    itraffic_unknown_today;
1451   in_total = in_total_today + itraffic_irc + itraffic_bn + itraffic_dcc +
1452              itraffic_trans + itraffic_unknown;
1453   out_total_today = otraffic_irc_today + otraffic_bn_today +
1454                     otraffic_dcc_today + itraffic_trans_today +
1455                     otraffic_unknown_today;
1456   out_total = out_total_today + otraffic_irc + otraffic_bn + otraffic_dcc +
1457               otraffic_trans + otraffic_unknown;
1458   sprintf(buf, "total %lu %lu %lu %lu", in_total_today, in_total,
1459           out_total_today, out_total);
1460   Tcl_AppendElement(irp, buf);
1461   return TCL_OK;
1462 }
1463 
1464 tcl_cmds tcldcc_cmds[] = {
1465   {"putdcc",             tcl_putdcc},
1466   {"putdccraw",       tcl_putdccraw},
1467   {"putidx",             tcl_putdcc},
1468   {"dccsimul",         tcl_dccsimul},
1469   {"dccbroadcast", tcl_dccbroadcast},
1470   {"hand2idx",         tcl_hand2idx},
1471   {"getchan",           tcl_getchan},
1472   {"setchan",           tcl_setchan},
1473   {"dccputchan",     tcl_dccputchan},
1474   {"console",           tcl_console},
1475   {"resetconsole", tcl_resetconsole},
1476   {"strip",               tcl_strip},
1477   {"echo",                 tcl_echo},
1478   {"page",                 tcl_page},
1479   {"control",           tcl_control},
1480   {"valididx",         tcl_valididx},
1481   {"killdcc",           tcl_killdcc},
1482   {"putbot",             tcl_putbot},
1483   {"putallbots",     tcl_putallbots},
1484   {"idx2hand",         tcl_idx2hand},
1485   {"bots",                 tcl_bots},
1486   {"botlist",           tcl_botlist},
1487   {"dcclist",           tcl_dcclist},
1488   {"socklist",         tcl_socklist},
1489   {"whom",                 tcl_whom},
1490   {"dccused",           tcl_dccused},
1491   {"getdccidle",     tcl_getdccidle},
1492   {"getdccaway",     tcl_getdccaway},
1493   {"setdccaway",     tcl_setdccaway},
1494   {"islinked",         tcl_islinked},
1495   {"link",                 tcl_link},
1496   {"unlink",             tcl_unlink},
1497   {"connect",           tcl_connect},
1498   {"listen",             tcl_listen},
1499   {"boot",                 tcl_boot},
1500   {"rehash",             tcl_rehash},
1501   {"restart",           tcl_restart},
1502   {"traffic",           tcl_traffic},
1503   {NULL,                       NULL}
1504 };
1505