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