]> git.donarmstrong.com Git - infobot.git/blob - src/Modules/UserDCC.pl
- attempt to fix .reset (chans) prob
[infobot.git] / src / Modules / UserDCC.pl
1 #
2 #  UserDCC.pl: User Commands, DCC CHAT.
3 #      Author: dms
4 #     Version: v0.2 (20010119)
5 #     Created: 20000707 (from UserExtra.pl)
6 #
7
8 if (&IsParam("useStrict")) { use strict; }
9
10 sub userDCC {
11     # hrm...
12     $message =~ s/\s+$//;
13
14     ### for all users.
15     # quit.
16     if ($message =~ /^(exit|quit)$/i) {
17         # do ircII clients support remote close? if so, cool!
18         &status("userDCC: quit called. FIXME");
19         &dcc_close($who);
20         &status("userDCC: after dcc_close!");
21
22         return;
23     }
24
25     # who.
26     if ($message =~ /^who$/) {
27         my $count = scalar(keys %{ $dcc{'CHAT'} });
28         my $dccCHAT = $message;
29
30         &pSReply("Start of who ($count users).");
31         foreach (keys %{ $dcc{'CHAT'} }) {
32             &pSReply("=> $_");
33         }
34         &pSReply("End of who.");
35
36         return;
37     }
38
39     ### for those users with enough flags.
40
41     if ($message =~ /^tellme(\s+(.*))?$/i) {
42         my $args = $2;
43         if ($args =~ /^\s*$/) {
44             &help("tellme");
45             return;
46         }
47
48         my $result = &doQuestion($args);
49         &pSReply($result);
50
51         return;
52     }
53
54     # 4op.
55     if ($message =~ /^4op(\s+($mask{chan}))?$/i) {
56         return unless (&hasFlag("o"));
57
58         my $chan = $2;
59
60         if ($chan eq "") {
61             &help("4op");
62             return;
63         }
64
65         if (!$channels{$chan}{'o'}{$ident}) {
66             &msg($who, "i don't have ops on $chan to do that.");
67             return;
68         }
69
70         # on non-4mode(<4) servers, this may be exploited.
71         if ($channels{$chan}{'o'}{$who}) {
72             rawout("MODE $chan -o+o-o+o". (" $who" x 4));
73         } else {
74             rawout("MODE $chan +o-o+o-o". (" $who" x 4));
75         }
76
77         return;
78     }
79
80     # backlog.
81     if ($message =~ /^backlog(\s+(.*))?$/i) {
82         return unless (&hasFlag("o"));
83         return unless (&hasParam("backlog"));
84         my $num = $2;
85         my $max = $param{'backlog'};
86
87         if (!defined $num) {
88             &help("backlog");
89             return;
90         } elsif ($num !~ /^\d+/) {
91             &msg($who, "error: argument is not positive integer.");
92             return;
93         } elsif ($num > $max or $num < 0) {
94             &msg($who, "error: argument is out of range (max $max).");
95             return;
96         }
97
98         &msg($who, "Start of backlog...");
99         for (0..$num-1) {
100             sleep 1 if ($_ % 4 == 0 and $_ != 0);
101             $conn->privmsg($who, "[".($_+1)."]: $backlog[$max-$num+$_]");
102         }
103         &msg($who, "End of backlog.");
104
105         return;
106     }
107
108     # dump variables.
109     if ($message =~ /^dumpvars$/i) {
110         return unless (&hasFlag("o"));
111         return unless (&IsParam("dumpvars"));
112
113         &status("Dumping all variables...");
114         &dumpallvars();
115
116         return;
117     }
118
119     # kick.
120     if ($message =~ /^kick(\s+(\S+)(\s+(\S+))?)?/) {
121         return unless (&hasFlag("o"));
122         my ($nick,$chan) = (lc $2,lc $4);
123
124         if ($nick eq "") {
125             &help("kick");
126             return;
127         }
128
129         if (&validChan($chan) == 0) {
130             &msg($who,"error: invalid channel \002$chan\002");
131             return;
132         }
133
134         if (&IsNickInChan($nick,$chan) == 0) {
135             &msg($who,"$nick is not in $chan.");
136             return;
137         }
138
139         &kick($nick,$chan);
140
141         return;
142     }
143
144     # kick.
145     if ($message =~ /^mode(\s+(.*))?$/) {
146         return unless (&hasFlag("n"));
147         my ($chan,$mode) = split /\s+/,$2,2;
148
149         if ($chan eq "") {
150             &help("mode");
151             return;
152         }
153
154         if (&validChan($chan) == 0) {
155             &msg($who,"error: invalid channel \002$chan\002");
156             return;
157         }
158
159         if (!$channels{$chan}{o}{$ident}) {
160             &msg($who,"error: don't have ops on \002$chan\002");
161             return;
162         }
163
164         &mode($chan, $mode);
165
166         return;
167     }
168
169     # part.
170     if ($message =~ /^part(\s+(\S+))?$/i) {
171         return unless (&hasFlag("o"));
172         my $jchan = $2;
173
174         if ($jchan !~ /^$mask{chan}$/) {
175             &msg($who, "error, invalid chan.");
176             &help("part");
177             return;
178         }
179
180         if (!&validChan($jchan)) {
181             &msg($who, "error, I'm not on that chan.");
182             return;
183         }
184
185         &msg($jchan, "Leaving. (courtesy of $who).");
186         &part($jchan);
187         return;
188     }
189
190     # lobotomy. sometimes we want the bot to be _QUIET_.
191     if ($message =~ /^(lobotomy|bequiet)$/i) {
192         return unless (&hasFlag("o"));
193
194         if ($lobotomized) {
195             &performReply("i'm already lobotomized");
196         } else {
197             &performReply("i have been lobotomized");
198             $lobotomized = 1;
199         }
200
201         return;
202     }
203
204     # unlobotomy.
205     if ($message =~ /^(unlobotomy|benoisy)$/i) {
206         return unless (&hasFlag("o"));
207
208         if ($lobotomized) {
209             &performReply("i have been unlobotomized, woohoo");
210             $lobotomized = 0;
211             delete $cache{lobotomy};
212 #           undef $cache{lobotomy};     # ??
213         } else {
214             &performReply("i'm not lobotomized");
215         }
216
217         return;
218     }
219
220     # op.
221     if ($message =~ /^op(\s+(.*))?$/i) {
222         return unless (&hasFlag("o"));
223         my ($opee) = lc $2;
224         my @chans;
225
226         if ($opee =~ / /) {
227             if ($opee =~ /^(\S+)\s+(\S+)$/) {
228                 $opee  = $1;
229                 @chans = ($2);
230                 if (!&validChan($2)) {
231                     &msg($who,"error: invalid chan ($2).");
232                     return;
233                 }
234             } else {
235                 &msg($who,"error: invalid params.");
236                 return;
237             }
238         } else {
239             @chans = keys %channels;
240         }
241
242         my $found = 0;
243         my $op = 0;
244         foreach (@chans) {
245             next unless (&IsNickInChan($opee,$_));
246             $found++;
247             if ($channels{$_}{'o'}{$opee}) {
248                 &pSReply("op: $opee already has ops on $_");
249                 next;
250             }
251             $op++;
252
253             &pSReply("opping $opee on $_");
254             &op($_, $opee);
255         }
256
257         if ($found != $op) {
258             &pSReply("op: opped on all possible channels.");
259         } else {
260             &DEBUG("op: found => '$found'.");
261             &DEBUG("op:    op => '$op'.");
262         }
263
264         return;
265     }
266
267     # deop.
268     if ($message =~ /^deop(\s+(.*))?$/i) {
269         return unless (&hasFlag("o"));
270         my ($opee) = lc $2;
271         my @chans;
272
273         if ($opee =~ / /) {
274             if ($opee =~ /^(\S+)\s+(\S+)$/) {
275                 $opee  = $1;
276                 @chans = ($2);
277                 if (!&validChan($2)) {
278                     &msg($who,"error: invalid chan ($2).");
279                     return;
280                 }
281             } else {
282                 &msg($who,"error: invalid params.");
283                 return;
284             }
285         } else {
286             @chans = keys %channels;
287         }
288
289         my $found = 0;
290         my $op = 0;
291         foreach (@chans) {
292             next unless (&IsNickInChan($opee,$_));
293             $found++;
294             if (!exists $channels{$_}{'o'}{$opee}) {
295                 &status("deop: $opee already has no ops on $_");
296                 next;
297             }
298             $op++;
299
300             &status("deopping $opee on $_ at ${who}'s request");
301             &deop($_, $opee);
302         }
303
304         if ($found != $op) {
305             &status("deop: deopped on all possible channels.");
306         } else {
307             &DEBUG("deop: found => '$found'.");
308             &DEBUG("deop: op => '$op'.");
309         }
310
311         return;
312     }
313
314     # say.
315     if ($message =~ s/^say\s+(\S+)\s+(.*)//) {
316         return unless (&hasFlag("o"));
317         my ($chan,$msg) = (lc $1, $2);
318         &DEBUG("chan => '$1', msg => '$msg'.");
319
320         if (&validChan($chan)) {
321             &msg($chan, $2);
322         } else {
323             &msg($who,"i'm not on \002$1\002, sorry.");
324         }
325         return;
326     }
327
328     # die.
329     if ($message =~ /^die$/) {
330         return unless (&hasFlag("n"));
331
332         &doExit();
333
334         &status("Dying by $who\'s request");
335         exit 0;
336     }
337
338     # global factoid substitution.
339     if ($message =~ m|^s([/,#])(.+?)\1(.*?)\1;?\s*$|) {
340         my ($delim,$op,$np) = ($1, $2, $3);
341         return unless (&hasFlag("n"));
342         ### TODO: support flags to do full-on global.
343
344         # incorrect format.
345         if ($np =~ /$delim/) {
346             &performReply("looks like you used the delimiter too many times. You may want to use a different delimiter, like ':' or '#'.");
347             return;
348         }
349
350         ### TODO: fix up $op to support mysql/pgsql/dbm(perl)
351         ### TODO: => add db/sql specific function to fix this.
352         my @list = &searchTable("factoids", "factoid_key",
353                         "factoid_value", $op);
354
355         if (!scalar @list) {
356             &performReply("Expression didn't match anything.");
357             return;
358         }
359
360         if (scalar @list > 100) {
361             &performReply("regex found more than 100 matches... not doing.");
362             return;
363         }
364
365         &status("gsubst: going to alter ".scalar(@list)." factoids.");
366         &performReply("going to alter ".scalar(@list)." factoids.");
367
368         my $error = 0;
369         foreach (@list) {
370             my $faqtoid = $_;
371
372             next if (&IsLocked($faqtoid) == 1);
373             my $result = &getFactoid($faqtoid);
374             my $was = $result;
375             &DEBUG("was($faqtoid) => '$was'.");
376
377             # global global
378             # we could support global local (once off).
379             if ($result =~ s/\Q$op/$np/gi) {
380                 if (length $result > $param{'maxDataSize'}) {
381                     &performReply("that's too long (or was long)");
382                     return;
383                 }
384                 &setFactInfo($faqtoid, "factoid_value", $result);
385                 &status("update: '$faqtoid' =is=> '$result'; was '$was'");
386             } else {
387                 &WARN("subst: that's weird... thought we found the string ($op) in '$faqtoid'.");
388                 $error++;
389             }
390         }
391
392         if ($error) {
393             &ERROR("Some warnings/errors?");
394         }
395
396         &performReply("Ok... did s/$op/$np/ for ".
397                                 (scalar(@list) - $error)." factoids");
398
399         return;
400     }
401
402     # jump.
403     if ($message =~ /^jump(\s+(\S+))?$/i) {
404         return unless (&hasFlag("n"));
405
406         if ($2 eq "") {
407             &help("jump");
408             return;
409         }
410
411         my ($server,$port);
412         if ($2 =~ /^(\S+)(:(\d+))?$/) {
413             $server = $1;
414             $port   = $3 || 6667;
415         } else {
416             &msg($who,"invalid format.");
417             return;
418         }
419
420         &status("jumping servers... $server...");
421         &rawout("QUIT :jumping to $server");
422
423         if (&irc($server,$port) == 0) {
424             &ircloop();
425         }
426     }
427
428     # reload.
429     if ($message =~ /^reload$/i) {
430         return unless (&hasFlag("n"));
431
432         &status("USER reload $who");
433         &pSReply("reloading...");
434         &reloadAllModules();
435         &pSReply("reloaded.");
436
437         return;
438     }
439
440     # reset.
441     if ($message =~ /^reset$/i) {
442         return unless (&hasFlag("n"));
443
444         &msg($who,"resetting...");
445         my @done;
446         foreach ( keys %channels, keys %chanconf ) {
447             &DEBUG("reset: c => $c");
448             next if (grep /^\Q$_\E$/i, @done);
449
450             &DEBUG("reset: should part... c => $c");
451             &part($_);
452
453             push(@done, $_);
454             sleep 1;
455         }
456         &DEBUG("before clearircvars");
457         &clearIRCVars();
458         &DEBUG("before joinnextchan");
459         &joinNextChan();
460         &DEBUG("after joinnextchan");
461
462         &status("USER reset $who");
463         &msg($who,"resetted");
464
465         return;
466     }
467
468     # rehash.
469     if ($message =~ /^rehash$/) {
470         return unless (&hasFlag("n"));
471
472         &msg($who,"rehashing...");
473         &restart("REHASH");
474         &status("USER rehash $who");
475         &msg($who,"rehashed");
476
477         return;
478     }
479
480     #####
481     ##### USER//CHAN SPECIFIC CONFIGURATION COMMANDS
482     #####
483
484     if ($message =~ /^chaninfo(\s+(.*))?$/) {
485         my @args = split /[\s\t]+/, $2; # hrm.
486
487         if (scalar @args != 1) {
488             &help("chaninfo");
489             return;
490         }
491
492         if (!exists $chanconf{$args[0]}) {
493             &pSReply("no such channel $args[0]");
494             return;
495         }
496
497         &pSReply("showing channel conf.");
498         foreach (sort keys %{ $chanconf{$args[0]} }) {
499             &pSReply("$chan: $_ => $chanconf{$args[0]}{$_}");
500         }
501         &pSReply("End of chaninfo.");
502
503         return;
504     }
505
506     # +chan.
507     if ($message =~ /^(chanset|\+chan)(\s+(.*?))?$/) {
508         my $cmd         = $1;
509         my $args        = $3;
510         my $no_chan     = 0;
511
512         if (!defined $args) {
513             &help($cmd);
514             return;
515         }
516
517         my @chans;
518         while ($args =~ s/^($mask{chan})\s*//) {
519             push(@chans, $1);
520         }
521
522         if (!scalar @chans) {
523             push(@chans, "_default");
524             $no_chan    = 1;
525         }
526
527         my($what,$val) = split /[\s\t]+/, $args, 2;
528
529         ### TODO: "cannot set values without +m".
530         return unless (&hasFlag("n"));
531
532         # READ ONLY.
533         if (defined $what and $what !~ /^[-+]/ and !defined $val and $no_chan) {
534             &pSReply("Showing $what values on all channels...");
535
536             my %vals;
537             foreach (keys %chanconf) {
538                 my $val = $chanconf{$_}{$what} || "NOT-SET";
539                 $vals{$val}{$_} = 1;
540             }
541
542             foreach (keys %vals) {
543                 &pSReply("  $what = $_: ".join(' ', keys %{ $vals{$_} } ) );
544             }
545
546             &pSReply("End of list.");
547
548             return;
549         }
550
551         ### TODO: move to UserDCC again.
552         if ($cmd eq "chanset" and !defined $what) {
553             &DEBUG("showing channel conf.");
554
555             foreach $chan ($chan, "_default") {
556                 &pSReply("chan: $chan");
557                 ### TODO: merge 2 or 3 per line.
558                 my @items;
559                 my $str = "";
560                 foreach (sort keys %{ $chanconf{$chan} }) {
561                     my $newstr = join(', ', @items);
562                     if (length $newstr > 60) {
563                         &pSReply("    $str");
564                         @items = ();
565                     }
566                     $str = $newstr;
567                     push(@items, "$_ => $chanconf{$chan}{$_}");
568                 }
569                 &pSReply("    $str") if (@items);
570             }
571             return;
572         }
573
574         $cache{confvars}{$what} = $val;
575         &rehashConfVars();
576
577         foreach (@chans) {
578             &chanSet($cmd, $_, $what, $val);
579         }
580
581         return;
582     }
583
584     if ($message =~ /^(chanunset|\-chan)(\s+(.*))?$/) {
585         return unless (&hasFlag("n"));
586         my $args        = $3;
587         my $no_chan     = 0;
588
589         if (!defined $args) {
590             &help("chanunset");
591             return;
592         }
593
594         my ($chan);
595         my $delete      = 0;
596         if ($args =~ s/^(\-)?($mask{chan})\s*//) {
597             $chan       = $2;
598             $delete     = ($1) ? 1 : 0;
599             &DEBUG("chan => $chan.");
600         } else {
601             &VERB("no chan arg; setting to default.",2);
602             $chan       = "_default";
603             $no_chan    = 1;
604         }
605
606         if (!exists $chanconf{$chan}) {
607             &pSReply("no such channel $chan");
608             return;
609         }
610
611         if ($args ne "") {
612
613             if (!&getChanConf($args,$chan)) {
614                 &pSReply("$args does not exist for $chan");
615                 return;
616             }
617
618             my @chans = &ChanConfList($args);
619             &DEBUG("scalar chans => ".scalar(@chans) );
620             if (scalar @chans == 1 and $chans[0] eq "_default" and !$no_chan) {
621                 &psReply("ok, $args was set only for _default; unsetting for _defaul but setting for other chans.");
622
623                 my $val = $chanconf{$_}{_default};
624                 foreach (keys %chanconf) {
625                     $chanconf{$_}{$args} = $val;
626                 }
627                 delete $chanconf{_default}{$args};
628                 $cache{confvars}{$args} = 0;
629                 &rehashConfVars();
630
631                 return;
632             }
633
634             if ($no_chan and !exists($chanconf{_default}{$args})) {
635                 &pSReply("ok, $args for _default does not exist, removing from all chans.");
636
637                 foreach (keys %chanconf) {
638                     next unless (exists $chanconf{$_}{$args});
639                     &DEBUG("delete chanconf{$_}{$args};");
640                     delete $chanconf{$_}{$args};
641                 }
642                 $cache{confvars}{$args} = 0;
643                 &rehashConfVars();
644
645                 return;
646             }
647
648             &pSReply("Unsetting channel ($chan) option $args. (was $chanconf{$chan}{$args})");
649             delete $chanconf{$chan}{$args};
650
651             return;
652         }
653
654         if ($delete) {
655             &pSReply("Deleting channel $chan for sure!");
656             $utime_chanfile = time();
657             $ucount_chanfile++;
658
659             &part($chan);
660             &pSReply("Leaving $chan...");
661
662             delete $chanconf{$chan};
663         } else {
664             &pSReply("Prefix channel with '-' to delete for sure.");
665         }
666
667         return;
668     }
669
670     if ($message =~ /^newpass(\s+(.*))?$/) {
671         my(@args) = split /[\s\t]+/, $2 || '';
672
673         if (scalar @args != 1) {
674             &help("newpass");
675             return;
676         }
677
678         my $u           = &getUser($who);
679         my $crypt       = &mkcrypt($args[0]);
680
681         &pSReply("Set your passwd to '$crypt'");
682         $users{$u}{PASS} = $crypt;
683
684         $utime_userfile = time();
685         $ucount_userfile++;
686
687         return;
688     }
689
690     if ($message =~ /^chpass(\s+(.*))?$/) {
691         my(@args) = split /[\s\t]+/, $2 || '';
692
693         if (!scalar @args) {
694             &help("chpass");
695             return;
696         }
697
698         if (!&IsUser($args[0])) {
699             &pSReply("user $args[0] is not valid.");
700             return;
701         }
702
703         my $u = &getUser($args[0]);
704         if (!defined $u) {
705             &pSReply("Internal error, u = NULL.");
706             return;
707         }
708
709         if (scalar @args == 1) {        # del pass.
710             if (!&IsFlag("n") and $who !~ /^\Q$verifyUser\E$/i) {
711                 &pSReply("cannot remove passwd of others.");
712                 return;
713             }
714
715             if (!exists $users{$u}{PASS}) {
716                 &pSReply("$u does not have pass set anyway.");
717                 return;
718             }
719
720             &pSReply("Deleted pass from $u.");
721
722             $utime_userfile = time();
723             $ucount_userfile++;
724
725             delete $users{$u}{PASS};
726
727             return;
728         }
729
730         my $crypt       = &mkcrypt($args[1]);
731         &pSReply("Set $u's passwd to '$crypt'");
732         $users{$u}{PASS} = $crypt;
733
734         $utime_userfile = time();
735         $ucount_userfile++;
736
737         return;
738     }
739
740     if ($message =~ /^chattr(\s+(.*))?$/) {
741         my(@args) = split /[\s\t]+/, $2 || '';
742
743         if (!scalar @args) {
744             &help("chattr");
745             return;
746         }
747
748         my $user;
749         if ($args[0] =~ /^$mask{nick}$/i) {     # <nick>
750             $user       = &getUser($args[0]);
751             $chflag     = $args[1];
752         } else {                                # <flags>
753             $user       = &getUser($who);
754             &DEBUG("user $who... nope.") unless (defined $user);
755             $user       = &getUser($verifyUser);
756             $chflag     = $args[0];
757         }
758
759         if (!defined $user) {
760             &pSReply("user does not exist.");
761             return;
762         }
763
764         my $flags = $users{$user}{FLAGS};
765         if (!defined $chflag) {
766             &pSReply("Flags for $user: $flags");
767             return;
768         }
769
770         &DEBUG("who => $who");
771         &DEBUG("verifyUser => $verifyUser");
772         if (!&IsFlag("n") and $who !~ /^\Q$verifyUser\E$/i) {
773             &pSReply("cannto change attributes of others.");
774             return "REPLY";
775         }
776
777         my $state;
778         my $change      = 0;
779         foreach (split //, $chflag) {
780             if ($_ eq "+") { $state = 1; next; }
781             if ($_ eq "-") { $state = 0; next; }
782
783             if (!defined $state) {
784                 &pSReply("no initial + or - was found in attr.");
785                 return;
786             }
787
788             if ($state) {
789                 next if ($flags =~ /\Q$_\E/);
790                 $flags .= $_;
791             } else {
792                 if (&IsParam("owner")
793                         and $param{owner} =~ /^\Q$user\E$/i
794                         and $flags =~ /[nmo]/
795                 ) {
796                     &pSReply("not removing flag $_ for $user.");
797                     next;
798                 }
799                 next unless ($flags =~ s/\Q$_\E//);
800             }
801
802             $change++;
803         }
804
805         if ($change) {
806             $utime_userfile = time();
807             $ucount_userfile++;
808             &pSReply("Current flags: $flags");
809             $users{$user}{FLAGS} = $flags;
810         } else {
811             &pSReply("No flags changed: $flags");
812         }
813
814         return;
815     }
816
817     if ($message =~ /^chnick(\s+(.*))?$/) {
818         my(@args) = split /[\s\t]+/, $2 || '';
819
820         if ($who eq "_default") {
821             &WARN("$who or verifyuser tried to run chnick.");
822             return "REPLY";
823         }
824
825         if (!scalar @args or scalar @args > 2) {
826             &help("chnick");
827             return;
828         }
829
830         if (scalar @args == 1) {        # 1
831             $user       = &getUser($who);
832             &DEBUG("nope, not $who.") unless (defined $user);
833             $user       ||= &getUser($verifyUser);
834             $chnick     = $args[0];
835         } else {                        # 2
836             $user       = &getUser($args[0]);
837             $chnick     = $args[1];
838         }
839
840         if (!defined $user) {
841             &pSReply("user $who or $args[0] does not exist.");
842             return;
843         }
844
845         if ($user =~ /^\Q$chnick\E$/i) {
846             &pSReply("user == chnick. why should I do that?");
847             return;
848         }
849
850         if (&getUser($chnick)) {
851             &pSReply("user $chnick is already used!");
852             return;
853         }
854
855         if (!&IsFlag("n") and $who !~ /^\Q$verifyUser\E$/i) {
856             &pSReply("cannto change nick of others.");
857             return "REPLY" if ($who eq "_default");
858             return;
859         }
860
861         foreach (keys %{ $users{$user} }) {
862             $users{$chnick}{$_} = $users{$user}{$_};
863             delete $users{$user}{$_};
864         }
865         undef $users{$user};    # ???
866
867         $utime_userfile = time();
868         $ucount_userfile++;
869
870         &pSReply("Changed '$user' to '$chnick' successfully.");
871
872         return;
873     }
874
875     if ($message =~ /^([-+])host(\s+(.*))?$/) {
876         my $cmd         = $1."host";
877         my(@args)       = split /[\s\t]+/, $3 || '';
878         my $state       = ($1 eq "+") ? 1 : 0;
879
880         if (!scalar @args) {
881             &help($cmd);
882             return;
883         }
884
885         if ($who eq "_default") {
886             &WARN("$who or verifyuser tried to run $cmd.");
887             return "REPLY";
888         }
889
890         my ($user,$mask);
891         if ($args[0] =~ /^$mask{nick}$/i) {     # <nick>
892             return unless (&hasFlag("n"));
893             $user       = &getUser($args[0]);
894             $mask       = $args[1];
895         } else {                                # <mask>
896             # who or verifyUser. FIXME!!!
897             $user       = &getUser($who);
898             $mask       = $args[0];
899         }
900
901         if (!defined $user) {
902             &pSReply("user $user does not exist.");
903             return;
904         }
905
906         if (!defined $mask) {
907             ### FIXME.
908             &pSReply("Hostmasks for $user: $users{$user}{HOSTS}");
909
910             return;
911         }
912
913         if (!&IsFlag("n") and $who !~ /^\Q$verifyUser\E$/i) {
914             &pSReply("cannto change masks of others.");
915             return;
916         }
917
918         if ($mask !~ /^$mask{nuh}$/) {
919             &pSReply("error: mask ($mask) is not a real hostmask.");
920             return;
921         }
922
923         my $count = scalar keys %{ $users{$user}{HOSTS} };
924
925         if ($state) {                           # add.
926             if (exists $users{$user}{HOSTS}{$mask}) {
927                 &pSReply("mask $mask already exists.");
928                 return;
929             }
930
931             ### TODO: override support.
932             $users{$user}{HOSTS}{$mask} = 1;
933
934             &pSReply("Added $mask to list of masks.");
935
936         } else {                                # delete.
937
938             if (!exists $users{$user}{HOSTS}{$mask}) {
939                 &pSReply("mask $mask does not exist.");
940                 return;
941             }
942
943             ### TODO: wildcard support. ?
944             delete $users{$user}{HOSTS}{$mask};
945
946             if (scalar keys %{ $users{$user}{HOSTS} } != $count) {
947                 &pSReply("Removed $mask from list of masks.");
948             } else {
949                 &pSReply("error: could not find $mask in list of masks.");
950                 return;
951             }
952         }
953
954         $utime_userfile = time();
955         $ucount_userfile++;
956
957         return;
958     }
959
960     if ($message =~ /^([-+])ban(\s+(.*))?$/) {
961         my $cmd         = $1."ban";
962         my $flatarg     = $3;
963         my(@args)       = split /[\s\t]+/, $3 || '';
964         my $state       = ($1 eq "+") ? 1 : 0;
965
966         if (!scalar @args) {
967             &help($cmd);
968             return;
969         }
970
971         my($mask,$chan,$time,$reason);
972
973         if ($flatarg =~ s/^($mask{nuh})\s*//) {
974             $mask = $1;
975         } else {
976             &DEBUG("arg does not contain nuh mask?");
977         }
978
979         if ($flatarg =~ s/^($mask{chan})\s*//) {
980             $chan = $1;
981         } else {
982             $chan = "*";        # _default instead?
983         }
984
985         if ($state == 0) {              # delete.
986             my @c = &banDel($mask);
987
988             foreach (@c) {
989                 &unban($mask, $_);
990             }
991
992             if ($c) {
993                 &pSReply("Removed $mask from chans: @c");
994             } else {
995                 &pSReply("$mask was not found in ban list.");
996             }
997
998             return;
999         }
1000
1001         ###
1002         # add ban.
1003         ###
1004
1005         # time.
1006         if ($flatarg =~ s/^(\d+)\s*//) {
1007             $time = $1;
1008             &DEBUG("time = $time.");
1009             if ($time < 0) {
1010                 &pSReply("error: time cannot be negatime?");
1011                 return;
1012             }
1013         } else {
1014             $time = 0;
1015         }
1016
1017         if ($flatarg =~ s/^(.*)$//) {   # need length?
1018             $reason     = $1;
1019         }
1020
1021         if (!&IsFlag("n") and $who !~ /^\Q$verifyUser\E$/i) {
1022             &pSReply("cannto change masks of others.");
1023             return;
1024         }
1025
1026         if ($mask !~ /^$mask{nuh}$/) {
1027             &pSReply("error: mask ($mask) is not a real hostmask.");
1028             return;
1029         }
1030
1031         if ( &banAdd($mask,$chan,$time,$reason) == 2) {
1032             &pSReply("ban already exists; overwriting.");
1033         }
1034         &pSReply("Added $mask for $chan (time => $time, reason => $reason)");
1035
1036         return;
1037     }
1038
1039     if ($message =~ /^whois(\s+(.*))?$/) {
1040         my $arg = $2;
1041
1042         if (!defined $arg) {
1043             &help("whois");
1044             return;
1045         }
1046
1047         my $user = &getUser($arg);
1048         if (!defined $user) {
1049             &pSReply("whois: user $user does not exist.");
1050             return;
1051         }
1052
1053         ### TODO: better (eggdrop-like) output.
1054         &pSReply("user: $user");
1055         foreach (keys %{ $users{$user} }) {
1056             my $ref = ref $users{$user}{$_};
1057
1058             if ($ref eq "HASH") {
1059                 my $type = $_;
1060                 ### DOES NOT WORK???
1061                 foreach (keys %{ $users{$user}{$type} }) {
1062                     &pSReply("    $type => $_");
1063                 }
1064                 next;
1065             }
1066
1067             &pSReply("    $_ => $users{$user}{$_}");
1068         }
1069         &pSReply("End of USER whois.");
1070
1071         return;
1072     }
1073
1074     if ($message =~ /^bans(\s+(.*))?$/) {
1075         my $arg = $2;
1076
1077         if (defined $arg) {
1078             if ($arg ne "_default" and !&validChan($arg) ) {
1079                 &pSReply("error: chan $chan is invalid.");
1080                 return;
1081             }
1082         }
1083
1084         if (!scalar keys %bans) {
1085             &pSReply("Ban list is empty.");
1086             return;
1087         }
1088
1089         my $c;
1090         &pSReply("     mask: expire, time-added, count, who-by, reason");
1091         foreach $c (keys %bans) {
1092             next unless (!defined $arg or $arg =~ /^\Q$c\E$/i);
1093             &pSReply("  $c:");
1094
1095             foreach (keys %{ $bans{$c} }) {
1096                 my $val = $bans{$c}{$_};
1097
1098                 if (ref $val eq "ARRAY") {
1099                     my @array = @{ $val };
1100                     &pSReply("    $_: @array");
1101                 } else {
1102                     &DEBUG("unknown ban: $val");
1103                 }
1104             }
1105         }
1106         &pSReply("END of bans.");
1107
1108         return;
1109     }
1110
1111     if ($message =~ /^banlist(\s+(.*))?$/) {
1112         my $arg = $2;
1113
1114         if (defined $arg and $arg !~ /^$mask_chan$/) {
1115             &pSReply("error: chan $chan is invalid.");
1116             return;
1117         }
1118
1119         &DEBUG("bans for global or arg => $arg.");
1120         foreach (keys %bans) {                  #CHANGE!!!
1121             &DEBUG("  $_ => $bans{$_}.");
1122         }
1123
1124         &DEBUG("End of bans.");
1125         &pSReply("END of bans.");
1126
1127         return;
1128     }
1129
1130     if ($message =~ /^save$/) {
1131         return unless (&hasFlag("o"));
1132
1133         &writeUserFile();
1134         &writeChanFile();
1135         &News::writeNews() if (&ChanConfList("news"));
1136
1137         return;
1138     }
1139
1140     ### ALIASES.
1141     $message =~ s/^addignore/+ignore/;
1142     $message =~ s/^(del|un)ignore/-ignore/;
1143
1144     # ignore.
1145     if ($message =~ /^(\+|\-)ignore(\s+(.*))?$/i) {
1146         return unless (&hasFlag("o"));
1147         my $state       = ($1 eq "+") ? 1 : 0;
1148         my $str         = $1."ignore";
1149         my $args        = $3;
1150
1151         if (!$args) {
1152             &help($str);
1153             return;
1154         }
1155
1156         my($mask,$chan,$time,$comment);
1157
1158         # mask.
1159         if ($args =~ s/^($mask{nuh})\s*//) {
1160             $mask = $1;
1161         } else {
1162             &ERROR("no NUH mask?");
1163             return;
1164         }
1165
1166         if (!$state) {                  # delignore.
1167             if ( &ignoreDel($mask) ) {
1168                 &pSReply("ok, deleted X ignores.");
1169             } else {
1170                 &pSReply("could not find $mask in ignore list.");
1171             }
1172             return;
1173         }
1174
1175         ###
1176         # addignore.
1177         ###
1178
1179         # chan.
1180         if ($args =~ s/^($mask{chan}|\*)\s*//) {
1181             $chan = $1;
1182         } else {
1183             $chan = "*";
1184         }
1185
1186         # time.
1187         if ($args =~ s/^(\d+)\s*//) {
1188             $time = $1*60;      # ??
1189         } else {
1190             $time = 0;
1191         }
1192
1193         # time.
1194         if ($args) {
1195             $comment = $args;
1196         } else {
1197             $comment = "added by $who";
1198         }
1199
1200         if ( &ignoreAdd($mask, $chan, $time, $comment) > 1) {
1201             &pSReply("warn: $mask already in ignore list; written over anyway. FIXME");
1202         } else {
1203             &pSReply("added $mask to ignore list.");
1204         }
1205
1206         return;
1207     }
1208
1209     if ($message =~ /^ignore(\s+(.*))?$/) {
1210         my $arg = $2;
1211
1212         if (defined $arg) {
1213             if ($arg !~ /^$mask{chan}$/) {
1214                 &pSReply("error: chan $chan is invalid.");
1215                 return;
1216             }
1217
1218             if (!&validChan($arg)) {
1219                 &pSReply("error: chan $arg is invalid.");
1220                 return;
1221             }
1222
1223             &pSReply("Showing bans for $arg only.");
1224         }
1225
1226         if (!scalar keys %ignore) {
1227             &pSReply("Ignore list is empty.");
1228             return;
1229         }
1230
1231         ### TODO: proper (eggdrop-like) formatting.
1232         my $c;
1233         &pSReply("    mask: expire, time-added, who, comment");
1234         foreach $c (keys %ignore) {
1235             next unless (!defined $arg or $arg =~ /^\Q$c\E$/i);
1236             &pSReply("  $c:");
1237
1238             foreach (keys %{ $ignore{$c} }) {
1239                 my $ref = ref $ignore{$c}{$_};
1240                 if ($ref eq "ARRAY") {
1241                     my @array = @{ $ignore{$c}{$_} };
1242                     &pSReply("      $_: @array");
1243                 } else {
1244                     &DEBUG("unknown ignore line?");
1245                 }
1246             }
1247         }
1248         &pSReply("END of ignore.");
1249
1250         return;
1251     }
1252
1253     # adduser/deluser.
1254     if ($message =~ /^(\+|\-|add|del)user(\s+(.*))?$/i) {
1255         my $str         = $1;
1256         my $strstr      = $1."user";
1257         my @args        = split /\s+/, $3 || '';
1258         my $args        = $3;
1259         my $state       = ($str =~ /^(\+|add)$/) ? 1 : 0;
1260
1261         if (!scalar @args) {
1262             &help($strstr);
1263             return;
1264         }
1265
1266         if ($str eq "+") {
1267             if (scalar @args != 2) {
1268                 &pSReply(".+host requires hostmask argument.");
1269                 return;
1270             }
1271         } elsif (scalar @args != 1) {
1272             &pSReply("too many arguments.");
1273             return;
1274         }
1275
1276         if ($state) {                   # adduser.
1277             if (scalar @args == 1) {
1278                 $args[1]        = &getHostMask($args[0]);
1279                 &pSReply("Attemping to guess $args[0]'s hostmask...");
1280
1281                 # crude hack... crappy Net::IRC
1282                 $conn->schedule(5, sub {
1283         # hopefully this is right.
1284         my $nick = (keys %{ $cache{nuhInfo} })[0];
1285         if (!defined $nick) {
1286             &pSReply("couldn't get nuhinfo... adding user without a hostmask.");
1287             &userAdd($nick);
1288             return;
1289         }
1290
1291         my $mask = &makeHostMask( $cache{nuhInfo}{$nick}{NUH} );
1292
1293         if ( &userAdd($nick, $mask) ) { # success.
1294                 &pSReply("Added $nick with flags $users{$nick}{FLAGS}");
1295                 my @hosts = keys %{ $users{$nick}{HOSTS} };
1296                 &pSReply("hosts: @hosts");
1297         }
1298 });
1299                 return;
1300             }
1301
1302             &DEBUG("args => @args");
1303             if ( &userAdd(@args) ) {    # success.
1304                 &pSReply("Added $args[0] with flags $users{$args[0]}{FLAGS}");
1305                 my @hosts = keys %{ $users{$args[0]}{HOSTS} };
1306                 &pSReply("hosts: @hosts");
1307
1308             } else {                    # failure.
1309                 &pSReply("User $args[0] already exists");
1310             }
1311
1312         } else {                        # deluser.
1313
1314             if ( &userDel($args[0]) ) { # success.
1315                 &pSReply("Deleted $args[0] successfully.");
1316
1317             } else {                    # failure.
1318                 &pSReply("User $args[0] does not exist.");
1319             }
1320
1321         }
1322         return;
1323     }
1324
1325     if ($message =~ /^sched$/) {
1326         my @list;
1327         my @run;
1328
1329         my %time;
1330         foreach (keys %sched) {
1331             next unless (exists $sched{$_}{TIME});
1332             $time{ $sched{$_}{TIME}-time() }{$_} = 1;
1333             push(@list,$_);
1334
1335             next unless (exists $sched{$_}{RUNNING});
1336             push(@run,$_);
1337         }
1338
1339         my @time;
1340         foreach (sort { $a <=> $b } keys %time) {
1341             my $str = join(", ", sort keys %{ $time{$_} });
1342             &DEBUG("time => $_, str => $str");
1343             push(@time, "$str (".&Time2String($_).")");
1344         }
1345
1346         &pSReply( &formListReply(0, "Schedulers: ", @time ) );
1347         &pSReply( &formListReply(0, "Scheds to run: ", sort @list ) );
1348         &pSReply( &formListReply(0, "Scheds running(should not happen?) ", sort @run ) );
1349
1350         return;
1351     }
1352
1353     # quite a cool hack: reply in DCC CHAT.
1354     $msgType = "chat";
1355
1356     my $done = 0;
1357     $done++ if &parseCmdHook("main", $message);
1358     $done++ if &parseCmdHook("extra", $message);
1359     $done++ unless (&Modules());
1360
1361     if ($done) {
1362         &DEBUG("running non DCC CHAT command inside DCC CHAT!");
1363         return;
1364     }
1365
1366     return "REPLY";
1367 }
1368
1369 1;