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