1 /*
2  * tcluser.c -- handles:
3  *   Tcl stubs for the user-record-oriented 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 "users.h"
26 #include "chan.h"
27 #include "tandem.h"
28 #include "modules.h"
29 #include "string.h"
30 
31 extern Tcl_Interp *interp;
32 extern struct userrec *userlist;
33 extern int default_flags, dcc_total, ignore_time;
34 extern struct dcc_t *dcc;
35 extern char botnetnick[];
36 extern time_t now;
37 extern struct user_entry_type *entry_type_list;
38 
39 static int tcl_countusers STDVAR
40 {
41   BADARGS(1, 1, "");
42 
43   Tcl_AppendResult(irp, int_to_base10(count_users(userlist)), NULL);
44   return TCL_OK;
45 }
46 
47 static int tcl_validuser STDVAR
48 {
49   BADARGS(2, 2, " handle");
50 
51   Tcl_AppendResult(irp, get_user_by_handle(userlist, argv[1]) ? "1" : "0",
52                    NULL);
53   return TCL_OK;
54 }
55 
56 static int tcl_finduser STDVAR
57 {
58   struct userrec *u;
59 
60   BADARGS(2, 2, " nick!user@host");
61 
62   u = get_user_by_host(argv[1]);
63   Tcl_AppendResult(irp, u ? u->handle : "*", NULL);
64   return TCL_OK;
65 }
66 
67 static int tcl_passwdOk STDVAR
68 {
69   struct userrec *u;
70 
71   BADARGS(3, 3, " handle passwd");
72 
73   Tcl_AppendResult(irp, ((u = get_user_by_handle(userlist, argv[1])) &&
74                    u_pass_match(u, argv[2])) ? "1" : "0", NULL);
75   return TCL_OK;
76 }
77 
78 static int tcl_chattr STDVAR
79 {
80   int of, ocf = 0;
81   char *chan, *chg, work[100];
82   struct flag_record pls, mns, user;
83   struct userrec *u;
84 
85   BADARGS(2, 4, " handle ?changes? ?channel?");
86 
87   if ((argv[1][0] == '*') || !(u = get_user_by_handle(userlist, argv[1]))) {
88     Tcl_AppendResult(irp, "*", NULL);
89     return TCL_OK;
90   }
91   if (argc == 4) {
92     user.match = FR_GLOBAL | FR_CHAN;
93     chan = argv[3];
94     chg = argv[2];
95   } else if (argc == 3 && argv[2][0]) {
96     int ischan = (findchan_by_dname(argv[2]) != NULL);
97 
98     if (strchr(CHANMETA, argv[2][0]) && !ischan && argv[2][0] != '+' &&
99         argv[2][0] != '-') {
100       Tcl_AppendResult(irp, "no such channel", NULL);
101       return TCL_ERROR;
102     } else if (ischan) {
103       /* Channel exists */
104       user.match = FR_GLOBAL | FR_CHAN;
105       chan = argv[2];
106       chg = NULL;
107     } else {
108       /* 3rd possibility... channel doesnt exist, does start with a +.
109        * In this case we assume the string is flags.
110        */
111       user.match = FR_GLOBAL;
112       chan = NULL;
113       chg = argv[2];
114     }
115   } else {
116     user.match = FR_GLOBAL;
117     chan = NULL;
118     chg = NULL;
119   }
120   if (chan && !findchan_by_dname(chan)) {
121     Tcl_AppendResult(irp, "no such channel", NULL);
122     return TCL_ERROR;
123   }
124   /* Retrieve current flags */
125   get_user_flagrec(u, &user, chan);
126   /* Make changes */
127   if (chg) {
128     of = user.global;
129     pls.match = user.match;
130     break_down_flags(chg, &pls, &mns);
131     /* No-one can change these flags on-the-fly */
132     pls.global &=~(USER_BOT);
133     mns.global &=~(USER_BOT);
134 
135     if (chan) {
136       pls.chan &= ~(BOT_AGGRESSIVE);
137       mns.chan &= ~(BOT_AGGRESSIVE);
138     }
139     user_sanity_check(&(user.global), pls.global, mns.global);
140 
141     user.udef_global = (user.udef_global | pls.udef_global)
142                        & ~mns.udef_global;
143     if (chan) {
144       ocf = user.chan;
145       chan_sanity_check(&(user.chan), pls.chan, mns.chan, user.global);
146       user.udef_chan = (user.udef_chan | pls.udef_chan) & ~mns.udef_chan;
147 
148     }
149     set_user_flagrec(u, &user, chan);
150     check_dcc_attrs(u, of);
151     if (chan)
152       check_dcc_chanattrs(u, chan, user.chan, ocf);
153   }
154   user.chan &= ~BOT_AGGRESSIVE; /* actually not a user flag, hide it */
155   /* Build flag string */
156   build_flags(work, &user, NULL);
157   Tcl_AppendResult(irp, work, NULL);
158   return TCL_OK;
159 }
160 
161 static int tcl_botattr STDVAR
162 {
163   char *chan, *chg, work[100];
164   struct flag_record pls, mns, user;
165   struct userrec *u;
166 
167   BADARGS(2, 4, " bot-handle ?changes? ?channel?");
168 
169   u = get_user_by_handle(userlist, argv[1]);
170   if ((argv[1][0] == '*') || !u || !(u->flags & USER_BOT)) {
171     Tcl_AppendResult(irp, "*", NULL);
172     return TCL_OK;
173   }
174   if (argc == 4) {
175     user.match = FR_BOT | FR_CHAN;
176     chan = argv[3];
177     chg = argv[2];
178   } else if (argc == 3 && argv[2][0] && strchr(CHANMETA, argv[2][0]) != NULL) {
179     /* We need todo extra checking here to stop us mixing up +channel's
180      * with flags. <cybah>
181      */
182     if (!findchan_by_dname(argv[2]) && argv[2][0] != '+') {
183       /* Channel doesnt exist, and it cant possibly be flags as there
184        * is no + at the start of the string.
185        */
186       Tcl_AppendResult(irp, "no such channel", NULL);
187       return TCL_ERROR;
188     } else if (findchan_by_dname(argv[2])) {
189       /* Channel exists */
190       user.match = FR_BOT | FR_CHAN;
191       chan = argv[2];
192       chg = NULL;
193     } else {
194       /* 3rd possibility... channel doesnt exist, does start with a +.
195        * In this case we assume the string is flags.
196        */
197       user.match = FR_BOT;
198       chan = NULL;
199       chg = argv[2];
200     }
201   } else {
202     user.match = FR_BOT;
203     chan = NULL;
204     if (argc < 3)
205       chg = NULL;
206     else
207       chg = argv[2];
208   }
209   if (chan && !findchan_by_dname(chan)) {
210     Tcl_AppendResult(irp, "no such channel", NULL);
211     return TCL_ERROR;
212   }
213   /* Retrieve current flags */
214   get_user_flagrec(u, &user, chan);
215   /* Make changes */
216   if (chg) {
217     pls.match = user.match;
218     break_down_flags(chg, &pls, &mns);
219     /* No-one can change these flags on-the-fly */
220     if (chan) {
221       pls.chan &= BOT_AGGRESSIVE;
222       mns.chan &= BOT_AGGRESSIVE;
223     }
224     /* this merges user.bot with pls.bot and mns.bot in user.bot
225      * squelch the msgids as this is the tcl version of botattr */
226     bot_sanity_check(&(user.bot), pls.bot, mns.bot);
227     if (chan) {
228       user.chan = (user.chan | pls.chan) & ~mns.chan;
229       user.udef_chan = (user.udef_chan | pls.udef_chan) & ~mns.udef_chan;
230     }
231     set_user_flagrec(u, &user, chan);
232   }
233   /* Only user flags can be set per channel, not bot ones,
234      so BOT_AGGRESSIVE is a hack to allow botattr |+s */
235   user.chan &= BOT_AGGRESSIVE;
236   user.udef_chan = 0; /* User definable bot flags are global only,
237                          anything here is a regular flag, so hide it. */
238   /* Build flag string */
239   build_flags(work, &user, NULL);
240   Tcl_AppendResult(irp, work, NULL);
241   return TCL_OK;
242 }
243 
244 static int tcl_matchattr STDVAR
245 {
246   struct userrec *u;
247   struct flag_record plus = {0}, minus = {0}, user = {0};
248   int ok = 0, nom = 0;
249 
250   BADARGS(3, 4, " handle flags ?channel?");
251 
252   if ((u = get_user_by_handle(userlist, argv[1]))) {
253     user.match = FR_GLOBAL | (argc == 4 ? FR_CHAN : 0) | FR_BOT;
254     get_user_flagrec(u, &user, argv[3]);
255     plus.match = user.match;
256     break_down_flags(argv[2], &plus, &minus);
257     minus.match = plus.match ^ (FR_AND | FR_OR);
258     if (!minus.global && !minus.udef_global && !minus.chan &&
259         !minus.udef_chan && !minus.bot) {
260       nom = 1;
261       if (!plus.global && !plus.udef_global && !plus.chan &&
262           !plus.udef_chan && !plus.bot) {
263         Tcl_AppendResult(irp, "Unknown flag specified for matching", NULL);
264         return TCL_ERROR;
265       }
266     }
267     if (flagrec_eq(&plus, &user)) {
268       if (nom || !flagrec_eq(&minus, &user)) {
269         ok = 1;
270       }
271     }
272   }
273   Tcl_AppendResult(irp, ok ? "1" : "0", NULL);
274   return TCL_OK;
275 }
276 
277 static int tcl_adduser STDVAR
278 {
279   unsigned char *p;
280 
281   BADARGS(2, 3, " handle ?hostmask?");
282 
283   if (strlen(argv[1]) > HANDLEN)
284     argv[1][HANDLEN] = 0;
285   for (p = (unsigned char *) argv[1]; *p; p++)
286     if (*p <= 32 || *p == '@')
287       *p = '?';
288 
289   if ((argv[1][0] == '*') || strchr(BADHANDCHARS, argv[1][0]) ||
290       get_user_by_handle(userlist, argv[1]))
291     Tcl_AppendResult(irp, "0", NULL);
292   else {
293     userlist = adduser(userlist, argv[1], argv[2], "-", default_flags);
294     Tcl_AppendResult(irp, "1", NULL);
295   }
296   return TCL_OK;
297 }
298 
299 static int tcl_addbot STDVAR
300 {
301   struct bot_addr *bi;
302   /* Max addr len is 60 ? (see cmd_pls_bot in cmds.c) + brackets + delims + ports + 0 */
303   char *p, *q, addr[75], hand[HANDLEN + 1];
304   int i, colon=0, braced = 0, ipv6 = 0, count = 0;
305 
306 
307   BADARGS(3, 5, " handle address ?telnet-port ?relay-port??");
308 
309   /* Copy to adjustable char*'s */
310   strlcpy(hand, argv[1], sizeof hand);
311   strlcpy(addr, argv[2], sizeof addr);
312 
313   for (p = hand; *p; p++)
314     if ((unsigned char) *p <= 32 || *p == '@') {
315       Tcl_AppendResult(irp, "Invalid character in handle", NULL);
316       return TCL_ERROR;
317     }
318 
319   if ((argv[1][0] == '*') || strchr(BADHANDCHARS, argv[1][0]) ||
320       get_user_by_handle(userlist, hand))
321     Tcl_AppendResult(irp, "0", NULL);
322   else {
323     for (i=0; addr[i]; i++) {
324       if (addr[i] == ':') {
325         count++;
326         colon=i;
327       }
328       if (addr[i] == ']') {
329         braced = i;
330       }
331     }
332     if (count > 1) {
333       ipv6 = 1;
334     }
335     if ((!ipv6 && braced)
336 #ifndef IPV6
337         || (ipv6)
338 #endif
339       ) {
340       Tcl_AppendResult(irp, "0", NULL);
341       return TCL_OK;
342     }
343     /* Check that the char following the / is not null */
344     if ((q = strrchr(addr, '/'))) {
345       if (!q[1]) {
346         *q = 0;
347         q = NULL;
348       }
349     }
350 
351     /* Find ports, still allowing them in address for backward compat */
352     if (!ipv6) {
353       q = strchr(addr, ':');
354     } else if (braced && (colon > braced)) {
355       q = strrchr(addr, ':');
356     } else {
357       /* IPv6 address with no braces or braces without port(s) after */
358       q = NULL;
359     }
360 
361     if (q) {
362       /* Split and count, ignore any following args */
363       *q++ = 0;
364       p = strchr(q, '/');
365       if (p)
366         *p++ = 0;
367     } else {
368       /* No port in address field, check next args */
369       p = NULL;
370       if (argc == 4 || argc == 5) {
371         q = argv[3];
372       }
373       if (argc == 5) {
374         p = argv[4];
375       }
376     }
377 
378     /* Verify ports */
379 #ifndef TLS
380     /* Check TLS */
381     if ((q && *q == '+') || (p && *p == '+')) {
382       Tcl_AppendResult(irp, "0", NULL);
383       return TCL_OK;
384     }
385 
386 #endif
387     /* Verify */
388     /* check_int_range returns 0 if q or p is NULL */
389     if (q && !check_int_range(q, 0, 65536)) {
390       Tcl_AppendResult(irp, "0", NULL);
391       return TCL_OK;
392     }
393     if (p && !check_int_range(p, 0, 65536)) {
394       Tcl_AppendResult(irp, "0", NULL);
395       return TCL_OK;
396     }
397 
398     userlist = adduser(userlist, hand, "none", "-", USER_BOT);
399     bi = user_malloc(sizeof(struct bot_addr));
400 #ifdef TLS
401     bi->ssl = 0;
402 #endif
403 
404     if ((count = strlen(addr)) > 60) {
405       count = 60;
406       addr[count] = 0;
407     }
408     /* Trim IPv6 []s out if present */
409     if (braced) {
410       --count;
411       addr[count] = 0;
412       memmove(addr, addr + 1, count);
413     }
414 
415     if (!q) {
416       bi->address = user_malloc(count + 1);
417       strcpy(bi->address, addr);
418       bi->telnet_port = 3333;
419       bi->relay_port = 3333;
420     } else {
421       bi->address = user_malloc(count + 1);
422       strcpy(bi->address, addr);
423       bi->telnet_port = atoi(q);
424 #ifdef TLS
425       if (*q == '+')
426         bi->ssl = TLS_BOT;
427 #endif
428       if (!p) {
429         bi->relay_port = bi->telnet_port;
430 #ifdef TLS
431         bi->ssl *= TLS_BOT + TLS_RELAY;
432 #endif
433       } else {
434         bi->relay_port = atoi(p);
435 #ifdef TLS
436         if (*p == '+')
437           bi->ssl |= TLS_RELAY;
438 #endif
439       }
440     }
441     set_user(&USERENTRY_BOTADDR, get_user_by_handle(userlist, hand), bi);
442     Tcl_AppendResult(irp, "1", NULL);
443   }
444   return TCL_OK;
445 }
446 
447 static int tcl_deluser STDVAR
448 {
449   BADARGS(2, 2, " handle");
450 
451   Tcl_AppendResult(irp, (argv[1][0] == '*') ? "0" :
452                    int_to_base10(deluser(argv[1])), NULL);
453   return TCL_OK;
454 }
455 
456 static int tcl_delhost STDVAR
457 {
458   BADARGS(3, 3, " handle hostmask");
459 
460   if ((!get_user_by_handle(userlist, argv[1])) || (argv[1][0] == '*')) {
461     Tcl_AppendResult(irp, "non-existent user", NULL);
462     return TCL_ERROR;
463   }
464   Tcl_AppendResult(irp, delhost_by_handle(argv[1], argv[2]) ? "1" : "0", NULL);
465   return TCL_OK;
466 }
467 
468 static int tcl_userlist STDVAR
469 {
470   struct userrec *u;
471   struct flag_record user, plus, minus;
472   int ok = 1, f = 0;
473 
474   BADARGS(1, 3, " ?flags ?channel??");
475 
476   if (argc == 3 && !findchan_by_dname(argv[2])) {
477     Tcl_AppendResult(irp, "Invalid channel: ", argv[2], NULL);
478     return TCL_ERROR;
479   }
480   if (argc >= 2) {
481     plus.match = FR_GLOBAL | FR_CHAN | FR_BOT;
482     break_down_flags(argv[1], &plus, &minus);
483     f = (minus.global ||minus.udef_global || minus.chan || minus.udef_chan ||
484          minus.bot);
485     minus.match = plus.match ^ (FR_AND | FR_OR);
486   }
487   for (u = userlist; u; u = u->next) {
488     if (argc >= 2) {
489       user.match = FR_GLOBAL | FR_CHAN | FR_BOT | (argc == 3 ? 0 : FR_ANYWH);
490       if (argc == 3)
491         get_user_flagrec(u, &user, argv[2]);
492       else
493         get_user_flagrec(u, &user, NULL);
494       if (flagrec_eq(&plus, &user) && !(f && flagrec_eq(&minus, &user)))
495         ok = 1;
496       else
497         ok = 0;
498     }
499     if (ok)
500       Tcl_AppendElement(interp, u->handle);
501   }
502   return TCL_OK;
503 }
504 
505 static int tcl_save STDVAR
506 {
507   write_userfile(-1);
508   return TCL_OK;
509 }
510 
511 static int tcl_reload STDVAR
512 {
513   reload();
514   return TCL_OK;
515 }
516 
517 static int tcl_chhandle STDVAR
518 {
519   struct userrec *u;
520   char newhand[HANDLEN + 1];
521   int x = 1, i;
522 
523   BADARGS(3, 3, " oldnick newnick");
524 
525   u = get_user_by_handle(userlist, argv[1]);
526   if (!u)
527     x = 0;
528   else {
529     strlcpy(newhand, argv[2], sizeof newhand);
530     for (i = 0; i < strlen(newhand); i++)
531       if (((unsigned char) newhand[i] <= 32) || (newhand[i] == '@'))
532         newhand[i] = '?';
533     if (strchr(BADHANDCHARS, newhand[0]) != NULL)
534       x = 0;
535     else if (strlen(newhand) < 1)
536       x = 0;
537     else if (get_user_by_handle(userlist, newhand))
538       x = 0;
539     else if (!strcasecmp(botnetnick, newhand) && (!(u->flags & USER_BOT) ||
540              nextbot(argv[1]) != -1))
541       x = 0;
542     else if (newhand[0] == '*')
543       x = 0;
544   }
545   if (x)
546     x = change_handle(u, newhand);
547 
548   Tcl_AppendResult(irp, x ? "1" : "0", NULL);
549   return TCL_OK;
550 }
551 
552 static int tcl_getting_users STDVAR
553 {
554   int i;
555 
556   BADARGS(1, 1, "");
557 
558   for (i = 0; i < dcc_total; i++) {
559     if (dcc[i].type == &DCC_BOT && dcc[i].status & STAT_GETTING) {
560       Tcl_AppendResult(irp, "1", NULL);
561       return TCL_OK;
562     }
563   }
564   Tcl_AppendResult(irp, "0", NULL);
565   return TCL_OK;
566 }
567 
568 static int tcl_isignore STDVAR
569 {
570   BADARGS(2, 2, " nick!user@host");
571 
572   Tcl_AppendResult(irp, match_ignore(argv[1]) ? "1" : "0", NULL);
573   return TCL_OK;
574 }
575 
576 static int tcl_newignore STDVAR
577 {
578   time_t expire_time;
579   char ign[UHOSTLEN], cmt[66], from[HANDLEN + 1];
580 
581   BADARGS(4, 5, " hostmask creator comment ?lifetime?");
582 
583   strlcpy(ign, argv[1], sizeof ign);
584   strlcpy(from, argv[2], sizeof from);
585   strlcpy(cmt, argv[3], sizeof cmt);
586   if (argc == 4)
587     expire_time = now + 60 * ignore_time;
588   else if ((expire_time = get_expire_time(irp, argv[4])) == -1)
589     return TCL_ERROR;
590   addignore(ign, from, cmt, expire_time);
591   return TCL_OK;
592 }
593 
594 static int tcl_killignore STDVAR
595 {
596   BADARGS(2, 2, " hostmask");
597 
598   Tcl_AppendResult(irp, delignore(argv[1]) ? "1" : "0", NULL);
599   return TCL_OK;
600 }
601 
602 static int tcl_ignorelist STDVAR
603 {
604   char expire[11], added[11], *p;
605   long tv;
606   EGG_CONST char *list[5];
607   struct igrec *i;
608 
609   BADARGS(1, 1, "");
610 
611   for (i = global_ign; i; i = i->next) {
612     list[0] = i->igmask;
613     list[1] = i->msg;
614 
615     tv = i->expire;
616     egg_snprintf(expire, sizeof expire, "%lu", tv);
617     list[2] = expire;
618 
619     tv = i->added;
620     egg_snprintf(added, sizeof added, "%lu", tv);
621     list[3] = added;
622 
623     list[4] = i->user;
624     p = Tcl_Merge(5, list);
625     Tcl_AppendElement(irp, p);
626     Tcl_Free((char *) p);
627   }
628   return TCL_OK;
629 }
630 
631 static int tcl_getuser STDVAR
632 {
633   struct user_entry_type *et = NULL;
634   struct userrec *u;
635   struct user_entry *e;
636 
637   BADARGS(2, -1, " handle ?type?");
638 
639   if (!(u = get_user_by_handle(userlist, argv[1]))) {
640     if (argv[1][0] != '*') {
641       Tcl_AppendResult(irp, "No such user.", NULL);
642       return TCL_ERROR;
643     } else
644       return TCL_OK; /* silently ignore user */
645   }
646   if (argc >= 3) {
647     if (!(et = find_entry_type(argv[2])) &&
648         strcasecmp(argv[2], "HANDLE")) {
649       Tcl_AppendResult(irp, "No such info type: ", argv[2], NULL);
650       return TCL_ERROR;
651     }
652     if (!strcasecmp(argv[2], "HANDLE"))
653       Tcl_AppendResult(irp, u->handle, NULL);
654     else {
655       e = find_user_entry(et, u);
656       if (e)
657         return et->tcl_get(irp, u, e, argc, argv);
658     }
659   } else {
660     for (et = entry_type_list; et; et = et->next) {
661       if (!et->tcl_append)
662         continue;
663       e = find_user_entry(et, u);
664       if (e && e->name) {
665         Tcl_AppendElement(irp, e->name);
666       } else if (et->name) {
667         Tcl_AppendElement(irp, et->name);
668       } else {
669         continue;
670       }
671       if (e) {
672         et->tcl_append(irp, u, e);
673       } else {
674         Tcl_AppendElement(irp, "");
675       }
676     }
677   }
678   return TCL_OK;
679 }
680 
681 static int tcl_setuser STDVAR
682 {
683   struct user_entry_type *et;
684   struct userrec *u;
685   struct user_entry *e;
686   int r;
687   module_entry *me;
688 
689   BADARGS(3, -1, " handle type ?setting....?");
690 
691   if (!(et = find_entry_type(argv[2]))) {
692     Tcl_AppendResult(irp, "No such info type: ", argv[2], NULL);
693     return TCL_ERROR;
694   }
695   if (!(u = get_user_by_handle(userlist, argv[1]))) {
696     if (argv[1][0] != '*') {
697       Tcl_AppendResult(irp, "No such user.", NULL);
698       return TCL_ERROR;
699     } else
700       return TCL_OK; /* Silently ignore user * */
701   }
702   me = module_find("irc", 0, 0);
703   if (me && !strcasecmp(argv[2], "hosts") && argc == 3) {
704     Function *func = me->funcs;
705 
706     (func[IRC_CHECK_THIS_USER]) (argv[1], 1, NULL);
707   }
708   if (!(e = find_user_entry(et, u))) {
709     e = user_malloc(sizeof(struct user_entry));
710     e->type = et;
711     e->name = NULL;
712     e->u.list = NULL;
713     list_insert((&(u->entries)), e);
714   }
715   r = et->tcl_set(irp, u, e, argc, argv);
716   /* Yeah... e is freed, and we read it... (tcl: setuser hand HOSTS none) */
717   if ((!e->u.list) && (egg_list_delete((struct list_type **) &(u->entries),
718       (struct list_type *) e)))
719     nfree(e);
720     /* else maybe already freed... (entry_type==HOSTS) <drummer> */
721   if (me && !strcasecmp(argv[2], "hosts") && argc == 4) {
722     Function *func = me->funcs;
723 
724     (func[IRC_CHECK_THIS_USER]) (argv[1], 0, NULL);
725   }
726   return r;
727 }
728 
729 tcl_cmds tcluser_cmds[] = {
730   {"countusers",       tcl_countusers},
731   {"validuser",         tcl_validuser},
732   {"finduser",           tcl_finduser},
733   {"passwdok",           tcl_passwdOk},
734   {"chattr",               tcl_chattr},
735   {"botattr",             tcl_botattr},
736   {"matchattr",         tcl_matchattr},
737   {"matchchanattr",     tcl_matchattr},
738   {"adduser",             tcl_adduser},
739   {"addbot",               tcl_addbot},
740   {"deluser",             tcl_deluser},
741   {"delhost",             tcl_delhost},
742   {"userlist",           tcl_userlist},
743   {"save",                   tcl_save},
744   {"reload",               tcl_reload},
745   {"chhandle",           tcl_chhandle},
746   {"chnick",             tcl_chhandle},
747   {"getting-users", tcl_getting_users},
748   {"isignore",           tcl_isignore},
749   {"newignore",         tcl_newignore},
750   {"killignore",       tcl_killignore},
751   {"ignorelist",       tcl_ignorelist},
752   {"getuser",             tcl_getuser},
753   {"setuser",             tcl_setuser},
754   {NULL,                         NULL}
755 };
756