1 /*
2  * tclhash.c -- handles:
3  *   bind and unbind
4  *   checking and triggering the various in-bot bindings
5  *   listing current bindings
6  *   adding/removing new binding tables
7  *   (non-Tcl) procedure lookups for msg/dcc/file commands
8  *   (Tcl) binding internal procedures to msg/dcc/file commands
9  */
10 /*
11  * Copyright (C) 1997 Robey Pointer
12  * Copyright (C) 1999 - 2021 Eggheads Development Team
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28 
29 #include <sys/time.h>
30 #include <sys/resource.h>
31 #include "main.h"
32 #include "chan.h"
33 #include "users.h"
34 
35 extern Tcl_Interp *interp;
36 extern struct dcc_t *dcc;
37 extern struct userrec *userlist;
38 extern int dcc_total;
39 extern time_t now;
40 
41 p_tcl_bind_list bind_table_list;
42 p_tcl_bind_list H_chat, H_act, H_bcst, H_chon, H_chof, H_load, H_unld, H_link,
43                 H_disc, H_dcc, H_chjn, H_chpt, H_bot, H_time, H_nkch, H_away,
44                 H_note, H_filt, H_event, H_die, H_cron, H_log = NULL;
45 #ifdef TLS
46 p_tcl_bind_list H_tls = NULL;
47 static int builtin_idx();
48 #endif
49 
50 static int builtin_2char();
51 static int builtin_3char();
52 static int builtin_5int();
53 static int builtin_cron();
54 static int builtin_char();
55 static int builtin_chpt();
56 static int builtin_chjn();
57 static int builtin_idxchar();
58 static int builtin_charidx();
59 static int builtin_chat();
60 static int builtin_dcc();
61 static int builtin_log();
62 
63 
64 /* Allocate and initialise a chunk of memory.
65  */
n_malloc_null(int size,const char * file,int line)66 static void *n_malloc_null(int size, const char *file, int line)
67 {
68 #ifdef DEBUG_MEM
69 #  define nmalloc_null(size) n_malloc_null(size, __FILE__, __LINE__)
70   void *ptr = n_malloc(size, file, line);
71 #else
72 #  define nmalloc_null(size) n_malloc_null(size, NULL, 0)
73   void *ptr = nmalloc(size);
74 #endif
75 
76   egg_bzero(ptr, size);
77   return ptr;
78 }
79 
80 
81 /* Delete trigger/command.
82  */
tcl_cmd_delete(tcl_cmd_t * tc)83 static void tcl_cmd_delete(tcl_cmd_t *tc)
84 {
85   nfree(tc->func_name);
86   nfree(tc);
87 }
88 
89 /* Delete bind and its elements.
90  */
tcl_bind_mask_delete(tcl_bind_mask_t * tm)91 static void tcl_bind_mask_delete(tcl_bind_mask_t *tm)
92 {
93   tcl_cmd_t *tc, *tc_next;
94 
95   for (tc = tm->first; tc; tc = tc_next) {
96     tc_next = tc->next;
97     tcl_cmd_delete(tc);
98   }
99   nfree(tm->mask);
100   nfree(tm);
101 }
102 
103 /* Delete bind list and its elements.
104  */
tcl_bind_list_delete(tcl_bind_list_t * tl)105 static void tcl_bind_list_delete(tcl_bind_list_t *tl)
106 {
107   tcl_bind_mask_t *tm, *tm_next;
108 
109   for (tm = tl->first; tm; tm = tm_next) {
110     tm_next = tm->next;
111     tcl_bind_mask_delete(tm);
112   }
113   nfree(tl);
114 }
115 
garbage_collect_tclhash(void)116 void garbage_collect_tclhash(void)
117 {
118   tcl_bind_list_t *tl, *tl_next, *tl_prev;
119   tcl_bind_mask_t *tm, *tm_next, *tm_prev;
120   tcl_cmd_t *tc, *tc_next, *tc_prev;
121 
122   for (tl = bind_table_list, tl_prev = NULL; tl; tl = tl_next) {
123     tl_next = tl->next;
124 
125     if (tl->flags & HT_DELETED) {
126       if (tl_prev)
127         tl_prev->next = tl->next;
128       else
129         bind_table_list = tl->next;
130       tcl_bind_list_delete(tl);
131     } else {
132       for (tm = tl->first, tm_prev = NULL; tm; tm = tm_next) {
133         tm_next = tm->next;
134 
135         if (!(tm->flags & TBM_DELETED)) {
136           for (tc = tm->first, tc_prev = NULL; tc; tc = tc_next) {
137             tc_next = tc->next;
138 
139             if (tc->attributes & TC_DELETED) {
140               if (tc_prev)
141                 tc_prev->next = tc->next;
142               else
143                 tm->first = tc->next;
144               tcl_cmd_delete(tc);
145             } else
146               tc_prev = tc;
147           }
148         }
149 
150         /* Delete the bind when it's marked as deleted or when it's empty. */
151         if ((tm->flags & TBM_DELETED) || tm->first == NULL) {
152           if (tm_prev)
153             tm_prev->next = tm->next;
154           else
155             tl->first = tm_next;
156           tcl_bind_mask_delete(tm);
157         } else
158           tm_prev = tm;
159       }
160       tl_prev = tl;
161     }
162   }
163 }
164 
tcl_cmd_expmem(tcl_cmd_t * tc)165 static int tcl_cmd_expmem(tcl_cmd_t *tc)
166 {
167   int tot;
168 
169   tot = sizeof(*tc);
170   if (tc->func_name)
171     tot += strlen(tc->func_name) + 1;
172   return tot;
173 }
174 
tcl_bind_mask_expmem(tcl_bind_mask_t * tm)175 static int tcl_bind_mask_expmem(tcl_bind_mask_t *tm)
176 {
177   int tot = 0;
178   tcl_cmd_t *tc;
179 
180   for (tc = tm->first; tc; tc = tc->next)
181     tot += tcl_cmd_expmem(tc);
182   if (tm->mask)
183     tot += strlen(tm->mask) + 1;
184   tot += sizeof(*tm);
185   return tot;
186 }
187 
tcl_bind_list_expmem(tcl_bind_list_t * tl)188 static int tcl_bind_list_expmem(tcl_bind_list_t *tl)
189 {
190   int tot = 0;
191   tcl_bind_mask_t *tm;
192 
193   for (tm = tl->first; tm; tm = tm->next)
194     tot += tcl_bind_mask_expmem(tm);
195   tot += sizeof(*tl);
196   return tot;
197 }
198 
expmem_tclhash(void)199 int expmem_tclhash(void)
200 {
201   int tot = 0;
202   tcl_bind_list_t *tl;
203 
204   for (tl = bind_table_list; tl; tl = tl->next)
205     tot += tcl_bind_list_expmem(tl);
206   return tot;
207 }
208 
209 
210 extern cmd_t C_dcc[];
211 static int tcl_bind();
212 
213 static cd_tcl_cmd cd_cmd_table[] = {
214   {"bind",   tcl_bind, (void *) 0},
215   {"unbind", tcl_bind, (void *) 1},
216   {0}
217 };
218 
init_bind(void)219 void init_bind(void)
220 {
221   bind_table_list = NULL;
222   Context;
223   add_cd_tcl_cmds(cd_cmd_table);
224   H_unld = add_bind_table("unld", HT_STACKABLE, builtin_char);
225   H_time = add_bind_table("time", HT_STACKABLE, builtin_5int);
226   H_cron = add_bind_table("cron", HT_STACKABLE, builtin_cron);
227   H_note = add_bind_table("note", 0, builtin_3char);
228   H_nkch = add_bind_table("nkch", HT_STACKABLE, builtin_2char);
229   H_load = add_bind_table("load", HT_STACKABLE, builtin_char);
230   H_link = add_bind_table("link", HT_STACKABLE, builtin_2char);
231   H_filt = add_bind_table("filt", HT_STACKABLE, builtin_idxchar);
232   H_disc = add_bind_table("disc", HT_STACKABLE, builtin_char);
233   H_dcc = add_bind_table("dcc", 0, builtin_dcc);
234   H_chpt = add_bind_table("chpt", HT_STACKABLE, builtin_chpt);
235   H_chon = add_bind_table("chon", HT_STACKABLE, builtin_charidx);
236   H_chof = add_bind_table("chof", HT_STACKABLE, builtin_charidx);
237   H_chjn = add_bind_table("chjn", HT_STACKABLE, builtin_chjn);
238   H_chat = add_bind_table("chat", HT_STACKABLE, builtin_chat);
239   H_bot = add_bind_table("bot", 0, builtin_3char);
240   H_bcst = add_bind_table("bcst", HT_STACKABLE, builtin_chat);
241   H_away = add_bind_table("away", HT_STACKABLE, builtin_chat);
242   H_act = add_bind_table("act", HT_STACKABLE, builtin_chat);
243   H_event = add_bind_table("evnt", HT_STACKABLE, builtin_char);
244   H_die = add_bind_table("die", HT_STACKABLE, builtin_char);
245   H_log = add_bind_table("log", HT_STACKABLE, builtin_log);
246 #ifdef TLS
247   H_tls = add_bind_table("tls", HT_STACKABLE, builtin_idx);
248 #endif
249   add_builtins(H_dcc, C_dcc);
250   Context;
251 }
252 
kill_bind(void)253 void kill_bind(void)
254 {
255   tcl_bind_list_t *tl, *tl_next;
256 
257   rem_builtins(H_dcc, C_dcc);
258   for (tl = bind_table_list; tl; tl = tl_next) {
259     tl_next = tl->next;
260 
261     if (!(tl->flags |= HT_DELETED))
262       putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
263     tcl_bind_list_delete(tl);
264   }
265   H_log = NULL;
266   bind_table_list = NULL;
267 }
268 
add_bind_table(const char * nme,int flg,IntFunc func)269 tcl_bind_list_t *add_bind_table(const char *nme, int flg, IntFunc func)
270 {
271   tcl_bind_list_t *tl, *tl_prev;
272   int v;
273 
274   /* Do not allow coders to use bind table names longer than
275    * 15 characters. */
276   Assert(strlen(nme) <= 15);
277 
278   for (tl = bind_table_list, tl_prev = NULL; tl; tl_prev = tl, tl = tl->next) {
279     if (tl->flags & HT_DELETED)
280       continue;
281     v = strcasecmp(tl->name, nme);
282     if (!v)
283       return tl;                /* Duplicate, just return old value.    */
284     if (v > 0)
285       break;                    /* New. Insert at start of list.        */
286   }
287 
288   tl = nmalloc_null(sizeof *tl);
289   strcpy(tl->name, nme);
290   tl->flags = flg;
291   tl->func = func;
292 
293   if (tl_prev) {
294     tl->next = tl_prev->next;
295     tl_prev->next = tl;
296   } else {
297     tl->next = bind_table_list;
298     bind_table_list = tl;
299   }
300 
301   putlog(LOG_DEBUG, "*", "Allocated bind table %s (flags %d)", nme, flg);
302   return tl;
303 }
304 
del_bind_table(tcl_bind_list_t * tl_which)305 void del_bind_table(tcl_bind_list_t *tl_which)
306 {
307   tcl_bind_list_t *tl;
308 
309   for (tl = bind_table_list; tl; tl = tl->next) {
310     if (tl->flags & HT_DELETED)
311       continue;
312     if (tl == tl_which) {
313       tl->flags |= HT_DELETED;
314       putlog(LOG_DEBUG, "*", "De-Allocated bind table %s", tl->name);
315       return;
316     }
317   }
318   putlog(LOG_DEBUG, "*", "??? Tried to delete not listed bind table ???");
319 }
320 
find_bind_table(const char * nme)321 tcl_bind_list_t *find_bind_table(const char *nme)
322 {
323   tcl_bind_list_t *tl;
324   int v;
325 
326   for (tl = bind_table_list; tl; tl = tl->next) {
327     if (tl->flags & HT_DELETED)
328       continue;
329     v = strcasecmp(tl->name, nme);
330     if (!v)
331       return tl;
332     if (v > 0)
333       return NULL;
334   }
335   return NULL;
336 }
337 
dump_bind_tables(Tcl_Interp * irp)338 static void dump_bind_tables(Tcl_Interp *irp)
339 {
340   tcl_bind_list_t *tl;
341   uint8_t i;
342 
343   for (tl = bind_table_list, i = 0; tl; tl = tl->next) {
344     if (tl->flags & HT_DELETED)
345       continue;
346     if (i)
347       Tcl_AppendResult(irp, ", ", NULL);
348     else
349       i = 1;
350     Tcl_AppendResult(irp, tl->name, NULL);
351   }
352 }
353 
unbind_bind_entry(tcl_bind_list_t * tl,const char * flags,const char * cmd,const char * proc)354 static int unbind_bind_entry(tcl_bind_list_t *tl, const char *flags,
355                              const char *cmd, const char *proc)
356 {
357   tcl_bind_mask_t *tm;
358 
359   /* Search for matching bind in bind list. */
360   for (tm = tl->first; tm; tm = tm->next) {
361     if (tm->flags & TBM_DELETED)
362       continue;
363     if (!strcmp(cmd, tm->mask))
364       break;                    /* Found it! fall out! */
365   }
366 
367   if (tm) {
368     tcl_cmd_t *tc;
369 
370     /* Search for matching proc in bind. */
371     for (tc = tm->first; tc; tc = tc->next) {
372       if (tc->attributes & TC_DELETED)
373         continue;
374       if (!strcasecmp(tc->func_name, proc)) {
375         /* Erase proc regardless of flags. */
376         tc->attributes |= TC_DELETED;
377         return 1;               /* Match.       */
378       }
379     }
380   }
381   return 0;                     /* No match.    */
382 }
383 
384 /* Add command (remove old one if necessary)
385  */
bind_bind_entry(tcl_bind_list_t * tl,const char * flags,const char * cmd,const char * proc)386 static int bind_bind_entry(tcl_bind_list_t *tl, const char *flags,
387                            const char *cmd, const char *proc)
388 {
389   tcl_cmd_t *tc;
390   tcl_bind_mask_t *tm;
391 
392   /* Search for matching bind in bind list. */
393   for (tm = tl->first; tm; tm = tm->next) {
394     if (tm->flags & TBM_DELETED)
395       continue;
396     if (!strcmp(cmd, tm->mask))
397       break;                    /* Found it! fall out! */
398   }
399 
400   /* Create bind if it doesn't exist yet. */
401   if (!tm) {
402     tm = nmalloc_null(sizeof *tm);
403     tm->mask = nmalloc(strlen(cmd) + 1);
404     strcpy(tm->mask, cmd);
405 
406     /* Link into linked list of binds. */
407     tm->next = tl->first;
408     tl->first = tm;
409   }
410 
411   /* Proc already defined? If so, replace. */
412   for (tc = tm->first; tc; tc = tc->next) {
413     if (tc->attributes & TC_DELETED)
414       continue;
415     if (!strcasecmp(tc->func_name, proc)) {
416       tc->flags.match = FR_GLOBAL | FR_CHAN;
417       break_down_flags(flags, &(tc->flags), NULL);
418       return 1;
419     }
420   }
421 
422   /* If this bind list is not stackable, remove the
423    * old entry from this bind. */
424   if (!(tl->flags & HT_STACKABLE)) {
425     for (tc = tm->first; tc; tc = tc->next) {
426       if (tc->attributes & TC_DELETED)
427         continue;
428       /* NOTE: We assume there's only one not-yet-deleted entry. */
429       tc->attributes |= TC_DELETED;
430       break;
431     }
432   }
433 
434   tc = nmalloc_null(sizeof *tc);
435   tc->flags.match = FR_GLOBAL | FR_CHAN;
436   break_down_flags(flags, &(tc->flags), NULL);
437   tc->func_name = nmalloc(strlen(proc) + 1);
438   strcpy(tc->func_name, proc);
439 
440   /* Link into linked list of the bind's command list. */
441   tc->next = tm->first;
442   tm->first = tc;
443 
444   return 1;
445 }
446 
tcl_getbinds(tcl_bind_list_t * tl_kind,const char * name)447 static int tcl_getbinds(tcl_bind_list_t *tl_kind, const char *name)
448 {
449   tcl_bind_mask_t *tm;
450 
451   for (tm = tl_kind->first; tm; tm = tm->next) {
452     if (tm->flags & TBM_DELETED)
453       continue;
454     if (!strcasecmp(tm->mask, name)) {
455       tcl_cmd_t *tc;
456 
457       for (tc = tm->first; tc; tc = tc->next) {
458         if (tc->attributes & TC_DELETED)
459           continue;
460         Tcl_AppendElement(interp, tc->func_name);
461       }
462       break;
463     }
464   }
465   return TCL_OK;
466 }
467 
468 static int tcl_bind STDVAR
469 {
470   tcl_bind_list_t *tl;
471 
472   /* cd defines what tcl_bind is supposed do: 0 = bind, 1 = unbind. */
473   if ((long int) cd == 1)
474     BADARGS(5, 5, " type flags cmd/mask procname");
475 
476   else
477     BADARGS(4, 5, " type flags cmd/mask ?procname?");
478 
479   tl = find_bind_table(argv[1]);
480   if (!tl) {
481     Tcl_AppendResult(irp, "bad type, should be one of: ", NULL);
482     dump_bind_tables(irp);
483     return TCL_ERROR;
484   }
485   if ((long int) cd == 1) {
486     if (!unbind_bind_entry(tl, argv[2], argv[3], argv[4])) {
487       /* Don't error if trying to re-unbind a builtin */
488       if (argv[4][0] != '*' || argv[4][4] != ':' ||
489           strcmp(argv[3], &argv[4][5]) || strncmp(argv[1], &argv[4][1], 3)) {
490         Tcl_AppendResult(irp, "no such binding", NULL);
491         return TCL_ERROR;
492       }
493     }
494   } else {
495     if (argc == 4)
496       return tcl_getbinds(tl, argv[3]);
497     bind_bind_entry(tl, argv[2], argv[3], argv[4]);
498   }
499   Tcl_AppendResult(irp, argv[3], NULL);
500   return TCL_OK;
501 }
502 
check_validity(char * nme,IntFunc func)503 int check_validity(char *nme, IntFunc func)
504 {
505   char *p;
506   tcl_bind_list_t *tl;
507 
508   if (*nme != '*')
509     return 0;
510   p = strchr(nme + 1, ':');
511   if (p == NULL)
512     return 0;
513   *p = 0;
514   tl = find_bind_table(nme + 1);
515   *p = ':';
516   if (!tl)
517     return 0;
518   if (tl->func != func)
519     return 0;
520   return 1;
521 }
522 
523 static int builtin_3char STDVAR
524 {
525   Function F = (Function) cd;
526 
527   BADARGS(4, 4, " from to args");
528 
529   CHECKVALIDITY(builtin_3char);
530   F(argv[1], argv[2], argv[3]);
531   return TCL_OK;
532 }
533 
534 static int builtin_2char STDVAR
535 {
536   Function F = (Function) cd;
537 
538   BADARGS(3, 3, " nick msg");
539 
540   CHECKVALIDITY(builtin_2char);
541   F(argv[1], argv[2]);
542   return TCL_OK;
543 }
544 
545 static int builtin_5int STDVAR
546 {
547   Function F = (Function) cd;
548 
549   BADARGS(6, 6, " min hrs dom mon year");
550 
551   CHECKVALIDITY(builtin_5int);
552   F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]));
553   return TCL_OK;
554 }
555 
556 static int builtin_cron STDVAR
557 {
558   Function F = (Function) cd;
559 
560   BADARGS(6, 6, " min hrs dom mon weekday");
561 
562   CHECKVALIDITY(builtin_cron);
563   F(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]));
564   return TCL_OK;
565 }
566 
567 static int builtin_char STDVAR
568 {
569   Function F = (Function) cd;
570 
571   BADARGS(2, 2, " handle");
572 
573   CHECKVALIDITY(builtin_char);
574   F(argv[1]);
575   return TCL_OK;
576 }
577 
578 static int builtin_chpt STDVAR
579 {
580   Function F = (Function) cd;
581 
582   BADARGS(3, 3, " bot nick sock");
583 
584   CHECKVALIDITY(builtin_chpt);
585   F(argv[1], argv[2], atoi(argv[3]));
586   return TCL_OK;
587 }
588 
589 static int builtin_chjn STDVAR
590 {
591   Function F = (Function) cd;
592 
593   BADARGS(6, 6, " bot nick chan# flag&sock host");
594 
595   CHECKVALIDITY(builtin_chjn);
596   F(argv[1], argv[2], atoi(argv[3]), argv[4][0],
597     argv[4][0] ? atoi(argv[4] + 1) : 0, argv[5]);
598   return TCL_OK;
599 }
600 
601 static int builtin_idxchar STDVAR
602 {
603   Function F = (Function) cd;
604   int idx;
605   char *r;
606 
607   BADARGS(3, 3, " idx args");
608 
609   CHECKVALIDITY(builtin_idxchar);
610   idx = findidx(atoi(argv[1]));
611   if (idx < 0) {
612     Tcl_AppendResult(irp, "invalid idx", NULL);
613     return TCL_ERROR;
614   }
615   r = (((char *(*)()) F) (idx, argv[2]));
616 
617   Tcl_ResetResult(irp);
618   Tcl_AppendResult(irp, r, NULL);
619   return TCL_OK;
620 }
621 
622 static int builtin_charidx STDVAR
623 {
624   Function F = (Function) cd;
625   int idx;
626 
627   BADARGS(3, 3, " handle idx");
628 
629   CHECKVALIDITY(builtin_charidx);
630   idx = findanyidx(atoi(argv[2]));
631   if (idx < 0) {
632     Tcl_AppendResult(irp, "invalid idx", NULL);
633     return TCL_ERROR;
634   }
635   Tcl_AppendResult(irp, int_to_base10(F(argv[1], idx)), NULL);
636 
637   return TCL_OK;
638 }
639 
640 static int builtin_chat STDVAR
641 {
642   Function F = (Function) cd;
643   int ch;
644 
645   BADARGS(4, 4, " handle idx text");
646 
647   CHECKVALIDITY(builtin_chat);
648   ch = atoi(argv[2]);
649   F(argv[1], ch, argv[3]);
650   return TCL_OK;
651 }
652 
653 static int builtin_dcc STDVAR
654 {
655   int idx;
656   Function F = (Function) cd;
657 
658   BADARGS(4, 4, " hand idx param");
659 
660   CHECKVALIDITY(builtin_dcc);
661   idx = findidx(atoi(argv[2]));
662   if (idx < 0) {
663     Tcl_AppendResult(irp, "invalid idx", NULL);
664     return TCL_ERROR;
665   }
666 
667   /* FIXME: This is an ugly hack. It is not documented as a
668    *        'feature' because it will eventually go away.
669    */
670   if (F == CMD_LEAVE) {
671     Tcl_AppendResult(irp, "break", NULL);
672     return TCL_OK;
673   }
674 
675   /* Check if it's a password change, if so, don't show the password. We
676    * don't need pretty formats here, as it's only for debugging purposes.
677    */
678   debug4("tcl: builtin dcc call: %s %s %s %s", argv[0], argv[1], argv[2],
679          (!strcmp(argv[0] + 5, "newpass") || !strcmp(argv[0] + 5, "chpass")) ?
680          "[something]" : argv[3]);
681   F(dcc[idx].user, idx, argv[3]);
682   Tcl_ResetResult(irp);
683   Tcl_AppendResult(irp, "0", NULL);
684   return TCL_OK;
685 }
686 
687 static int builtin_log STDVAR
688 {
689   Function F = (Function) cd;
690 
691   BADARGS(3, 3, " lvl chan msg");
692 
693   CHECKVALIDITY(builtin_log);
694   F(argv[1], argv[2], argv[3]);
695   return TCL_OK;
696 }
697 
698 #ifdef TLS
699 static int builtin_idx STDVAR
700 {
701   Function F = (Function) cd;
702 
703   BADARGS(2, 2, " idx");
704 
705   CHECKVALIDITY(builtin_idx);
706   F(atoi(argv[1]));
707   return TCL_OK;
708 }
709 #endif
710 
711 /* Trigger (execute) a Tcl proc
712  *
713  * Note: This is INLINE code for check_tcl_bind().
714  */
trigger_bind(const char * proc,const char * param,char * mask)715 static int trigger_bind(const char *proc, const char *param,
716                                char *mask)
717 {
718   int x;
719   struct rusage ru1, ru2;
720   int r = 0;
721 #ifdef DEBUG_CONTEXT
722   #define FORMAT "Tcl proc: %s, param: %s"
723   char *buf;
724 
725   /* We now try to debug the Tcl_VarEval() call below by remembering both
726    * the called proc name and it's parameters. This should render us a bit
727    * less helpless when we see context dumps.
728    */
729   Context;
730   /* reuse x */
731   x = snprintf(NULL, 0, FORMAT, proc ? proc : "<null>", param ? param : "<null>");
732   buf = nmalloc(x + 1);
733   sprintf(buf, FORMAT, proc ? proc : "<null>", param ? param : "<null>");
734   ContextNote(buf);
735   nfree(buf);
736 #endif /* DEBUG_CONTEXT */
737 
738   /* Set the lastbind variable before evaluating the proc so that the name
739    * of the command that triggered the bind will be available to the proc.
740    * This feature is used by scripts such as userinfo.tcl
741    */
742   Tcl_SetVar(interp, "lastbind", (char *) mask, TCL_GLOBAL_ONLY);
743 
744   if(proc && proc[0] != '*') { /* proc[0] != '*' excludes internal binds */
745     debug1("triggering bind %s", proc);
746     r = getrusage(RUSAGE_SELF, &ru1);
747   }
748   x = Tcl_VarEval(interp, proc, param, NULL);
749   Context;
750   if (proc && proc[0] != '*' && !r) {
751     if (!getrusage(RUSAGE_SELF, &ru2)) {
752       debug3("triggered bind %s, user %.3fms sys %.3fms", proc,
753              (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 +
754              (double) (ru2.ru_utime.tv_sec  - ru1.ru_utime.tv_sec ) * 1000,
755              (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 +
756              (double) (ru2.ru_stime.tv_sec  - ru1.ru_stime.tv_sec ) * 1000);
757     }
758   }
759 
760   if (x == TCL_ERROR) {
761     /* FIXME: we really should be able to log longer errors */
762     putlog(LOG_MISC, "*", "Tcl error [%s]: %.*s", proc, 400, tcl_resultstring());
763     Tcl_BackgroundError(interp);
764 
765     return BIND_EXECUTED;
766   }
767 
768   /* FIXME: This is an ugly hack. It is not documented as a
769    *        'feature' because it will eventually go away.
770    */
771   if (!strcmp(tcl_resultstring(), "break"))
772     return BIND_QUIT;
773 
774   return (tcl_resultint() > 0) ? BIND_EXEC_LOG : BIND_EXECUTED;
775 }
776 
777 
778 /* Find out whether this bind matches the mask or provides the
779  * requested attributes, depending on the specified requirements.
780  *
781  * Note: This is INLINE code for check_tcl_bind().
782  */
check_bind_match(const char * match,char * mask,int match_type)783 static int check_bind_match(const char *match, char *mask,
784                                    int match_type)
785 {
786   switch (match_type & 0x07) {
787   case MATCH_PARTIAL:
788     return (!strncasecmp(match, mask, strlen(match)));
789     break;
790   case MATCH_EXACT:
791     return (!strcasecmp(match, mask));
792     break;
793   case MATCH_CASE:
794     return (!strcmp(match, mask));
795     break;
796   case MATCH_MASK:
797     return (wild_match_per(mask, match));
798     break;
799   case MATCH_MODE:
800     return (wild_match_partial_case(mask, match));
801     break;
802   case MATCH_CRON:
803     return (cron_match(mask, match));
804     break;
805   default:
806     /* Do nothing */
807     break;
808   }
809   return 0;
810 }
811 
812 
813 /* Check if the provided flags suffice for this command/trigger.
814  *
815  * Note: This is INLINE code for check_tcl_bind().
816  */
check_bind_flags(struct flag_record * flags,struct flag_record * atr,int match_type)817 static int check_bind_flags(struct flag_record *flags,
818                                    struct flag_record *atr, int match_type)
819 {
820   if (match_type & BIND_USE_ATTR) {
821     if (match_type & BIND_HAS_BUILTINS)
822       return (flagrec_ok(flags, atr));
823     else
824       return (flagrec_eq(flags, atr));
825   }
826   return 1;
827 }
828 
829 
830 /* Check for and process Tcl binds */
check_tcl_bind(tcl_bind_list_t * tl,const char * match,struct flag_record * atr,const char * param,int match_type)831 int check_tcl_bind(tcl_bind_list_t *tl, const char *match,
832                    struct flag_record *atr, const char *param, int match_type)
833 {
834   int x, result = 0, cnt = 0, finish = 0;
835   char *proc = NULL, *mask = NULL;
836   tcl_bind_mask_t *tm, *tm_last = NULL, *tm_p = NULL;
837   tcl_cmd_t *tc, *htc = NULL;
838   char *str, *varName, *brkt;
839 
840   for (tm = tl->first; tm && !finish; tm_last = tm, tm = tm->next) {
841 
842     if (tm->flags & TBM_DELETED)
843       continue;                 /* This bind mask was deleted */
844 
845     if (!check_bind_match(match, tm->mask, match_type))
846       continue;                 /* This bind does not match. */
847 
848     for (tc = tm->first; tc; tc = tc->next) {
849 
850       /* Search for valid entry. */
851       if (!(tc->attributes & TC_DELETED)) {
852 
853         /* Check if the provided flags suffice for this command. */
854         if (check_bind_flags(&tc->flags, atr, match_type)) {
855           cnt++;
856           tm_p = tm_last;
857 
858           /* Not stackable */
859           if (!(match_type & BIND_STACKABLE)) {
860 
861             /* Remember information about this bind. */
862             proc = tc->func_name;
863             mask = tm->mask;
864             htc = tc;
865 
866             /* Either this is a non-partial match, which means we
867              * only want to execute _one_ bind ...
868              */
869             if ((match_type & 0x07) != MATCH_PARTIAL ||
870               /* ... or this happens to be an exact match. */
871               !strcasecmp(match, tm->mask)) {
872               cnt = 1;
873               finish = 1;
874             }
875 
876             /* We found a match so break out of the inner loop. */
877             break;
878           }
879 
880           /*
881            * Stackable; could be multiple commands/triggers.
882            * Note: This code assumes BIND_ALTER_ARGS, BIND_WANTRET, and
883            *       BIND_STACKRET will only be used for stackable binds.
884            */
885 
886           /* We will only return if BIND_ALTER_ARGS or BIND_WANTRET was
887            * specified because we want to trigger all binds in a stack.
888            */
889 
890           tc->hits++;
891           x = trigger_bind(tc->func_name, param, tm->mask);
892 
893           if (match_type & BIND_ALTER_ARGS) {
894             if (tcl_resultempty())
895               goto finally;
896           } else if ((match_type & BIND_STACKRET) && x == BIND_EXEC_LOG) {
897             /* If we have multiple commands/triggers, and if any of the
898              * commands return 1, we store the result so we can return it
899              * after processing all stacked binds.
900              */
901             if (!result)
902               result = x;
903             continue;
904           } else if ((match_type & BIND_WANTRET) && x == BIND_EXEC_LOG)
905             /* Return immediately if any commands return 1 */
906             goto finally;
907         }
908       }
909     }
910   }
911 
912   if (!cnt) {
913     x = BIND_NOMATCH;
914     goto finally;
915   }
916 
917   /* Do this before updating the preferred entries information,
918    * since we don't want to change the order of stacked binds
919    */
920   if (result) {           /* BIND_STACKRET */
921     x = result;
922     goto finally;
923   }
924 
925   if ((match_type & 0x07) == MATCH_MASK || (match_type & 0x07) == MATCH_CASE) {
926     x = BIND_EXECUTED;
927     goto finally;
928   }
929 
930   /* Hit counter */
931   if (htc)
932     htc->hits++;
933 
934   if (cnt > 1) {
935     x = BIND_AMBIGUOUS;
936     goto finally;
937   }
938 
939   /* Now that we have found exactly one bind, we can update the
940    * preferred entries information.
941    * Do this only for cnt == 1,
942    * since we don't want to change the order of raw binds vs. builtin binds.
943    * reason 1: order should be raw then builtin
944    * reason 2: builtin could modify args
945    */
946   if (tm_p && tm_p->next) {
947     tm = tm_p->next;            /* Move mask to front of bind's mask list. */
948     tm_p->next = tm->next;      /* Unlink mask from list. */
949     tm->next = tl->first;       /* Readd mask to front of list. */
950     tl->first = tm;
951   }
952 
953   x = trigger_bind(proc, param, mask);
954 
955 finally:
956   str = nmalloc(strlen(param) + 1);
957   strcpy(str, param);
958 
959   for (varName = strtok_r(str,  " $:", &brkt);
960        varName;
961        varName = strtok_r(NULL, " $:", &brkt))
962   {
963     Tcl_UnsetVar(interp, varName, 0);
964   }
965 
966   nfree(str);
967   return x;
968 }
969 
970 
971 /* Check for tcl-bound dcc command, return 1 if found
972  * dcc: proc-name <handle> <sock> <args...>
973  */
check_tcl_dcc(const char * cmd,int idx,const char * args)974 int check_tcl_dcc(const char *cmd, int idx, const char *args)
975 {
976   struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
977   int x;
978   char s[11];
979 
980   get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
981   egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
982   Tcl_SetVar(interp, "_dcc1", (char *) dcc[idx].nick, 0);
983   Tcl_SetVar(interp, "_dcc2", (char *) s, 0);
984   Tcl_SetVar(interp, "_dcc3", (char *) args, 0);
985   x = check_tcl_bind(H_dcc, cmd, &fr, " $_dcc1 $_dcc2 $_dcc3",
986                      MATCH_PARTIAL | BIND_USE_ATTR | BIND_HAS_BUILTINS);
987   if (x == BIND_AMBIGUOUS) {
988     dprintf(idx, MISC_AMBIGUOUS);
989     return 0;
990   }
991   if (x == BIND_NOMATCH) {
992     dprintf(idx, MISC_NOSUCHCMD);
993     return 0;
994   }
995 
996   /* We return 1 to leave the partyline */
997   if (x == BIND_QUIT)           /* CMD_LEAVE, 'quit' */
998     return 1;
999 
1000   if (x == BIND_EXEC_LOG)
1001     putlog(LOG_CMDS, "*", "#%s# %s %s", dcc[idx].nick, cmd, args);
1002   return 0;
1003 }
1004 
check_tcl_bot(const char * nick,const char * code,const char * param)1005 void check_tcl_bot(const char *nick, const char *code, const char *param)
1006 {
1007   Tcl_SetVar(interp, "_bot1", (char *) nick, 0);
1008   Tcl_SetVar(interp, "_bot2", (char *) code, 0);
1009   Tcl_SetVar(interp, "_bot3", (char *) param, 0);
1010   check_tcl_bind(H_bot, code, 0, " $_bot1 $_bot2 $_bot3", MATCH_EXACT);
1011 }
1012 
check_tcl_chonof(char * hand,int sock,tcl_bind_list_t * tl)1013 void check_tcl_chonof(char *hand, int sock, tcl_bind_list_t *tl)
1014 {
1015   struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1016   char s[11];
1017   struct userrec *u;
1018 
1019   u = get_user_by_handle(userlist, hand);
1020   touch_laston(u, "partyline", now);
1021   get_user_flagrec(u, &fr, NULL);
1022   Tcl_SetVar(interp, "_chonof1", (char *) hand, 0);
1023   egg_snprintf(s, sizeof s, "%d", sock);
1024   Tcl_SetVar(interp, "_chonof2", (char *) s, 0);
1025   check_tcl_bind(tl, hand, &fr, " $_chonof1 $_chonof2", MATCH_MASK |
1026                  BIND_USE_ATTR | BIND_STACKABLE | BIND_WANTRET);
1027 }
1028 
check_tcl_chatactbcst(const char * from,int chan,const char * text,tcl_bind_list_t * tl)1029 void check_tcl_chatactbcst(const char *from, int chan, const char *text,
1030                            tcl_bind_list_t *tl)
1031 {
1032   char s[11];
1033 
1034   egg_snprintf(s, sizeof s, "%d", chan);
1035   Tcl_SetVar(interp, "_cab1", (char *) from, 0);
1036   Tcl_SetVar(interp, "_cab2", (char *) s, 0);
1037   Tcl_SetVar(interp, "_cab3", (char *) text, 0);
1038   check_tcl_bind(tl, text, 0, " $_cab1 $_cab2 $_cab3",
1039                  MATCH_MASK | BIND_STACKABLE);
1040 }
1041 
check_tcl_nkch(const char * ohand,const char * nhand)1042 void check_tcl_nkch(const char *ohand, const char *nhand)
1043 {
1044   Tcl_SetVar(interp, "_nkch1", (char *) ohand, 0);
1045   Tcl_SetVar(interp, "_nkch2", (char *) nhand, 0);
1046   check_tcl_bind(H_nkch, ohand, 0, " $_nkch1 $_nkch2",
1047                  MATCH_MASK | BIND_STACKABLE);
1048 }
1049 
check_tcl_link(const char * bot,const char * via)1050 void check_tcl_link(const char *bot, const char *via)
1051 {
1052   Tcl_SetVar(interp, "_link1", (char *) bot, 0);
1053   Tcl_SetVar(interp, "_link2", (char *) via, 0);
1054   check_tcl_bind(H_link, bot, 0, " $_link1 $_link2",
1055                  MATCH_MASK | BIND_STACKABLE);
1056 }
1057 
check_tcl_disc(const char * bot)1058 void check_tcl_disc(const char *bot)
1059 {
1060   Tcl_SetVar(interp, "_disc1", (char *) bot, 0);
1061   check_tcl_bind(H_disc, bot, 0, " $_disc1", MATCH_MASK | BIND_STACKABLE);
1062 }
1063 
check_tcl_loadunld(const char * mod,tcl_bind_list_t * tl)1064 void check_tcl_loadunld(const char *mod, tcl_bind_list_t *tl)
1065 {
1066   Tcl_SetVar(interp, "_lu1", (char *) mod, 0);
1067   check_tcl_bind(tl, mod, 0, " $_lu1", MATCH_MASK | BIND_STACKABLE);
1068 }
1069 
check_tcl_filt(int idx,const char * text)1070 const char *check_tcl_filt(int idx, const char *text)
1071 {
1072   char s[11];
1073   int x;
1074   struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1075 
1076   egg_snprintf(s, sizeof s, "%ld", dcc[idx].sock);
1077   get_user_flagrec(dcc[idx].user, &fr, dcc[idx].u.chat->con_chan);
1078   Tcl_SetVar(interp, "_filt1", (char *) s, 0);
1079   Tcl_SetVar(interp, "_filt2", (char *) text, 0);
1080   x = check_tcl_bind(H_filt, text, &fr, " $_filt1 $_filt2",
1081                      MATCH_MASK | BIND_USE_ATTR | BIND_STACKABLE |
1082                      BIND_WANTRET | BIND_ALTER_ARGS);
1083   if (x == BIND_EXECUTED || x == BIND_EXEC_LOG) {
1084     if (tcl_resultempty())
1085       return "";
1086     else
1087       return tcl_resultstring();
1088   } else
1089     return text;
1090 }
1091 
check_tcl_note(const char * from,const char * to,const char * text)1092 int check_tcl_note(const char *from, const char *to, const char *text)
1093 {
1094   int x;
1095 
1096   Tcl_SetVar(interp, "_note1", (char *) from, 0);
1097   Tcl_SetVar(interp, "_note2", (char *) to, 0);
1098   Tcl_SetVar(interp, "_note3", (char *) text, 0);
1099 
1100   x = check_tcl_bind(H_note, to, 0, " $_note1 $_note2 $_note3",
1101                      MATCH_MASK | BIND_STACKABLE | BIND_WANTRET);
1102 
1103   return (x == BIND_EXEC_LOG);
1104 }
1105 
check_tcl_listen(const char * cmd,int idx)1106 void check_tcl_listen(const char *cmd, int idx)
1107 {
1108   char s[11];
1109   int x;
1110 
1111   egg_snprintf(s, sizeof s, "%d", idx);
1112   Tcl_SetVar(interp, "_n", (char *) s, 0);
1113   x = Tcl_VarEval(interp, cmd, " $_n", NULL);
1114   if (x == TCL_ERROR)
1115     putlog(LOG_MISC, "*", "error on listen: %s", tcl_resultstring());
1116 }
1117 
check_tcl_chjn(const char * bot,const char * nick,int chan,const char type,int sock,const char * host)1118 void check_tcl_chjn(const char *bot, const char *nick, int chan,
1119                     const char type, int sock, const char *host)
1120 {
1121   struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
1122   char s[11], t[2], u[11];
1123 
1124   t[0] = type;
1125   t[1] = 0;
1126   switch (type) {
1127   case '*':
1128     fr.global = USER_OWNER;
1129 
1130     break;
1131   case '+':
1132     fr.global = USER_MASTER;
1133 
1134     break;
1135   case '@':
1136     fr.global = USER_OP;
1137 
1138     break;
1139   case '^':
1140     fr.global = USER_HALFOP;
1141 
1142     break;
1143   case '%':
1144     fr.global = USER_BOTMAST;
1145   }
1146   egg_snprintf(s, sizeof s, "%d", chan);
1147   egg_snprintf(u, sizeof u, "%d", sock);
1148   Tcl_SetVar(interp, "_chjn1", (char *) bot, 0);
1149   Tcl_SetVar(interp, "_chjn2", (char *) nick, 0);
1150   Tcl_SetVar(interp, "_chjn3", (char *) s, 0);
1151   Tcl_SetVar(interp, "_chjn4", (char *) t, 0);
1152   Tcl_SetVar(interp, "_chjn5", (char *) u, 0);
1153   Tcl_SetVar(interp, "_chjn6", (char *) host, 0);
1154   check_tcl_bind(H_chjn, s, &fr,
1155                  " $_chjn1 $_chjn2 $_chjn3 $_chjn4 $_chjn5 $_chjn6",
1156                  MATCH_MASK | BIND_STACKABLE);
1157 }
1158 
check_tcl_chpt(const char * bot,const char * hand,int sock,int chan)1159 void check_tcl_chpt(const char *bot, const char *hand, int sock, int chan)
1160 {
1161   char u[11], v[11];
1162 
1163   egg_snprintf(u, sizeof u, "%d", sock);
1164   egg_snprintf(v, sizeof v, "%d", chan);
1165   Tcl_SetVar(interp, "_chpt1", (char *) bot, 0);
1166   Tcl_SetVar(interp, "_chpt2", (char *) hand, 0);
1167   Tcl_SetVar(interp, "_chpt3", (char *) u, 0);
1168   Tcl_SetVar(interp, "_chpt4", (char *) v, 0);
1169   check_tcl_bind(H_chpt, v, 0, " $_chpt1 $_chpt2 $_chpt3 $_chpt4",
1170                  MATCH_MASK | BIND_STACKABLE);
1171 }
1172 
check_tcl_away(const char * bot,int idx,const char * msg)1173 void check_tcl_away(const char *bot, int idx, const char *msg)
1174 {
1175   char u[11];
1176 
1177   egg_snprintf(u, sizeof u, "%d", idx);
1178   Tcl_SetVar(interp, "_away1", (char *) bot, 0);
1179   Tcl_SetVar(interp, "_away2", (char *) u, 0);
1180   Tcl_SetVar(interp, "_away3", msg ? (char *) msg : "", 0);
1181   check_tcl_bind(H_away, bot, 0, " $_away1 $_away2 $_away3",
1182                  MATCH_MASK | BIND_STACKABLE);
1183 }
1184 
check_tcl_time_and_cron(struct tm * tm)1185 void check_tcl_time_and_cron(struct tm *tm)
1186 {
1187   /* Undersized due to sane assumption that struct tm is sane and at the same
1188    * time oversized to silence a gcc format-truncation warning */
1189   char y[24];
1190 
1191   egg_snprintf(y, sizeof y, "%02d", tm->tm_min);
1192   Tcl_SetVar(interp, "_time1", (char *) y, 0);
1193   Tcl_SetVar(interp, "_cron1", (char *) y, 0);
1194   egg_snprintf(y, sizeof y, "%02d", tm->tm_hour);
1195   Tcl_SetVar(interp, "_time2", (char *) y, 0);
1196   Tcl_SetVar(interp, "_cron2", (char *) y, 0);
1197   egg_snprintf(y, sizeof y, "%02d", tm->tm_mday);
1198   Tcl_SetVar(interp, "_time3", (char *) y, 0);
1199   Tcl_SetVar(interp, "_cron3", (char *) y, 0);
1200   egg_snprintf(y, sizeof y, "%02d", tm->tm_mon);
1201   Tcl_SetVar(interp, "_time4", (char *) y, 0);
1202   egg_snprintf(y, sizeof y, "%04d", tm->tm_year + 1900);
1203   Tcl_SetVar(interp, "_time5", (char *) y, 0);
1204   egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %04d", tm->tm_min, tm->tm_hour,
1205                tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
1206   check_tcl_bind(H_time, y, 0,
1207                  " $_time1 $_time2 $_time3 $_time4 $_time5",
1208                  MATCH_MASK | BIND_STACKABLE);
1209 
1210   egg_snprintf(y, sizeof y, "%02d", tm->tm_mon + 1);
1211   Tcl_SetVar(interp, "_cron4", (char *) y, 0);
1212   egg_snprintf(y, sizeof y, "%02d", tm->tm_wday);
1213   Tcl_SetVar(interp, "_cron5", (char *) y, 0);
1214   egg_snprintf(y, sizeof y, "%02d %02d %02d %02d %02d", tm->tm_min, tm->tm_hour,
1215                tm->tm_mday, tm->tm_mon + 1, tm->tm_wday);
1216   check_tcl_bind(H_cron, y, 0,
1217                  " $_cron1 $_cron2 $_cron3 $_cron4 $_cron5",
1218                  MATCH_CRON | BIND_STACKABLE);
1219 }
1220 
check_tcl_event(const char * event)1221 void check_tcl_event(const char *event)
1222 {
1223   Tcl_SetVar(interp, "_event1", (char *) event, TCL_GLOBAL_ONLY);
1224   check_tcl_bind(H_event, event, 0, " $::_event1",
1225                  MATCH_EXACT | BIND_STACKABLE);
1226 }
1227 
check_tcl_signal(const char * event)1228 int check_tcl_signal(const char *event)
1229 {
1230   int x;
1231 
1232   Tcl_SetVar(interp, "_event1", (char *) event, TCL_GLOBAL_ONLY);
1233   x = check_tcl_bind(H_event, event, 0, " $::_event1",
1234                  MATCH_EXACT | BIND_STACKABLE | BIND_STACKRET);
1235   return (x == BIND_EXEC_LOG);
1236 }
1237 
check_tcl_die(char * reason)1238 void check_tcl_die(char *reason)
1239 {
1240   Tcl_SetVar(interp, "_die1", reason, 0);
1241   check_tcl_bind(H_die, reason, 0, " $_die1", MATCH_MASK | BIND_STACKABLE);
1242 }
1243 
check_tcl_log(int lv,char * chan,char * msg)1244 void check_tcl_log(int lv, char *chan, char *msg)
1245 {
1246   char mask[512];
1247 
1248   egg_snprintf(mask, sizeof mask, "%s %s", chan, msg);
1249   Tcl_SetVar(interp, "_log1", masktype(lv), TCL_GLOBAL_ONLY);
1250   Tcl_SetVar(interp, "_log2", chan, TCL_GLOBAL_ONLY);
1251   Tcl_SetVar(interp, "_log3", msg, TCL_GLOBAL_ONLY);
1252   check_tcl_bind(H_log, mask, 0, " $::_log1 $::_log2 $::_log3",
1253                  MATCH_MASK | BIND_STACKABLE);
1254 }
1255 
1256 #ifdef TLS
check_tcl_tls(int sock)1257 int check_tcl_tls(int sock)
1258 {
1259   int x;
1260   char s[11];
1261 
1262   egg_snprintf(s, sizeof s, "%d", sock);
1263   Tcl_SetVar(interp, "_tls", s, 0);
1264   x = check_tcl_bind(H_tls, s, 0, " $_tls", MATCH_MASK | BIND_STACKABLE |
1265                      BIND_WANTRET);
1266   return (x == BIND_EXEC_LOG);
1267 }
1268 #endif
1269 
tell_binds(int idx,char * par)1270 void tell_binds(int idx, char *par)
1271 {
1272   tcl_bind_list_t *tl, *tl_kind;
1273   tcl_bind_mask_t *tm;
1274   int fnd = 0, showall = 0, patmatc = 0, maxname = 0;
1275   tcl_cmd_t *tc;
1276   char *name, *proc, *s, flg[100];
1277 
1278   if (par[0])
1279     name = newsplit(&par);
1280   else
1281     name = NULL;
1282   if (par[0])
1283     s = newsplit(&par);
1284   else
1285     s = NULL;
1286 
1287   if (name)
1288     tl_kind = find_bind_table(name);
1289   else
1290     tl_kind = NULL;
1291 
1292   if ((name && name[0] && !strcasecmp(name, "all")) ||
1293       (s && s[0] && !strcasecmp(s, "all")))
1294     showall = 1;
1295   if (tl_kind == NULL && name && name[0] && strcasecmp(name, "all"))
1296     patmatc = 1;
1297 
1298   for (tl = tl_kind ? tl_kind : bind_table_list; tl;
1299        tl = tl_kind ? 0 : tl->next) {
1300     if (tl->flags & HT_DELETED)
1301       continue;
1302     for (tm = tl->first; tm; tm = tm->next) {
1303       if (tm->flags & TBM_DELETED)
1304         continue;
1305       for (tc = tm->first; tc; tc = tc->next) {
1306         if (tc->attributes & TC_DELETED)
1307           continue;
1308         if (strlen(tl->name) > maxname) {
1309           maxname = strlen(tl->name);
1310         }
1311       }
1312     }
1313   }
1314   dprintf(idx, MISC_CMDBINDS);
1315   dprintf(idx, "  %+*s FLAGS    COMMAND              HITS BINDING (TCL)\n",
1316         maxname, "TYPE");
1317 
1318   for (tl = tl_kind ? tl_kind : bind_table_list; tl;
1319        tl = tl_kind ? 0 : tl->next) {
1320     if (tl->flags & HT_DELETED)
1321       continue;
1322     for (tm = tl->first; tm; tm = tm->next) {
1323       if (tm->flags & TBM_DELETED)
1324         continue;
1325       for (tc = tm->first; tc; tc = tc->next) {
1326         if (tc->attributes & TC_DELETED)
1327           continue;
1328         proc = tc->func_name;
1329         build_flags(flg, &(tc->flags), NULL);
1330         if (showall || proc[0] != '*') {
1331           int ok = 0;
1332 
1333           if (patmatc == 1) {
1334             if (wild_match_per(name, tl->name) ||
1335                 wild_match_per(name, tm->mask) ||
1336                 wild_match_per(name, tc->func_name))
1337               ok = 1;
1338           } else
1339             ok = 1;
1340 
1341           if (ok) {
1342             dprintf(idx, "  %*s %-8s %-20s %4d %s\n", maxname, tl->name, flg,
1343                     tm->mask, tc->hits, tc->func_name);
1344             fnd = 1;
1345           }
1346         }
1347       }
1348     }
1349   }
1350   if (!fnd) {
1351     if (patmatc)
1352       dprintf(idx, "No command bindings found that match %s\n", name);
1353     else if (tl_kind)
1354       dprintf(idx, "No command bindings for type: %s.\n", name);
1355     else
1356       dprintf(idx, "No command bindings exist.\n");
1357   }
1358 }
1359 
1360 /* Bring the default msg/dcc/fil commands into the Tcl interpreter */
add_builtins(tcl_bind_list_t * tl,cmd_t * cc)1361 void add_builtins(tcl_bind_list_t *tl, cmd_t *cc)
1362 {
1363   int k, i;
1364   char p[1024], *l;
1365   cd_tcl_cmd table[2];
1366 
1367   table[0].name = p;
1368   table[0].callback = tl->func;
1369   table[1].name = NULL;
1370   for (i = 0; cc[i].name; i++) {
1371     egg_snprintf(p, sizeof p, "*%s:%s", tl->name,
1372                  cc[i].funcname ? cc[i].funcname : cc[i].name);
1373     l = nmalloc(Tcl_ScanElement(p, &k) + 1);
1374     Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
1375     table[0].cdata = (void *) cc[i].func;
1376     add_cd_tcl_cmds(table);
1377     bind_bind_entry(tl, cc[i].flags, cc[i].name, l);
1378     nfree(l);
1379   }
1380 }
1381 
1382 /* Remove the default msg/dcc/fil commands from the Tcl interpreter */
rem_builtins(tcl_bind_list_t * table,cmd_t * cc)1383 void rem_builtins(tcl_bind_list_t *table, cmd_t *cc)
1384 {
1385   int k, i;
1386   char p[1024], *l;
1387 
1388   for (i = 0; cc[i].name; i++) {
1389     egg_snprintf(p, sizeof p, "*%s:%s", table->name,
1390                  cc[i].funcname ? cc[i].funcname : cc[i].name);
1391     l = nmalloc(Tcl_ScanElement(p, &k) + 1);
1392     Tcl_ConvertElement(p, l, k | TCL_DONT_USE_BRACES);
1393     Tcl_DeleteCommand(interp, p);
1394     unbind_bind_entry(table, cc[i].flags, cc[i].name, l);
1395     nfree(l);
1396   }
1397 }
1398