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