1 /*
2  * cmds.c -- handles:
3  *   commands from a user via dcc
4  *   (split in 2, this portion contains no-irc commands)
5  */
6 /*
7  * Copyright (C) 1997 Robey Pointer
8  * Copyright (C) 1999 - 2021 Eggheads Development Team
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24 
25 #include "main.h"
26 #include "tandem.h"
27 #include "modules.h"
28 #include <signal.h>
29 
30 extern struct chanset_t *chanset;
31 extern struct dcc_t *dcc;
32 extern struct userrec *userlist;
33 extern tcl_timer_t *timer, *utimer;
34 extern int dcc_total, remote_boots, backgrd, make_userfile, conmask, require_p,
35            must_be_owner;
36 extern volatile sig_atomic_t do_restart;
37 extern unsigned long otraffic_irc, otraffic_irc_today, itraffic_irc,
38                      itraffic_irc_today, otraffic_bn, otraffic_bn_today,
39                      itraffic_bn, itraffic_bn_today, otraffic_dcc,
40                      otraffic_dcc_today, itraffic_dcc, itraffic_dcc_today,
41                      otraffic_trans, otraffic_trans_today, itraffic_trans,
42                      itraffic_trans_today, otraffic_unknown,
43                      otraffic_unknown_today, itraffic_unknown,
44                      itraffic_unknown_today;
45 extern Tcl_Interp *interp;
46 extern char botnetnick[], origbotname[], ver[], network[], owner[], quit_msg[];
47 extern time_t now, online_since;
48 extern module_entry *module_list;
49 
50 static char *btos(unsigned long);
51 
52 /* Define some characters not allowed in address/port string
53  */
54 #define BADADDRCHARS "+/"
55 
56 
57 /* Add hostmask to a bot's record if possible.
58  */
add_bot_hostmask(int idx,char * nick)59 static int add_bot_hostmask(int idx, char *nick)
60 {
61   struct chanset_t *chan;
62 
63   for (chan = chanset; chan; chan = chan->next)
64     if (channel_active(chan)) {
65       memberlist *m = ismember(chan, nick);
66 
67       if (m) {
68         char s[UHOSTLEN+NICKLEN+5];
69         struct userrec *u;
70 
71         egg_snprintf(s, sizeof s, "%s!%s", m->nick, m->userhost);
72         u = get_user_by_host(s);
73         if (u) {
74           dprintf(idx, "(Can't add hostmask for %s because it matches %s)\n",
75                   nick, u->handle);
76           return 0;
77         }
78         if (strchr("~^+=-", m->userhost[0]))
79           egg_snprintf(s, sizeof s, "*!?%s", m->userhost + 1);
80         else
81           egg_snprintf(s, sizeof s, "*!%s", m->userhost);
82         dprintf(idx, "(Added hostmask for %s from %s)\n", nick, chan->dname);
83         addhost_by_handle(nick, s);
84         return 1;
85       }
86     }
87   return 0;
88 }
89 
tell_who(struct userrec * u,int idx,int chan)90 static void tell_who(struct userrec *u, int idx, int chan)
91 {
92   int i, k, ok = 0, atr = u ? u->flags : 0;
93   int nicklen;
94   char format[81];
95   char s[1024]; /* temp fix - 1.4 has a better one */
96 
97   if (!chan)
98     dprintf(idx, "%s (* = owner, + = master, %% = botmaster, @ = op, "
99             "^ = halfop)\n", BOT_PARTYMEMBS);
100   else {
101     simple_sprintf(s, "assoc %d", chan);
102     if ((Tcl_Eval(interp, s) != TCL_OK) || tcl_resultempty())
103       dprintf(idx, "%s %s%d: (* = owner, + = master, %% = botmaster, @ = op, "
104               "^ = halfop)\n", BOT_PEOPLEONCHAN, (chan < GLOBAL_CHANS) ? "" :
105               "*", chan % GLOBAL_CHANS);
106     else
107       dprintf(idx, "%s '%s' (%s%d): (* = owner, + = master, %% = botmaster, @ = op, "
108               "^ = halfop)\n", BOT_PEOPLEONCHAN, tcl_resultstring(),
109               (chan < GLOBAL_CHANS) ? "" : "*", chan % GLOBAL_CHANS);
110   }
111 
112   /* calculate max nicklen */
113   nicklen = 0;
114   for (i = 0; i < dcc_total; i++) {
115     if (strlen(dcc[i].nick) > nicklen)
116       nicklen = strlen(dcc[i].nick);
117   }
118   if (nicklen < 9)
119     nicklen = 9;
120 
121   for (i = 0; i < dcc_total; i++)
122     if (dcc[i].type == &DCC_CHAT)
123       if (dcc[i].u.chat->channel == chan) {
124         if (atr & USER_OWNER) {
125           egg_snprintf(format, sizeof format, "  [%%.2lu]  %%c%%-%us %%s",
126                        nicklen);
127           sprintf(s, format, dcc[i].sock,
128                   (geticon(i) == '-' ? ' ' : geticon(i)), dcc[i].nick,
129                   dcc[i].host);
130         } else {
131           egg_snprintf(format, sizeof format, "  %%c%%-%us %%s", nicklen);
132           sprintf(s, format,
133                   (geticon(i) == '-' ? ' ' : geticon(i)),
134                   dcc[i].nick, dcc[i].host);
135         }
136         if (atr & USER_MASTER) {
137           if (dcc[i].u.chat->con_flags)
138             sprintf(&s[strlen(s)], " (con:%s)",
139                     masktype(dcc[i].u.chat->con_flags));
140         }
141         if (now - dcc[i].timeval > 300) {
142           unsigned long days, hrs, mins;
143 
144           days = (now - dcc[i].timeval) / 86400;
145           hrs = ((now - dcc[i].timeval) - (days * 86400)) / 3600;
146           mins = ((now - dcc[i].timeval) - (hrs * 3600)) / 60;
147           if (days > 0)
148             sprintf(&s[strlen(s)], " (idle %lud%luh)", days, hrs);
149           else if (hrs > 0)
150             sprintf(&s[strlen(s)], " (idle %luh%lum)", hrs, mins);
151           else
152             sprintf(&s[strlen(s)], " (idle %lum)", mins);
153         }
154         dprintf(idx, "%s\n", s);
155         if (dcc[i].u.chat->away != NULL)
156           dprintf(idx, "      AWAY: %s\n", dcc[i].u.chat->away);
157       }
158   for (i = 0; i < dcc_total; i++)
159     if (dcc[i].type == &DCC_BOT) {
160       if (!ok) {
161         ok = 1;
162         dprintf(idx, "Bots connected:\n");
163       }
164       strftime(s, 14, "%d %b %H:%M", localtime(&dcc[i].timeval));
165       if (atr & USER_OWNER) {
166         egg_snprintf(format, sizeof format,
167                      "  [%%.2lu]  %%s%%c%%-%us (%%s) %%s\n", nicklen);
168         dprintf(idx, format, dcc[i].sock,
169                 dcc[i].status & STAT_CALLED ? "<-" : "->",
170                 dcc[i].status & STAT_SHARE ? '+' : ' ', dcc[i].nick, s,
171                 dcc[i].u.bot->version);
172       } else {
173         egg_snprintf(format, sizeof format, "  %%s%%c%%-%us (%%s) %%s\n",
174                      nicklen);
175         dprintf(idx, format, dcc[i].status & STAT_CALLED ? "<-" : "->",
176                 dcc[i].status & STAT_SHARE ? '+' : ' ', dcc[i].nick, s,
177                 dcc[i].u.bot->version);
178       }
179     }
180   ok = 0;
181   for (i = 0; i < dcc_total; i++) {
182     if ((dcc[i].type == &DCC_CHAT) && (dcc[i].u.chat->channel != chan)) {
183       if (!ok) {
184         ok = 1;
185         dprintf(idx, "Other people on the bot:\n");
186       }
187       if (atr & USER_OWNER) {
188         egg_snprintf(format, sizeof format, "  [%%.2lu]  %%c%%-%us ", nicklen);
189         sprintf(s, format, dcc[i].sock,
190                 (geticon(i) == '-' ? ' ' : geticon(i)), dcc[i].nick);
191       } else {
192         egg_snprintf(format, sizeof format, "  %%c%%-%us ", nicklen);
193         sprintf(s, format, (geticon(i) == '-' ? ' ' : geticon(i)), dcc[i].nick);
194       }
195       if (atr & USER_MASTER) {
196         if (dcc[i].u.chat->channel < 0)
197           strcat(s, "(-OFF-) ");
198         else if (!dcc[i].u.chat->channel)
199           strcat(s, "(party) ");
200         else
201           sprintf(&s[strlen(s)], "(%5d) ", dcc[i].u.chat->channel);
202       }
203       strcat(s, dcc[i].host);
204       if (atr & USER_MASTER) {
205         if (dcc[i].u.chat->con_flags)
206           sprintf(&s[strlen(s)], " (con:%s)",
207                   masktype(dcc[i].u.chat->con_flags));
208       }
209       if (now - dcc[i].timeval > 300) {
210         k = (now - dcc[i].timeval) / 60;
211         if (k < 60)
212           sprintf(&s[strlen(s)], " (idle %dm)", k);
213         else
214           sprintf(&s[strlen(s)], " (idle %dh%dm)", k / 60, k % 60);
215       }
216       dprintf(idx, "%s\n", s);
217       if (dcc[i].u.chat->away != NULL)
218         dprintf(idx, "      AWAY: %s\n", dcc[i].u.chat->away);
219     }
220     if ((atr & USER_MASTER) && (dcc[i].type->flags & DCT_SHOWWHO) &&
221         (dcc[i].type != &DCC_CHAT)) {
222       if (!ok) {
223         ok = 1;
224         dprintf(idx, "Other people on the bot:\n");
225       }
226       if (atr & USER_OWNER) {
227         egg_snprintf(format, sizeof format, "  [%%.2lu]  %%c%%-%us (files) %%s",
228                      nicklen);
229         sprintf(s, format,
230                 dcc[i].sock, dcc[i].status & STAT_CHAT ? '+' : ' ',
231                 dcc[i].nick, dcc[i].host);
232       } else {
233         egg_snprintf(format, sizeof format, "  %%c%%-%us (files) %%s", nicklen);
234         sprintf(s, format,
235                 dcc[i].status & STAT_CHAT ? '+' : ' ',
236                 dcc[i].nick, dcc[i].host);
237       }
238       dprintf(idx, "%s\n", s);
239     }
240   }
241 }
242 
cmd_botinfo(struct userrec * u,int idx,char * par)243 static void cmd_botinfo(struct userrec *u, int idx, char *par)
244 {
245   char s[512], s2[32];
246   struct chanset_t *chan;
247   time_t now2;
248   int hr, min;
249 
250   now2 = now - online_since;
251   s2[0] = 0;
252   if (now2 > 86400) {
253     int days = now2 / 86400;
254 
255     /* Days */
256     sprintf(s2, "%d day", days);
257     if (days >= 2)
258       strcat(s2, "s");
259     strcat(s2, ", ");
260     now2 -= days * 86400;
261   }
262   hr = (time_t) ((int) now2 / 3600);
263   now2 -= (hr * 3600);
264   min = (time_t) ((int) now2 / 60);
265   sprintf(&s2[strlen(s2)], "%02d:%02d", (int) hr, (int) min);
266   putlog(LOG_CMDS, "*", "#%s# botinfo", dcc[idx].nick);
267   simple_sprintf(s, "%d:%s@%s", dcc[idx].sock, dcc[idx].nick, botnetnick);
268   botnet_send_infoq(-1, s);
269   s[0] = 0;
270   if (module_find("server", 0, 0)) {
271     for (chan = chanset; chan; chan = chan->next) {
272       if (!channel_secret(chan)) {
273         if ((strlen(s) + strlen(chan->dname) + strlen(network)
274              + strlen(botnetnick) + strlen(ver) + 1) >= 490) {
275           strcat(s, "++  ");
276           break;                /* yeesh! */
277         }
278         strcat(s, chan->dname);
279         strcat(s, ", ");
280       }
281     }
282 
283     if (s[0]) {
284       s[strlen(s) - 2] = 0;
285       dprintf(idx, "*** [%s] %s <%s> (%s) [UP %s]\n", botnetnick,
286               ver, network, s, s2);
287     } else
288       dprintf(idx, "*** [%s] %s <%s> (%s) [UP %s]\n", botnetnick,
289               ver, network, BOT_NOCHANNELS, s2);
290   } else
291     dprintf(idx, "*** [%s] %s <NO_IRC> [UP %s]\n", botnetnick, ver, s2);
292 }
293 
cmd_whom(struct userrec * u,int idx,char * par)294 static void cmd_whom(struct userrec *u, int idx, char *par)
295 {
296   if (par[0] == '*') {
297     putlog(LOG_CMDS, "*", "#%s# whom %s", dcc[idx].nick, par);
298     answer_local_whom(idx, -1);
299     return;
300   } else if (dcc[idx].u.chat->channel < 0) {
301     dprintf(idx, "You have chat turned off.\n");
302     return;
303   }
304   putlog(LOG_CMDS, "*", "#%s# whom %s", dcc[idx].nick, par);
305   if (!par[0]) {
306     answer_local_whom(idx, dcc[idx].u.chat->channel);
307   } else {
308     int chan = -1;
309 
310     if ((par[0] < '0') || (par[0] > '9')) {
311       Tcl_SetVar(interp, "_chan", par, 0);
312       if ((Tcl_VarEval(interp, "assoc ", "$_chan", NULL) == TCL_OK) &&
313           !tcl_resultempty()) {
314         chan = tcl_resultint();
315       }
316       if (chan <= 0) {
317         dprintf(idx, "No such channel exists.\n");
318         return;
319       }
320     } else
321       chan = atoi(par);
322     if ((chan < 0) || (chan >= GLOBAL_CHANS)) {
323       dprintf(idx, "Channel number out of range: must be between 0 and %d."
324               "\n", GLOBAL_CHANS);
325       return;
326     }
327     answer_local_whom(idx, chan);
328   }
329 }
330 
cmd_me(struct userrec * u,int idx,char * par)331 static void cmd_me(struct userrec *u, int idx, char *par)
332 {
333   int i;
334 
335   if (dcc[idx].u.chat->channel < 0) {
336     dprintf(idx, "You have chat turned off.\n");
337     return;
338   }
339   if (!par[0]) {
340     dprintf(idx, "Usage: me <action>\n");
341     return;
342   }
343   if (dcc[idx].u.chat->away != NULL)
344     not_away(idx);
345   for (i = 0; i < dcc_total; i++)
346     if ((dcc[i].type->flags & DCT_CHAT) &&
347         (dcc[i].u.chat->channel == dcc[idx].u.chat->channel) &&
348         ((i != idx) || (dcc[i].status & STAT_ECHO)))
349       dprintf(i, "* %s %s\n", dcc[idx].nick, par);
350   botnet_send_act(idx, botnetnick, dcc[idx].nick,
351                   dcc[idx].u.chat->channel, par);
352   check_tcl_act(dcc[idx].nick, dcc[idx].u.chat->channel, par);
353 }
354 
cmd_motd(struct userrec * u,int idx,char * par)355 static void cmd_motd(struct userrec *u, int idx, char *par)
356 {
357   int i;
358 
359   if (par[0]) {
360     putlog(LOG_CMDS, "*", "#%s# motd %s", dcc[idx].nick, par);
361     if (!strcasecmp(par, botnetnick))
362       show_motd(idx);
363     else {
364       i = nextbot(par);
365       if (i < 0)
366         dprintf(idx, "That bot isn't connected.\n");
367       else {
368         char x[40];
369 
370         simple_sprintf(x, "%s%d:%s@%s",
371                        (u->flags & USER_HIGHLITE) ?
372                        ((dcc[idx].status & STAT_TELNET) ? "#" : "!") : "",
373                        dcc[idx].sock, dcc[idx].nick, botnetnick);
374         botnet_send_motd(i, x, par);
375       }
376     }
377   } else {
378     putlog(LOG_CMDS, "*", "#%s# motd", dcc[idx].nick);
379     show_motd(idx);
380   }
381 }
382 
cmd_away(struct userrec * u,int idx,char * par)383 static void cmd_away(struct userrec *u, int idx, char *par)
384 {
385   if (strlen(par) > 60)
386     par[60] = 0;
387   set_away(idx, par);
388 }
389 
cmd_back(struct userrec * u,int idx,char * par)390 static void cmd_back(struct userrec *u, int idx, char *par)
391 {
392   not_away(idx);
393 }
394 
395 /* Take a password provided by the user and check that it isn't too long,
396  * too short, or start with a '+' (for encryption reasons).
397  *
398  * If successful set it and return NULL.
399  *
400  * On failure return error message.
401  */
check_validpass(struct userrec * u,char * new)402 char *check_validpass(struct userrec *u, char *new) {
403   int l;
404   unsigned char *p = (unsigned char *) new;
405 
406   l = strlen(new);
407   if (l < 6)
408     return IRC_PASSFORMAT;
409   if (l > PASSWORDMAX)
410     return "Passwords cannot be longer than " STRINGIFY(PASSWORDMAX) " characters, please try again.";
411   if (new[0] == '+') /* See also: userent.c:pass_set() */
412     return "Password cannot start with '+', please try again.";
413   while (*p) {
414     if ((*p <= 32) || (*p == 127))
415       return "Password cannot use weird symbols, please try again.";
416     p++;
417   }
418   set_user(&USERENTRY_PASS, u, new);
419   return NULL;
420 }
421 
cmd_newpass(struct userrec * u,int idx,char * par)422 static void cmd_newpass(struct userrec *u, int idx, char *par)
423 {
424   char *new, *s;
425 
426   if (!par[0]) {
427     dprintf(idx, "Usage: newpass <newpassword>\n");
428     return;
429   }
430   new = newsplit(&par);
431   if ((s = check_validpass(u, new))) {
432     dprintf(idx, "%s\n", s);
433     return;
434   }
435   putlog(LOG_CMDS, "*", "#%s# newpass...", dcc[idx].nick);
436   dprintf(idx, "Changed password to '%s'.\n", new);
437 }
438 
cmd_bots(struct userrec * u,int idx,char * par)439 static void cmd_bots(struct userrec *u, int idx, char *par)
440 {
441   putlog(LOG_CMDS, "*", "#%s# bots", dcc[idx].nick);
442   tell_bots(idx);
443 }
444 
cmd_bottree(struct userrec * u,int idx,char * par)445 static void cmd_bottree(struct userrec *u, int idx, char *par)
446 {
447   putlog(LOG_CMDS, "*", "#%s# bottree", dcc[idx].nick);
448   tell_bottree(idx, 0);
449 }
450 
cmd_vbottree(struct userrec * u,int idx,char * par)451 static void cmd_vbottree(struct userrec *u, int idx, char *par)
452 {
453   putlog(LOG_CMDS, "*", "#%s# vbottree", dcc[idx].nick);
454   tell_bottree(idx, 1);
455 }
456 
cmd_rehelp(struct userrec * u,int idx,char * par)457 static void cmd_rehelp(struct userrec *u, int idx, char *par)
458 {
459   putlog(LOG_CMDS, "*", "#%s# rehelp", dcc[idx].nick);
460   dprintf(idx, "Reload help cache...\n");
461   reload_help_data();
462 }
463 
cmd_help(struct userrec * u,int idx,char * par)464 static void cmd_help(struct userrec *u, int idx, char *par)
465 {
466   struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
467 
468   get_user_flagrec(u, &fr, dcc[idx].u.chat->con_chan);
469   if (par[0]) {
470     putlog(LOG_CMDS, "*", "#%s# help %s", dcc[idx].nick, par);
471     if (!strcmp(par, "all"))
472       tellallhelp(idx, "all", &fr);
473     else if (strchr(par, '*') || strchr(par, '?')) {
474       char *p = par;
475 
476       /* Check if the search pattern only consists of '*' and/or '?'
477        * If it does, show help for "all" instead of listing all help
478        * entries.
479        */
480       for (p = par; *p && ((*p == '*') || (*p == '?')); p++);
481       if (*p)
482         tellwildhelp(idx, par, &fr);
483       else
484         tellallhelp(idx, "all", &fr);
485     } else
486       tellhelp(idx, par, &fr, 0);
487   } else {
488     putlog(LOG_CMDS, "*", "#%s# help", dcc[idx].nick);
489     if (glob_op(fr) || glob_botmast(fr) || chan_op(fr))
490       tellhelp(idx, "help", &fr, 0);
491     else
492       tellhelp(idx, "partyline", &fr, 0);
493   }
494 }
495 
cmd_addlog(struct userrec * u,int idx,char * par)496 static void cmd_addlog(struct userrec *u, int idx, char *par)
497 {
498   if (!par[0]) {
499     dprintf(idx, "Usage: addlog <message>\n");
500     return;
501   }
502   dprintf(idx, "Placed entry in the log file.\n");
503   putlog(LOG_MISC, "*", "%s: %s", dcc[idx].nick, par);
504 }
505 
cmd_who(struct userrec * u,int idx,char * par)506 static void cmd_who(struct userrec *u, int idx, char *par)
507 {
508   int i;
509 
510   if (par[0]) {
511     if (dcc[idx].u.chat->channel < 0) {
512       dprintf(idx, "You have chat turned off.\n");
513       return;
514     }
515     putlog(LOG_CMDS, "*", "#%s# who %s", dcc[idx].nick, par);
516     if (!strcasecmp(par, botnetnick))
517       tell_who(u, idx, dcc[idx].u.chat->channel);
518     else {
519       i = nextbot(par);
520       if (i < 0) {
521         dprintf(idx, "That bot isn't connected.\n");
522       } else if (dcc[idx].u.chat->channel >= GLOBAL_CHANS)
523         dprintf(idx, "You are on a local channel.\n");
524       else {
525         char s[40];
526 
527         simple_sprintf(s, "%d:%s@%s", dcc[idx].sock, dcc[idx].nick, botnetnick);
528         botnet_send_who(i, s, par, dcc[idx].u.chat->channel);
529       }
530     }
531   } else {
532     putlog(LOG_CMDS, "*", "#%s# who", dcc[idx].nick);
533     if (dcc[idx].u.chat->channel < 0)
534       tell_who(u, idx, 0);
535     else
536       tell_who(u, idx, dcc[idx].u.chat->channel);
537   }
538 }
539 
cmd_whois(struct userrec * u,int idx,char * par)540 static void cmd_whois(struct userrec *u, int idx, char *par)
541 {
542   if (!par[0]) {
543     dprintf(idx, "Usage: whois <handle>\n");
544     return;
545   }
546 
547   putlog(LOG_CMDS, "*", "#%s# whois %s", dcc[idx].nick, par);
548   tell_user_ident(idx, par);
549 }
550 
cmd_match(struct userrec * u,int idx,char * par)551 static void cmd_match(struct userrec *u, int idx, char *par)
552 {
553   int start = 1, limit = 20;
554   char *s, *s1, *chname;
555 
556   if (!par[0]) {
557     dprintf(idx, "Usage: match <nick/host> [[skip] count]\n");
558     return;
559   }
560   putlog(LOG_CMDS, "*", "#%s# match %s", dcc[idx].nick, par);
561   s = newsplit(&par);
562   if (strchr(CHANMETA, par[0]) != NULL)
563     chname = newsplit(&par);
564   else
565     chname = "";
566   if (atoi(par) > 0) {
567     s1 = newsplit(&par);
568     if (atoi(par) > 0) {
569       start = atoi(s1);
570       limit = atoi(par);
571     } else
572       limit = atoi(s1);
573   }
574   tell_users_match(idx, s, start, limit, chname);
575 }
576 
cmd_uptime(struct userrec * u,int idx,char * par)577 static void cmd_uptime(struct userrec *u, int idx, char *par)
578 {
579   putlog(LOG_CMDS, "*", "#%s# uptime", dcc[idx].nick);
580   tell_verbose_uptime(idx);
581 }
582 
cmd_status(struct userrec * u,int idx,char * par)583 static void cmd_status(struct userrec *u, int idx, char *par)
584 {
585   int atr = u ? u->flags : 0;
586 
587   if (!strcasecmp(par, "all")) {
588     if (!(atr & USER_MASTER)) {
589       dprintf(idx, "You do not have Bot Master privileges.\n");
590       return;
591     }
592     putlog(LOG_CMDS, "*", "#%s# status all", dcc[idx].nick);
593     tell_verbose_status(idx);
594     tell_mem_status_dcc(idx);
595     dprintf(idx, "\n");
596     tell_settings(idx);
597     do_module_report(idx, 1, NULL);
598   } else {
599     putlog(LOG_CMDS, "*", "#%s# status", dcc[idx].nick);
600     tell_verbose_status(idx);
601     tell_mem_status_dcc(idx);
602     do_module_report(idx, 0, NULL);
603   }
604 }
605 
cmd_dccstat(struct userrec * u,int idx,char * par)606 static void cmd_dccstat(struct userrec *u, int idx, char *par)
607 {
608   putlog(LOG_CMDS, "*", "#%s# dccstat", dcc[idx].nick);
609   tell_dcc(idx);
610 }
611 
cmd_boot(struct userrec * u,int idx,char * par)612 static void cmd_boot(struct userrec *u, int idx, char *par)
613 {
614   int i, files = 0, ok = 0;
615   char *who;
616   struct userrec *u2;
617 
618   if (!par[0]) {
619     dprintf(idx, "Usage: boot nick[@bot]\n");
620     return;
621   }
622   who = newsplit(&par);
623   if (strchr(who, '@') != NULL) {
624     char whonick[HANDLEN + 1];
625 
626     splitcn(whonick, who, '@', HANDLEN + 1);
627     if (!strcasecmp(who, botnetnick)) {
628       cmd_boot(u, idx, whonick);
629       return;
630     }
631     if (remote_boots > 0) {
632       i = nextbot(who);
633       if (i < 0) {
634         dprintf(idx, "No such bot connected.\n");
635         return;
636       }
637       botnet_send_reject(i, dcc[idx].nick, botnetnick, whonick,
638                          who, par[0] ? par : dcc[idx].nick);
639       putlog(LOG_BOTS, "*", "#%s# boot %s@%s (%s)", dcc[idx].nick, whonick,
640              who, par[0] ? par : dcc[idx].nick);
641     } else
642       dprintf(idx, "Remote boots are disabled here.\n");
643     return;
644   }
645   for (i = 0; i < dcc_total; i++)
646     if (!strcasecmp(dcc[i].nick, who) && !ok &&
647         (dcc[i].type->flags & DCT_CANBOOT)) {
648       u2 = get_user_by_handle(userlist, dcc[i].nick);
649       if (u2 && (u2->flags & USER_OWNER) &&
650           strcasecmp(dcc[idx].nick, who)) {
651         dprintf(idx, "You can't boot a bot owner.\n");
652         return;
653       }
654       if (u2 && (u2->flags & USER_MASTER) && !(u && (u->flags & USER_MASTER))) {
655         dprintf(idx, "You can't boot a bot master.\n");
656         return;
657       }
658       files = (dcc[i].type->flags & DCT_FILES);
659       if (files)
660         dprintf(idx, "Booted %s from the file area.\n", dcc[i].nick);
661       else
662         dprintf(idx, "Booted %s from the party line.\n", dcc[i].nick);
663       putlog(LOG_CMDS, "*", "#%s# boot %s %s", dcc[idx].nick, who, par);
664       do_boot(i, dcc[idx].nick, par);
665       ok = 1;
666     }
667   if (!ok)
668     dprintf(idx, "Who?  No such person on the party line.\n");
669 }
670 
671 /* Make changes to user console settings */
do_console(struct userrec * u,int idx,char * par,int reset)672 static void do_console(struct userrec *u, int idx, char *par, int reset)
673 {
674   char *nick, s[2], s1[512];
675   int dest = 0, i, ok = 0, pls;
676   struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
677   module_entry *me;
678 
679   get_user_flagrec(u, &fr, dcc[idx].u.chat->con_chan);
680   strlcpy(s1, par, sizeof s1);
681   nick = newsplit(&par);
682   /* Check if the parameter is a handle.
683    * Don't remove '+' as someone couldn't have '+' in CHANMETA cause
684    * he doesn't use IRCnet ++rtc.
685    */
686   if (nick[0] && !strchr(CHANMETA "+-*", nick[0]) && glob_master(fr)) {
687     for (i = 0; i < dcc_total; i++) {
688       if (!strcasecmp(nick, dcc[i].nick) &&
689           (dcc[i].type == &DCC_CHAT) && (!ok)) {
690         ok = 1;
691         dest = i;
692       }
693     }
694     if (!ok) {
695       dprintf(idx, "No such user on the party line!\n");
696       return;
697     }
698     nick[0] = 0;
699   } else
700     dest = idx;
701   if (!nick[0])
702     nick = newsplit(&par);
703   /* Check if the parameter is a channel.
704    * Consider modeless channels, starting with '+'
705    */
706   if (nick[0] && !reset && ((nick[0] == '+' && findchan_by_dname(nick)) ||
707       (nick[0] != '+' && strchr(CHANMETA "*", nick[0])))) {
708     if (strcmp(nick, "*") && !findchan_by_dname(nick)) {
709       dprintf(idx, "Invalid console channel: %s.\n", nick);
710       return;
711     }
712     get_user_flagrec(u, &fr, nick);
713     if (!chan_op(fr) && !(glob_op(fr) && !chan_deop(fr))) {
714       dprintf(idx, "You don't have op or master access to channel %s.\n",
715               nick);
716       return;
717     }
718     strlcpy(dcc[dest].u.chat->con_chan, nick,
719         sizeof dcc[dest].u.chat->con_chan);
720     nick[0] = 0;
721     if (dest != idx)
722       get_user_flagrec(dcc[dest].user, &fr, dcc[dest].u.chat->con_chan);
723   }
724   if (!nick[0])
725     nick = newsplit(&par);
726   pls = 1;
727   if (!reset && nick[0]) {
728     if ((nick[0] != '+') && (nick[0] != '-'))
729       dcc[dest].u.chat->con_flags = 0;
730     for (; *nick; nick++) {
731       if (*nick == '+')
732         pls = 1;
733       else if (*nick == '-')
734         pls = 0;
735       else {
736         s[0] = *nick;
737         s[1] = 0;
738         if (pls)
739           dcc[dest].u.chat->con_flags |= logmodes(s);
740         else
741           dcc[dest].u.chat->con_flags &= ~logmodes(s);
742       }
743     }
744   } else if (reset) {
745     dcc[dest].u.chat->con_flags = (u->flags & USER_MASTER) ? conmask : 0;
746   }
747   dcc[dest].u.chat->con_flags = check_conflags(&fr,
748                                                dcc[dest].u.chat->con_flags);
749   putlog(LOG_CMDS, "*", "#%s# %sconsole %s", dcc[idx].nick, reset ? "reset" : "", s1);
750   if (dest == idx) {
751     dprintf(idx, "Set your console to %s: %s (%s).\n",
752             dcc[idx].u.chat->con_chan,
753             masktype(dcc[idx].u.chat->con_flags),
754             maskname(dcc[idx].u.chat->con_flags));
755   } else {
756     dprintf(idx, "Set console of %s to %s: %s (%s).\n", dcc[dest].nick,
757             dcc[dest].u.chat->con_chan,
758             masktype(dcc[dest].u.chat->con_flags),
759             maskname(dcc[dest].u.chat->con_flags));
760     dprintf(dest, "%s set your console to %s: %s (%s).\n", dcc[idx].nick,
761             dcc[dest].u.chat->con_chan,
762             masktype(dcc[dest].u.chat->con_flags),
763             maskname(dcc[dest].u.chat->con_flags));
764   }
765   /* New style autosave -- drummer,07/25/1999 */
766   if ((me = module_find("console", 1, 1))) {
767     Function *func = me->funcs;
768 
769     (func[CONSOLE_DOSTORE]) (dest);
770   }
771 }
772 
cmd_console(struct userrec * u,int idx,char * par)773 static void cmd_console(struct userrec *u, int idx, char *par)
774 {
775   if (!par[0]) {
776     dprintf(idx, "Your console is %s: %s (%s).\n",
777             dcc[idx].u.chat->con_chan,
778             masktype(dcc[idx].u.chat->con_flags),
779             maskname(dcc[idx].u.chat->con_flags));
780     return;
781   }
782   do_console(u, idx, par, 0);
783 }
784 
785 /* Reset console flags to config defaults */
cmd_resetconsole(struct userrec * u,int idx,char * par)786 static void cmd_resetconsole(struct userrec *u, int idx, char *par)
787 {
788   do_console(u, idx, par, 1);
789 }
790 
791 /* Check if a string is a valid integer and lies non-inclusive
792  * between two given integers. Returns 1 if true, 0 if not.
793  */
check_int_range(char * value,int min,int max)794 int check_int_range(char *value, int min, int max) {
795   char *endptr = NULL;
796   long intvalue;
797 
798   if (value && value[0]) {
799     intvalue = strtol(value, &endptr, 10);
800     if ((intvalue < max) && (intvalue > min) && (*endptr == '\0')) {
801       return 1;
802     }
803   }
804   return 0;
805 }
806 
cmd_pls_bot(struct userrec * u,int idx,char * par)807 static void cmd_pls_bot(struct userrec *u, int idx, char *par)
808 {
809   char *handle, *addr, *port, *port2, *relay, *host, *p;
810   struct userrec *u1;
811   struct bot_addr *bi;
812   int i, found = 0;
813 
814   if (!par[0]) {
815     dprintf(idx, "Usage: +bot <handle> [address [telnet-port[/relay-port]]] "
816             "[host]\n");
817     return;
818   }
819 
820   handle = newsplit(&par);
821   addr = newsplit(&par);
822   port2 = newsplit(&par);
823   port = strtok(port2, "/");
824   relay = strtok(NULL, "/");
825 
826   if (strtok(NULL, "/")) {
827     dprintf(idx, "You've supplied more than 2 ports, make up your mind.\n");
828     return;
829   }
830 
831   host = newsplit(&par);
832 
833   if (strlen(handle) > HANDLEN)
834     handle[HANDLEN] = 0;
835 
836   if (get_user_by_handle(userlist, handle)) {
837     dprintf(idx, "Someone already exists by that name.\n");
838     return;
839   }
840 
841   if (strchr(BADHANDCHARS, handle[0]) != NULL) {
842     dprintf(idx, "You can't start a botnick with '%c'.\n", handle[0]);
843     return;
844   }
845 
846 /* Check for bad characters throughout the handle */
847   for (p = handle; *p; p++)
848     if ((unsigned char) *p <= 32 || *p == '@') {
849       dprintf(idx, "Invalid character '%c' in handle, try again\n", p[0]);
850       return;
851     }
852 
853   if (addr[0]) {
854 #ifndef IPV6
855  /* Reject IPv6 addresses */
856     for (i=0; addr[i]; i++) {
857       if (addr[i] == ':') {
858         dprintf(idx, "Invalid IP address format (this Eggdrop "
859           "was compiled without IPv6 support).\n");
860         return;
861       }
862     }
863 #endif
864  /* Check if user forgot address field by checking if argument is completely
865   * numerical, implying a port was provided as the next argument instead.
866   */
867     for (i=0; addr[i]; i++) {
868       if (strchr(BADADDRCHARS, addr[i])) {
869         dprintf(idx, "Bot address may not contain a '%c'. ", addr[i]);
870         break;
871       }
872       if (!isdigit((unsigned char) addr[i])) {
873         found=1;
874         break;
875       }
876     }
877     if (!found) {
878       dprintf(idx, "Invalid host address.\n");
879       dprintf(idx, "Usage: +bot <handle> [address [telnet-port[/relay-port]]] "
880               "[host]\n");
881       return;
882     }
883   }
884 
885 #ifndef TLS
886   if ((port && *port == '+') || (relay && relay[0] == '+')) {
887     dprintf(idx, "Ports prefixed with '+' are not enabled "
888       "(this Eggdrop was compiled without TLS support).\n");
889     return;
890   }
891 #endif
892   if (port) {
893     if (!check_int_range(port, 0, 65536)) {
894       dprintf(idx, "Ports must be integers between 1 and 65535.\n");
895       return;
896     }
897   }
898   if (relay) {
899     if (!check_int_range(relay, 0, 65536)) {
900       dprintf(idx, "Ports must be integers between 1 and 65535.\n");
901       return;
902     }
903   }
904 
905   if (strlen(addr) > 60)
906     addr[60] = 0;
907 
908 /* Trim IPv6 []s out if present */
909   if (addr[0] == '[') {
910     addr[strlen(addr)-1] = 0;
911     memmove(addr, addr + 1, strlen(addr));
912   }
913   userlist = adduser(userlist, handle, "none", "-", USER_BOT);
914   u1 = get_user_by_handle(userlist, handle);
915   bi = user_malloc(sizeof(struct bot_addr));
916 #ifdef TLS
917   bi->ssl = 0;
918 #endif
919   bi->address = user_malloc(strlen(addr) + 1);
920   strcpy(bi->address, addr);
921 
922   if (!port) {
923     bi->telnet_port = 3333;
924     bi->relay_port = 3333;
925   } else {
926 #ifdef TLS
927     if (*port == '+')
928       bi->ssl |= TLS_BOT;
929 #endif
930     bi->telnet_port = atoi(port);
931     if (!relay) {
932       bi->relay_port = bi->telnet_port;
933 #ifdef TLS
934       bi->ssl *= TLS_BOT + TLS_RELAY;
935 #endif
936     } else  {
937 #ifdef TLS
938       if (relay[0] == '+')
939         bi->ssl |= TLS_RELAY;
940 #endif
941       bi->relay_port = atoi(relay);
942     }
943   }
944 
945   set_user(&USERENTRY_BOTADDR, u1, bi);
946   if (addr[0]) {
947     putlog(LOG_CMDS, "*", "#%s# +bot %s %s%s%s%s%s %s%s", dcc[idx].nick, handle,
948            addr, port ? " " : "", port ? port : "", relay ? " " : "",
949            relay ? relay : "", host[0] ? " " : "", host);
950 #ifdef TLS
951     dprintf(idx, "Added bot '%s' with address [%s]:%s%d/%s%d and %s%s%s.\n",
952             handle, addr, (bi->ssl & TLS_BOT) ? "+" : "", bi->telnet_port,
953             (bi->ssl & TLS_RELAY) ? "+" : "", bi->relay_port, host[0] ?
954             "hostmask '" : "no hostmask", host[0] ? host : "",
955             host[0] ? "'" : "");
956 #else
957     dprintf(idx, "Added bot '%s' with address [%s]:%d/%d and %s%s%s.\n", handle,
958             addr, bi->telnet_port, bi->relay_port, host[0] ? "hostmask '" :
959             "no hostmask", host[0] ? host : "", host[0] ? "'" : "");
960 #endif
961   } else {
962     putlog(LOG_CMDS, "*", "#%s# +bot %s %s%s", dcc[idx].nick, handle,
963            host[0] ? " " : "", host);
964     dprintf(idx, "Added bot '%s' with no address and %s%s%s.\n", handle,
965             host[0] ? "hostmask '" : "no hostmask", host[0] ? host : "",
966             host[0] ? "'" : "");
967   }
968   if (host[0]) {
969     addhost_by_handle(handle, host);
970   } else if (!add_bot_hostmask(idx, handle)) {
971     dprintf(idx, "You'll want to add a hostmask if this bot will ever be on "
972             "any channels that I'm on.\n");
973   }
974 }
975 
cmd_chhandle(struct userrec * u,int idx,char * par)976 static void cmd_chhandle(struct userrec *u, int idx, char *par)
977 {
978   char hand[HANDLEN + 1], newhand[HANDLEN + 1];
979   int i, atr = u ? u->flags : 0, atr2;
980   struct userrec *u2;
981 
982   strlcpy(hand, newsplit(&par), sizeof hand);
983   strlcpy(newhand, newsplit(&par), sizeof newhand);
984 
985   if (!hand[0] || !newhand[0]) {
986     dprintf(idx, "Usage: chhandle <oldhandle> <newhandle>\n");
987     return;
988   }
989   for (i = 0; i < strlen(newhand); i++)
990     if (((unsigned char) newhand[i] <= 32) || (newhand[i] == '@'))
991       newhand[i] = '?';
992   if (strchr(BADHANDCHARS, newhand[0]) != NULL)
993     dprintf(idx, "Bizarre quantum forces prevent nicknames from starting with "
994             "'%c'.\n", newhand[0]);
995   else if (get_user_by_handle(userlist, newhand) &&
996            strcasecmp(hand, newhand))
997     dprintf(idx, "Somebody is already using %s.\n", newhand);
998   else {
999     u2 = get_user_by_handle(userlist, hand);
1000     atr2 = u2 ? u2->flags : 0;
1001     if ((atr & USER_BOTMAST) && !(atr & USER_MASTER) && !(atr2 & USER_BOT))
1002       dprintf(idx, "You can't change handles for non-bots.\n");
1003     else if (!strcasecmp(hand, EGG_BG_HANDLE))
1004       dprintf(idx, "You can't change the handle of a temporary user.\n");
1005     else if ((bot_flags(u2) & BOT_SHARE) && !(atr & USER_OWNER))
1006       dprintf(idx, "You can't change share bot's nick.\n");
1007     else if ((atr2 & USER_OWNER) && !(atr & USER_OWNER) &&
1008              strcasecmp(dcc[idx].nick, hand))
1009       dprintf(idx, "You can't change a bot owner's handle.\n");
1010     else if (isowner(hand) && strcasecmp(dcc[idx].nick, hand))
1011       dprintf(idx, "You can't change a permanent bot owner's handle.\n");
1012     else if (!strcasecmp(newhand, botnetnick) && (!(atr2 & USER_BOT) ||
1013              nextbot(hand) != -1))
1014       dprintf(idx, "Hey! That's MY name!\n");
1015     else if (change_handle(u2, newhand)) {
1016       putlog(LOG_CMDS, "*", "#%s# chhandle %s %s", dcc[idx].nick,
1017              hand, newhand);
1018       dprintf(idx, "Changed.\n");
1019     } else
1020       dprintf(idx, "Failed.\n");
1021   }
1022 }
1023 
cmd_handle(struct userrec * u,int idx,char * par)1024 static void cmd_handle(struct userrec *u, int idx, char *par)
1025 {
1026   char oldhandle[HANDLEN + 1], newhandle[HANDLEN + 1];
1027   int i;
1028 
1029   strlcpy(newhandle, newsplit(&par), sizeof newhandle);
1030 
1031   if (!newhandle[0]) {
1032     dprintf(idx, "Usage: handle <new-handle>\n");
1033     return;
1034   }
1035   for (i = 0; i < strlen(newhandle); i++)
1036     if (((unsigned char) newhandle[i] <= 32) || (newhandle[i] == '@'))
1037       newhandle[i] = '?';
1038   if (strchr(BADHANDCHARS, newhandle[0]) != NULL)
1039     dprintf(idx,
1040             "Bizarre quantum forces prevent handle from starting with '%c'.\n",
1041             newhandle[0]);
1042   else if (!strcasecmp(dcc[idx].nick, EGG_BG_HANDLE))
1043     dprintf(idx, "You can't change the handle of this temporary user.\n");
1044   else if (get_user_by_handle(userlist, newhandle) &&
1045            strcasecmp(dcc[idx].nick, newhandle))
1046     dprintf(idx, "Somebody is already using %s.\n", newhandle);
1047   else if (!strcasecmp(newhandle, botnetnick))
1048     dprintf(idx, "Hey!  That's MY name!\n");
1049   else {
1050     strlcpy(oldhandle, dcc[idx].nick, sizeof oldhandle);
1051     if (change_handle(u, newhandle)) {
1052       putlog(LOG_CMDS, "*", "#%s# handle %s", oldhandle, newhandle);
1053       dprintf(idx, "Okay, changed.\n");
1054     } else
1055       dprintf(idx, "Failed.\n");
1056   }
1057 }
1058 
cmd_chpass(struct userrec * u,int idx,char * par)1059 static void cmd_chpass(struct userrec *u, int idx, char *par)
1060 {
1061   char *handle, *new, *s;
1062   int atr = u ? u->flags : 0;
1063 
1064   if (!par[0])
1065     dprintf(idx, "Usage: chpass <handle> [password]\n");
1066   else {
1067     handle = newsplit(&par);
1068     u = get_user_by_handle(userlist, handle);
1069     if (!u)
1070       dprintf(idx, "No such user.\n");
1071     else if ((atr & USER_BOTMAST) && !(atr & USER_MASTER) &&
1072              !(u->flags & USER_BOT))
1073       dprintf(idx, "You can't change passwords for non-bots.\n");
1074     else if ((bot_flags(u) & BOT_SHARE) && !(atr & USER_OWNER))
1075       dprintf(idx, "You can't change a share bot's password.\n");
1076     else if ((u->flags & USER_OWNER) && !(atr & USER_OWNER) &&
1077              strcasecmp(handle, dcc[idx].nick))
1078       dprintf(idx, "You can't change a bot owner's password.\n");
1079     else if (isowner(handle) && strcasecmp(dcc[idx].nick, handle))
1080       dprintf(idx, "You can't change a permanent bot owner's password.\n");
1081     else if (!par[0]) {
1082       putlog(LOG_CMDS, "*", "#%s# chpass %s [nothing]", dcc[idx].nick, handle);
1083       set_user(&USERENTRY_PASS, u, NULL);
1084       dprintf(idx, "Removed password.\n");
1085     } else {
1086       new = newsplit(&par);
1087       if ((s = check_validpass(u, new))) {
1088         dprintf(idx, "%s\n", s);
1089         return;
1090       }
1091       putlog(LOG_CMDS, "*", "#%s# chpass %s [something]", dcc[idx].nick,
1092              handle);
1093       dprintf(idx, "Changed password.\n");
1094     }
1095   }
1096 }
1097 
1098 #ifdef TLS
cmd_fprint(struct userrec * u,int idx,char * par)1099 static void cmd_fprint(struct userrec *u, int idx, char *par)
1100 {
1101   char *new;
1102 
1103   if (!par[0]) {
1104     dprintf(idx, "Usage: fprint <newfingerprint|+>\n");
1105     return;
1106   }
1107   new = newsplit(&par);
1108   if (!strcmp(new, "+")) {
1109     if (!dcc[idx].ssl) {
1110       dprintf(idx, "You aren't connected with SSL. "
1111               "Please set your fingerprint manually.\n");
1112       return;
1113     } else if (!(new = ssl_getfp(dcc[idx].sock))) {
1114       dprintf(idx, "Can't get your current fingerprint. "
1115               "Set up your client to send a certificate!\n");
1116       return;
1117     }
1118   }
1119   if (set_user(&USERENTRY_FPRINT, u, new)) {
1120     putlog(LOG_CMDS, "*", "#%s# fprint...", dcc[idx].nick);
1121     dprintf(idx, "Changed fingerprint to '%s'.\n", new);
1122   } else
1123     dprintf(idx, "Invalid fingerprint. Must be a hexadecimal string.\n");
1124 }
1125 
cmd_chfinger(struct userrec * u,int idx,char * par)1126 static void cmd_chfinger(struct userrec *u, int idx, char *par)
1127 {
1128   char *handle, *new;
1129   int atr = u ? u->flags : 0;
1130 
1131   if (!par[0])
1132     dprintf(idx, "Usage: chfinger <handle> [fingerprint]\n");
1133   else {
1134     handle = newsplit(&par);
1135     u = get_user_by_handle(userlist, handle);
1136     if (!u)
1137       dprintf(idx, "No such user.\n");
1138     else if ((atr & USER_BOTMAST) && !(atr & USER_MASTER) &&
1139              !(u->flags & USER_BOT))
1140       dprintf(idx, "You can't change fingerprints for non-bots.\n");
1141     else if ((bot_flags(u) & BOT_SHARE) && !(atr & USER_OWNER))
1142       dprintf(idx, "You can't change a share bot's fingerprint.\n");
1143     else if ((u->flags & USER_OWNER) && !(atr & USER_OWNER) &&
1144              strcasecmp(handle, dcc[idx].nick))
1145       dprintf(idx, "You can't change a bot owner's fingerprint.\n");
1146     else if (isowner(handle) && strcasecmp(dcc[idx].nick, handle))
1147       dprintf(idx, "You can't change a permanent bot owner's fingerprint.\n");
1148     else if (!par[0]) {
1149       putlog(LOG_CMDS, "*", "#%s# chfinger %s [nothing]", dcc[idx].nick, handle);
1150       set_user(&USERENTRY_FPRINT, u, NULL);
1151       dprintf(idx, "Removed fingerprint.\n");
1152     } else {
1153       new = newsplit(&par);
1154       if (set_user(&USERENTRY_FPRINT, u, new)) {
1155         putlog(LOG_CMDS, "*", "#%s# chfinger %s %s", dcc[idx].nick,
1156                handle, new);
1157         dprintf(idx, "Changed fingerprint.\n");
1158       } else
1159         dprintf(idx, "Invalid fingerprint. Must be a hexadecimal string.\n");
1160     }
1161   }
1162 }
1163 #endif
1164 
cmd_chaddr(struct userrec * u,int idx,char * par)1165 static void cmd_chaddr(struct userrec *u, int idx, char *par)
1166 {
1167 #ifdef TLS
1168   int use_ssl = 0;
1169 #endif
1170   int i, found = 0, telnet_port = 3333, relay_port = 3333;
1171   char *handle, *addr, *port, *port2, *relay;
1172   struct bot_addr *bi;
1173   struct userrec *u1;
1174 
1175   handle = newsplit(&par);
1176   if (!par[0]) {
1177     dprintf(idx, "Usage: chaddr <botname> <address> "
1178             "[telnet-port[/relay-port]]>\n");
1179     return;
1180   }
1181   addr = newsplit(&par);
1182   port2 = newsplit(&par);
1183   port = strtok(port2, "/");
1184   relay = strtok(NULL, "/");
1185 
1186   if (strtok(NULL, "/")) {
1187     dprintf(idx, "You've supplied more than 2 ports, make up your mind.\n");
1188     return;
1189   }
1190 
1191   if (addr[0]) {
1192 #ifndef IPV6
1193     for (i=0; addr[i]; i++) {
1194       if (addr[i] == ':') {
1195         dprintf(idx, "Invalid IP address format (this Eggdrop "
1196           "was compiled without IPv6 support).\n");
1197         return;
1198       }
1199     }
1200 #endif
1201  /* Check if user forgot address field by checking if argument is completely
1202   * numerical, implying a port was provided as the next argument instead.
1203   */
1204     for (i=0; addr[i]; i++) {
1205       if (strchr(BADADDRCHARS, addr[i])) {
1206         dprintf(idx, "Bot address may not contain a '%c'. ", addr[i]);
1207         break;
1208       }
1209       if (!isdigit((unsigned char) addr[i])) {
1210         found=1;
1211         break;
1212       }
1213     }
1214     if (!found) {
1215       dprintf(idx, "Invalid host address.\n");
1216       dprintf(idx, "Usage: chaddr <botname> <address> "
1217               "[telnet-port[/relay-port]]>\n");
1218       return;
1219     }
1220   }
1221 
1222 #ifndef TLS
1223   if ((port && *port == '+') || (relay && relay[0] == '+')) {
1224     dprintf(idx, "Ports prefixed with '+' are not enabled "
1225       "(this Eggdrop was compiled without TLS support).\n");
1226     return;
1227   }
1228 #endif
1229   if (port && port[0]) {
1230     if (!check_int_range(port, 0, 65536)) {
1231       dprintf(idx, "Ports must be integers between 1 and 65535.\n");
1232       return;
1233     }
1234   }
1235   if (relay) {
1236     if (!check_int_range(relay, 0, 65536)) {
1237       dprintf(idx, "Ports must be integers between 1 and 65535.\n");
1238       return;
1239     }
1240   }
1241 
1242   if (strlen(addr) > UHOSTMAX)
1243     addr[UHOSTMAX] = 0;
1244   u1 = get_user_by_handle(userlist, handle);
1245   if (!u1 || !(u1->flags & USER_BOT)) {
1246     dprintf(idx, "This command is only useful for tandem bots.\n");
1247     return;
1248   }
1249   if ((bot_flags(u1) & BOT_SHARE) && (!u || !(u->flags & USER_OWNER))) {
1250     dprintf(idx, "You can't change a share bot's address.\n");
1251     return;
1252   }
1253 
1254   bi = (struct bot_addr *) get_user(&USERENTRY_BOTADDR, u1);
1255   if (bi) {
1256     telnet_port = bi->telnet_port;
1257     relay_port = bi->relay_port;
1258 #ifdef TLS
1259     use_ssl = bi->ssl;
1260 #endif
1261   }
1262 
1263 /* Trim IPv6 []s out if present */
1264   if (addr[0] == '[') {
1265     addr[strlen(addr)-1] = 0;
1266     memmove(addr, addr + 1, strlen(addr));
1267   }
1268   bi = user_malloc(sizeof(struct bot_addr));
1269   bi->address = user_malloc(strlen(addr) + 1);
1270   strcpy(bi->address, addr);
1271 
1272   if (!port) {
1273     bi->telnet_port = telnet_port;
1274     bi->relay_port = relay_port;
1275 #ifdef TLS
1276     bi->ssl = use_ssl;
1277   } else {
1278     bi->ssl = 0;
1279     if (*port == '+')
1280       bi->ssl |= TLS_BOT;
1281     bi->telnet_port = atoi(port);
1282     if (!relay) {
1283       bi->relay_port = bi->telnet_port;
1284       bi->ssl *= TLS_BOT + TLS_RELAY;
1285     } else {
1286       if (*relay == '+') {
1287         bi->ssl |= TLS_RELAY;
1288       }
1289 #else
1290   } else {
1291     bi->telnet_port = atoi(port);
1292     if (!relay) {
1293       bi->relay_port = bi->telnet_port;
1294     } else {
1295 #endif
1296      bi->relay_port = atoi(relay);
1297     }
1298   }
1299   set_user(&USERENTRY_BOTADDR, u1, bi);
1300   putlog(LOG_CMDS, "*", "#%s# chaddr %s %s%s%s%s%s", dcc[idx].nick, handle,
1301          addr, port ? " " : "", port ? port : "", relay ? "/" : "", relay ? relay : "");
1302   dprintf(idx, "Changed bot's address.\n");
1303 }
1304 
1305 static void cmd_comment(struct userrec *u, int idx, char *par)
1306 {
1307   char *handle;
1308   struct userrec *u1;
1309 
1310   handle = newsplit(&par);
1311   if (!par[0]) {
1312     dprintf(idx, "Usage: comment <handle> <newcomment>\n");
1313     return;
1314   }
1315   u1 = get_user_by_handle(userlist, handle);
1316   if (!u1) {
1317     dprintf(idx, "No such user!\n");
1318     return;
1319   }
1320   if ((u1->flags & USER_OWNER) && !(u && (u->flags & USER_OWNER)) &&
1321       strcasecmp(handle, dcc[idx].nick)) {
1322     dprintf(idx, "You can't change comment on a bot owner.\n");
1323     return;
1324   }
1325   putlog(LOG_CMDS, "*", "#%s# comment %s %s", dcc[idx].nick, handle, par);
1326   if (!strcasecmp(par, "none")) {
1327     dprintf(idx, "Okay, comment blanked.\n");
1328     set_user(&USERENTRY_COMMENT, u1, NULL);
1329     return;
1330   }
1331   dprintf(idx, "Changed comment.\n");
1332   set_user(&USERENTRY_COMMENT, u1, par);
1333 }
1334 
1335 static void cmd_restart(struct userrec *u, int idx, char *par)
1336 {
1337   putlog(LOG_CMDS, "*", "#%s# restart", dcc[idx].nick);
1338   if (!backgrd) {
1339     dprintf(idx, "You cannot .restart a bot when running -n/-t (due to Tcl).\n");
1340     return;
1341   }
1342   dprintf(idx, "Restarting.\n");
1343   if (make_userfile) {
1344     putlog(LOG_MISC, "*", "Uh, guess you don't need to create a new userfile.");
1345     make_userfile = 0;
1346   }
1347   write_userfile(-1);
1348   putlog(LOG_MISC, "*", "Restarting ...");
1349   wipe_timers(interp, &utimer);
1350   wipe_timers(interp, &timer);
1351   do_restart = idx;
1352 }
1353 
1354 static void cmd_rehash(struct userrec *u, int idx, char *par)
1355 {
1356   putlog(LOG_CMDS, "*", "#%s# rehash", dcc[idx].nick);
1357   dprintf(idx, "Rehashing.\n");
1358   if (make_userfile) {
1359     putlog(LOG_MISC, "*", "Uh, guess you don't need to create a new userfile.");
1360     make_userfile = 0;
1361   }
1362   write_userfile(-1);
1363   putlog(LOG_MISC, "*", "Rehashing ...");
1364   do_restart = -2;
1365 }
1366 
1367 static void cmd_reload(struct userrec *u, int idx, char *par)
1368 {
1369   putlog(LOG_CMDS, "*", "#%s# reload", dcc[idx].nick);
1370   dprintf(idx, "Reloading user file...\n");
1371   reload();
1372 }
1373 
1374 void cmd_die(struct userrec *u, int idx, char *par)
1375 {
1376   char s1[1024], s2[1024];
1377 
1378   putlog(LOG_CMDS, "*", "#%s# die %s", dcc[idx].nick, par);
1379   if (par[0]) {
1380     egg_snprintf(s1, sizeof s1, "BOT SHUTDOWN (%s: %s)", dcc[idx].nick, par);
1381     egg_snprintf(s2, sizeof s2, "DIE BY %s!%s (%s)", dcc[idx].nick,
1382                  dcc[idx].host, par);
1383     strlcpy(quit_msg, par, 1024);
1384   } else {
1385     egg_snprintf(s1, sizeof s1, "BOT SHUTDOWN (Authorized by %s)",
1386                  dcc[idx].nick);
1387     egg_snprintf(s2, sizeof s2, "DIE BY %s!%s (request)", dcc[idx].nick,
1388                  dcc[idx].host);
1389     strlcpy(quit_msg, dcc[idx].nick, 1024);
1390   }
1391   kill_bot(s1, s2);
1392 }
1393 
1394 static void cmd_debug(struct userrec *u, int idx, char *par)
1395 {
1396   if (!strcasecmp(par, "help")) {
1397     putlog(LOG_CMDS, "*", "#%s# debug help", dcc[idx].nick);
1398     debug_help(idx);
1399   } else {
1400     putlog(LOG_CMDS, "*", "#%s# debug", dcc[idx].nick);
1401     debug_mem_to_dcc(idx);
1402   }
1403 }
1404 
1405 static void cmd_simul(struct userrec *u, int idx, char *par)
1406 {
1407   char *nick;
1408   int i, ok = 0;
1409 
1410   nick = newsplit(&par);
1411   if (!par[0]) {
1412     dprintf(idx, "Usage: simul <hand> <text>\n");
1413     return;
1414   }
1415   if (isowner(nick)) {
1416     dprintf(idx, "Unable to '.simul' permanent owners.\n");
1417     return;
1418   }
1419   for (i = 0; i < dcc_total; i++)
1420     if (!strcasecmp(nick, dcc[i].nick) && !ok &&
1421         (dcc[i].type->flags & DCT_SIMUL)) {
1422       putlog(LOG_CMDS, "*", "#%s# simul %s %s", dcc[idx].nick, nick, par);
1423       if (dcc[i].type && dcc[i].type->activity) {
1424         dcc[i].type->activity(i, par, strlen(par));
1425         ok = 1;
1426       }
1427     }
1428   if (!ok)
1429     dprintf(idx, "No such user on the party line.\n");
1430 }
1431 
1432 static void cmd_link(struct userrec *u, int idx, char *par)
1433 {
1434   char *s;
1435   int i;
1436 
1437   if (!par[0]) {
1438     dprintf(idx, "Usage: link [some-bot] <new-bot>\n");
1439     return;
1440   }
1441   putlog(LOG_CMDS, "*", "#%s# link %s", dcc[idx].nick, par);
1442   s = newsplit(&par);
1443   if (!par[0] || !strcasecmp(par, botnetnick))
1444     botlink(dcc[idx].nick, idx, s);
1445   else {
1446     char x[40];
1447 
1448     i = nextbot(s);
1449     if (i < 0) {
1450       dprintf(idx, "No such bot online.\n");
1451       return;
1452     }
1453     simple_sprintf(x, "%d:%s@%s", dcc[idx].sock, dcc[idx].nick, botnetnick);
1454     botnet_send_link(i, x, s, par);
1455   }
1456 }
1457 
1458 static void cmd_unlink(struct userrec *u, int idx, char *par)
1459 {
1460   int i;
1461   char *bot;
1462 
1463   if (!par[0]) {
1464     dprintf(idx, "Usage: unlink <bot> [reason]\n");
1465     return;
1466   }
1467   putlog(LOG_CMDS, "*", "#%s# unlink %s", dcc[idx].nick, par);
1468   bot = newsplit(&par);
1469   i = nextbot(bot);
1470   if (i < 0) {
1471     botunlink(idx, bot, par, dcc[idx].nick);
1472     return;
1473   }
1474   /* If we're directly connected to that bot, just do it
1475    * (is nike gunna sue?)
1476    */
1477   if (!strcasecmp(dcc[i].nick, bot))
1478     botunlink(idx, bot, par, dcc[i].nick);
1479   else {
1480     char x[40];
1481 
1482     simple_sprintf(x, "%d:%s@%s", dcc[idx].sock, dcc[idx].nick, botnetnick);
1483     botnet_send_unlink(i, x, lastbot(bot), bot, par);
1484   }
1485 }
1486 
1487 static void cmd_relay(struct userrec *u, int idx, char *par)
1488 {
1489   if (!par[0]) {
1490     dprintf(idx, "Usage: relay <bot>\n");
1491     return;
1492   }
1493   putlog(LOG_CMDS, "*", "#%s# relay %s", dcc[idx].nick, par);
1494   tandem_relay(idx, par, 0);
1495 }
1496 
1497 static void cmd_save(struct userrec *u, int idx, char *par)
1498 {
1499   putlog(LOG_CMDS, "*", "#%s# save", dcc[idx].nick);
1500   dprintf(idx, "Saving user file...\n");
1501   write_userfile(-1);
1502 }
1503 
1504 static void cmd_backup(struct userrec *u, int idx, char *par)
1505 {
1506   putlog(LOG_CMDS, "*", "#%s# backup", dcc[idx].nick);
1507   dprintf(idx, "Backing up the channel & user files...\n");
1508   call_hook(HOOK_BACKUP);
1509 }
1510 
1511 static void cmd_trace(struct userrec *u, int idx, char *par)
1512 {
1513   int i;
1514   char x[NOTENAMELEN + 11], y[22];
1515 
1516   if (!par[0]) {
1517     dprintf(idx, "Usage: trace <botname>\n");
1518     return;
1519   }
1520   if (!strcasecmp(par, botnetnick)) {
1521     dprintf(idx, "That's me!  Hiya! :)\n");
1522     return;
1523   }
1524   i = nextbot(par);
1525   if (i < 0) {
1526     dprintf(idx, "Unreachable bot.\n");
1527     return;
1528   }
1529   putlog(LOG_CMDS, "*", "#%s# trace %s", dcc[idx].nick, par);
1530   simple_sprintf(x, "%d:%s@%s", dcc[idx].sock, dcc[idx].nick, botnetnick);
1531   snprintf(y, sizeof y, ":%" PRId64, (int64_t) now);
1532   botnet_send_trace(i, x, par, y);
1533 }
1534 
1535 static void cmd_binds(struct userrec *u, int idx, char *par)
1536 {
1537   putlog(LOG_CMDS, "*", "#%s# binds %s", dcc[idx].nick, par);
1538   tell_binds(idx, par);
1539 }
1540 
1541 static void cmd_banner(struct userrec *u, int idx, char *par)
1542 {
1543   char s[1024];
1544   int i;
1545 
1546   if (!par[0]) {
1547     dprintf(idx, "Usage: banner <message>\n");
1548     return;
1549   }
1550   simple_sprintf(s, "\007### Botwide: [%s] %s\n", dcc[idx].nick, par);
1551   for (i = 0; i < dcc_total; i++)
1552     if (dcc[i].type->flags & DCT_MASTER)
1553       dprintf(i, "%s", s);
1554 }
1555 
1556 /* After messing with someone's user flags, make sure the dcc-chat flags
1557  * are set correctly.
1558  */
1559 int check_dcc_attrs(struct userrec *u, int oatr)
1560 {
1561   int i, stat;
1562   struct flag_record fr = { FR_GLOBAL | FR_CHAN, 0, 0, 0, 0, 0 };
1563 
1564   if (!u)
1565     return 0;
1566   /* Make sure default owners are +n */
1567   if (isowner(u->handle)) {
1568     u->flags = sanity_check(u->flags | USER_OWNER);
1569   }
1570   for (i = 0; i < dcc_total; i++) {
1571     if ((dcc[i].type->flags & DCT_MASTER) &&
1572         (!strcasecmp(u->handle, dcc[i].nick))) {
1573       stat = dcc[i].status;
1574       if ((dcc[i].type == &DCC_CHAT) &&
1575           ((u->flags & (USER_OP | USER_MASTER | USER_OWNER | USER_BOTMAST)) !=
1576           (oatr & (USER_OP | USER_MASTER | USER_OWNER | USER_BOTMAST)))) {
1577         botnet_send_join_idx(i, -1);
1578       }
1579       if ((oatr & USER_MASTER) && !(u->flags & USER_MASTER)) {
1580         dprintf(i, "*** POOF! ***\n");
1581         dprintf(i, "You are no longer a master on this bot.\n");
1582       }
1583       if (!(oatr & USER_MASTER) && (u->flags & USER_MASTER)) {
1584         dcc[i].u.chat->con_flags |= conmask;
1585         dprintf(i, "*** POOF! ***\n");
1586         dprintf(i, "You are now a master on this bot.\n");
1587       }
1588       if (!(oatr & USER_BOTMAST) && (u->flags & USER_BOTMAST)) {
1589         dprintf(i, "### POOF! ###\n");
1590         dprintf(i, "You are now a botnet master on this bot.\n");
1591       }
1592       if ((oatr & USER_BOTMAST) && !(u->flags & USER_BOTMAST)) {
1593         dprintf(i, "### POOF! ###\n");
1594         dprintf(i, "You are no longer a botnet master on this bot.\n");
1595       }
1596       if (!(oatr & USER_OWNER) && (u->flags & USER_OWNER)) {
1597         dprintf(i, "@@@ POOF! @@@\n");
1598         dprintf(i, "You are now an OWNER of this bot.\n");
1599       }
1600       if ((oatr & USER_OWNER) && !(u->flags & USER_OWNER)) {
1601         dprintf(i, "@@@ POOF! @@@\n");
1602         dprintf(i, "You are no longer an owner of this bot.\n");
1603       }
1604       get_user_flagrec(u, &fr, dcc[i].u.chat->con_chan);
1605       dcc[i].u.chat->con_flags = check_conflags(&fr,
1606                                                 dcc[i].u.chat->con_flags);
1607       if ((stat & STAT_PARTY) && (u->flags & USER_OP))
1608         stat &= ~STAT_PARTY;
1609       if (!(stat & STAT_PARTY) && !(u->flags & USER_OP) &&
1610           !(u->flags & USER_MASTER))
1611         stat |= STAT_PARTY;
1612       if ((stat & STAT_CHAT) && !(u->flags & USER_PARTY) &&
1613           !(u->flags & USER_MASTER) && (!(u->flags & USER_OP) || require_p))
1614         stat &= ~STAT_CHAT;
1615       if ((dcc[i].type->flags & DCT_FILES) && !(stat & STAT_CHAT) &&
1616           ((u->flags & USER_MASTER) || (u->flags & USER_PARTY) ||
1617           ((u->flags & USER_OP) && !require_p)))
1618         stat |= STAT_CHAT;
1619       dcc[i].status = stat;
1620       /* Check if they no longer have access to wherever they are.
1621        *
1622        * NOTE: DON'T kick someone off the party line just cuz they lost +p
1623        *       (pinvite script removes +p after 5 mins automatically)
1624        */
1625       if ((dcc[i].type->flags & DCT_FILES) && !(u->flags & USER_XFER) &&
1626           !(u->flags & USER_MASTER)) {
1627         dprintf(i, "-+- POOF! -+-\n");
1628         dprintf(i, "You no longer have file area access.\n\n");
1629         putlog(LOG_MISC, "*", "DCC user [%s]%s removed from file system",
1630                dcc[i].nick, dcc[i].host);
1631         if (dcc[i].status & STAT_CHAT) {
1632           struct chat_info *ci;
1633 
1634           ci = dcc[i].u.file->chat;
1635           nfree(dcc[i].u.file);
1636           dcc[i].u.chat = ci;
1637           dcc[i].status &= (~STAT_CHAT);
1638           dcc[i].type = &DCC_CHAT;
1639           if (dcc[i].u.chat->channel >= 0) {
1640             chanout_but(-1, dcc[i].u.chat->channel, DCC_RETURN, dcc[i].nick);
1641             if (dcc[i].u.chat->channel < GLOBAL_CHANS)
1642               botnet_send_join_idx(i, -1);
1643           }
1644         } else {
1645           killsock(dcc[i].sock);
1646           lostdcc(i);
1647         }
1648       }
1649     }
1650 
1651     if (dcc[i].type == &DCC_BOT && !strcasecmp(u->handle, dcc[i].nick)) {
1652       if ((dcc[i].status & STAT_LEAF) && !(bot_flags(u) & BOT_LEAF))
1653         dcc[i].status &= ~(STAT_LEAF | STAT_WARNED);
1654       if (!(dcc[i].status & STAT_LEAF) && (bot_flags(u) & BOT_LEAF))
1655         dcc[i].status |= STAT_LEAF;
1656     }
1657   }
1658 
1659   return u->flags;
1660 }
1661 
1662 int check_dcc_chanattrs(struct userrec *u, char *chname, int chflags,
1663                         int ochatr)
1664 {
1665   int i, found = 0;
1666   struct flag_record fr = { FR_CHAN, 0, 0, 0, 0, 0 };
1667   struct chanset_t *chan;
1668 
1669   if (!u)
1670     return 0;
1671   for (i = 0; i < dcc_total; i++) {
1672     if ((dcc[i].type->flags & DCT_MASTER) &&
1673         !strcasecmp(u->handle, dcc[i].nick)) {
1674       if ((dcc[i].type == &DCC_CHAT) &&
1675           ((chflags & (USER_OP | USER_MASTER | USER_OWNER)) !=
1676           (ochatr & (USER_OP | USER_MASTER | USER_OWNER))))
1677         botnet_send_join_idx(i, -1);
1678       if ((ochatr & USER_MASTER) && !(chflags & USER_MASTER)) {
1679         dprintf(i, "*** POOF! ***\n");
1680         dprintf(i, "You are no longer a master on %s.\n", chname);
1681       }
1682       if (!(ochatr & USER_MASTER) && (chflags & USER_MASTER)) {
1683         dcc[i].u.chat->con_flags |= conmask;
1684         dprintf(i, "*** POOF! ***\n");
1685         dprintf(i, "You are now a master on %s.\n", chname);
1686       }
1687       if (!(ochatr & USER_OWNER) && (chflags & USER_OWNER)) {
1688         dprintf(i, "@@@ POOF! @@@\n");
1689         dprintf(i, "You are now an OWNER of %s.\n", chname);
1690       }
1691       if ((ochatr & USER_OWNER) && !(chflags & USER_OWNER)) {
1692         dprintf(i, "@@@ POOF! @@@\n");
1693         dprintf(i, "You are no longer an owner of %s.\n", chname);
1694       }
1695       if (((ochatr & (USER_OP | USER_MASTER | USER_OWNER)) &&
1696           (!(chflags & (USER_OP | USER_MASTER | USER_OWNER)))) ||
1697           ((chflags & (USER_OP | USER_MASTER | USER_OWNER)) &&
1698           (!(ochatr & (USER_OP | USER_MASTER | USER_OWNER))))) {
1699 
1700         for (chan = chanset; chan && !found; chan = chan->next) {
1701           get_user_flagrec(u, &fr, chan->dname);
1702           if (fr.chan & (USER_OP | USER_MASTER | USER_OWNER))
1703             found = 1;
1704         }
1705         if (!chan)
1706           chan = chanset;
1707         if (chan)
1708           strcpy(dcc[i].u.chat->con_chan, chan->dname);
1709         else
1710           strcpy(dcc[i].u.chat->con_chan, "*");
1711       }
1712       fr.match = (FR_CHAN | FR_GLOBAL);
1713       get_user_flagrec(u, &fr, dcc[i].u.chat->con_chan);
1714       dcc[i].u.chat->con_flags = check_conflags(&fr,
1715                                                 dcc[i].u.chat->con_flags);
1716     }
1717   }
1718   return chflags;
1719 }
1720 
1721 /* helper function to inform the user of conflicts with botattr */
1722 static void bot_attr_inform(const int idx, const int msgids)
1723 {
1724   if (msgids & BOT_SANE_ALTOWNSHUB)
1725     dprintf(idx, "INFO: adding +a removes the existing +h flag.\n");
1726   if (msgids & BOT_SANE_HUBOWNSALT)
1727     dprintf(idx, "INFO: adding +h removes the existing +a flag.\n");
1728   if (msgids & BOT_SANE_OWNSALTHUB)
1729     dprintf(idx, "INFO: adding +ah is not possible, please choose only one.\n");
1730   if (msgids & BOT_SANE_SHPOWNSAGGR)
1731     dprintf(idx, "INFO: adding any of the +(bcejnud) flags removes the existing"
1732         " +s flag.\n");
1733   if (msgids & BOT_SANE_AGGROWNSSHP)
1734     dprintf(idx, "INFO: adding +s removes any existing +(bcejnud) flags.\n");
1735   if (msgids & BOT_SANE_OWNSSHPAGGR)
1736     dprintf(idx, "INFO: adding +s with any of the +(bcejnud) flags is not"
1737         " possible, please choose only one.\n");
1738   if (msgids & BOT_SANE_SHPOWNSPASS)
1739     dprintf(idx, "INFO: adding any of the +(bcejnud) flags removes the existing"
1740          " +p flag.\n");
1741   if (msgids & BOT_SANE_PASSOWNSSHP)
1742     dprintf(idx, "INFO: adding +p removes any existing +(bcejnud) flags.\n");
1743   if (msgids & BOT_SANE_OWNSSHPPASS)
1744     dprintf(idx, "INFO: adding +p with any of the +(bcejnud) flags is not"
1745         " possible, please choose only one.\n");
1746   if (msgids & BOT_SANE_SHAREOWNSREJ)
1747     dprintf(idx, "INFO: adding any of the +(bcejnudps) flags removes the"
1748         " existing +r flag.\n");
1749   if (msgids & BOT_SANE_REJOWNSSHARE)
1750     dprintf(idx, "INFO: adding +r removes any existing +(bcejnudps) flags.\n");
1751   if (msgids & BOT_SANE_OWNSSHAREREJ)
1752     dprintf(idx, "INFO: adding +r with any of the +(bcejnudps) flags is not"
1753         " possible, please choose only one.\n");
1754   if (msgids & BOT_SANE_HUBOWNSREJ)
1755     dprintf(idx, "INFO: adding +h removes the existing +r flag.\n");
1756   if (msgids & BOT_SANE_REJOWNSHUB)
1757     dprintf(idx, "INFO: adding +r removes the existing +h flag.\n");
1758   if (msgids & BOT_SANE_OWNSHUBREJ)
1759     dprintf(idx, "INFO: adding +hr is not possible, please choose only one of"
1760         " them.\n");
1761   if (msgids & BOT_SANE_ALTOWNSREJ)
1762     dprintf(idx, "INFO: adding +a removes the existing +r flag.\n");
1763   if (msgids & BOT_SANE_REJOWNSALT)
1764     dprintf(idx, "INFO: adding +r removes the existing +a flag.\n");
1765   if (msgids & BOT_SANE_OWNSALTREJ)
1766     dprintf(idx, "INFO: adding +ar is not possible, please choose only one of"
1767         " them.\n");
1768   if (msgids & BOT_SANE_AGGROWNSPASS)
1769     dprintf(idx, "INFO: adding +s removes the existing +p flag.\n");
1770   if (msgids & BOT_SANE_PASSOWNSAGGR)
1771     dprintf(idx, "INFO: adding +p removes the existing +s flag.\n");
1772   if (msgids & BOT_SANE_OWNSAGGRPASS)
1773     dprintf(idx, "INFO: adding +ps is not possible, please choose only one of"
1774         " them.\n");
1775   if (msgids & BOT_SANE_NOSHAREOWNSGLOB)
1776     dprintf(idx, "INFO: removing the -(bcejnudps) flags will also remove the"
1777         " current +g flag.\n");
1778   if (msgids & BOT_SANE_OWNSGLOB)
1779     dprintf(idx, "INFO: adding +g is only possible with one of the"
1780         " +(bcejnudps) flags.\n");
1781 }
1782 
1783 /* helper function to inform the user of conflicts with chattr */
1784 static void uc_attr_inform(const int idx, const int msgids)
1785 {
1786   if (msgids & UC_SANE_DEOPOWNSOP)
1787     dprintf(idx, "INFO: adding +d removes the existing +o flag.\n");
1788   if (msgids & UC_SANE_OPOWNSDEOP)
1789     dprintf(idx, "INFO: adding +o removes the existing +d flag.\n");
1790   if (msgids & UC_SANE_OWNSDEOPOP)
1791     dprintf(idx, "INFO: adding +do is not possible, please choose only one of"
1792         " them.\n");
1793   if (msgids & UC_SANE_DEHALFOPOWNSHALFOP)
1794     dprintf(idx, "INFO: adding +r removes the existing +l flag.\n");
1795   if (msgids & UC_SANE_HALFOPOWNSDEHALFOP)
1796     dprintf(idx, "INFO: adding +l removes the existing +r flag.\n");
1797   if (msgids & UC_SANE_OWNSDEHALFOPHALFOP)
1798     dprintf(idx, "INFO: adding +rl is not possible, please choose only one of"
1799         " them.\n");
1800   if (msgids & UC_SANE_DEOPOWNSAUTOOP)
1801     dprintf(idx, "INFO: adding +d removes the existing +a flag.\n");
1802   if (msgids & UC_SANE_AUTOOPOWNSDEOP)
1803     dprintf(idx, "INFO: adding +a removes the existing +d flag.\n");
1804   if (msgids & UC_SANE_OWNSDEOPAUTOOP)
1805     dprintf(idx, "INFO: adding +da is not possible, please choose only one of"
1806         " them.\n");
1807   if (msgids & UC_SANE_DEHALFOPOWNSAHALFOP)
1808     dprintf(idx, "INFO: adding +r removes the existing +y flag.\n");
1809   if (msgids & UC_SANE_AHALFOPOWNSDEHALFOP)
1810     dprintf(idx, "INFO: adding +y removes the existing +r flag.\n");
1811   if (msgids & UC_SANE_OWNSDEHALFOPAHALFOP)
1812     dprintf(idx, "INFO: adding +ry is not possible, please choose only one of"
1813         " them.\n");
1814   if (msgids & UC_SANE_QUIETOWNSVOICE)
1815     dprintf(idx, "INFO: adding +q removes the existing +v flag.\n");
1816   if (msgids & UC_SANE_VOICEOWNSQUIET)
1817     dprintf(idx, "INFO: adding +v removes the existing +q flag.\n");
1818   if (msgids & UC_SANE_OWNSQUIETVOICE)
1819     dprintf(idx, "INFO: adding +qv is not possible, please choose only one of"
1820         " them.\n");
1821   if (msgids & UC_SANE_QUIETOWNSGVOICE)
1822     dprintf(idx, "INFO: adding +q removes the existing +g flag.\n");
1823   if (msgids & UC_SANE_GVOICEOWNSQUIET)
1824     dprintf(idx, "INFO: adding +g removes the existing +q flag.\n");
1825   if (msgids & UC_SANE_OWNSQUIETGVOICE)
1826     dprintf(idx, "INFO: adding +qg is not possible, please choose only one of"
1827         " them.\n");
1828   if (msgids & UC_SANE_OWNERADDSMASTER)
1829     dprintf(idx, "INFO: adding +n implies adding the +m flag.\n");
1830   if (msgids & UC_SANE_MASTERADDSOP)
1831     dprintf(idx, "INFO: adding +m implies adding the +o flag.\n");
1832   if (msgids & UC_SANE_MASTERADDSBOTMOPJAN)
1833     dprintf(idx, "INFO: adding +m implies adding the +toj flags.\n");
1834   if (msgids & UC_SANE_BOTMASTADDSPARTY)
1835     dprintf(idx, "INFO: adding +t implies adding the +p flag.\n");
1836   if (msgids & UC_SANE_JANADDSXFER)
1837     dprintf(idx, "INFO: adding +j implies adding the +x flag.\n");
1838   if (msgids & UC_SANE_OPADDSHALFOP)
1839     dprintf(idx, "INFO: adding +o implies adding the +l flag.\n");
1840   if (msgids & UC_SANE_NOBOTOWNSAGGR)
1841     dprintf(idx, "INFO: the +s flag can only be added to bots.\n");
1842   if (msgids & UC_SANE_BOTOWNSPARTY)
1843     dprintf(idx, "INFO: a bot can't have the +p flag.\n");
1844   if (msgids & UC_SANE_BOTOWNSMASTER)
1845     dprintf(idx, "INFO: a bot can't have the +m flag.\n");
1846   if (msgids & UC_SANE_BOTOWNSCOMMON)
1847     dprintf(idx, "INFO: a bot can't have the +c flag.\n");
1848   if (msgids & UC_SANE_BOTOWNSOWNER)
1849     dprintf(idx, "INFO: a bot can't have the +n flag.\n");
1850   if (msgids & UC_SANE_AUTOOPADDSOP)
1851     dprintf(idx, "INFO: adding +a also adds +o for your convenience, if unwanted one can revert with -o.\n");
1852   if (msgids & UC_SANE_AUTOHALFOPADDSHALFOP)
1853     dprintf(idx, "INFO: adding +y also adds +l for your convenience, if unwanted one can revert with -l.\n");
1854   if (msgids & UC_SANE_GVOICEADDSVOICE)
1855     dprintf(idx, "INFO: adding +g also adds +v for your convenience, if unwanted one can revert with -v.\n");
1856 }
1857 
1858 static void cmd_chattr(struct userrec *u, int idx, char *par)
1859 {
1860   char *hand, *arg = NULL, *tmpchg = NULL, *chg = NULL, work[1024];
1861   struct chanset_t *chan = NULL;
1862   struct userrec *u2;
1863   struct flag_record pls = { 0, 0, 0, 0, 0, 0 },
1864                      mns = { 0, 0, 0, 0, 0, 0 },
1865                      user = { 0, 0, 0, 0, 0, 0 };
1866   module_entry *me;
1867   int fl = -1, of = 0, ocf = 0, msgidsu = 0, msgidsc = 0;
1868 
1869   if (!par[0]) {
1870     dprintf(idx, "Usage: chattr <handle> [changes] [channel]\n");
1871     return;
1872   }
1873   hand = newsplit(&par);
1874   u2 = get_user_by_handle(userlist, hand);
1875   if (!u2) {
1876     dprintf(idx, "No such user!\n");
1877     return;
1878   }
1879 
1880   /* Parse args */
1881   if (par[0]) {
1882     arg = newsplit(&par);
1883     if (par[0]) {
1884       /* .chattr <handle> <changes> <channel> */
1885       chg = arg;
1886       arg = newsplit(&par);
1887       chan = findchan_by_dname(arg);
1888     } else {
1889       chan = findchan_by_dname(arg);
1890       /* Consider modeless channels, starting with '+' */
1891       if (!(arg[0] == '+' && chan) &&
1892           !(arg[0] != '+' && strchr(CHANMETA, arg[0]))) {
1893         /* .chattr <handle> <changes> */
1894         chg = arg;
1895         chan = NULL; /* uh, !strchr (CHANMETA, channel[0]) && channel found?? */
1896         arg = NULL;
1897       }
1898       /* .chattr <handle> <channel>: nothing to do... */
1899     }
1900   }
1901   /* arg:  pointer to channel name, NULL if none specified
1902    * chan: pointer to channel structure, NULL if none found or none specified
1903    * chg:  pointer to changes, NULL if none specified
1904    */
1905   Assert(!(!arg && chan));
1906   if (arg && !chan) {
1907     dprintf(idx, "No channel record for %s.\n", arg);
1908     return;
1909   }
1910   if (chg) {
1911     if (!arg && strpbrk(chg, "&|")) {
1912       /* .chattr <handle> *[&|]*: use console channel if found... */
1913       if (!strcmp((arg = dcc[idx].u.chat->con_chan), "*"))
1914         arg = NULL;
1915       else
1916         chan = findchan_by_dname(arg);
1917       if (arg && !chan) {
1918         dprintf(idx, "Invalid console channel %s.\n", arg);
1919         return;
1920       }
1921     } else if (arg && !strpbrk(chg, "&|")) {
1922       tmpchg = nmalloc(strlen(chg) + 2);
1923       strcpy(tmpchg, "|");
1924       strcat(tmpchg, chg);
1925       chg = tmpchg;
1926     }
1927   }
1928   par = arg;
1929   user.match = FR_GLOBAL;
1930   if (chan)
1931     user.match |= FR_CHAN;
1932   get_user_flagrec(u, &user, chan ? chan->dname : 0);
1933   if (!chan && !glob_botmast(user)) {
1934     dprintf(idx, "You do not have Bot Master privileges.\n");
1935     if (tmpchg)
1936       nfree(tmpchg);
1937     return;
1938   }
1939   if (chan && !glob_master(user) && !chan_master(user)) {
1940     dprintf(idx, "You do not have channel master privileges for channel %s.\n",
1941             par);
1942     if (tmpchg)
1943       nfree(tmpchg);
1944     return;
1945   }
1946   user.match &= fl;
1947   if (chg) {
1948     pls.match = user.match;
1949     break_down_flags(chg, &pls, &mns);
1950     /* No-one can change these flags on-the-fly */
1951     pls.global &=~(USER_BOT);
1952     mns.global &=~(USER_BOT);
1953 
1954     if (chan) {
1955       pls.chan &= ~(BOT_AGGRESSIVE);
1956       mns.chan &= ~(BOT_AGGRESSIVE);
1957     }
1958     if (!glob_owner(user)) {
1959       pls.global &=~(USER_OWNER | USER_MASTER | USER_BOTMAST | USER_UNSHARED);
1960       mns.global &=~(USER_OWNER | USER_MASTER | USER_BOTMAST | USER_UNSHARED);
1961 
1962       if (chan) {
1963         pls.chan &= ~USER_OWNER;
1964         mns.chan &= ~USER_OWNER;
1965       }
1966       if (!glob_master(user)) {
1967         pls.global &=USER_PARTY | USER_XFER;
1968         mns.global &=USER_PARTY | USER_XFER;
1969 
1970         if (!glob_botmast(user)) {
1971           pls.global = 0;
1972           mns.global = 0;
1973         }
1974       }
1975     }
1976     if (chan && !chan_owner(user) && !glob_owner(user)) {
1977       pls.chan &= ~USER_MASTER;
1978       mns.chan &= ~USER_MASTER;
1979       if (!chan_master(user) && !glob_master(user)) {
1980         pls.chan = 0;
1981         mns.chan = 0;
1982       }
1983     }
1984     get_user_flagrec(u2, &user, par);
1985     if (user.match & FR_GLOBAL) {
1986       of = user.global;
1987       msgidsu = user_sanity_check(&(user.global), pls.global, mns.global);
1988 
1989       user.udef_global = (user.udef_global | pls.udef_global)
1990                          & ~mns.udef_global;
1991     }
1992     if (chan) {
1993       ocf = user.chan;
1994       msgidsc = chan_sanity_check(&(user.chan), pls.chan, mns.chan, user.global);
1995 
1996       user.udef_chan = (user.udef_chan | pls.udef_chan) & ~mns.udef_chan;
1997     }
1998     set_user_flagrec(u2, &user, par);
1999   }
2000   if (chan)
2001     putlog(LOG_CMDS, "*", "#%s# (%s) chattr %s %s",
2002            dcc[idx].nick, chan->dname, hand, chg ? chg : "");
2003   else
2004     putlog(LOG_CMDS, "*", "#%s# chattr %s %s", dcc[idx].nick, hand,
2005            chg ? chg : "");
2006   /* Get current flags and display them */
2007   if (user.match & FR_GLOBAL) {
2008     user.match = FR_GLOBAL;
2009     if (chg)
2010       check_dcc_attrs(u2, of);
2011     get_user_flagrec(u2, &user, NULL);
2012     build_flags(work, &user, NULL);
2013     /* Display any remarks */
2014     if (msgidsu)
2015       uc_attr_inform(idx, msgidsu);
2016     if (work[0] != '-')
2017       dprintf(idx, "Global flags for %s are now +%s.\n", hand, work);
2018     else
2019       dprintf(idx, "No global flags for %s.\n", hand);
2020   }
2021   if (chan) {
2022     user.match = FR_CHAN;
2023     get_user_flagrec(u2, &user, par);
2024     user.chan &= ~BOT_AGGRESSIVE;
2025     if (chg)
2026       check_dcc_chanattrs(u2, chan->dname, user.chan, ocf);
2027     build_flags(work, &user, NULL);
2028     /* Display any remarks */
2029     if (msgidsc)
2030       uc_attr_inform(idx, msgidsc);
2031     if (work[0] != '-')
2032       dprintf(idx, "Channel flags for %s on %s are now +%s.\n", hand,
2033               chan->dname, work);
2034     else
2035       dprintf(idx, "No flags for %s on %s.\n", hand, chan->dname);
2036   }
2037   if (chg && (me = module_find("irc", 0, 0))) {
2038     Function *func = me->funcs;
2039     (func[IRC_CHECK_THIS_USER]) (hand, 0, NULL);
2040   }
2041   if (tmpchg)
2042     nfree(tmpchg);
2043 }
2044 
2045 static void cmd_botattr(struct userrec *u, int idx, char *par)
2046 {
2047   char *hand, *chg = NULL, *arg = NULL, *tmpchg = NULL, work[1024];
2048   int msgids = 0;
2049   struct chanset_t *chan = NULL;
2050   struct userrec *u2;
2051   struct flag_record  pls = { 0, 0, 0, 0, 0, 0 },
2052                       mns = { 0, 0, 0, 0, 0, 0 },
2053                       user = { 0, 0, 0, 0, 0, 0 };
2054   int idx2;
2055 
2056   if (!par[0]) {
2057     dprintf(idx, "Usage: botattr <handle> [changes] [channel]\n");
2058     return;
2059   }
2060   hand = newsplit(&par);
2061   u2 = get_user_by_handle(userlist, hand);
2062   if (!u2 || !(u2->flags & USER_BOT)) {
2063     dprintf(idx, "No such bot!\n");
2064     return;
2065   }
2066   for (idx2 = 0; idx2 < dcc_total; idx2++)
2067     if (dcc[idx2].type != &DCC_RELAY && dcc[idx2].type != &DCC_FORK_BOT &&
2068         !strcasecmp(dcc[idx2].nick, hand))
2069       break;
2070   if (idx2 != dcc_total) {
2071     dprintf(idx,
2072             "You may not change the attributes of a directly linked bot.\n");
2073     return;
2074   }
2075   /* Parse args */
2076   if (par[0]) {
2077     arg = newsplit(&par);
2078     if (par[0]) {
2079       /* .botattr <handle> <changes> <channel> */
2080       chg = arg;
2081       arg = newsplit(&par);
2082       chan = findchan_by_dname(arg);
2083     } else {
2084       chan = findchan_by_dname(arg);
2085       /* Consider modeless channels, starting with '+' */
2086       if (!(arg[0] == '+' && chan) &&
2087           !(arg[0] != '+' && strchr(CHANMETA, arg[0]))) {
2088         /* .botattr <handle> <changes> */
2089         chg = arg;
2090         chan = NULL; /* uh, !strchr (CHANMETA, channel[0]) && channel found?? */
2091         arg = NULL;
2092       }
2093       /* .botattr <handle> <channel>: nothing to do... */
2094     }
2095   }
2096   /* arg:  pointer to channel name, NULL if none specified
2097    * chan: pointer to channel structure, NULL if none found or none specified
2098    * chg:  pointer to changes, NULL if none specified
2099    */
2100   Assert(!(!arg && chan));
2101   if (arg && !chan) {
2102     dprintf(idx, "No channel record for %s.\n", arg);
2103     return;
2104   }
2105   if (chg) {
2106     if (!arg && strpbrk(chg, "&|")) {
2107       /* botattr <handle> *[&|]*: use console channel if found... */
2108       if (!strcmp((arg = dcc[idx].u.chat->con_chan), "*"))
2109         arg = NULL;
2110       else
2111         chan = findchan_by_dname(arg);
2112       if (arg && !chan) {
2113         dprintf(idx, "Invalid console channel %s.\n", arg);
2114         return;
2115       }
2116     } else if (arg && !strpbrk(chg, "&|")) {
2117       tmpchg = nmalloc(strlen(chg) + 2);
2118       strcpy(tmpchg, "|");
2119       strcat(tmpchg, chg);
2120       chg = tmpchg;
2121     }
2122   }
2123   par = arg;
2124 
2125   user.match = FR_GLOBAL;
2126   get_user_flagrec(u, &user, chan ? chan->dname : 0);
2127   if (!glob_botmast(user)) {
2128     dprintf(idx, "You do not have Bot Master privileges.\n");
2129     if (tmpchg)
2130       nfree(tmpchg);
2131     return;
2132   }
2133   if (chg) {
2134     user.match = FR_BOT | (chan ? FR_CHAN : 0);
2135     pls.match = user.match;
2136     break_down_flags(chg, &pls, &mns);
2137     /* No-one can change these flags on-the-fly */
2138     if (chan && glob_owner(user)) {
2139       pls.chan &= BOT_AGGRESSIVE;
2140       mns.chan &= BOT_AGGRESSIVE;
2141     } else {
2142       pls.chan = 0;
2143       mns.chan = 0;
2144     }
2145     if (!glob_owner(user)) {
2146       pls.bot &= ~(BOT_SHARE | BOT_GLOBAL);
2147       mns.bot &= ~(BOT_SHARE | BOT_GLOBAL);
2148     }
2149     user.match = FR_BOT | (chan ? FR_CHAN : 0);
2150     get_user_flagrec(u2, &user, par);
2151     msgids = bot_sanity_check(&(user.bot), pls.bot, mns.bot);
2152     if (chan)
2153       user.chan = (user.chan | pls.chan) & ~mns.chan;
2154     set_user_flagrec(u2, &user, par);
2155   }
2156   if (chan)
2157     putlog(LOG_CMDS, "*", "#%s# (%s) botattr %s %s",
2158            dcc[idx].nick, chan->dname, hand, chg ? chg : "");
2159   else
2160     putlog(LOG_CMDS, "*", "#%s# botattr %s %s", dcc[idx].nick, hand,
2161            chg ? chg : "");
2162   /* Display any remarks */
2163   if (msgids)
2164     bot_attr_inform(idx, msgids);
2165   /* get current flags and display them */
2166   if (!chan || pls.bot || mns.bot) {
2167     user.match = FR_BOT;
2168     get_user_flagrec(u2, &user, NULL);
2169     build_flags(work, &user, NULL);
2170     if (work[0] != '-')
2171       dprintf(idx, "Bot flags for %s are now +%s.\n", hand, work);
2172     else
2173       dprintf(idx, "There are no bot flags for %s.\n", hand);
2174   }
2175   if (chan) {
2176     user.match = FR_CHAN;
2177     get_user_flagrec(u2, &user, par);
2178     user.chan &= BOT_AGGRESSIVE;
2179     user.udef_chan = 0; /* udef chan flags are user only */
2180     build_flags(work, &user, NULL);
2181     if (work[0] != '-')
2182       dprintf(idx, "Bot flags for %s on %s are now +%s.\n", hand,
2183               chan->dname, work);
2184     else
2185       dprintf(idx, "There are no bot flags for %s on %s.\n", hand, chan->dname);
2186   }
2187   if (tmpchg)
2188     nfree(tmpchg);
2189 }
2190 
2191 static void cmd_chat(struct userrec *u, int idx, char *par)
2192 {
2193   char *arg;
2194   int localchan = 0;
2195   int newchan = 0;
2196   int oldchan;
2197   module_entry *me;
2198 
2199   arg = newsplit(&par);
2200   if (!strcasecmp(arg, "off")) {
2201     /* Turn chat off */
2202     if (dcc[idx].u.chat->channel < 0) {
2203       dprintf(idx, "You weren't in chat anyway!\n");
2204       return;
2205     } else {
2206       dprintf(idx, "Leaving chat mode...\n");
2207       check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock,
2208                      dcc[idx].u.chat->channel);
2209       chanout_but(-1, dcc[idx].u.chat->channel,
2210                   "*** %s left the party line.\n", dcc[idx].nick);
2211       if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
2212         botnet_send_part_idx(idx, "");
2213     }
2214     dcc[idx].u.chat->channel = -1;
2215   } else {
2216     if (arg[0] == '*') {
2217       if (((arg[1] < '0') || (arg[1] > '9'))) {
2218         if (!arg[1])
2219           newchan = 0;
2220         else {
2221           Tcl_SetVar(interp, "_chan", arg, 0);
2222           if ((Tcl_VarEval(interp, "assoc ", "$_chan", NULL) == TCL_OK) &&
2223               !tcl_resultempty())
2224             newchan = tcl_resultint();
2225           else
2226             newchan = -1;
2227         }
2228         if (newchan < 0) {
2229           dprintf(idx, "No channel exists by that name.\n");
2230           return;
2231         }
2232       } else
2233         newchan = GLOBAL_CHANS + atoi(arg + 1);
2234       if (newchan < GLOBAL_CHANS || newchan > 199999) {
2235         dprintf(idx, "Channel number out of range: local channels must be "
2236                 "*0-*99999.\n");
2237         return;
2238       }
2239     } else {
2240       if (((arg[0] < '0') || (arg[0] > '9')) && (arg[0])) {
2241         if (!strcasecmp(arg, "on"))
2242           newchan = 0;
2243         else {
2244           Tcl_SetVar(interp, "_chan", arg, 0);
2245           if ((Tcl_VarEval(interp, "assoc ", "$_chan", NULL) == TCL_OK) &&
2246               !tcl_resultempty()) {
2247             newchan = tcl_resultint();
2248             if ((newchan >= GLOBAL_CHANS) && (newchan <= 199999)) {
2249               localchan = 1;
2250             }
2251           }
2252           else
2253             newchan = -1;
2254         }
2255         if (newchan < 0) {
2256           dprintf(idx, "No channel exists by that name.\n");
2257           return;
2258         }
2259       } else
2260         newchan = atoi(arg);
2261       if ((newchan < 0) || ((newchan >= GLOBAL_CHANS) && (!localchan)) ||
2262           (newchan >= 199999)) {
2263         dprintf(idx, "Channel number out of range: must be between 0 and %d."
2264                 "\n", GLOBAL_CHANS);
2265         return;
2266       }
2267     }
2268     /* If coming back from being off the party line, make sure they're
2269      * not away.
2270      */
2271     if ((dcc[idx].u.chat->channel < 0) && (dcc[idx].u.chat->away != NULL))
2272       not_away(idx);
2273     if (dcc[idx].u.chat->channel == newchan) {
2274       if (!newchan) {
2275         dprintf(idx, "You're already on the party line!\n");
2276         return;
2277       } else {
2278         dprintf(idx, "You're already on channel %s%d!\n",
2279                 (newchan < GLOBAL_CHANS) ? "" : "*", newchan % GLOBAL_CHANS);
2280         return;
2281       }
2282     } else {
2283       oldchan = dcc[idx].u.chat->channel;
2284       if (oldchan >= 0)
2285         check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock, oldchan);
2286       if (!oldchan)
2287         chanout_but(-1, 0, "*** %s left the party line.\n", dcc[idx].nick);
2288       else if (oldchan > 0)
2289         chanout_but(-1, oldchan, "*** %s left the channel.\n", dcc[idx].nick);
2290       dcc[idx].u.chat->channel = newchan;
2291       if (!newchan) {
2292         dprintf(idx, "Entering the party line...\n");
2293         chanout_but(-1, 0, "*** %s joined the party line.\n", dcc[idx].nick);
2294       } else {
2295         dprintf(idx, "Joining channel '%s'...\n", arg);
2296         chanout_but(-1, newchan, "*** %s joined the channel.\n", dcc[idx].nick);
2297       }
2298       check_tcl_chjn(botnetnick, dcc[idx].nick, newchan, geticon(idx),
2299                      dcc[idx].sock, dcc[idx].host);
2300       if (newchan < GLOBAL_CHANS)
2301         botnet_send_join_idx(idx, oldchan);
2302       else if (oldchan < GLOBAL_CHANS)
2303         botnet_send_part_idx(idx, "");
2304     }
2305   }
2306   /* New style autosave here too -- rtc, 09/28/1999 */
2307   if ((me = module_find("console", 1, 1))) {
2308     Function *func = me->funcs;
2309 
2310     (func[CONSOLE_DOSTORE]) (idx);
2311   }
2312 }
2313 
2314 static void cmd_echo(struct userrec *u, int idx, char *par)
2315 {
2316   module_entry *me;
2317 
2318   if (!par[0]) {
2319     dprintf(idx, "Echo is currently %s.\n", dcc[idx].status & STAT_ECHO ?
2320             "on" : "off");
2321     return;
2322   }
2323   if (!strcasecmp(par, "on")) {
2324     dprintf(idx, "Echo turned on.\n");
2325     dcc[idx].status |= STAT_ECHO;
2326   } else if (!strcasecmp(par, "off")) {
2327     dprintf(idx, "Echo turned off.\n");
2328     dcc[idx].status &= ~STAT_ECHO;
2329   } else {
2330     dprintf(idx, "Usage: echo <on/off>\n");
2331     return;
2332   }
2333   /* New style autosave here too -- rtc, 09/28/1999 */
2334   if ((me = module_find("console", 1, 1))) {
2335     Function *func = me->funcs;
2336 
2337     (func[CONSOLE_DOSTORE]) (idx);
2338   }
2339 }
2340 
2341 int stripmodes(char *s)
2342 {
2343   int res = 0;
2344 
2345   for (; *s; s++)
2346     switch (tolower((unsigned) *s)) {
2347     case 'c':
2348       res |= STRIP_COLOR;
2349       break;
2350     case 'b':
2351       res |= STRIP_BOLD;
2352       break;
2353     case 'r':
2354       res |= STRIP_REVERSE;
2355       break;
2356     case 'u':
2357       res |= STRIP_UNDERLINE;
2358       break;
2359     case 'a':
2360       res |= STRIP_ANSI;
2361       break;
2362     case 'g':
2363       res |= STRIP_BELLS;
2364       break;
2365     case 'o':
2366       res |= STRIP_ORDINARY;
2367       break;
2368     case 'i':
2369       res |= STRIP_ITALICS;
2370       break;
2371     case '*':
2372       res |= STRIP_ALL;
2373       break;
2374     }
2375   return res;
2376 }
2377 
2378 char *stripmasktype(int x)
2379 {
2380   static char s[20];
2381   char *p = s;
2382 
2383   if (x & STRIP_COLOR)
2384     *p++ = 'c';
2385   if (x & STRIP_BOLD)
2386     *p++ = 'b';
2387   if (x & STRIP_REVERSE)
2388     *p++ = 'r';
2389   if (x & STRIP_UNDERLINE)
2390     *p++ = 'u';
2391   if (x & STRIP_ANSI)
2392     *p++ = 'a';
2393   if (x & STRIP_BELLS)
2394     *p++ = 'g';
2395   if (x & STRIP_ORDINARY)
2396     *p++ = 'o';
2397   if (x & STRIP_ITALICS)
2398     *p++ = 'i';
2399   if (p == s)
2400     *p++ = '-';
2401   *p = 0;
2402   return s;
2403 }
2404 
2405 static char *stripmaskname(int x)
2406 {
2407   static char s[161];
2408   int i = 0;
2409 
2410   s[i] = 0;
2411   if (x & STRIP_COLOR)
2412     i += my_strcpy(s + i, "color, ");
2413   if (x & STRIP_BOLD)
2414     i += my_strcpy(s + i, "bold, ");
2415   if (x & STRIP_REVERSE)
2416     i += my_strcpy(s + i, "reverse, ");
2417   if (x & STRIP_UNDERLINE)
2418     i += my_strcpy(s + i, "underline, ");
2419   if (x & STRIP_ANSI)
2420     i += my_strcpy(s + i, "ansi, ");
2421   if (x & STRIP_BELLS)
2422     i += my_strcpy(s + i, "bells, ");
2423   if (x & STRIP_ORDINARY)
2424     i += my_strcpy(s + i, "ordinary, ");
2425   if (x & STRIP_ITALICS)
2426     i += my_strcpy(s + i, "italics, ");
2427   if (!i)
2428     strcpy(s, "none");
2429   else
2430     s[i - 2] = 0;
2431   return s;
2432 }
2433 
2434 static void cmd_strip(struct userrec *u, int idx, char *par)
2435 {
2436   char *nick, *changes, *c, s[2];
2437   int dest = 0, i, pls, md, ok = 0;
2438   module_entry *me;
2439 
2440   if (!par[0]) {
2441     dprintf(idx, "Your current strip settings are: %s (%s).\n",
2442             stripmasktype(dcc[idx].u.chat->strip_flags),
2443             stripmaskname(dcc[idx].u.chat->strip_flags));
2444     return;
2445   }
2446   nick = newsplit(&par);
2447   if ((nick[0] != '+') && (nick[0] != '-') && u && (u->flags & USER_MASTER)) {
2448     for (i = 0; i < dcc_total; i++)
2449       if (!strcasecmp(nick, dcc[i].nick) && dcc[i].type == &DCC_CHAT && !ok) {
2450         ok = 1;
2451         dest = i;
2452       }
2453     if (!ok) {
2454       dprintf(idx, "No such user on the party line!\n");
2455       return;
2456     }
2457     changes = par;
2458   } else {
2459     changes = nick;
2460     nick = "";
2461     dest = idx;
2462   }
2463   c = changes;
2464   if ((c[0] != '+') && (c[0] != '-'))
2465     dcc[dest].u.chat->strip_flags = 0;
2466   s[1] = 0;
2467   for (pls = 1; *c; c++) {
2468     switch (*c) {
2469     case '+':
2470       pls = 1;
2471       break;
2472     case '-':
2473       pls = 0;
2474       break;
2475     default:
2476       s[0] = *c;
2477       md = stripmodes(s);
2478       if (pls == 1)
2479         dcc[dest].u.chat->strip_flags |= md;
2480       else
2481         dcc[dest].u.chat->strip_flags &= ~md;
2482     }
2483   }
2484   if (nick[0])
2485     putlog(LOG_CMDS, "*", "#%s# strip %s %s", dcc[idx].nick, nick, changes);
2486   else
2487     putlog(LOG_CMDS, "*", "#%s# strip %s", dcc[idx].nick, changes);
2488   if (dest == idx) {
2489     dprintf(idx, "Your strip settings are: %s (%s).\n",
2490             stripmasktype(dcc[idx].u.chat->strip_flags),
2491             stripmaskname(dcc[idx].u.chat->strip_flags));
2492   } else {
2493     dprintf(idx, "Strip setting for %s: %s (%s).\n", dcc[dest].nick,
2494             stripmasktype(dcc[dest].u.chat->strip_flags),
2495             stripmaskname(dcc[dest].u.chat->strip_flags));
2496     dprintf(dest, "%s set your strip settings to: %s (%s).\n", dcc[idx].nick,
2497             stripmasktype(dcc[dest].u.chat->strip_flags),
2498             stripmaskname(dcc[dest].u.chat->strip_flags));
2499   }
2500   /* Set highlight flag here so user is able to control stripping of
2501    * bold also as intended -- dw 27/12/1999
2502    */
2503   if (dcc[dest].u.chat->strip_flags & STRIP_BOLD && u->flags & USER_HIGHLITE) {
2504     u->flags &= ~USER_HIGHLITE;
2505   } else if (!(dcc[dest].u.chat->strip_flags & STRIP_BOLD) &&
2506            !(u->flags & USER_HIGHLITE)) {
2507     u->flags |= USER_HIGHLITE;
2508   }
2509   /* New style autosave here too -- rtc, 09/28/1999 */
2510   if ((me = module_find("console", 1, 1))) {
2511     Function *func = me->funcs;
2512 
2513     (func[CONSOLE_DOSTORE]) (dest);
2514   }
2515 }
2516 
2517 static void cmd_su(struct userrec *u, int idx, char *par)
2518 {
2519   int atr = u ? u->flags : 0;
2520   struct flag_record fr = { FR_ANYWH | FR_CHAN | FR_GLOBAL, 0, 0, 0, 0, 0 };
2521 
2522   u = get_user_by_handle(userlist, par);
2523 
2524   if (!par[0])
2525     dprintf(idx, "Usage: su <user>\n");
2526   else if (!u)
2527     dprintf(idx, "No such user.\n");
2528   else if (u->flags & USER_BOT)
2529     dprintf(idx, "You can't su to a bot... then again, why would you wanna?\n");
2530   else if (dcc[idx].u.chat->su_nick)
2531     dprintf(idx, "You cannot currently double .su; try .su'ing directly.\n");
2532   else {
2533     get_user_flagrec(u, &fr, NULL);
2534     if ((!glob_party(fr) && (require_p || !(glob_op(fr) || chan_op(fr)))) &&
2535         !(atr & USER_BOTMAST))
2536       dprintf(idx, "No party line access permitted for %s.\n", par);
2537     else {
2538       correct_handle(par);
2539       putlog(LOG_CMDS, "*", "#%s# su %s", dcc[idx].nick, par);
2540       if (!(atr & USER_OWNER) || ((u->flags & USER_OWNER) && (isowner(par)) &&
2541           !(isowner(dcc[idx].nick)))) {
2542         /* This check is only important for non-owners */
2543         if (u_pass_match(u, "-")) {
2544           dprintf(idx, "No password set for user. You may not .su to them.\n");
2545           return;
2546         }
2547         if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
2548           botnet_send_part_idx(idx, "");
2549         chanout_but(-1, dcc[idx].u.chat->channel,
2550                     "*** %s left the party line.\n", dcc[idx].nick);
2551         /* Store the old nick in the away section, for weenies who can't get
2552          * their password right ;)
2553          */
2554         if (dcc[idx].u.chat->away != NULL)
2555           nfree(dcc[idx].u.chat->away);
2556         dcc[idx].u.chat->away = get_data_ptr(strlen(dcc[idx].nick) + 1);
2557         strcpy(dcc[idx].u.chat->away, dcc[idx].nick);
2558         dcc[idx].u.chat->su_nick = get_data_ptr(strlen(dcc[idx].nick) + 1);
2559         strcpy(dcc[idx].u.chat->su_nick, dcc[idx].nick);
2560         dcc[idx].user = u;
2561         strcpy(dcc[idx].nick, par);
2562         /* Display password prompt and turn off echo (send IAC WILL ECHO). */
2563         dprintf(idx, "Enter password for %s%s\n", par,
2564                 (dcc[idx].status & STAT_TELNET) ? TLN_IAC_C TLN_WILL_C
2565                 TLN_ECHO_C : "");
2566         dcc[idx].type = &DCC_CHAT_PASS;
2567       } else if (atr & USER_OWNER) {
2568         if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
2569           botnet_send_part_idx(idx, "");
2570         chanout_but(-1, dcc[idx].u.chat->channel,
2571                     "*** %s left the party line.\n", dcc[idx].nick);
2572         dprintf(idx, "Setting your username to %s.\n", par);
2573         if (atr & USER_MASTER)
2574           dcc[idx].u.chat->con_flags = conmask;
2575         dcc[idx].u.chat->su_nick = get_data_ptr(strlen(dcc[idx].nick) + 1);
2576         strcpy(dcc[idx].u.chat->su_nick, dcc[idx].nick);
2577         dcc[idx].user = u;
2578         strlcpy(dcc[idx].nick, par, sizeof dcc[idx].nick);
2579         dcc_chatter(idx);
2580       }
2581     }
2582   }
2583 }
2584 
2585 static void cmd_fixcodes(struct userrec *u, int idx, char *par)
2586 {
2587   if (dcc[idx].status & STAT_TELNET) {
2588     dcc[idx].status |= STAT_ECHO;
2589     dcc[idx].status &= ~STAT_TELNET;
2590     dprintf(idx, "Turned off telnet codes.\n");
2591     putlog(LOG_CMDS, "*", "#%s# fixcodes (telnet off)", dcc[idx].nick);
2592   } else {
2593     dcc[idx].status |= STAT_TELNET;
2594     dcc[idx].status &= ~STAT_ECHO;
2595     dprintf(idx, "Turned on telnet codes.\n");
2596     putlog(LOG_CMDS, "*", "#%s# fixcodes (telnet on)", dcc[idx].nick);
2597   }
2598 }
2599 
2600 static void cmd_page(struct userrec *u, int idx, char *par)
2601 {
2602   int a;
2603   module_entry *me;
2604 
2605   if (!par[0]) {
2606     if (dcc[idx].status & STAT_PAGE) {
2607       dprintf(idx, "Currently paging outputs to %d lines.\n",
2608               dcc[idx].u.chat->max_line);
2609     } else
2610       dprintf(idx, "You don't have paging on.\n");
2611     return;
2612   }
2613   a = atoi(par);
2614   if ((!a && !par[0]) || !strcasecmp(par, "off")) {
2615     dcc[idx].status &= ~STAT_PAGE;
2616     dcc[idx].u.chat->max_line = 0x7ffffff;      /* flush_lines needs this */
2617     while (dcc[idx].u.chat->buffer)
2618       flush_lines(idx, dcc[idx].u.chat);
2619     dprintf(idx, "Paging turned off.\n");
2620     putlog(LOG_CMDS, "*", "#%s# page off", dcc[idx].nick);
2621   } else if (a > 0) {
2622     dprintf(idx, "Paging turned on, stopping every %d line%s.\n", a,
2623             (a != 1) ? "s" : "");
2624     dcc[idx].status |= STAT_PAGE;
2625     dcc[idx].u.chat->max_line = a;
2626     dcc[idx].u.chat->line_count = 0;
2627     dcc[idx].u.chat->current_lines = 0;
2628     putlog(LOG_CMDS, "*", "#%s# page %d", dcc[idx].nick, a);
2629   } else {
2630     dprintf(idx, "Usage: page <off or #>\n");
2631     return;
2632   }
2633   /* New style autosave here too -- rtc, 09/28/1999 */
2634   if ((me = module_find("console", 1, 1))) {
2635     Function *func = me->funcs;
2636 
2637     (func[CONSOLE_DOSTORE]) (idx);
2638   }
2639 }
2640 
2641 /* Evaluate a Tcl command, send output to a dcc user.
2642  */
2643 static void cmd_tcl(struct userrec *u, int idx, char *msg)
2644 {
2645   int code;
2646   char *result;
2647   Tcl_DString dstr;
2648 
2649   if (!(isowner(dcc[idx].nick)) && (must_be_owner)) {
2650     dprintf(idx, MISC_NOSUCHCMD);
2651     return;
2652   }
2653   debug1("tcl: evaluate (.tcl): %s", msg);
2654   code = Tcl_GlobalEval(interp, msg);
2655 
2656   /* properly convert string to system encoding. */
2657   Tcl_DStringInit(&dstr);
2658   Tcl_UtfToExternalDString(NULL, tcl_resultstring(), -1, &dstr);
2659   result = Tcl_DStringValue(&dstr);
2660 
2661   if (code == TCL_OK)
2662     dumplots(idx, "Tcl: ", result);
2663   else
2664     dumplots(idx, "Tcl error: ", result);
2665 
2666   Tcl_DStringFree(&dstr);
2667 }
2668 
2669 /* Perform a 'set' command
2670  */
2671 static void cmd_set(struct userrec *u, int idx, char *msg)
2672 {
2673   int code;
2674   char s[512], *result;
2675   Tcl_DString dstr;
2676 
2677   if (!(isowner(dcc[idx].nick)) && (must_be_owner)) {
2678     dprintf(idx, MISC_NOSUCHCMD);
2679     return;
2680   }
2681   putlog(LOG_CMDS, "*", "#%s# set %s", dcc[idx].nick, msg);
2682   if (!msg[0]) {
2683     Tcl_Eval(interp, "info globals");
2684     dumplots(idx, "Global vars: ", tcl_resultstring());
2685     return;
2686   }
2687   strcpy(s, "set ");
2688   strlcpy(s + 4, msg, (sizeof s) - 4);
2689   code = Tcl_Eval(interp, s);
2690 
2691   /* properly convert string to system encoding. */
2692   Tcl_DStringInit(&dstr);
2693   Tcl_UtfToExternalDString(NULL, tcl_resultstring(), -1, &dstr);
2694   result = Tcl_DStringValue(&dstr);
2695 
2696   if (code == TCL_OK) {
2697     if (!strchr(msg, ' '))
2698       dumplots(idx, "Currently: ", result);
2699     else
2700       dprintf(idx, "Ok, set.\n");
2701   } else
2702     dprintf(idx, "Error: %s\n", result);
2703 
2704   Tcl_DStringFree(&dstr);
2705 }
2706 
2707 static void cmd_module(struct userrec *u, int idx, char *par)
2708 {
2709   putlog(LOG_CMDS, "*", "#%s# module %s", dcc[idx].nick, par);
2710   do_module_report(idx, 2, par[0] ? par : NULL);
2711 }
2712 
2713 static void cmd_loadmod(struct userrec *u, int idx, char *par)
2714 {
2715   const char *p;
2716 
2717   if (!(isowner(dcc[idx].nick)) && (must_be_owner)) {
2718     dprintf(idx, MISC_NOSUCHCMD);
2719     return;
2720   }
2721   if (!par[0]) {
2722     dprintf(idx, "%s: loadmod <module>\n", MISC_USAGE);
2723   } else {
2724     p = module_load(par);
2725     if (p)
2726       dprintf(idx, "%s: %s %s\n", par, MOD_LOADERROR, p);
2727     else {
2728       putlog(LOG_CMDS, "*", "#%s# loadmod %s", dcc[idx].nick, par);
2729       dprintf(idx, MOD_LOADED, par);
2730       dprintf(idx, "\n");
2731     }
2732   }
2733 }
2734 
2735 static void cmd_unloadmod(struct userrec *u, int idx, char *par)
2736 {
2737   char *p;
2738 
2739   if (!(isowner(dcc[idx].nick)) && (must_be_owner)) {
2740     dprintf(idx, MISC_NOSUCHCMD);
2741     return;
2742   }
2743   if (!par[0])
2744     dprintf(idx, "%s: unloadmod <module>\n", MISC_USAGE);
2745   else {
2746     p = module_unload(par, dcc[idx].nick);
2747     if (p)
2748       dprintf(idx, "%s %s: %s\n", MOD_UNLOADERROR, par, p);
2749     else {
2750       putlog(LOG_CMDS, "*", "#%s# unloadmod %s", dcc[idx].nick, par);
2751       dprintf(idx, "%s %s\n", MOD_UNLOADED, par);
2752     }
2753   }
2754 }
2755 
2756 static void cmd_pls_ignore(struct userrec *u, int idx, char *par)
2757 {
2758   char *who, s[UHOSTLEN], *p, *p_expire;
2759   long expire_foo;
2760   unsigned long expire_time = 0;
2761 
2762   if (!par[0]) {
2763     dprintf(idx, "Usage: +ignore <hostmask> [%%<XyXdXhXm>] [comment]\n");
2764     return;
2765   }
2766 
2767   who = newsplit(&par);
2768   if (par[0] == '%') {
2769     p = newsplit(&par);
2770     p_expire = p + 1;
2771     while (*(++p) != 0) {
2772       switch (tolower((unsigned) *p)) {
2773       case 'y':
2774         *p = 0;
2775         expire_foo = strtol(p_expire, NULL, 10);
2776         expire_time += 60 * 60 * 24 * 365 * expire_foo;
2777         p_expire = p + 1;
2778         break;
2779       case 'd':
2780         *p = 0;
2781         expire_foo = strtol(p_expire, NULL, 10);
2782         expire_time += 60 * 60 * 24 * expire_foo;
2783         p_expire = p + 1;
2784         break;
2785       case 'h':
2786         *p = 0;
2787         expire_foo = strtol(p_expire, NULL, 10);
2788         expire_time += 60 * 60 * expire_foo;
2789         p_expire = p + 1;
2790         break;
2791       case 'm':
2792         *p = 0;
2793         expire_foo = strtol(p_expire, NULL, 10);
2794         expire_time += 60 * expire_foo;
2795         p_expire = p + 1;
2796       }
2797     }
2798     /* For whomever is stuck with maintaining this in 2033- this will
2799      * break. Hopefully we've dealt with the max unixtime issue by now
2800      * (Year 2038 problem), but if you're reading this, clearly we
2801      * haven't because we are lazy. Sorry.
2802      */
2803     if (expire_time > (60 * 60 * 24 * 365 * 5)) {
2804       dprintf(idx, "expire time must be equal to or less than 5 years"
2805           "(1825 days)\n");
2806       return;
2807     }
2808   }
2809   if (!par[0])
2810     par = "requested";
2811   else if (strlen(par) > 65)
2812     par[65] = 0;
2813   if (strlen(who) > UHOSTMAX - 4)
2814     who[UHOSTMAX - 4] = 0;
2815 
2816   /* Fix missing ! or @ BEFORE continuing */
2817   if (!strchr(who, '!')) {
2818     if (!strchr(who, '@'))
2819       simple_sprintf(s, "%s!*@*", who);
2820     else
2821       simple_sprintf(s, "*!%s", who);
2822   } else if (!strchr(who, '@'))
2823     simple_sprintf(s, "%s@*", who);
2824   else
2825     strcpy(s, who);
2826 
2827   if (match_ignore(s))
2828     dprintf(idx, "That already matches an existing ignore.\n");
2829   else {
2830     dprintf(idx, "Now ignoring: %s (%s)\n", s, par);
2831     addignore(s, dcc[idx].nick, par, expire_time ? now + expire_time : 0L);
2832     putlog(LOG_CMDS, "*", "#%s# +ignore %s %s", dcc[idx].nick, s, par);
2833   }
2834 }
2835 
2836 static void cmd_mns_ignore(struct userrec *u, int idx, char *par)
2837 {
2838   char buf[UHOSTLEN];
2839 
2840   if (!par[0]) {
2841     dprintf(idx, "Usage: -ignore <hostmask | ignore #>\n");
2842     return;
2843   }
2844   strlcpy(buf, par, sizeof buf);
2845   if (delignore(buf)) {
2846     putlog(LOG_CMDS, "*", "#%s# -ignore %s", dcc[idx].nick, buf);
2847     dprintf(idx, "No longer ignoring: %s\n", buf);
2848   } else
2849     dprintf(idx, "That ignore cannot be found.\n");
2850 }
2851 
2852 static void cmd_ignores(struct userrec *u, int idx, char *par)
2853 {
2854   putlog(LOG_CMDS, "*", "#%s# ignores %s", dcc[idx].nick, par);
2855   tell_ignores(idx, par);
2856 }
2857 
2858 static void cmd_pls_user(struct userrec *u, int idx, char *par)
2859 {
2860   char *handle, *host;
2861 
2862   if (!par[0]) {
2863     dprintf(idx, "Usage: +user <handle> [hostmask]\n");
2864     return;
2865   }
2866   handle = newsplit(&par);
2867   host = newsplit(&par);
2868   if (strlen(handle) > HANDLEN)
2869     handle[HANDLEN] = 0;
2870   if (get_user_by_handle(userlist, handle))
2871     dprintf(idx, "Someone already exists by that name.\n");
2872   else if (strchr(BADHANDCHARS, handle[0]) != NULL)
2873     dprintf(idx, "You can't start a handle with '%c'.\n", handle[0]);
2874   else if (!strcasecmp(handle, botnetnick))
2875     dprintf(idx, "Hey! That's MY name!\n");
2876   else {
2877     putlog(LOG_CMDS, "*", "#%s# +user %s %s", dcc[idx].nick, handle, host);
2878     userlist = adduser(userlist, handle, host, "-", 0);
2879     dprintf(idx, "Added %s (%s) with no password and no flags.\n", handle,
2880             host[0] ? host : "no host");
2881   }
2882 }
2883 
2884 static void cmd_mns_user(struct userrec *u, int idx, char *par)
2885 {
2886   int idx2;
2887   char *handle;
2888   struct userrec *u2;
2889   module_entry *me;
2890 
2891   if (!par[0]) {
2892     dprintf(idx, "Usage: -user <hand>\n");
2893     return;
2894   }
2895   handle = newsplit(&par);
2896   u2 = get_user_by_handle(userlist, handle);
2897   if (!u2 || !u) {
2898     dprintf(idx, "No such user!\n");
2899     return;
2900   }
2901   if (isowner(u2->handle)) {
2902     dprintf(idx, "You can't remove a permanent bot owner!\n");
2903     return;
2904   }
2905   if ((u2->flags & USER_OWNER) && !isowner(u->handle)) {
2906     dprintf(idx, "You can't remove a bot owner!\n");
2907     return;
2908   }
2909   if ((u2->flags & USER_MASTER) && !(u->flags & USER_OWNER)) {
2910     dprintf(idx, "Only owners can remove a master!\n");
2911     return;
2912   }
2913   if (u2->flags & USER_BOT) {
2914     if ((bot_flags(u2) & BOT_SHARE) && !(u->flags & USER_OWNER)) {
2915       dprintf(idx, "You can't remove share bots.\n");
2916       return;
2917     }
2918     for (idx2 = 0; idx2 < dcc_total; idx2++)
2919       if (dcc[idx2].type != &DCC_RELAY && dcc[idx2].type != &DCC_FORK_BOT &&
2920           !strcasecmp(dcc[idx2].nick, handle))
2921         break;
2922     if (idx2 != dcc_total) {
2923       dprintf(idx, "You can't remove a directly linked bot.\n");
2924       return;
2925     }
2926   }
2927   if ((u->flags & USER_BOTMAST) && !(u->flags & USER_MASTER) &&
2928       !(u2->flags & USER_BOT)) {
2929     dprintf(idx, "You can't remove users who aren't bots!\n");
2930     return;
2931   }
2932   if ((me = module_find("irc", 0, 0))) {
2933     Function *func = me->funcs;
2934 
2935     (func[IRC_CHECK_THIS_USER]) (handle, 1, NULL);
2936   }
2937   if (deluser(handle)) {
2938     putlog(LOG_CMDS, "*", "#%s# -user %s", dcc[idx].nick, handle);
2939     dprintf(idx, "Deleted %s.\n", handle);
2940   } else
2941     dprintf(idx, "Failed.\n");
2942 }
2943 
2944 static void cmd_pls_host(struct userrec *u, int idx, char *par)
2945 {
2946   char *handle, *host;
2947   struct userrec *u2;
2948   struct list_type *q;
2949   struct flag_record fr2 = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 },
2950                       fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
2951   module_entry *me;
2952 
2953   if (!par[0]) {
2954     dprintf(idx, "Usage: +host [handle] <newhostmask>\n");
2955     return;
2956   }
2957 
2958   handle = newsplit(&par);
2959 
2960   if (par[0]) {
2961     host = newsplit(&par);
2962     u2 = get_user_by_handle(userlist, handle);
2963   } else {
2964     host = handle;
2965     handle = dcc[idx].nick;
2966     u2 = u;
2967   }
2968   if (!u2 || !u) {
2969     dprintf(idx, "No such user.\n");
2970     return;
2971   }
2972   get_user_flagrec(u, &fr, NULL);
2973   if (strcasecmp(handle, dcc[idx].nick)) {
2974     get_user_flagrec(u2, &fr2, NULL);
2975     if (!glob_master(fr) && !glob_bot(fr2) && !chan_master(fr)) {
2976       dprintf(idx, "You can't add hostmasks to non-bots.\n");
2977       return;
2978     }
2979     if (!(glob_owner(fr) || glob_botmast(fr)) && glob_bot(fr2) && (bot_flags(u2) & BOT_SHARE)) {
2980       dprintf(idx, "You can't add hostmasks to share bots.\n");
2981       return;
2982     }
2983     if ((glob_owner(fr2) || glob_master(fr2)) && !glob_owner(fr)) {
2984       dprintf(idx, "You can't add hostmasks to a bot owner/master.\n");
2985       return;
2986     }
2987     if ((chan_owner(fr2) || chan_master(fr2)) && !glob_master(fr) &&
2988         !glob_owner(fr) && !chan_owner(fr)) {
2989       dprintf(idx, "You can't add hostmasks to a channel owner/master.\n");
2990       return;
2991     }
2992     if (!glob_botmast(fr) && !glob_master(fr) && !chan_master(fr)) {
2993       dprintf(idx, "Permission denied.\n");
2994       return;
2995     }
2996   }
2997   if (!glob_botmast(fr) && !chan_master(fr) && get_user_by_host(host)) {
2998     dprintf(idx, "You cannot add a host matching another user!\n");
2999     return;
3000   }
3001   for (q = get_user(&USERENTRY_HOSTS, u); q; q = q->next)
3002     if (!strcasecmp(q->extra, host)) {
3003       dprintf(idx, "That hostmask is already there.\n");
3004       return;
3005     }
3006   putlog(LOG_CMDS, "*", "#%s# +host %s %s", dcc[idx].nick, handle, host);
3007   addhost_by_handle(handle, host);
3008   dprintf(idx, "Added '%s' to %s.\n", host, handle);
3009   if ((me = module_find("irc", 0, 0))) {
3010     Function *func = me->funcs;
3011 
3012     (func[IRC_CHECK_THIS_USER]) (handle, 0, NULL);
3013   }
3014 }
3015 
3016 static void cmd_mns_host(struct userrec *u, int idx, char *par)
3017 {
3018   char *handle, *host;
3019   struct userrec *u2;
3020   struct flag_record fr2 = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 },
3021                       fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
3022   module_entry *me;
3023 
3024   if (!par[0]) {
3025     dprintf(idx, "Usage: -host [handle] <hostmask>\n");
3026     return;
3027   }
3028   handle = newsplit(&par);
3029   if (par[0]) {
3030     host = newsplit(&par);
3031     u2 = get_user_by_handle(userlist, handle);
3032   } else {
3033     host = handle;
3034     handle = dcc[idx].nick;
3035     u2 = u;
3036   }
3037   if (!u2 || !u) {
3038     dprintf(idx, "No such user.\n");
3039     return;
3040   }
3041 
3042   get_user_flagrec(u, &fr, NULL);
3043   get_user_flagrec(u2, &fr2, NULL);
3044   /* check to see if user is +d or +k and don't let them remove hosts */
3045   if (((glob_deop(fr) || glob_kick(fr)) && !glob_master(fr)) ||
3046       ((chan_deop(fr) || chan_kick(fr)) && !chan_master(fr))) {
3047     dprintf(idx, "You can't remove hostmasks while having the +d or +k "
3048             "flag.\n");
3049     return;
3050   }
3051 
3052   if (strcasecmp(handle, dcc[idx].nick)) {
3053     if (!glob_master(fr) && !glob_bot(fr2) && !chan_master(fr)) {
3054       dprintf(idx, "You can't remove hostmasks from non-bots.\n");
3055       return;
3056     }
3057     if (glob_bot(fr2) && (bot_flags(u2) & BOT_SHARE) && !glob_owner(fr)) {
3058       dprintf(idx, "You can't remove hostmasks from a share bot.\n");
3059       return;
3060     }
3061     if ((glob_owner(fr2) || glob_master(fr2)) && !glob_owner(fr)) {
3062       dprintf(idx, "You can't remove hostmasks from a bot owner/master.\n");
3063       return;
3064     }
3065     if ((chan_owner(fr2) || chan_master(fr2)) && !glob_master(fr) &&
3066         !glob_owner(fr) && !chan_owner(fr)) {
3067       dprintf(idx, "You can't remove hostmasks from a channel owner/master.\n");
3068       return;
3069     }
3070     if (!glob_botmast(fr) && !glob_master(fr) && !chan_master(fr)) {
3071       dprintf(idx, "Permission denied.\n");
3072       return;
3073     }
3074   }
3075   if (delhost_by_handle(handle, host)) {
3076     putlog(LOG_CMDS, "*", "#%s# -host %s %s", dcc[idx].nick, handle, host);
3077     dprintf(idx, "Removed '%s' from %s.\n", host, handle);
3078     if ((me = module_find("irc", 0, 0))) {
3079       Function *func = me->funcs;
3080 
3081       (func[IRC_CHECK_THIS_USER]) (handle, 2, host);
3082     }
3083   } else
3084     dprintf(idx, "Failed.\n");
3085 }
3086 
3087 static void cmd_modules(struct userrec *u, int idx, char *par)
3088 {
3089   int ptr;
3090   char *bot;
3091   module_entry *me;
3092 
3093   putlog(LOG_CMDS, "*", "#%s# modules %s", dcc[idx].nick, par);
3094 
3095   if (!par[0]) {
3096     dprintf(idx, "Modules loaded:\n");
3097     for (me = module_list; me; me = me->next)
3098       dprintf(idx, "  Module: %s (v%d.%d)\n", me->name, me->major, me->minor);
3099     dprintf(idx, "End of modules list.\n");
3100   } else {
3101     bot = newsplit(&par);
3102     if ((ptr = nextbot(bot)) >= 0)
3103       dprintf(ptr, "v %s %s %d:%s\n", botnetnick, bot, dcc[idx].sock,
3104               dcc[idx].nick);
3105     else
3106       dprintf(idx, "No such bot online.\n");
3107   }
3108 }
3109 
3110 static void cmd_traffic(struct userrec *u, int idx, char *par)
3111 {
3112   unsigned long itmp, itmp2;
3113 
3114   dprintf(idx, "Traffic since last restart\n");
3115   dprintf(idx, "==========================\n");
3116   if (otraffic_irc > 0 || itraffic_irc > 0 || otraffic_irc_today > 0 ||
3117       itraffic_irc_today > 0) {
3118     dprintf(idx, "IRC:\n");
3119     dprintf(idx, "  out: %s", btos(otraffic_irc + otraffic_irc_today));
3120     dprintf(idx, " (%s today)\n", btos(otraffic_irc_today));
3121     dprintf(idx, "   in: %s", btos(itraffic_irc + itraffic_irc_today));
3122     dprintf(idx, " (%s today)\n", btos(itraffic_irc_today));
3123   }
3124   if (otraffic_bn > 0 || itraffic_bn > 0 || otraffic_bn_today > 0 ||
3125       itraffic_bn_today > 0) {
3126     dprintf(idx, "Botnet:\n");
3127     dprintf(idx, "  out: %s", btos(otraffic_bn + otraffic_bn_today));
3128     dprintf(idx, " (%s today)\n", btos(otraffic_bn_today));
3129     dprintf(idx, "   in: %s", btos(itraffic_bn + itraffic_bn_today));
3130     dprintf(idx, " (%s today)\n", btos(itraffic_bn_today));
3131   }
3132   if (otraffic_dcc > 0 || itraffic_dcc > 0 || otraffic_dcc_today > 0 ||
3133       itraffic_dcc_today > 0) {
3134     dprintf(idx, "Partyline:\n");
3135     itmp = otraffic_dcc + otraffic_dcc_today;
3136     itmp2 = otraffic_dcc_today;
3137     dprintf(idx, "  out: %s", btos(itmp));
3138     dprintf(idx, " (%s today)\n", btos(itmp2));
3139     dprintf(idx, "   in: %s", btos(itraffic_dcc + itraffic_dcc_today));
3140     dprintf(idx, " (%s today)\n", btos(itraffic_dcc_today));
3141   }
3142   if (otraffic_trans > 0 || itraffic_trans > 0 || otraffic_trans_today > 0 ||
3143       itraffic_trans_today > 0) {
3144     dprintf(idx, "Transfer.mod:\n");
3145     dprintf(idx, "  out: %s", btos(otraffic_trans + otraffic_trans_today));
3146     dprintf(idx, " (%s today)\n", btos(otraffic_trans_today));
3147     dprintf(idx, "   in: %s", btos(itraffic_trans + itraffic_trans_today));
3148     dprintf(idx, " (%s today)\n", btos(itraffic_trans_today));
3149   }
3150   if (otraffic_unknown > 0 || otraffic_unknown_today > 0) {
3151     dprintf(idx, "Misc:\n");
3152     dprintf(idx, "  out: %s", btos(otraffic_unknown + otraffic_unknown_today));
3153     dprintf(idx, " (%s today)\n", btos(otraffic_unknown_today));
3154     dprintf(idx, "   in: %s", btos(itraffic_unknown + itraffic_unknown_today));
3155     dprintf(idx, " (%s today)\n", btos(itraffic_unknown_today));
3156   }
3157   dprintf(idx, "---\n");
3158   dprintf(idx, "Total:\n");
3159   itmp = otraffic_irc + otraffic_bn + otraffic_dcc + otraffic_trans +
3160          otraffic_unknown + otraffic_irc_today + otraffic_bn_today +
3161          otraffic_dcc_today + otraffic_trans_today + otraffic_unknown_today;
3162   itmp2 = otraffic_irc_today + otraffic_bn_today + otraffic_dcc_today +
3163           otraffic_trans_today + otraffic_unknown_today;
3164   dprintf(idx, "  out: %s", btos(itmp));
3165   dprintf(idx, " (%s today)\n", btos(itmp2));
3166   dprintf(idx, "   in: %s", btos(itraffic_irc + itraffic_bn + itraffic_dcc +
3167           itraffic_trans + itraffic_unknown + itraffic_irc_today +
3168           itraffic_bn_today + itraffic_dcc_today + itraffic_trans_today +
3169           itraffic_unknown_today));
3170   dprintf(idx, " (%s today)\n", btos(itraffic_irc_today + itraffic_bn_today +
3171           itraffic_dcc_today + itraffic_trans_today + itraffic_unknown_today));
3172   putlog(LOG_CMDS, "*", "#%s# traffic", dcc[idx].nick);
3173 }
3174 
3175 static char traffictxt[20];
3176 static char *btos(unsigned long bytes)
3177 {
3178   const char *unit;
3179   float xbytes;
3180 
3181   xbytes = bytes;
3182   if (xbytes > 1024.0) {
3183     unit = "KBytes";
3184     xbytes = xbytes / 1024.0;
3185   }
3186   if (xbytes > 1024.0) {
3187     unit = "MBytes";
3188     xbytes = xbytes / 1024.0;
3189   }
3190   if (xbytes > 1024.0) {
3191     unit = "GBytes";
3192     xbytes = xbytes / 1024.0;
3193   }
3194   if (xbytes > 1024.0) {
3195     unit = "TBytes";
3196     xbytes = xbytes / 1024.0;
3197   }
3198   if (bytes > 1024)
3199     sprintf(traffictxt, "%.2f %s", xbytes, unit);
3200   else
3201     sprintf(traffictxt, "%lu Bytes", bytes);
3202   return traffictxt;
3203 }
3204 
3205 static void cmd_whoami(struct userrec *u, int idx, char *par)
3206 {
3207   dprintf(idx, "You are %s@%s.\n", dcc[idx].nick, botnetnick);
3208   putlog(LOG_CMDS, "*", "#%s# whoami", dcc[idx].nick);
3209 }
3210 
3211 /* DCC CHAT COMMANDS
3212  */
3213 /* Function call should be:
3214  *   static void cmd_whatever(struct userrec *u, int idx, char *par);
3215  * As with msg commands, function is responsible for any logging.
3216  */
3217 cmd_t C_dcc[] = {
3218   {"+bot",      "t",    (IntFunc) cmd_pls_bot,    NULL},
3219   {"+host",     "t|m",  (IntFunc) cmd_pls_host,   NULL},
3220   {"+ignore",   "m",    (IntFunc) cmd_pls_ignore, NULL},
3221   {"+user",     "m",    (IntFunc) cmd_pls_user,   NULL},
3222   {"-bot",      "t",    (IntFunc) cmd_mns_user,   NULL},
3223   {"-host",     "",     (IntFunc) cmd_mns_host,   NULL},
3224   {"-ignore",   "m",    (IntFunc) cmd_mns_ignore, NULL},
3225   {"-user",     "m",    (IntFunc) cmd_mns_user,   NULL},
3226   {"addlog",    "to|o", (IntFunc) cmd_addlog,     NULL},
3227   {"away",      "",     (IntFunc) cmd_away,       NULL},
3228   {"back",      "",     (IntFunc) cmd_back,       NULL},
3229   {"backup",    "m|m",  (IntFunc) cmd_backup,     NULL},
3230   {"banner",    "t",    (IntFunc) cmd_banner,     NULL},
3231   {"binds",     "m",    (IntFunc) cmd_binds,      NULL},
3232   {"boot",      "t",    (IntFunc) cmd_boot,       NULL},
3233   {"botattr",   "t",    (IntFunc) cmd_botattr,    NULL},
3234   {"botinfo",   "",     (IntFunc) cmd_botinfo,    NULL},
3235   {"bots",      "",     (IntFunc) cmd_bots,       NULL},
3236   {"bottree",   "",     (IntFunc) cmd_bottree,    NULL},
3237   {"chaddr",    "t",    (IntFunc) cmd_chaddr,     NULL},
3238   {"chat",      "",     (IntFunc) cmd_chat,       NULL},
3239   {"chattr",    "m|m",  (IntFunc) cmd_chattr,     NULL},
3240 #ifdef TLS
3241   {"chfinger",  "t",    (IntFunc) cmd_chfinger,   NULL},
3242 #endif
3243   {"chhandle",  "t",    (IntFunc) cmd_chhandle,   NULL},
3244   {"chnick",    "t",    (IntFunc) cmd_chhandle,   NULL},
3245   {"chpass",    "t",    (IntFunc) cmd_chpass,     NULL},
3246   {"comment",   "m",    (IntFunc) cmd_comment,    NULL},
3247   {"console",   "to|o", (IntFunc) cmd_console,    NULL},
3248   {"resetconsole", "to|o", (IntFunc) cmd_resetconsole, NULL},
3249   {"dccstat",   "t",    (IntFunc) cmd_dccstat,    NULL},
3250   {"debug",     "m",    (IntFunc) cmd_debug,      NULL},
3251   {"die",       "n",    (IntFunc) cmd_die,        NULL},
3252   {"echo",      "",     (IntFunc) cmd_echo,       NULL},
3253 #ifdef TLS
3254   {"fprint",    "",     (IntFunc) cmd_fprint,     NULL},
3255 #endif
3256   {"fixcodes",  "",     (IntFunc) cmd_fixcodes,   NULL},
3257   {"help",      "",     (IntFunc) cmd_help,       NULL},
3258   {"ignores",   "m",    (IntFunc) cmd_ignores,    NULL},
3259   {"link",      "t",    (IntFunc) cmd_link,       NULL},
3260   {"loadmod",   "n",    (IntFunc) cmd_loadmod,    NULL},
3261   {"match",     "to|o", (IntFunc) cmd_match,      NULL},
3262   {"me",        "",     (IntFunc) cmd_me,         NULL},
3263   {"module",    "m",    (IntFunc) cmd_module,     NULL},
3264   {"modules",   "n",    (IntFunc) cmd_modules,    NULL},
3265   {"motd",      "",     (IntFunc) cmd_motd,       NULL},
3266   {"newpass",   "",     (IntFunc) cmd_newpass,    NULL},
3267   {"handle",    "",     (IntFunc) cmd_handle,     NULL},
3268   {"nick",      "",     (IntFunc) cmd_handle,     NULL},
3269   {"page",      "",     (IntFunc) cmd_page,       NULL},
3270   {"quit",      "",     (IntFunc) CMD_LEAVE,      NULL},
3271   {"rehash",    "m",    (IntFunc) cmd_rehash,     NULL},
3272   {"rehelp",    "n",    (IntFunc) cmd_rehelp,     NULL},
3273   {"relay",     "o",    (IntFunc) cmd_relay,      NULL},
3274   {"reload",    "m|m",  (IntFunc) cmd_reload,     NULL},
3275   {"restart",   "m",    (IntFunc) cmd_restart,    NULL},
3276   {"save",      "m|m",  (IntFunc) cmd_save,       NULL},
3277   {"set",       "n",    (IntFunc) cmd_set,        NULL},
3278   {"simul",     "n",    (IntFunc) cmd_simul,      NULL},
3279   {"status",    "m|m",  (IntFunc) cmd_status,     NULL},
3280   {"strip",     "",     (IntFunc) cmd_strip,      NULL},
3281   {"su",        "",     (IntFunc) cmd_su,         NULL},
3282   {"tcl",       "n",    (IntFunc) cmd_tcl,        NULL},
3283   {"trace",     "t",    (IntFunc) cmd_trace,      NULL},
3284   {"unlink",    "t",    (IntFunc) cmd_unlink,     NULL},
3285   {"unloadmod", "n",    (IntFunc) cmd_unloadmod,  NULL},
3286   {"uptime",    "m|m",  (IntFunc) cmd_uptime,     NULL},
3287   {"vbottree",  "",     (IntFunc) cmd_vbottree,   NULL},
3288   {"who",       "",     (IntFunc) cmd_who,        NULL},
3289   {"whois",     "to|o", (IntFunc) cmd_whois,      NULL},
3290   {"whom",      "",     (IntFunc) cmd_whom,       NULL},
3291   {"traffic",   "m|m",  (IntFunc) cmd_traffic,    NULL},
3292   {"whoami",    "",     (IntFunc) cmd_whoami,     NULL},
3293   {NULL,        NULL,   NULL,                      NULL}
3294 };
3295