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.
25 &ERROR("old or invalid user file found.");
38 if (/^--(\S+)[\s\t]+(.*)$/) { # user: middle entry.
39 my ($what,$val) = ($1,$2);
41 if (!defined $val or $val eq "") {
42 &WARN("$what: val == NULL.");
47 if ($what eq "HOSTS") {
48 $users{$nick}{$what}{$val} = 1;
50 $users{$nick}{$what} = $val;
53 } elsif (/^(\S+)$/) { # user: start entry.
56 } elsif (/^::(\S+) ignore$/) { # ignore: start entry.
60 } elsif (/^- (\S+):\+(\d+):\+(\d+):(\S+):(.*)$/ and $type eq "ignore") {
61 ### ignore: middle entry.
63 ### DEBUG purposes only!
64 if ($mask !~ /^$mask{nuh}$/) {
65 &WARN("ignore: mask $mask is invalid.");
68 my(@array) = ($2,$3,$4,$5);
69 $ignore{$chan}{$mask} = \@array;
71 } elsif (/^::(\S+) bans$/) { # bans: start entry.
75 } elsif (/^- (\S+):\+(\d+):\+(\d+):(\d+):(\S+):(.*)$/ and $type eq "bans") {
76 ### bans: middle entry.
77 # $btime, $atime, $count, $whoby, $reason.
78 my(@array) = ($2,$3,$4,$5,$6);
79 $bans{$chan}{$1} = \@array;
82 &WARN("unknown line: $_");
87 &status( sprintf("USERFILE: Loaded: %d users, %d bans, %d ignore",
88 scalar(keys %users)-1,
89 scalar(keys %bans), # ??
90 scalar(keys %ignore), # ??
96 if (!open OUT,">$bot_misc_dir/blootbot.users") {
97 &ERROR("cannot write to userfile.");
101 my $time = scalar(localtime);
103 print OUT "#v1: blootbot -- $ident -- written $time\n\n";
107 foreach (sort keys %users) {
110 my $count = scalar keys %{ $users{$user} };
112 &WARN("user $user has no other attributes; skipping.");
118 foreach (sort keys %{ $users{$user} }) {
120 my $val = $users{$user}{$_};
122 if (ref($val) eq "HASH") {
123 foreach (sort keys %{ $users{$user}{$_} }) {
124 print OUT "--$what\t\t$_\n";
128 print OUT "--$_\t\t$val\n";
136 foreach (keys %bans) {
140 my $count = scalar keys %{ $bans{$chan} };
142 &WARN("bans: chan $chan has no other attributes; skipping.");
146 print OUT "::$chan bans\n";
147 foreach (keys %{ $bans{$chan} }) {
148 # format: bans: mask expire time-added count who-added reason
149 printf OUT "- %s:+%d:+%d:%d:%s:%s\n",
150 $_, @{ $bans{$chan}{$_} };
153 print OUT "\n" if ($cbans);
157 foreach (keys %ignore) {
161 my $count = scalar keys %{ $ignore{$chan} };
163 &WARN("ignore: chan $chan has no other attributes; skipping.");
167 ### TODO: use hash instead of array for flexibility?
168 print OUT "::$chan ignore\n";
169 &DEBUG("ignore: chan => $chan");
170 foreach (keys %{ $ignore{$chan} }) {
172 # format: ignore: mask expire time-added who-added reason
173 printf OUT "- %s:+%d:+%d:%s:%s\n", $_,
174 @{ $ignore{$chan}{$_} };
175 foreach ( @{ $ignore{$chan}{$_} } ) {
183 $wtime_userfile = time();
184 &status("--- Saved USERFILE ($cusers users; $cbans bans; $cignore ignore) at $time");
185 if (defined $msgType and $msgType =~ /^chat$/) {
186 &performStrictReply("--- Writing user file...");
191 ##### CHANNEL CONFIGURATION READER/WRITER
195 if (!open IN,"$bot_misc_dir/blootbot.chan") {
196 &ERROR("cannot erad chanfile.");
200 undef %chanconf; # reset.
201 undef %bans; # reset.
202 undef %ingore; # reset.
203 $_ = <IN>; # version string.
215 next unless (defined $chan);
217 if (/^[\s\t]+\+(\S+)$/) { # bool, true.
218 $chanconf{$chan}{$1} = 1;
220 } elsif (/^[\s\t]+\-(\S+)$/) { # bool, false.
221 $chanconf{$chan}{$1} = 0;
223 } elsif (/^[\s\t]+(\S+)[\ss\t]+(.*)$/) {# what = val.
224 $chanconf{$chan}{$1} = $2;
227 &WARN("unknown line: $_") unless (/^#/);
232 # verify configuration
233 ### TODO: check against valid params.
234 foreach $chan (keys %chanconf) {
235 foreach (keys %{ $chanconf{$chan} }) {
236 next unless (/^[+-]/);
237 &WARN("invalid param: chanconf{$chan}{$_}; removing.");
238 delete $chanconf{$chan}{$_};
239 undef $chanconf{$chan}{$_};
243 &status("CHANFILE: Loaded: ".(scalar(keys %chanconf)-1)." chans");
247 if (!open OUT,">$bot_misc_dir/blootbot.chan") {
248 &ERROR("cannot write chanfile.");
252 my $time = scalar(localtime);
253 print OUT "#v1: blootbot -- $ident -- written $time\n\n";
257 ### Process 1: if defined in _default, remove same definition
258 ### from non-default channels.
259 foreach (keys %{ $chanconf{_default} }) {
261 my $val = $chanconf{_default}{$opt};
264 foreach (keys %chanconf) {
267 next if ($chan eq "_default");
268 next unless (exists $chanconf{$chan}{$opt});
269 next unless ($val eq $chanconf{$chan}{$opt});
271 delete $chanconf{$chan}{$opt};
275 &DEBUG("Removed config $opt to @chans since it's defiend in '_default'");
279 ### Process 2: if defined in all chans but _default, set in
280 ### _default and remove all others.
281 my (%optsval, %opts);
282 foreach (keys %chanconf) {
284 next if ($chan eq "_default");
287 foreach (keys %{ $chanconf{$chan} }) {
289 if (exists $optsval{$opt} and $optsval{$opt} eq $chanconf{$chan}{$opt}) {
293 $optsval{$opt} = $chanconf{$chan}{$opt};
298 &DEBUG("chans => ".scalar(keys %chanconf)." - 1");
299 foreach (keys %opts) {
300 &DEBUG(" opts{$_} => $opts{$_}");
303 ### other optimizations are in UserDCC.pl
307 foreach (sort keys %chanconf) {
312 foreach (sort keys %{ $chanconf{$chan} }) {
313 my $val = $chanconf{$chan}{$_};
315 if ($val =~ /^0$/) { # bool, false.
318 } elsif ($val =~ /^1$/) { # bool, true.
321 } else { # what = val.
322 print OUT " $_ $val\n";
332 $wtime_chanfile = time();
333 &status("--- Saved CHANFILE (".scalar(keys %chanconf).
336 if (defined $msgType and $msgType =~ /^chat$/) {
337 &performStrictReply("--- Writing chan file...");
347 my ($ret, $f, $o) = "";
349 &verifyUser($who, $nuh);
351 foreach $f (split //, $users{$userHandle}{FLAGS}) {
352 foreach $o ( split //, $flags ) {
353 next unless ($f eq $o);
364 my ($nick, $lnuh) = @_;
367 if ($userHandle = $dcc{'CHATvrfy'}{$who}) {
368 &VERB("vUser: cached auth for $who.",2);
374 foreach $user (keys %users) {
375 next if ($user eq "_default");
377 foreach $m (keys %{$users{$user}{HOSTS}}) {
380 $m =~ s/([\@\(\)\[\]])/\\$1/g;
382 next unless ($lnuh =~ /^$m$/i);
384 if ($user !~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user}) {
385 &status("vU: host matched but diff nick ($nick != $user).");
386 $cache{VUSERWARN}{$user} = 1;
393 last if ($userHandle ne "");
395 if ($user =~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user}) {
396 &status("vU: nick matched but host is not in list ($lnuh).");
397 $cache{VUSERWARN}{$user} = 1;
401 $userHandle ||= "_default";
402 # what's talkchannel for?
403 $talkWho{$talkchannel} = $who if (defined $talkchannel);
410 # returns true if arg1 encrypts to arg2
411 my ($plain, $encrypted) = @_;
412 if ($encrypted eq "") {
413 ($plain, $encrypted) = split(/\s+/, $plain, 2);
415 return 0 unless ($plain ne "" and $encrypted ne "");
417 # MD5 // DES. Bobby Billingsley++.
418 my $salt = substr($encrypted, 0, 2);
419 if ($encrypted =~ /^\$\d\$(\w\w)\$/) {
423 return ($encrypted eq crypt($plain, $salt));
426 # mainly for dcc chat... hrm.
430 if (&IsFlag($flag) eq $flag) {
433 &status("DCC CHAT: <$who> $message -- not enough flags.");
434 &performStrictReply("error: you do not have enough flags for that. ($flag required)");
440 my($mask,$chan,$expire,$comment) = @_;
442 $chan ||= "*"; # global if undefined.
443 $comment ||= ""; # optional.
444 $expire ||= 0; # permament.
448 $expire = $expire*60 + time();
454 $exist++ if (exists $ignore{$chan}{$mask});
456 $ignore{$chan}{$mask} = [$expire, time(), $who, $comment];
459 $utime_userfile = time();
472 ### TODO: support wildcards.
473 foreach (keys %ignore) {
476 foreach (grep /^\Q$mask\E$/i, keys %{ $ignore{$chan} }) {
477 delete $ignore{$chan}{$mask};
481 &DEBUG("iD: scalar => ".scalar(keys %{ $ignore{$chan} }) );
485 $utime_userfile = time();
493 my($nick,$mask) = @_;
495 if (exists $users{$nick}) {
499 $utime_userfile = time();
502 $users{$nick}{HOSTS}{$mask} = 1;
503 $users{$nick}{FLAGS} ||= $users{_default}{FLAGS};
511 if (!exists $users{$nick}) {
515 $utime_userfile = time();
518 delete $users{$nick};
524 my($mask,$chan,$expire,$reason) = @_;
530 $expire = $expire*60 + time();
534 $exist++ if (exists $bans{$chan}{$mask} or
535 exists $bans{'*'}{$mask});
536 $bans{$chan}{$mask} = [$expire, time(), 0, $who, $reason];
539 $utime_userfile = time();
550 foreach (keys %bans) {
553 foreach (grep /^\Q$mask\E$/i, keys %{ $bans{$chan} }) {
554 delete $bans{$chan}{$_};
558 &DEBUG("bans: scalar => ".scalar(keys %{ $bans{$chan} }) );
562 $utime_userfile = time();
572 if ( &getUser($user) ) {
582 if (!defined $user) {
583 &WARN("getUser: user == NULL.");
587 if (my @retval = grep /^\Q$user\E$/i, keys %users) {
588 if ($retval[0] ne $user) {
589 &WARN("getUser: retval[0] ne user ($retval[0] ne $user)");
591 my $count = scalar keys %{ $users{$retval[0]} };
592 &DEBUG("count => $count.");
603 "limitcheckInterval",
607 ### TODO: finish off this list.
610 my @regFlagsUser = ("mno"); # todo...