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