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