2 # UserDCC.pl: User Commands, DCC CHAT.
4 # Version: v0.2 (20010119)
5 # Created: 20000707 (from UserExtra.pl)
8 if (&IsParam("useStrict")) { use strict; }
16 if ($message =~ /^(exit|quit)$/i) {
17 # do ircII clients support remote close? if so, cool!
18 &status("userDCC: quit called. FIXME");
26 if ($message =~ /^who$/) {
27 my $count = scalar(keys %{$dcc{'CHAT'}});
28 my $dccCHAT = $message;
30 &pSReply("Start of who ($count users).");
31 foreach (keys %{$dcc{'CHAT'}}) {
34 &pSReply("End of who.");
39 ### for those users with enough flags.
42 if ($message =~ /^4op(\s+($mask{chan}))?$/i) {
43 return unless (&hasFlag("o"));
52 if (!$channels{$chan}{'o'}{$ident}) {
53 &msg($who, "i don't have ops on $chan to do that.");
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));
61 rawout("MODE $chan +o-o+o-o". (" $who" x 4));
68 if ($message =~ /^backlog(\s+(.*))?$/i) {
69 return unless (&hasFlag("o"));
70 return unless (&hasParam("backlog"));
72 my $max = $param{'backlog'};
77 } elsif ($num !~ /^\d+/) {
78 &msg($who, "error: argument is not positive integer.");
80 } elsif ($num > $max or $num < 0) {
81 &msg($who, "error: argument is out of range (max $max).");
85 &msg($who, "Start of backlog...");
87 sleep 1 if ($_ % 4 == 0 and $_ != 0);
88 $conn->privmsg($who, "[".($_+1)."]: $backlog[$max-$num+$_]");
90 &msg($who, "End of backlog.");
96 if ($message =~ /^dumpvars$/i) {
97 return unless (&hasFlag("o"));
98 return '' unless (&IsParam("dumpvars"));
100 &status("Dumping all variables...");
107 if ($message =~ /^kick(\s+(\S+)(\s+(\S+))?)?/) {
108 return unless (&hasFlag("o"));
109 my ($nick,$chan) = (lc $2,lc $4);
116 if (&validChan($chan) == 0) {
117 &msg($who,"error: invalid channel \002$chan\002");
121 if (&IsNickInChan($nick,$chan) == 0) {
122 &msg($who,"$nick is not in $chan.");
132 if ($message =~ /^mode(\s+(.*))?$/) {
133 return unless (&hasFlag("m"));
134 my ($chan,$mode) = split /\s+/,$2,2;
141 if (&validChan($chan) == 0) {
142 &msg($who,"error: invalid channel \002$chan\002");
146 if (!$channels{$chan}{o}{$ident}) {
147 &msg($who,"error: don't have ops on \002$chan\002");
157 if ($message =~ /^part(\s+(\S+))?$/i) {
158 return unless (&hasFlag("o"));
161 if ($jchan !~ /^$mask{chan}$/) {
162 &msg($who, "error, invalid chan.");
167 if (!&validChan($jchan)) {
168 &msg($who, "error, I'm not on that chan.");
172 &msg($jchan, "Leaving. (courtesy of $who).");
177 # lobotomy. sometimes we want the bot to be _QUIET_.
178 if ($message =~ /^(lobotomy|bequiet)$/i) {
179 return unless (&hasFlag("o"));
182 &performReply("i'm already lobotomized");
184 &performReply("i have been lobotomized");
192 if ($message =~ /^(unlobotomy|benoisy)$/i) {
193 return unless (&hasFlag("o"));
195 &performReply("i have been unlobotomized, woohoo");
198 &performReply("i'm not lobotomized");
204 if ($message =~ /^op(\s+(.*))?$/i) {
205 return unless (&hasFlag("o"));
210 if ($opee =~ /^(\S+)\s+(\S+)$/) {
213 if (!&validChan($2)) {
214 &msg($who,"error: invalid chan ($2).");
218 &msg($who,"error: invalid params.");
222 @chans = keys %channels;
228 next unless (&IsNickInChan($opee,$_));
230 if ($channels{$_}{'o'}{$opee}) {
231 &status("op: $opee already has ops on $_");
236 &status("opping $opee on $_ at ${who}'s request");
237 &pSReply("opping $opee on $_");
242 &status("op: opped on all possible channels.");
244 &DEBUG("found => '$found'.");
245 &DEBUG("op => '$op'.");
252 if ($message =~ /^deop(\s+(.*))?$/i) {
253 return unless (&hasFlag("o"));
258 if ($opee =~ /^(\S+)\s+(\S+)$/) {
261 if (!&validChan($2)) {
262 &msg($who,"error: invalid chan ($2).");
266 &msg($who,"error: invalid params.");
270 @chans = keys %channels;
276 next unless (&IsNickInChan($opee,$_));
278 if (!exists $channels{$_}{'o'}{$opee}) {
279 &status("deop: $opee already has no ops on $_");
284 &status("deopping $opee on $_ at ${who}'s request");
289 &status("deop: deopped on all possible channels.");
291 &DEBUG("deop: found => '$found'.");
292 &DEBUG("deop: op => '$op'.");
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'.");
304 if (&validChan($chan)) {
307 &msg($who,"i'm not on \002$1\002, sorry.");
313 if ($message =~ /^die$/) {
314 return unless (&hasFlag("n"));
318 &status("Dying by $who\'s request");
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.
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 '#'.");
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);
340 &performReply("Expression didn't match anything.");
344 if (scalar @list > 100) {
345 &performReply("regex found more than 100 matches... not doing.");
349 &status("gsubst: going to alter ".scalar(@list)." factoids.");
350 &performReply("going to alter ".scalar(@list)." factoids.");
356 next if (&IsLocked($faqtoid) == 1);
357 my $result = &getFactoid($faqtoid);
359 &DEBUG("was($faqtoid) => '$was'.");
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)");
368 &setFactInfo($faqtoid, "factoid_value", $result);
369 &status("update: '$faqtoid' =is=> '$result'; was '$was'");
371 &WARN("subst: that's weird... thought we found the string ($op) in '$faqtoid'.");
377 &ERROR("Some warnings/errors?");
380 &performReply("Ok... did s/$op/$np/ for ".
381 (scalar(@list) - $error)." factoids");
387 if ($message =~ /^jump(\s+(\S+))?$/i) {
388 return unless (&hasFlag("n"));
396 if ($2 =~ /^(\S+)(:(\d+))?$/) {
400 &msg($who,"invalid format.");
404 &status("jumping servers... $server...");
405 &rawout("QUIT :jumping to $server");
407 if (&irc($server,$port) == 0) {
413 if ($message =~ /^reload$/i) {
414 return unless (&hasFlag("n"));
416 &status("USER reload $who");
417 &msg($who,"reloading...");
419 &msg($who,"reloaded.");
425 if ($message =~ /^rehash$/) {
426 return unless (&hasFlag("n"));
428 &msg($who,"rehashing...");
430 &status("USER rehash $who");
431 &msg($who,"rehashed");
437 ##### USER//CHAN SPECIFIC CONFIGURATION COMMANDS
440 if ($message =~ /^chaninfo(\s+(.*))?$/) {
441 my @args = split /[\s\t]+/, $2; # hrm.
443 if (scalar @args != 1) {
448 if (!exists $chanconf{$args[0]}) {
449 &pSReply("no such channel $args[0]");
453 &pSReply("showing channel conf.");
454 foreach (sort keys %{ $chanconf{$args[0]} }) {
455 &pSReply("$chan: $_ => $chanconf{$args[0]}{$_}");
457 &pSReply("End of chaninfo.");
463 if ($message =~ /^(chanset|\+chan)(\s+(.*?))?$/) {
467 if (!defined $args) {
473 if ($args =~ s/^($mask{chan})\s*//) {
475 &DEBUG("chan => $chan.");
477 &DEBUG("no chan arg; setting to default.");
481 my($what,$val) = split /[\s\t]+/, $args, 2;
483 ### TODO: "cannot set values without +m".
484 return unless (&hasFlag("m"));
486 if ($cmd eq "+chan") {
487 &DEBUG("setting _time_added...");
488 $chanconf{$chan}{_time_added} = time();
490 &pSReply("Joining $chan...");
496 if (!exists $chanconf{$chan}) {
497 &pSReply("no such channel $chan");
502 if ($what =~ /^\+(\S+)/) {
503 my $was = $chanconf{$chan}{$1};
504 $was = ($was) ? "; was '$was'" : "";
505 &pSReply("Setting $1 for $chan to '1'$was.");
507 $chanconf{$chan}{$1} = 1;
510 } elsif ($what =~ /^\-(\S+)/) {
511 my $was = $chanconf{$chan}{$1};
512 $was = ($was) ? "; was '$was'" : "";
513 &pSReply("Setting $1 for $chan to '0'$was.");
515 $chanconf{$chan}{$1} = 0;
518 } elsif (defined $val) {
519 my $was = $chanconf{$chan}{$what};
520 $was = ($was) ? "; was '$was'" : "";
521 &pSReply("Setting $what for $chan to '$val'$was.");
523 $chanconf{$chan}{$what} = $val;
526 } elsif (defined $what) {
527 &pSReply("$what for $chan is '$chanconf{$chan}{$what}'");
531 $utime_chanfile = time();
536 if ($cmd eq "chanset" and !defined $what) {
537 &DEBUG("showing channel conf.");
539 foreach (keys %{ $chanconf{$chan} }) {
540 &DEBUG("$chan: $_ => $chanconf{$chan}{$_}");
549 if ($message =~ /^(chanunset|\-chan)(\s+(.*))?$/) {
550 return unless (&hasFlag("m"));
553 if (!defined $args) {
560 if ($args =~ s/^(\-)?($mask{chan})\s*//) {
562 $delete = ($1) ? 1 : 0;
563 &DEBUG("chan => $chan.");
565 &DEBUG("no chan arg; setting to default.");
569 if (!exists $chanconf{$chan}) {
570 &pSReply("no such channel $chan");
575 if (&getChanConf($args,$chan)) {
576 &pSReply("Unsetting channel ($chan) option $args. (was $chanconf{$chan}{$args})");
577 undef $chanconf{$chan}{$args};
580 &pSReply("$args does not exist for $chan");
587 &pSReply("Deleting channel $chan for sure!");
588 $utime_chanfile = time();
592 &pSReply("Leaving $chan...");
594 delete $chanconf{$chan};
596 &pSReply("Prefix channel with '-' to delete for sure.");
603 if ($message =~ /^chpass(\s+(.*))?$/) {
604 my(@args) = split /[\s\t]+/, $2 || '';
611 if (!&IsUser($args[0])) {
612 &pSReply("user $args[0] is not valid.");
616 my $u = &getUser($who);
618 if (scalar @args == 1) { # del pass.
619 if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
620 &pSReply("cannto remove passwd of others.");
624 if (!exists $users{$u}{PASS}) {
625 &pSReply("$u does not have pass set anyway.");
629 &pSReply("Deleted pass from $u.");
631 $utime_userfile = time();
634 delete $users{$u}{PASS};
639 my $salt = join '',('.','/',0..9,'A'..'Z','a'..'z')[rand 64, rand 64];
640 my $crypt = crypt($args[1], $salt);
641 &pSReply("Set $u's passwd to '$crypt'");
642 $users{$u}{PASS} = $crypt;
644 $utime_userfile = time();
650 if ($message =~ /^chattr(\s+(.*))?$/) {
651 my(@args) = split /[\s\t]+/, $2 || '';
659 if ($args[0] =~ /^$mask{nick}$/i) { # <nick>
660 $user = &getUser($args[0]);
663 $user = &getUser($who);
664 &DEBUG("user $who... nope.") unless (defined $user);
665 $user = &getUser($verifyUser);
669 if (!defined $user) {
670 &pSReply("user $user does not exist.");
674 my $flags = $users{$user}{FLAGS};
675 if (!defined $chflag) {
676 &pSReply("Flags for $user: $flags");
680 &DEBUG("who => $who");
681 &DEBUG("verifyUser => $verifyUser");
682 if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
683 &pSReply("cannto change attributes of others.");
689 foreach (split //, $chflag) {
690 if ($_ eq "+") { $state = 1; next; }
691 if ($_ eq "-") { $state = 0; next; }
693 if (!defined $state) {
694 &pSReply("no initial + or - was found in attr.");
699 next if ($flags =~ /\Q$_\E/);
702 if (&IsParam("owner")
703 and $param{owner} =~ /^\Q$user\E$/i
704 and $flags =~ /[nmo]/
706 &pSReply("not removing flag $_ for $user.");
709 next unless ($flags =~ s/\Q$_\E//);
716 $utime_userfile = time();
718 &pSReply("Current flags: $flags");
719 $users{$user}{FLAGS} = $flags;
721 &pSReply("No flags changed: $flags");
727 if ($message =~ /^chnick(\s+(.*))?$/) {
728 my(@args) = split /[\s\t]+/, $2 || '';
730 if ($who eq "_default") {
731 &WARN("$who or verifyuser tried to run chnick.");
735 if (!scalar @args or scalar @args > 2) {
740 if (scalar @args == 1) { # 1
741 $user = &getUser($who);
742 &DEBUG("nope, not $who.") unless (defined $user);
743 $user ||= &getUser($verifyUser);
746 $user = &getUser($args[0]);
750 if (!defined $user) {
751 &pSReply("user $who or $args[0] does not exist.");
755 if ($user =~ /^\Q$chnick\E$/i) {
756 &pSReply("user == chnick. why should I do that?");
760 if (&getUser($chnick)) {
761 &pSReply("user $chnick is already used!");
765 if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
766 &pSReply("cannto change nick of others.");
767 return "REPLY" if ($who eq "_default");
771 foreach (keys %{ $users{$user} }) {
772 $users{$chnick}{$_} = $users{$user}{$_};
773 delete $users{$user}{$_};
775 undef $users{$user}; # ???
777 $utime_userfile = time();
780 &pSReply("Changed '$user' to '$chnick' successfully.");
785 if ($message =~ /^([-+])host(\s+(.*))?$/) {
787 my(@args) = split /[\s\t]+/, $3 || '';
788 my $state = ($1 eq "+") ? 1 : 0;
795 if ($who eq "_default") {
796 &WARN("$who or verifyuser tried to run $cmd.");
801 if ($args[0] =~ /^$mask{nick}$/i) { # <nick>
802 return unless (&hasFlag("m"));
803 $user = &getUser($args[0]);
806 # who or verifyUser. FIXME!!!
807 $user = &getUser($who);
811 if (!defined $user) {
812 &pSReply("user $user does not exist.");
816 if (!defined $mask) {
818 &pSReply("Hostmasks for $user: $users{$user}{HOSTS}");
823 if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
824 &pSReply("cannto change masks of others.");
828 if ($mask !~ /^$mask{nuh}$/) {
829 &pSReply("error: mask ($mask) is not a real hostmask.");
833 my $count = scalar keys %{ $users{$user}{HOSTS} };
836 if (exists $users{$user}{HOSTS}{$mask}) {
837 &pSReply("mask $mask already exists.");
841 ### TODO: override support.
842 $users{$user}{HOSTS}{$mask} = 1;
844 &pSReply("Added $mask to list of masks.");
848 if (!exists $users{$user}{HOSTS}{$mask}) {
849 &pSReply("mask $mask does not exist.");
853 ### TODO: wildcard support. ?
854 delete $users{$user}{HOSTS}{$mask};
856 if (scalar keys %{ $users{$user}{HOSTS} } != $count) {
857 &pSReply("Removed $mask from list of masks.");
859 &pSReply("error: could not find $mask in list of masks.");
864 $utime_userfile = time();
870 if ($message =~ /^([-+])ban(\s+(.*))?$/) {
873 my(@args) = split /[\s\t]+/, $3 || '';
874 my $state = ($1 eq "+") ? 1 : 0;
881 my($mask,$chan,$time,$reason);
883 if ($flatarg =~ s/^($mask{nuh})\s*//) {
886 &DEBUG("arg does not contain nuh mask?");
889 if ($flatarg =~ s/^($mask{chan})\s*//) {
892 $chan = "*"; # _default instead?
895 if ($state == 0) { # delete.
896 my $c = join(' ', &banDel($mask) );
899 &pSReply("Removed $mask from chans: $c");
901 &pSReply("$mask was not found in ban list.");
912 if ($flatarg =~ s/^(\d+)\s*//) {
914 &DEBUG("time = $time.");
916 &pSReply("error: time cannot be negatime?");
923 if ($flatarg =~ s/^(.*)$//) { # need length?
927 if (!&IsFlag("m") and $who !~ /^\Q$verifyUser\E$/i) {
928 &pSReply("cannto change masks of others.");
932 if ($mask !~ /^$mask{nuh}$/) {
933 &pSReply("error: mask ($mask) is not a real hostmask.");
937 if ( &banAdd($mask,$chan,$time,$reason) == 2) {
938 &pSReply("ban already exists; overwriting.");
940 &pSReply("Added $mask for $chan (time => $time, reason => $reason)");
945 if ($message =~ /^whois(\s+(.*))?$/) {
953 my $user = &getUser($arg);
954 if (!defined $user) {
955 &pSReply("whois: user $user does not exist.");
959 ### TODO: better (eggdrop-like) output.
960 &pSReply("user: $user");
961 foreach (keys %{ $users{$user} }) {
962 my $ref = ref $users{$user}{$_};
964 if ($ref eq "HASH") {
967 foreach (keys %{ $users{$user}{$type} }) {
968 &pSReply(" $type => $_");
973 &pSReply(" $_ => $users{$user}{$_}");
975 &pSReply("End of USER whois.");
980 if ($message =~ /^bans(\s+(.*))?$/) {
984 if ($arg ne "_default" and !&validChan($arg) ) {
985 &pSReply("error: chan $chan is invalid.");
990 if (!scalar keys %bans) {
991 &pSReply("Ban list is empty.");
996 &pSReply(" expire, count, who-by, time-added, reason");
997 foreach $c (keys %bans) {
998 next unless (!defined $arg or $arg =~ /^\Q$c\E$/i);
1001 foreach (keys %{ $bans{$c} }) {
1002 my $val = $bans{$c}{$_};
1004 if (ref $val eq "ARRAY") {
1005 my @array = @{ $val };
1006 &pSReply(" @array");
1008 &DEBUG("unknown ban: $val");
1012 &pSReply("END of bans.");
1017 if ($message =~ /^banlist(\s+(.*))?$/) {
1020 if (defined $arg and $arg !~ /^$mask_chan$/) {
1021 &pSReply("error: chan $chan is invalid.");
1025 &DEBUG("bans for global or arg => $arg.");
1026 foreach (keys %bans) { #CHANGE!!!
1027 &DEBUG(" $_ => $bans{$_}.");
1030 &DEBUG("End of bans.");
1031 &pSReply("END of bans.");
1036 if ($message =~ /^save$/) {
1037 return unless (&hasFlag("o"));
1046 $message =~ s/^addignore/+ignore/;
1047 $message =~ s/^(del|un)ignore/-ignore/;
1050 if ($message =~ /^(\+|\-)ignore(\s+(.*))?$/i) {
1051 return unless (&hasFlag("o"));
1052 my $state = ($1 eq "+") ? 1 : 0;
1053 my $str = $1."ignore";
1061 my($mask,$chan,$time,$comment);
1064 if ($args =~ s/^($mask{nuh})\s*//) {
1067 &ERROR("no NUH mask?");
1071 if (!$state) { # delignore.
1072 if ( &ignoreDel($mask) ) {
1073 &pSReply("ok, deleted X ignores.");
1075 &pSReply("could not find $mask in ignore list.");
1085 if ($args =~ s/^($mask{chan}|\*)\s*//) {
1087 &DEBUG("chan => $chan");
1093 if ($args =~ s/^(\d+)\s*//) {
1095 &DEBUG("time => $time");
1103 &DEBUG("comment => $comment");
1105 $comment = "added by $who";
1108 if ( &ignoreAdd($mask, $chan, $time, $comment) ) {
1109 &pSReply("added $mask to ignore list.");
1111 &pSReply("warn: $mask already in ignore list; written over anyway. FIXME");
1117 if ($message =~ /^ignore(\s+(.*))?$/) {
1121 if ($arg !~ /^$mask{chan}$/) {
1122 &pSReply("error: chan $chan is invalid.");
1126 if (!&validChan($arg)) {
1127 &pSReply("error: chan $arg is invalid.");
1131 &pSReply("Showing bans for $arg only.");
1134 if (!scalar keys %ignore) {
1135 &pSReply("Ignore list is empty.");
1139 ### TODO: proper (eggdrop-like) formatting.
1141 &pSReply(" expire, count, who, added, comment");
1142 foreach $c (keys %ignore) {
1143 next unless (!defined $arg or $arg =~ /^\Q$c\E$/i);
1146 foreach (keys %{ $ignore{$c} }) {
1147 my $ref = ref $ignore{$c}{$_};
1148 if ($ref eq "ARRAY") {
1149 my @array = @{ $ignore{$c}{$_} };
1150 &pSReply(" $_: @array");
1152 &DEBUG("unknown ignore line?");
1156 &pSReply("END of ignore.");
1162 if ($message =~ /^(\+|\-|add|del)user(\s+(.*))?$/i) {
1164 my $strstr = $1."user";
1165 my @args = split /\s+/, $3 || '';
1167 my $state = ($str =~ /^(\+|add)$/) ? 1 : 0;
1169 if (!scalar @args) {
1175 if (scalar @args != 2) {
1176 &pSReply(".+host requires hostmask argument.");
1179 } elsif (scalar @args != 1) {
1180 &pSReply("too many arguments.");
1184 if ($state) { # adduser.
1185 if (scalar @args == 1) {
1186 $args[1] = &getHostMask($args[0]);
1187 if (!defined $args[1]) {
1188 &ERROR("could not get hostmask?");
1193 if ( &userAdd(@args) ) { # success.
1194 &pSReply("Added $args[0]...");
1197 &pSReply("User $args[0] already exists");
1202 if ( &userDel($args[0]) ) { # success.
1203 &pSReply("Deleted $args[0] successfully.");
1206 &pSReply("User $args[0] does not exist.");
1213 if ($message =~ /^sched$/) {
1216 foreach (keys %sched) {
1217 next unless (exists $sched{$_}{TIME});
1220 next unless (exists $sched{$_}{RUNNING});
1224 &pSReply( &formListReply(0,"Scheds to run: ", sort @list ) );
1225 &pSReply( &formListReply(0, "Scheds running(should not happen?) ", sort @run ) );