2 # DynaConfig.pl: Read/Write configuration files dynamically.
4 # Version: v0.1 (20010120)
6 # NOTE: Merged from User.pl
9 if (&IsParam("useStrict")) { use strict; }
12 ##### USERFILE CONFIGURATION READER/WRITER
16 if (!open IN,"$bot_misc_dir/blootbot.users") {
17 &ERROR("cannot read userfile.");
22 undef %users; # clear on reload.
24 undef %ingore; # reset.
28 &ERROR("old or invalid user file found.");
41 if (/^--(\S+)[\s\t]+(.*)$/) { # user: middle entry.
42 my ($what,$val) = ($1,$2);
44 if (!defined $val or $val eq "") {
45 &WARN("$what: val == NULL.");
50 if ($what eq "HOSTS") {
51 $users{$nick}{$what}{$val} = 1;
53 $users{$nick}{$what} = $val;
56 } elsif (/^(\S+)$/) { # user: start entry.
59 } elsif (/^::(\S+) ignore$/) { # ignore: start entry.
63 } elsif (/^- (\S+):\+(\d+):\+(\d+):(\S+):(.*)$/ and $type eq "ignore") {
64 ### ignore: middle entry.
66 my(@array) = ($2,$3,$4,$5);
67 ### DEBUG purposes only!
68 if ($mask !~ /^$mask{nuh}$/) {
69 &WARN("ignore: mask $mask is invalid.");
72 $ignore{$chan}{$mask} = \@array;
74 } elsif (/^::(\S+) bans$/) { # bans: start entry.
78 } elsif (/^- (\S+):\+(\d+):\+(\d+):(\d+):(\S+):(.*)$/ and $type eq "bans") {
79 ### bans: middle entry.
80 # $btime, $atime, $count, $whoby, $reason.
81 my(@array) = ($2,$3,$4,$5,$6);
82 $bans{$chan}{$1} = \@array;
85 &WARN("unknown line: $_");
90 &status( sprintf("USERFILE: Loaded: %d users, %d bans, %d ignore",
91 scalar(keys %users)-1,
92 scalar(keys %bans), # ??
93 scalar(keys %ignore), # ??
99 if (!open OUT,">$bot_misc_dir/blootbot.users") {
100 &ERROR("cannot write to userfile.");
104 my $time = scalar(localtime);
106 print OUT "#v1: blootbot -- $ident -- written $time\n\n";
110 foreach (sort keys %users) {
113 my $count = scalar keys %{ $users{$user} };
115 &WARN("user $user has no other attributes; skipping.");
121 foreach (sort keys %{ $users{$user} }) {
123 my $val = $users{$user}{$_};
125 if (ref($val) eq "HASH") {
126 foreach (sort keys %{ $users{$user}{$_} }) {
127 print OUT "--$what\t\t$_\n";
131 print OUT "--$_\t\t$val\n";
139 foreach (keys %bans) {
143 my $count = scalar keys %{ $bans{$chan} };
145 &WARN("bans: chan $chan has no other attributes; skipping.");
149 print OUT "::$chan bans\n";
150 foreach (keys %{ $bans{$chan} }) {
151 # format: bans: mask expire time-added count who-added reason
152 my @array = @{ $bans{$chan}{$_} };
153 if (scalar @array != 5) {
154 &WARN("bans: $chan/$_ is corrupted.");
158 printf OUT "- %s:+%d:+%d:%d:%s:%s\n", $_, @array;
161 print OUT "\n" if ($cbans);
165 foreach (keys %ignore) {
169 my $count = scalar keys %{ $ignore{$chan} };
171 &WARN("ignore: chan $chan has no other attributes; skipping.");
175 ### TODO: use hash instead of array for flexibility?
176 print OUT "::$chan ignore\n";
177 foreach (keys %{ $ignore{$chan} }) {
178 # format: ignore: mask expire time-added who-added reason
179 my @array = @{ $ignore{$chan}{$_} };
180 if (scalar @array != 4) {
181 &WARN("ignore: $chan/$_ is corrupted.");
185 printf OUT "- %s:+%d:+%d:%s:%s\n", $_, @array;
191 $wtime_userfile = time();
192 &status("--- Saved USERFILE ($cusers users; $cbans bans; $cignore ignore) at $time");
193 if (defined $msgType and $msgType =~ /^chat$/) {
194 &performStrictReply("--- Writing user file...");
199 ##### CHANNEL CONFIGURATION READER/WRITER
203 if (!open IN,"$bot_misc_dir/blootbot.chan") {
204 &ERROR("cannot erad chanfile.");
208 undef %chanconf; # reset.
210 $_ = <IN>; # version string.
222 next unless (defined $chan);
224 if (/^[\s\t]+\+(\S+)$/) { # bool, true.
225 $chanconf{$chan}{$1} = 1;
227 } elsif (/^[\s\t]+\-(\S+)$/) { # bool, false.
228 &DEBUG("deprecated support of negative options.") unless ($cache{negative});
229 # although this is supported in run-time configuration.
230 $cache{negative} = 1;
231 # $chanconf{$chan}{$1} = 0;
233 } elsif (/^[\s\t]+(\S+)[\ss\t]+(.*)$/) {# what = val.
234 $chanconf{$chan}{$1} = $2;
237 &WARN("unknown line: $_") unless (/^#/);
242 # verify configuration
243 ### TODO: check against valid params.
244 foreach $chan (keys %chanconf) {
245 foreach (keys %{ $chanconf{$chan} }) {
246 next unless (/^[+-]/);
247 &WARN("invalid param: chanconf{$chan}{$_}; removing.");
248 delete $chanconf{$chan}{$_};
249 undef $chanconf{$chan}{$_};
253 delete $cache{negative};
255 &status("CHANFILE: Loaded: ".(scalar(keys %chanconf)-1)." chans");
259 if (!open OUT,">$bot_misc_dir/blootbot.chan") {
260 &ERROR("cannot write chanfile.");
264 my $time = scalar(localtime);
265 print OUT "#v1: blootbot -- $ident -- written $time\n\n";
269 ### Process 1: if defined in _default, remove same definition
270 ### from non-default channels.
271 foreach (keys %{ $chanconf{_default} }) {
273 my $val = $chanconf{_default}{$opt};
276 foreach (keys %chanconf) {
279 next if ($chan eq "_default");
280 next unless (exists $chanconf{$chan}{$opt});
281 next unless ($val eq $chanconf{$chan}{$opt});
283 delete $chanconf{$chan}{$opt};
287 &DEBUG("Removed config $opt to @chans since it's defiend in '_default'");
291 ### Process 2: if defined in all chans but _default, set in
292 ### _default and remove all others.
293 my (%optsval, %opts);
294 foreach (keys %chanconf) {
296 next if ($chan eq "_default");
299 foreach (keys %{ $chanconf{$chan} }) {
301 if (exists $optsval{$opt} and $optsval{$opt} eq $chanconf{$chan}{$opt}) {
305 $optsval{$opt} = $chanconf{$chan}{$opt};
310 foreach (keys %opts) {
311 next unless ($opts{$_} > 1);
312 &DEBUG(" opts{$_} => $opts{$_}");
315 ### other optimizations are in UserDCC.pl
319 foreach (sort keys %chanconf) {
324 foreach (sort keys %{ $chanconf{$chan} }) {
325 my $val = $chanconf{$chan}{$_};
327 if ($val =~ /^0$/) { # bool, false.
330 } elsif ($val =~ /^1$/) { # bool, true.
333 } else { # what = val.
334 print OUT " $_ $val\n";
344 $wtime_chanfile = time();
345 &status("--- Saved CHANFILE (".scalar(keys %chanconf).
348 if (defined $msgType and $msgType =~ /^chat$/) {
349 &performStrictReply("--- Writing chan file...");
359 my ($ret, $f, $o) = "";
361 &verifyUser($who, $nuh);
363 foreach $f (split //, $users{$userHandle}{FLAGS}) {
364 foreach $o ( split //, $flags ) {
365 next unless ($f eq $o);
376 my ($nick, $lnuh) = @_;
379 if ($userHandle = $dcc{'CHATvrfy'}{$who}) {
380 &VERB("vUser: cached auth for $who.",2);
386 foreach $user (keys %users) {
387 next if ($user eq "_default");
389 foreach $m (keys %{$users{$user}{HOSTS}}) {
392 $m =~ s/([\@\(\)\[\]])/\\$1/g;
394 next unless ($lnuh =~ /^$m$/i);
396 if ($user !~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user}) {
397 &status("vU: host matched but diff nick ($nick != $user).");
398 $cache{VUSERWARN}{$user} = 1;
405 last if ($userHandle ne "");
407 if ($user =~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user}) {
408 &status("vU: nick matched but host is not in list ($lnuh).");
409 $cache{VUSERWARN}{$user} = 1;
413 $userHandle ||= "_default";
414 # what's talkchannel for?
415 $talkWho{$talkchannel} = $who if (defined $talkchannel);
422 # returns true if arg1 encrypts to arg2
423 my ($plain, $encrypted) = @_;
424 if ($encrypted eq "") {
425 ($plain, $encrypted) = split(/\s+/, $plain, 2);
427 return 0 unless ($plain ne "" and $encrypted ne "");
429 # MD5 // DES. Bobby Billingsley++.
431 if ($encrypted =~ /^(\S{2})/ and length $encrypted == 13) {
433 } elsif ($encrypted =~ /^\$\d\$(\w\w)\$/) {
436 &DEBUG("unknown salt from $encrypted.");
440 return ($encrypted eq crypt($plain, $salt));
443 # mainly for dcc chat... hrm.
447 if (&IsFlag($flag) eq $flag) {
450 &status("DCC CHAT: <$who> $message -- not enough flags.");
451 &performStrictReply("error: you do not have enough flags for that. ($flag required)");
457 my($mask,$chan,$expire,$comment) = @_;
459 $chan ||= "*"; # global if undefined.
460 $comment ||= ""; # optional.
461 $expire ||= 0; # permament.
465 $expire = $expire*60 + time();
471 $exist++ if (exists $ignore{$chan}{$mask});
473 $ignore{$chan}{$mask} = [$expire, time(), $who, $comment];
476 $utime_userfile = time();
489 ### TODO: support wildcards.
490 foreach (keys %ignore) {
493 foreach (grep /^\Q$mask\E$/i, keys %{ $ignore{$chan} }) {
494 delete $ignore{$chan}{$mask};
498 &DEBUG("iD: scalar => ".scalar(keys %{ $ignore{$chan} }) );
502 $utime_userfile = time();
510 my($nick,$mask) = @_;
512 if (exists $users{$nick}) {
516 $utime_userfile = time();
519 $users{$nick}{HOSTS}{$mask} = 1;
520 $users{$nick}{FLAGS} ||= $users{_default}{FLAGS};
528 if (!exists $users{$nick}) {
532 $utime_userfile = time();
535 delete $users{$nick};
541 my($mask,$chan,$expire,$reason) = @_;
547 $expire = $expire*60 + time();
551 $exist++ if (exists $bans{$chan}{$mask} or
552 exists $bans{'*'}{$mask});
553 $bans{$chan}{$mask} = [$expire, time(), 0, $who, $reason];
555 my @chans = ($chan eq "*") ? keys %channels : $chan;
561 foreach (keys %{ $channels{$chan}{''} }) {
562 next unless (exists $nuh{lc $_});
563 next unless ($nuh{lc $_} =~ /^$m$/i);
564 &FIXME("nuh{$_} =~ /$m/");
569 $utime_userfile = time();
580 foreach (keys %bans) {
583 foreach (grep /^\Q$mask\E$/i, keys %{ $bans{$chan} }) {
584 delete $bans{$chan}{$_};
588 &DEBUG("bans: scalar => ".scalar(keys %{ $bans{$chan} }) );
592 $utime_userfile = time();
602 if ( &getUser($user) ) {
612 if (!defined $user) {
613 &WARN("getUser: user == NULL.");
617 if (my @retval = grep /^\Q$user\E$/i, keys %users) {
618 if ($retval[0] ne $user) {
619 &WARN("getUser: retval[0] ne user ($retval[0] ne $user)");
621 my $count = scalar keys %{ $users{$retval[0]} };
622 &DEBUG("count => $count.");
631 my($cmd, $chan, $what, $val) = @_;
633 if ($cmd eq "+chan") {
634 if (exists $chanconf{$chan}) {
635 &pSReply("chan $chan already exists.");
638 $chanconf{$chan}{_time_added} = time();
639 $chanconf{$what}{autojoin} = 1;
641 &pSReply("Joining $chan...");
647 if (!exists $chanconf{$chan}) {
648 &pSReply("no such channel $chan");
655 ### ".chanset +blah 10" -- error.
656 if (defined $what and $what =~ s/^([+-])(\S+)/$2/) {
657 my $state = ($1 eq "+") ? 1 : 0;
658 my $was = $chanconf{$chan}{$what};
660 if ($state) { # add/set.
661 if (defined $was and $was eq "1") {
662 &pSReply("setting $what for $chan already 1.");
666 $was = ($was) ? "; was '$was'" : "";
669 } else { # delete/unset.
671 &pSReply("setting $what for $chan is not set.");
676 &pSReply("setting $what for $chan already 0.");
680 $was = ($was) ? "; was '$was'" : "";
684 $chanconf{$chan}{$what} = $val;
685 &pSReply("Setting $what for $chan to '$val'$was.");
688 ### ".chanset blah testing"
689 } elsif (defined $val) {
690 my $was = $chanconf{$chan}{$what};
691 if (defined $was and $was eq $val) {
692 &pSReply("setting $what for $chan already '$val'.");
695 $was = ($was) ? "; was '$was'" : "";
696 &pSReply("Setting $what for $chan to '$val'$was.");
698 $chanconf{$chan}{$what} = $val;
704 } else { # read only.
705 if (!defined $what) {
706 &WARN("chanset/DC: what == undefine.");
710 if (exists $chanconf{$chan}{$what}) {
711 &pSReply("$what for $chan is '$chanconf{$chan}{$what}'");
713 &pSReply("$what for $chan is not set.");
718 $utime_chanfile = time();
728 "limitcheckInterval",
732 ### TODO: finish off this list.