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 my $f = "$bot_misc_dir/blootbot.users";
18 if ( -f $f and -f "$f~") {
23 &DEBUG("rUF: backup file bigger than current file. FIXME");
28 &ERROR("cannot read userfile.");
33 undef %users; # clear on reload.
35 undef %ingore; # reset.
39 &ERROR("old or invalid user file found.");
52 if (/^--(\S+)[\s\t]+(.*)$/) { # user: middle entry.
53 my ($what,$val) = ($1,$2);
55 if (!defined $val or $val eq "") {
56 &WARN("$what: val == NULL.");
61 if ($what eq "HOSTS") {
62 $users{$nick}{$what}{$val} = 1;
64 $users{$nick}{$what} = $val;
67 } elsif (/^(\S+)$/) { # user: start entry.
70 } elsif (/^::(\S+) ignore$/) { # ignore: start entry.
74 } elsif (/^- (\S+):\+(\d+):\+(\d+):(\S+):(.*)$/ and $type eq "ignore") {
75 ### ignore: middle entry.
77 my(@array) = ($2,$3,$4,$5);
78 ### DEBUG purposes only!
79 if ($mask !~ /^$mask{nuh}$/) {
80 &WARN("ignore: mask $mask is invalid.");
83 $ignore{$chan}{$mask} = \@array;
85 } elsif (/^::(\S+) bans$/) { # bans: start entry.
89 } elsif (/^- (\S+):\+(\d+):\+(\d+):(\d+):(\S+):(.*)$/ and $type eq "bans") {
90 ### bans: middle entry.
91 # $btime, $atime, $count, $whoby, $reason.
92 my(@array) = ($2,$3,$4,$5,$6);
93 $bans{$chan}{$1} = \@array;
96 &WARN("unknown line: $_");
101 &status( sprintf("USERFILE: Loaded: %d users, %d bans, %d ignore",
102 scalar(keys %users)-1,
103 scalar(keys %bans), # ??
104 scalar(keys %ignore), # ??
110 if (!scalar keys %users) {
111 &DEBUG("wUF: nothing to write.");
115 if (!open OUT,">$bot_misc_dir/blootbot.users") {
116 &ERROR("cannot write to userfile.");
120 my $time = scalar(localtime);
122 print OUT "#v1: blootbot -- $ident -- written $time\n\n";
126 foreach (sort keys %users) {
129 my $count = scalar keys %{ $users{$user} };
131 &WARN("user $user has no other attributes; skipping.");
137 foreach (sort keys %{ $users{$user} }) {
139 my $val = $users{$user}{$_};
141 if (ref($val) eq "HASH") {
142 foreach (sort keys %{ $users{$user}{$_} }) {
143 print OUT "--$what\t\t$_\n";
147 print OUT "--$_\t\t$val\n";
155 foreach (keys %bans) {
159 my $count = scalar keys %{ $bans{$chan} };
161 &WARN("bans: chan $chan has no other attributes; skipping.");
165 print OUT "::$chan bans\n";
166 foreach (keys %{ $bans{$chan} }) {
167 # format: bans: mask expire time-added count who-added reason
168 my @array = @{ $bans{$chan}{$_} };
169 if (scalar @array != 5) {
170 &WARN("bans: $chan/$_ is corrupted.");
174 printf OUT "- %s:+%d:+%d:%d:%s:%s\n", $_, @array;
177 print OUT "\n" if ($cbans);
181 foreach (keys %ignore) {
185 my $count = scalar keys %{ $ignore{$chan} };
187 &WARN("ignore: chan $chan has no other attributes; skipping.");
191 ### TODO: use hash instead of array for flexibility?
192 print OUT "::$chan ignore\n";
193 foreach (keys %{ $ignore{$chan} }) {
194 # format: ignore: mask expire time-added who-added reason
195 my @array = @{ $ignore{$chan}{$_} };
196 if (scalar @array != 4) {
197 &WARN("ignore: $chan/$_ is corrupted.");
201 printf OUT "- %s:+%d:+%d:%s:%s\n", $_, @array;
207 $wtime_userfile = time();
208 &status("--- Saved USERFILE ($cusers users; $cbans bans; $cignore ignore) at $time");
209 if (defined $msgType and $msgType =~ /^chat$/) {
210 &performStrictReply("--- Writing user file...");
215 ##### CHANNEL CONFIGURATION READER/WRITER
219 my $f = "$bot_misc_dir/blootbot.chan";
220 if ( -f $f and -f "$f~") {
225 &DEBUG("rCF: backup file bigger than current file. FIXME");
230 &ERROR("cannot erad chanfile.");
234 undef %chanconf; # reset.
236 $_ = <IN>; # version string.
248 next unless (defined $chan);
250 if (/^[\s\t]+\+(\S+)$/) { # bool, true.
251 $chanconf{$chan}{$1} = 1;
253 } elsif (/^[\s\t]+\-(\S+)$/) { # bool, false.
254 &DEBUG("deprecated support of negative options.") unless ($cache{negative});
255 # although this is supported in run-time configuration.
256 $cache{negative} = 1;
257 # $chanconf{$chan}{$1} = 0;
259 } elsif (/^[\s\t]+(\S+)[\ss\t]+(.*)$/) {# what = val.
260 $chanconf{$chan}{$1} = $2;
263 &WARN("unknown line: $_") unless (/^#/);
268 # verify configuration
269 ### TODO: check against valid params.
270 foreach $chan (keys %chanconf) {
271 foreach (keys %{ $chanconf{$chan} }) {
272 next unless (/^[+-]/);
273 &WARN("invalid param: chanconf{$chan}{$_}; removing.");
274 delete $chanconf{$chan}{$_};
275 undef $chanconf{$chan}{$_};
279 delete $cache{negative};
281 &status("CHANFILE: Loaded: ".(scalar(keys %chanconf)-1)." chans");
285 if (!scalar keys %chanconf) {
286 &DEBUG("wCF: nothing to write.");
290 if (!open OUT,">$bot_misc_dir/blootbot.chan") {
291 &ERROR("cannot write chanfile.");
295 my $time = scalar(localtime);
296 print OUT "#v1: blootbot -- $ident -- written $time\n\n";
300 ### Process 1: if defined in _default, remove same definition
301 ### from non-default channels.
302 foreach (keys %{ $chanconf{_default} }) {
304 my $val = $chanconf{_default}{$opt};
307 foreach (keys %chanconf) {
310 next if ($chan eq "_default");
311 next unless (exists $chanconf{$chan}{$opt});
312 next unless ($val eq $chanconf{$chan}{$opt});
314 delete $chanconf{$chan}{$opt};
318 &DEBUG("Removed config $opt to @chans since it's defiend in '_default'");
322 ### Process 2: if defined in all chans but _default, set in
323 ### _default and remove all others.
324 my (%optsval, %opts);
325 foreach (keys %chanconf) {
327 next if ($chan eq "_default");
330 foreach (keys %{ $chanconf{$chan} }) {
332 if (exists $optsval{$opt} and $optsval{$opt} eq $chanconf{$chan}{$opt}) {
336 $optsval{$opt} = $chanconf{$chan}{$opt};
341 foreach (keys %opts) {
342 next unless ($opts{$_} > 1);
343 &DEBUG(" opts{$_} => $opts{$_}");
346 ### other optimizations are in UserDCC.pl
350 foreach (sort keys %chanconf) {
355 foreach (sort keys %{ $chanconf{$chan} }) {
356 my $val = $chanconf{$chan}{$_};
358 if ($val =~ /^0$/) { # bool, false.
361 } elsif ($val =~ /^1$/) { # bool, true.
364 } else { # what = val.
365 print OUT " $_ $val\n";
375 $wtime_chanfile = time();
376 &status("--- Saved CHANFILE (".scalar(keys %chanconf).
379 if (defined $msgType and $msgType =~ /^chat$/) {
380 &performStrictReply("--- Writing chan file...");
390 my ($ret, $f, $o) = "";
392 &verifyUser($who, $nuh);
394 foreach $f (split //, $users{$userHandle}{FLAGS}) {
395 foreach $o ( split //, $flags ) {
396 next unless ($f eq $o);
407 my ($nick, $lnuh) = @_;
410 if ($userHandle = $dcc{'CHATvrfy'}{$who}) {
411 &VERB("vUser: cached auth for $who.",2);
417 foreach $user (keys %users) {
418 next if ($user eq "_default");
420 foreach $m (keys %{ $users{$user}{HOSTS} }) {
423 $m =~ s/([\@\(\)\[\]])/\\$1/g;
425 next unless ($lnuh =~ /^$m$/i);
427 if ($user !~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user}) {
428 &status("vU: host matched but diff nick ($nick != $user).");
429 $cache{VUSERWARN}{$user} = 1;
436 last if ($userHandle ne "");
438 if ($user =~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user}) {
439 &status("vU: nick matched but host is not in list ($lnuh).");
440 $cache{VUSERWARN}{$user} = 1;
444 $userHandle ||= "_default";
445 # what's talkchannel for?
446 $talkWho{$talkchannel} = $who if (defined $talkchannel);
453 # returns true if arg1 encrypts to arg2
454 my ($plain, $encrypted) = @_;
455 if ($encrypted eq "") {
456 ($plain, $encrypted) = split(/\s+/, $plain, 2);
458 return 0 unless ($plain ne "" and $encrypted ne "");
460 # MD5 // DES. Bobby Billingsley++.
462 if ($encrypted =~ /^(\S{2})/ and length $encrypted == 13) {
464 } elsif ($encrypted =~ /^\$\d\$(\w\w)\$/) {
467 &DEBUG("unknown salt from $encrypted.");
471 return ($encrypted eq crypt($plain, $salt));
474 # mainly for dcc chat... hrm.
478 if (&IsFlag($flag) eq $flag) {
481 &status("DCC CHAT: <$who> $message -- not enough flags.");
482 &performStrictReply("error: you do not have enough flags for that. ($flag required)");
488 my($mask,$chan,$expire,$comment) = @_;
490 $chan ||= "*"; # global if undefined.
491 $comment ||= ""; # optional.
492 $expire ||= 0; # permament.
496 $expire = $expire*60 + time();
502 $exist++ if (exists $ignore{$chan}{$mask});
504 $ignore{$chan}{$mask} = [$expire, time(), $who, $comment];
507 $utime_userfile = time();
520 ### TODO: support wildcards.
521 foreach (keys %ignore) {
524 foreach (grep /^\Q$mask\E$/i, keys %{ $ignore{$chan} }) {
525 delete $ignore{$chan}{$mask};
529 &DEBUG("iD: scalar => ".scalar(keys %{ $ignore{$chan} }) );
533 $utime_userfile = time();
541 my($nick,$mask) = @_;
543 if (exists $users{$nick}) {
547 $utime_userfile = time();
550 $users{$nick}{HOSTS}{$mask} = 1;
551 $users{$nick}{FLAGS} ||= $users{_default}{FLAGS};
559 if (!exists $users{$nick}) {
563 $utime_userfile = time();
566 delete $users{$nick};
572 my($mask,$chan,$expire,$reason) = @_;
578 $expire = $expire*60 + time();
582 $exist++ if (exists $bans{$chan}{$mask} or
583 exists $bans{'*'}{$mask});
584 $bans{$chan}{$mask} = [$expire, time(), 0, $who, $reason];
586 my @chans = ($chan eq "*") ? keys %channels : $chan;
592 foreach (keys %{ $channels{$chan}{''} }) {
593 next unless (exists $nuh{lc $_});
594 next unless ($nuh{lc $_} =~ /^$m$/i);
595 &FIXME("nuh{$_} =~ /$m/");
600 $utime_userfile = time();
611 foreach (keys %bans) {
614 foreach (grep /^\Q$mask\E$/i, keys %{ $bans{$chan} }) {
615 delete $bans{$chan}{$_};
619 &DEBUG("bans: scalar => ".scalar(keys %{ $bans{$chan} }) );
623 $utime_userfile = time();
633 if ( &getUser($user) ) {
643 if (!defined $user) {
644 &WARN("getUser: user == NULL.");
648 if (my @retval = grep /^\Q$user\E$/i, keys %users) {
649 if ($retval[0] ne $user) {
650 &WARN("getUser: retval[0] ne user ($retval[0] ne $user)");
652 my $count = scalar keys %{ $users{$retval[0]} };
653 &DEBUG("count => $count.");
662 my($cmd, $chan, $what, $val) = @_;
664 if ($cmd eq "+chan") {
665 if (exists $chanconf{$chan}) {
666 &pSReply("chan $chan already exists.");
669 $chanconf{$chan}{_time_added} = time();
670 $chanconf{$what}{autojoin} = 1;
672 &pSReply("Joining $chan...");
678 if (!exists $chanconf{$chan}) {
679 &pSReply("no such channel $chan");
686 ### ".chanset +blah 10" -- error.
687 if (defined $what and $what =~ s/^([+-])(\S+)/$2/) {
688 my $state = ($1 eq "+") ? 1 : 0;
689 my $was = $chanconf{$chan}{$what};
691 if ($state) { # add/set.
692 if (defined $was and $was eq "1") {
693 &pSReply("setting $what for $chan already 1.");
697 $was = ($was) ? "; was '$was'" : "";
700 } else { # delete/unset.
702 &pSReply("setting $what for $chan is not set.");
707 &pSReply("setting $what for $chan already 0.");
711 $was = ($was) ? "; was '$was'" : "";
716 &pSReply("Unsetting $what for $chan$was.");
717 delete $chanconf{$chan}{$what};
719 &pSReply("Setting $what for $chan to '$val'$was.");
720 $chanconf{$chan}{$what} = $val;
725 ### ".chanset blah testing"
726 } elsif (defined $val) {
727 my $was = $chanconf{$chan}{$what};
728 if (defined $was and $was eq $val) {
729 &pSReply("setting $what for $chan already '$val'.");
732 $was = ($was) ? "; was '$was'" : "";
733 &pSReply("Setting $what for $chan to '$val'$was.");
735 $chanconf{$chan}{$what} = $val;
741 } else { # read only.
742 if (!defined $what) {
743 &WARN("chanset/DC: what == undefine.");
747 if (exists $chanconf{$chan}{$what}) {
748 &pSReply("$what for $chan is '$chanconf{$chan}{$what}'");
750 &pSReply("$what for $chan is not set.");
755 $utime_chanfile = time();
765 "limitcheckInterval",
769 ### TODO: finish off this list.
782 # +r - ability to remove factoids
783 # +t - ability to teach factoids
784 # +m - ability to modify factoids
786 # +o - authorised user of bot (like +m on eggdrop)