]> git.donarmstrong.com Git - infobot.git/blob - src/Modules/UserDCC.pl
fixed '.chanset' code.
[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("hrmm....");
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 $chan;
474         if ($args =~ s/^($mask{chan})\s*//) {
475             $chan       = $1;
476         } else {
477             $chan       = "_default";
478             $no_chan    = 1;
479         }
480
481         my($what,$val) = split /[\s\t]+/, $args, 2;
482
483         ### TODO: "cannot set values without +m".
484         return unless (&hasFlag("m"));
485
486         if ($cmd eq "+chan") {
487             &DEBUG("setting _time_added...");
488             $chanconf{$chan}{_time_added}       = time();
489
490             &pSReply("Joining $chan...");
491             &joinchan($chan);
492
493             return;
494         }
495
496         if (!exists $chanconf{$chan}) {
497             &pSReply("no such channel $chan");
498             return;
499         }
500
501         my $update      = 0;
502         ### TODO: $what can be undefined. fix it!
503         if ($what =~ s/^\+(\S+)/$1/) {
504             my $was     = $chanconf{$chan}{$1};
505             if ($was eq "1") {
506                 &pSReply("setting $what for $chan already 1.");
507                 return;
508             }
509
510             $was        = ($was) ? "; was '$was'" : "";
511             &pSReply("Setting $what for $chan to '1'$was.");
512
513             $chanconf{$chan}{$what} = 1;
514
515             $update++;
516         } elsif ($what =~ s/^\-(\S+)/$1/) {
517             my $was     = $chanconf{$chan}{$1};
518             # hrm...
519             if (!defined $was) {
520                 &pSReply("setting $1 for $chan is not set.");
521                 return;
522             }
523
524             if ($was eq "0") {
525                 &pSReply("setting $1 for $chan already 0.");
526                 return;
527             }
528
529             $was        = ($was) ? "; was '$was'" : "";
530             &pSReply("Setting $what for $chan to '0'$was.");
531
532             $chanconf{$chan}{$what} = 0;
533
534             $update++;
535         } elsif (defined $val) {
536             my $was     = $chanconf{$chan}{$what};
537             if ($was eq $val) {
538                 &pSReply("setting $1 for $chan already '$val'.");
539                 return;
540             }
541             $was        = ($was) ? "; was '$was'" : "";
542             &pSReply("Setting $what for $chan to '$val'$was.");
543
544             $chanconf{$chan}{$what} = $val;
545
546             $update++;
547         } elsif (defined $what) {
548             if (exists $chanconf{$chan}{$what}) {
549                 &pSReply("$what for $chan is '$chanconf{$chan}{$what}'");
550             } else {
551                 &pSReply("$what for $chan is not set.'");
552             }
553         }
554
555         if ($update) {
556             $utime_chanfile = time();
557             $ucount_chanfile++;
558             return;
559         }
560
561         if ($cmd eq "chanset" and !defined $what) {
562             &DEBUG("showing channel conf.");
563
564             foreach $chan ($chan, "_default") {
565                 &pSReply("chan: $chan");
566                 ### TODO: merge 2 or 3 per line.
567                 my @items;
568                 my $str = "";
569                 foreach (sort keys %{ $chanconf{$chan} }) {
570                     my $newstr = join(', ', @items);
571                     if (length $newstr > 60) {
572                         &pSReply("    $str");
573                         @items = ();
574                     }
575                     $str = $newstr;
576                     push(@items, "$_ => $chanconf{$chan}{$_}");
577                 }
578             }
579
580             return;
581         }
582
583         return;
584     }
585
586     if ($message =~ /^(chanunset|\-chan)(\s+(.*))?$/) {
587         return unless (&hasFlag("m"));
588         my $args        = $3;
589
590         if (!defined $args) {
591             &help("chanunset");
592             return;
593         }
594
595         my ($chan);
596         my $delete      = 0;
597         if ($args =~ s/^(\-)?($mask{chan})\s*//) {
598             $chan       = $2;
599             $delete     = ($1) ? 1 : 0;
600             &DEBUG("chan => $chan.");
601         } else {
602             &DEBUG("no chan arg; setting to default.");
603             $chan       = "_default";
604         }
605
606         if (!exists $chanconf{$chan}) {
607             &pSReply("no such channel $chan");
608             return;
609         }
610
611         if ($args ne "") {
612             if (&getChanConf($args,$chan)) {
613                 &pSReply("Unsetting channel ($chan) option $args. (was $chanconf{$chan}{$args})");
614                 undef $chanconf{$chan}{$args};
615
616             } else {
617                 &pSReply("$args does not exist for $chan");
618             }
619
620             return;
621         }
622
623         if ($delete) {
624             &pSReply("Deleting channel $chan for sure!");
625             $utime_chanfile = time();
626             $ucount_chanfile++;
627
628             &part($chan);
629             &pSReply("Leaving $chan...");
630
631             delete $chanconf{$chan};
632         } else {
633             &pSReply("Prefix channel with '-' to delete for sure.");
634         }
635
636         return;
637     }
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($who);
654
655         if (scalar @args == 1) {        # del pass.
656             if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
657                 &pSReply("cannto remove passwd of others.");
658                 return;
659             }
660
661             if (!exists $users{$u}{PASS}) {
662                 &pSReply("$u does not have pass set anyway.");
663                 return;
664             }
665
666             &pSReply("Deleted pass from $u.");
667
668             $utime_userfile = time();
669             $ucount_userfile++;
670
671             delete $users{$u}{PASS};
672
673             return;
674         }
675
676         my $salt = join '',('.','/',0..9,'A'..'Z','a'..'z')[rand 64, rand 64];
677         my $crypt = crypt($args[1], $salt);
678         &pSReply("Set $u's passwd to '$crypt'");
679         $users{$u}{PASS} = $crypt;
680
681         $utime_userfile = time();
682         $ucount_userfile++;
683
684         return;
685     }
686
687     if ($message =~ /^chattr(\s+(.*))?$/) {
688         my(@args) = split /[\s\t]+/, $2 || '';
689
690         if (!scalar @args) {
691             &help("chattr");
692             return;
693         }
694
695         my $user;
696         if ($args[0] =~ /^$mask{nick}$/i) {     # <nick>
697             $user       = &getUser($args[0]);
698             $chflag     = $args[1];
699         } else {                                # <flags>
700             $user       = &getUser($who);
701             &DEBUG("user $who... nope.") unless (defined $user);
702             $user       = &getUser($verifyUser);
703             $chflag     = $args[0];
704         }
705
706         if (!defined $user) {
707             &pSReply("user $user does not exist.");
708             return;
709         }
710
711         my $flags = $users{$user}{FLAGS};
712         if (!defined $chflag) {
713             &pSReply("Flags for $user: $flags");
714             return;
715         }
716
717         &DEBUG("who => $who");
718         &DEBUG("verifyUser => $verifyUser");
719         if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
720             &pSReply("cannto change attributes of others.");
721             return "REPLY";
722         }
723
724         my $state;
725         my $change      = 0;
726         foreach (split //, $chflag) {
727             if ($_ eq "+") { $state = 1; next; }
728             if ($_ eq "-") { $state = 0; next; }
729
730             if (!defined $state) {
731                 &pSReply("no initial + or - was found in attr.");
732                 return;
733             }
734
735             if ($state) {
736                 next if ($flags =~ /\Q$_\E/);
737                 $flags .= $_;
738             } else {
739                 if (&IsParam("owner")
740                         and $param{owner} =~ /^\Q$user\E$/i
741                         and $flags =~ /[nmo]/
742                 ) {
743                     &pSReply("not removing flag $_ for $user.");
744                     next;
745                 }
746                 next unless ($flags =~ s/\Q$_\E//);
747             }
748
749             $change++;
750         }
751
752         if ($change) {
753             $utime_userfile = time();
754             $ucount_userfile++;
755             &pSReply("Current flags: $flags");
756             $users{$user}{FLAGS} = $flags;
757         } else {
758             &pSReply("No flags changed: $flags");
759         }
760
761         return;
762     }
763
764     if ($message =~ /^chnick(\s+(.*))?$/) {
765         my(@args) = split /[\s\t]+/, $2 || '';
766
767         if ($who eq "_default") {
768             &WARN("$who or verifyuser tried to run chnick.");
769             return "REPLY";
770         }
771
772         if (!scalar @args or scalar @args > 2) {
773             &help("chnick");
774             return;
775         }
776
777         if (scalar @args == 1) {        # 1
778             $user       = &getUser($who);
779             &DEBUG("nope, not $who.") unless (defined $user);
780             $user       ||= &getUser($verifyUser);
781             $chnick     = $args[0];
782         } else {                        # 2
783             $user       = &getUser($args[0]);
784             $chnick     = $args[1];
785         }
786
787         if (!defined $user) {
788             &pSReply("user $who or $args[0] does not exist.");
789             return;
790         }
791
792         if ($user =~ /^\Q$chnick\E$/i) {
793             &pSReply("user == chnick. why should I do that?");
794             return;
795         }
796
797         if (&getUser($chnick)) {
798             &pSReply("user $chnick is already used!");
799             return;
800         }
801
802         if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
803             &pSReply("cannto change nick of others.");
804             return "REPLY" if ($who eq "_default");
805             return;
806         }
807
808         foreach (keys %{ $users{$user} }) {
809             $users{$chnick}{$_} = $users{$user}{$_};
810             delete $users{$user}{$_};
811         }
812         undef $users{$user};    # ???
813
814         $utime_userfile = time();
815         $ucount_userfile++;
816
817         &pSReply("Changed '$user' to '$chnick' successfully.");
818
819         return;
820     }
821
822     if ($message =~ /^([-+])host(\s+(.*))?$/) {
823         my $cmd         = $1."host";
824         my(@args)       = split /[\s\t]+/, $3 || '';
825         my $state       = ($1 eq "+") ? 1 : 0;
826
827         if (!scalar @args) {
828             &help($cmd);
829             return;
830         }
831
832         if ($who eq "_default") {
833             &WARN("$who or verifyuser tried to run $cmd.");
834             return "REPLY";
835         }
836
837         my ($user,$mask);
838         if ($args[0] =~ /^$mask{nick}$/i) {     # <nick>
839             return unless (&hasFlag("m"));
840             $user       = &getUser($args[0]);
841             $mask       = $args[1];
842         } else {                                # <mask>
843             # who or verifyUser. FIXME!!!
844             $user       = &getUser($who);
845             $mask       = $args[0];
846         }
847
848         if (!defined $user) {
849             &pSReply("user $user does not exist.");
850             return;
851         }
852
853         if (!defined $mask) {
854             ### FIXME.
855             &pSReply("Hostmasks for $user: $users{$user}{HOSTS}");
856
857             return;
858         }
859
860         if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
861             &pSReply("cannto change masks of others.");
862             return;
863         }
864
865         if ($mask !~ /^$mask{nuh}$/) {
866             &pSReply("error: mask ($mask) is not a real hostmask.");
867             return;
868         }
869
870         my $count = scalar keys %{ $users{$user}{HOSTS} };
871
872         if ($state) {                           # add.
873             if (exists $users{$user}{HOSTS}{$mask}) {
874                 &pSReply("mask $mask already exists.");
875                 return;
876             }
877
878             ### TODO: override support.
879             $users{$user}{HOSTS}{$mask} = 1;
880
881             &pSReply("Added $mask to list of masks.");
882
883         } else {                                # delete.
884
885             if (!exists $users{$user}{HOSTS}{$mask}) {
886                 &pSReply("mask $mask does not exist.");
887                 return;
888             }
889
890             ### TODO: wildcard support. ?
891             delete $users{$user}{HOSTS}{$mask};
892
893             if (scalar keys %{ $users{$user}{HOSTS} } != $count) {
894                 &pSReply("Removed $mask from list of masks.");
895             } else {
896                 &pSReply("error: could not find $mask in list of masks.");
897                 return;
898             }
899         }
900
901         $utime_userfile = time();
902         $ucount_userfile++;
903
904         return;
905     }
906
907     if ($message =~ /^([-+])ban(\s+(.*))?$/) {
908         my $cmd         = $1."ban";
909         my $flatarg     = $3;
910         my(@args)       = split /[\s\t]+/, $3 || '';
911         my $state       = ($1 eq "+") ? 1 : 0;
912
913         if (!scalar @args) {
914             &help($cmd);
915             return;
916         }
917
918         my($mask,$chan,$time,$reason);
919
920         if ($flatarg =~ s/^($mask{nuh})\s*//) {
921             $mask = $1;
922         } else {
923             &DEBUG("arg does not contain nuh mask?");
924         }
925
926         if ($flatarg =~ s/^($mask{chan})\s*//) {
927             $chan = $1;
928         } else {
929             $chan = "*";        # _default instead?
930         }
931
932         if ($state == 0) {              # delete.
933             my $c = join(' ', &banDel($mask) );
934
935             if ($c) {
936                 &pSReply("Removed $mask from chans: $c");
937             } else {
938                 &pSReply("$mask was not found in ban list.");
939             }
940
941             return;
942         }
943
944         ###
945         # add ban.
946         ###
947
948         # time.
949         if ($flatarg =~ s/^(\d+)\s*//) {
950             $time = $1;
951             &DEBUG("time = $time.");
952             if ($time < 0) {
953                 &pSReply("error: time cannot be negatime?");
954                 return;
955             }
956         } else {
957             $time = 0;
958         }
959
960         if ($flatarg =~ s/^(.*)$//) {   # need length?
961             $reason     = $1;
962         }
963
964         if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
965             &pSReply("cannto change masks of others.");
966             return;
967         }
968
969         if ($mask !~ /^$mask{nuh}$/) {
970             &pSReply("error: mask ($mask) is not a real hostmask.");
971             return;
972         }
973
974         if ( &banAdd($mask,$chan,$time,$reason) == 2) {
975             &pSReply("ban already exists; overwriting.");
976         }
977         &pSReply("Added $mask for $chan (time => $time, reason => $reason)");
978
979         return;
980     }
981
982     if ($message =~ /^whois(\s+(.*))?$/) {
983         my $arg = $2;
984
985         if (!defined $arg) {
986             &help("whois");
987             return;
988         }
989
990         my $user = &getUser($arg);
991         if (!defined $user) {
992             &pSReply("whois: user $user does not exist.");
993             return;
994         }
995
996         ### TODO: better (eggdrop-like) output.
997         &pSReply("user: $user");
998         foreach (keys %{ $users{$user} }) {
999             my $ref = ref $users{$user}{$_};
1000
1001             if ($ref eq "HASH") {
1002                 my $type = $_;
1003                 ### DOES NOT WORK???
1004                 foreach (keys %{ $users{$user}{$type} }) {
1005                     &pSReply("    $type => $_");
1006                 }
1007                 next;
1008             }
1009
1010             &pSReply("    $_ => $users{$user}{$_}");
1011         }
1012         &pSReply("End of USER whois.");
1013
1014         return;
1015     }
1016
1017     if ($message =~ /^bans(\s+(.*))?$/) {
1018         my $arg = $2;
1019
1020         if (defined $arg) {
1021             if ($arg ne "_default" and !&validChan($arg) ) {
1022                 &pSReply("error: chan $chan is invalid.");
1023                 return;
1024             }
1025         }
1026
1027         if (!scalar keys %bans) {
1028             &pSReply("Ban list is empty.");
1029             return;
1030         }
1031
1032         my $c;
1033         &pSReply("     mask: expire, time-added, count, who-by, reason");
1034         foreach $c (keys %bans) {
1035             next unless (!defined $arg or $arg =~ /^\Q$c\E$/i);
1036             &pSReply("  $c:");
1037
1038             foreach (keys %{ $bans{$c} }) {
1039                 my $val = $bans{$c}{$_};
1040
1041                 if (ref $val eq "ARRAY") {
1042                     my @array = @{ $val };
1043                     &pSReply("    $_: @array");
1044                 } else {
1045                     &DEBUG("unknown ban: $val");
1046                 }
1047             }
1048         }
1049         &pSReply("END of bans.");
1050
1051         return;
1052     }
1053
1054     if ($message =~ /^banlist(\s+(.*))?$/) {
1055         my $arg = $2;
1056
1057         if (defined $arg and $arg !~ /^$mask_chan$/) {
1058             &pSReply("error: chan $chan is invalid.");
1059             return;
1060         }
1061
1062         &DEBUG("bans for global or arg => $arg.");
1063         foreach (keys %bans) {                  #CHANGE!!!
1064             &DEBUG("  $_ => $bans{$_}.");
1065         }
1066
1067         &DEBUG("End of bans.");
1068         &pSReply("END of bans.");
1069
1070         return;
1071     }
1072
1073     if ($message =~ /^save$/) {
1074         return unless (&hasFlag("o"));
1075
1076         &writeUserFile();
1077         &writeChanFile();
1078
1079         return;
1080     }
1081
1082     ### ALIASES.
1083     $message =~ s/^addignore/+ignore/;
1084     $message =~ s/^(del|un)ignore/-ignore/;
1085
1086     # ignore.
1087     if ($message =~ /^(\+|\-)ignore(\s+(.*))?$/i) {
1088         return unless (&hasFlag("o"));
1089         my $state       = ($1 eq "+") ? 1 : 0;
1090         my $str         = $1."ignore";
1091         my $args        = $3;
1092
1093         if (!$args) {
1094             &help($str);
1095             return;
1096         }
1097
1098         my($mask,$chan,$time,$comment);
1099
1100         # mask.
1101         if ($args =~ s/^($mask{nuh})\s*//) {
1102             $mask = $1;
1103         } else {
1104             &ERROR("no NUH mask?");
1105             return;
1106         }
1107
1108         if (!$state) {                  # delignore.
1109             if ( &ignoreDel($mask) ) {
1110                 &pSReply("ok, deleted X ignores.");
1111             } else {
1112                 &pSReply("could not find $mask in ignore list.");
1113             }
1114             return;
1115         }
1116
1117         ###
1118         # addignore.
1119         ###
1120
1121         # chan.
1122         if ($args =~ s/^($mask{chan}|\*)\s*//) {
1123             $chan = $1;
1124         } else {
1125             $chan = "*";
1126         }
1127
1128         # time.
1129         if ($args =~ s/^(\d+)\s*//) {
1130             $time = $1*60;      # ??
1131         } else {
1132             $time = 0;
1133         }
1134
1135         # time.
1136         if ($args) {
1137             $comment = $args;
1138         } else {
1139             $comment = "added by $who";
1140         }
1141
1142         if ( &ignoreAdd($mask, $chan, $time, $comment) > 1) {
1143             &pSReply("warn: $mask already in ignore list; written over anyway. FIXME");
1144         } else {
1145             &pSReply("added $mask to ignore list.");
1146         }
1147
1148         return;
1149     }
1150
1151     if ($message =~ /^ignore(\s+(.*))?$/) {
1152         my $arg = $2;
1153
1154         if (defined $arg) {
1155             if ($arg !~ /^$mask{chan}$/) {
1156                 &pSReply("error: chan $chan is invalid.");
1157                 return;
1158             }
1159
1160             if (!&validChan($arg)) {
1161                 &pSReply("error: chan $arg is invalid.");
1162                 return;
1163             }
1164
1165             &pSReply("Showing bans for $arg only.");
1166         }
1167
1168         if (!scalar keys %ignore) {
1169             &pSReply("Ignore list is empty.");
1170             return;
1171         }
1172
1173         ### TODO: proper (eggdrop-like) formatting.
1174         my $c;
1175         &pSReply("    mask: expire, time-added, who, comment");
1176         foreach $c (keys %ignore) {
1177             next unless (!defined $arg or $arg =~ /^\Q$c\E$/i);
1178             &pSReply("  $c:");
1179
1180             foreach (keys %{ $ignore{$c} }) {
1181                 my $ref = ref $ignore{$c}{$_};
1182                 if ($ref eq "ARRAY") {
1183                     my @array = @{ $ignore{$c}{$_} };
1184                     &pSReply("      $_: @array");
1185                 } else {
1186                     &DEBUG("unknown ignore line?");
1187                 }
1188             }
1189         }
1190         &pSReply("END of ignore.");
1191
1192         return;
1193     }
1194
1195     # adduser/deluser.
1196     if ($message =~ /^(\+|\-|add|del)user(\s+(.*))?$/i) {
1197         my $str         = $1;
1198         my $strstr      = $1."user";
1199         my @args        = split /\s+/, $3 || '';
1200         my $args        = $3;
1201         my $state       = ($str =~ /^(\+|add)$/) ? 1 : 0;
1202
1203         if (!scalar @args) {
1204             &help($strstr);
1205             return;
1206         }
1207
1208         if ($str eq "+") {
1209             if (scalar @args != 2) {
1210                 &pSReply(".+host requires hostmask argument.");
1211                 return;
1212             }
1213         } elsif (scalar @args != 1) {
1214             &pSReply("too many arguments.");
1215             return;
1216         }
1217
1218         if ($state) {                   # adduser.
1219             if (scalar @args == 1) {
1220                 $args[1]        = &getHostMask($args[0]);
1221                 if (!defined $args[1]) {
1222                     &ERROR("could not get hostmask?");
1223                     return;
1224                 }
1225             }
1226
1227             if ( &userAdd(@args) ) {    # success.
1228                 &pSReply("Added $args[0]...");
1229
1230             } else {                    # failure.
1231                 &pSReply("User $args[0] already exists");
1232             }
1233
1234         } else {                        # deluser.
1235
1236             if ( &userDel($args[0]) ) { # success.
1237                 &pSReply("Deleted $args[0] successfully.");
1238
1239             } else {                    # failure.
1240                 &pSReply("User $args[0] does not exist.");
1241             }
1242
1243         }
1244         return;
1245     }
1246
1247     if ($message =~ /^sched$/) {
1248         my @list;
1249         my @run;
1250         foreach (keys %sched) {
1251             next unless (exists $sched{$_}{TIME});
1252             push(@list,$_);
1253
1254             next unless (exists $sched{$_}{RUNNING});
1255             push(@run,$_);
1256         }
1257
1258         &pSReply( &formListReply(0,"Scheds to run: ", sort @list ) );
1259         &pSReply( &formListReply(0, "Scheds running(should not happen?) ", sort @run ) );
1260
1261         return;
1262     }
1263
1264     return "REPLY";
1265 }
1266
1267 1;