]> git.donarmstrong.com Git - infobot.git/blob - src/DynaConfig.pl
dunno
[infobot.git] / src / DynaConfig.pl
1 #
2 # DynaConfig.pl: Read/Write configuration files dynamically.
3 #        Author: dms
4 #       Version: v0.1 (20010120)
5 #       Created: 20010119
6 #          NOTE: Merged from User.pl
7 #
8
9 use strict;
10
11 use vars qw(%chanconf %cache %bans %channels %nuh %users %ignore
12   %talkWho %dcc %mask);
13 use vars qw($utime_userfile $ucount_userfile $utime_chanfile $who
14   $ucount_chanfile $userHandle $chan $msgType $talkchannel
15   $ident $bot_state_dir $talkWho $flag_quit $wtime_userfile
16   $wcount_userfile $wtime_chanfile $nuh $message);
17
18 my @regFlagsUser = (
19
20     # possible chars to include in FLAG
21     'A',    # bot administration over /msg
22             # default is only via DCC CHAT
23     'O',    # dynamic ops (as on channel). (automatic +o)
24     'T',    # add topics.
25     'a',    # ask/request factoid.
26     'm',    # modify all factoids. (includes renaming)
27     'M',    # modify own factoids. (includes renaming)
28     'n',    # bot owner, can 'reload'
29     'o',    # master of bot (automatic +amrt)
30             # can search on factoid strings shorter than 2 chars
31             # can tell bot to join new channels
32             # can [un]lock factoids
33     'r',    # remove factoid.
34     't',    # teach/add factoid.
35     's',    # Bypass +silent on channels
36 );
37
38 #####
39 ##### USERFILE CONFIGURATION READER/WRITER
40 #####
41
42 sub readUserFile {
43     my $f = "$bot_state_dir/infobot.users";
44     if (! -e $f and -e "$bot_state_dir/blootbot.users") {
45          $f = "$bot_state_dir/blootbot.users";
46     }
47
48     if ( !-f $f ) {
49         &DEBUG('userfile not found; new fresh run detected.');
50         return;
51     }
52
53     if ( -f $f and -f "$f~" ) {
54         my $s1 = -s $f;
55         my $s2 = -s "$f~";
56
57         if ( $s2 > $s1 * 3 ) {
58             &FIXME('rUF: backup file bigger than current file.');
59         }
60     }
61
62     if ( !open IN, $f ) {
63         &ERROR("Cannot read userfile ($f): $!");
64         &closeLog();
65         exit 1;
66     }
67
68     undef %users;     # clear on reload.
69     undef %bans;      # reset.
70     undef %ignore;    # reset.
71
72     my $ver = <IN>;
73     if ( $ver !~ /^#v1/ ) {
74         &ERROR('old or invalid user file found.');
75         &closeLog();
76         exit 1;       # correct?
77     }
78
79     my $nick;
80     my $type;
81     while (<IN>) {
82         chop;
83
84         next if /^$/;
85         next if /^#/;
86
87         if (/^--(\S+)[\s\t]+(.*)$/) {    # user: middle entry.
88             my ( $what, $val ) = ( $1, $2 );
89
90             if ( !defined $val or $val eq '' ) {
91                 &WARN("$what: val == NULL.");
92                 next;
93             }
94
95             if ( !defined $nick ) {
96                 &WARN("DynaConfig: invalid line: $_");
97                 next;
98             }
99
100             # nice little hack.
101             if ( $what eq 'HOSTS' ) {
102                 $users{$nick}{$what}{$val} = 1;
103             }
104             else {
105                 $users{$nick}{$what} = $val;
106             }
107
108         }
109         elsif (/^(\S+)$/) {    # user: start entry.
110             $nick = $1;
111
112         }
113         elsif (/^::(\S+) ignore$/) {    # ignore: start entry.
114             $chan = $1;
115             $type = 'ignore';
116
117         }
118         elsif ( /^- (\S+):\+(\d+):\+(\d+):(\S+):(.*)$/ and $type eq 'ignore' ) {
119             ### ignore: middle entry.
120             my $mask = $1;
121             my (@array) = ( $2, $3, $4, $5 );
122             ### DEBUG purposes only!
123             if ( $mask !~ /^$mask{nuh}$/ ) {
124                 &WARN("ignore: mask $mask is invalid.");
125                 next;
126             }
127             $ignore{$chan}{$mask} = \@array;
128
129         }
130         elsif (/^::(\S+) bans$/) {    # bans: start entry.
131             $chan = $1;
132             $type = 'bans';
133
134         }
135         elsif ( /^- (\S+):\+(\d+):\+(\d+):(\d+):(\S+):(.*)$/
136             and $type eq 'bans' )
137         {
138             ### bans: middle entry.
139             # $btime, $atime, $count, $whoby, $reason.
140             my (@array) = ( $2, $3, $4, $5, $6 );
141             $bans{$chan}{$1} = \@array;
142
143         }
144         else {    # unknown.
145             &WARN("unknown line: $_");
146         }
147     }
148     close IN;
149
150     &status(
151         sprintf(
152             'USERFILE: Loaded: %d users, %d bans, %d ignore',
153             scalar( keys %users ) - 1,
154             scalar( keys %bans ),      # ??
155             scalar( keys %ignore ),    # ??
156         )
157     );
158 }
159
160 sub writeUserFile {
161     if ( !scalar keys %users ) {
162         &DEBUG('wUF: nothing to write.');
163         return;
164     }
165
166     if ( !open OUT, ">$bot_state_dir/infobot.users" ) {
167         &ERROR("Cannot write userfile ($bot_state_dir/infobot.users): $!");
168         return;
169     }
170
171     my $time = scalar(gmtime);
172
173     print OUT "#v1: infobot -- $ident -- written $time\n\n";
174
175     ### USER LIST.
176     my $cusers = 0;
177     foreach ( sort keys %users ) {
178         my $user = $_;
179         $cusers++;
180         my $count = scalar keys %{ $users{$user} };
181         if ( !$count ) {
182             &WARN("user $user has no other attributes; skipping.");
183             next;
184         }
185
186         print OUT "$user\n";
187
188         foreach ( sort keys %{ $users{$user} } ) {
189             my $what = $_;
190             my $val  = $users{$user}{$_};
191
192             if ( ref($val) eq 'HASH' ) {
193                 foreach ( sort keys %{ $users{$user}{$_} } ) {
194                     print OUT "--$what\t\t$_\n";
195                 }
196
197             }
198             elsif ( $_ eq 'FLAGS' ) {
199                 print OUT "--$_\t\t"
200                   . join( '', sort split( '', $val ) ) . "\n";
201             }
202             else {
203                 print OUT "--$_\t\t$val\n";
204             }
205         }
206         print OUT "\n";
207     }
208
209     ### BAN LIST.
210     my $cbans = 0;
211     foreach ( keys %bans ) {
212         my $chan = $_;
213         $cbans++;
214
215         my $count = scalar keys %{ $bans{$chan} };
216         if ( !$count ) {
217             &WARN("bans: chan $chan has no other attributes; skipping.");
218             next;
219         }
220
221         print OUT "::$chan bans\n";
222         foreach ( keys %{ $bans{$chan} } ) {
223
224             # format: bans: mask expire time-added count who-added reason
225             my @array = @{ $bans{$chan}{$_} };
226             if ( scalar @array != 5 ) {
227                 &WARN("bans: $chan/$_ is corrupted.");
228                 next;
229             }
230
231             printf OUT "- %s:+%d:+%d:%d:%s:%s\n", $_, @array;
232         }
233     }
234     print OUT "\n" if ($cbans);
235
236     ### IGNORE LIST.
237     my $cignore = 0;
238     foreach ( keys %ignore ) {
239         my $chan = $_;
240         $cignore++;
241
242         my $count = scalar keys %{ $ignore{$chan} };
243         if ( !$count ) {
244             &WARN("ignore: chan $chan has no other attributes; skipping.");
245             next;
246         }
247
248         ### TODO: use hash instead of array for flexibility?
249         print OUT "::$chan ignore\n";
250         foreach ( keys %{ $ignore{$chan} } ) {
251
252             # format: ignore: mask expire time-added who-added reason
253             my @array = @{ $ignore{$chan}{$_} };
254             if ( scalar @array != 4 ) {
255                 &WARN("ignore: $chan/$_ is corrupted.");
256                 next;
257             }
258
259             printf OUT "- %s:+%d:+%d:%s:%s\n", $_, @array;
260         }
261     }
262
263     close OUT;
264
265     $wtime_userfile = time();
266     &status(
267 "--- Saved USERFILE ($cusers users; $cbans bans; $cignore ignore) at $time"
268     );
269     if ( defined $msgType and $msgType =~ /^chat$/ ) {
270         &performStrictReply('--- Writing user file...');
271     }
272 }
273
274 #####
275 ##### CHANNEL CONFIGURATION READER/WRITER
276 #####
277
278 sub readChanFile {
279     my $f = "$bot_state_dir/infobot.chan";
280     if (-e "$bot_state_dir/infobot.chan" and -e "$bot_state_dir/blootbot.chan") {
281          $f = "$bot_state_dir/blootbot.chan";
282     }
283     if ( -f $f and -f "$f~" ) {
284         my $s1 = -s $f;
285         my $s2 = -s "$f~";
286
287         if ( $s2 > $s1 * 3 ) {
288             &FIXME('rCF: backup file bigger than current file.');
289         }
290     }
291
292     if ( !open IN, $f ) {
293         &ERROR("Cannot read chanfile ($f): $!");
294         return;
295     }
296
297     undef %chanconf;    # reset.
298
299     $_ = <IN>;          # version string.
300
301     my $chan;
302     while (<IN>) {
303         chop;
304
305         next if /^\s*$/;
306         next if /^\// or /^\;/;    # / or ; are comment lines.
307
308         if (/^(\S+)\s*$/) {
309             $chan = $1;
310             next;
311         }
312         next unless ( defined $chan );
313
314         if (/^[\s\t]+\+(\S+)$/) {    # bool, true.
315             $chanconf{$chan}{$1} = 1;
316
317         }
318         elsif (/^[\s\t]+\-(\S+)$/) {    # bool, false.
319                 # although this is supported in run-time configuration.
320             $chanconf{$chan}{$1} = 0;
321
322         }
323         elsif (/^[\s\t]+(\S+)[\s\t]+(.*)$/) {    # what = val.
324             $chanconf{$chan}{$1} = $2;
325
326         }
327         else {
328             &WARN("unknown line: $_") unless (/^#/);
329         }
330     }
331     close IN;
332
333     # verify configuration
334     ### TODO: check against valid params.
335     foreach $chan ( keys %chanconf ) {
336         foreach ( keys %{ $chanconf{$chan} } ) {
337             next unless /^[+-]/;
338
339             &WARN("invalid param: chanconf{$chan}{$_}; removing.");
340             delete $chanconf{$chan}{$_};
341             undef $chanconf{$chan}{$_};
342         }
343     }
344
345     &status(
346         'CHANFILE: Loaded: ' . ( scalar( keys %chanconf ) - 1 ) . ' chans' );
347 }
348
349 sub writeChanFile {
350     if ( !scalar keys %chanconf ) {
351         &DEBUG('wCF: nothing to write.');
352         return;
353     }
354
355     if ( !open OUT, ">$bot_state_dir/infobot.chan" ) {
356         &ERROR("Cannot write chanfile ($bot_state_dir/infobot.chan): $!");
357         return;
358     }
359
360     my $time = scalar(gmtime);
361     print OUT "#v1: infobot -- $ident -- written $time\n\n";
362
363     if ($flag_quit) {
364
365         ### Process 1: if defined in _default, remove same definition
366         ###             from non-default channels.
367         foreach ( keys %{ $chanconf{_default} } ) {
368             my $opt = $_;
369             my $val = $chanconf{_default}{$opt};
370             my @chans;
371
372             foreach ( keys %chanconf ) {
373                 $chan = $_;
374
375                 next if ( $chan eq '_default' );
376                 next unless ( exists $chanconf{$chan}{$opt} );
377                 next unless ( $val eq $chanconf{$chan}{$opt} );
378
379                 push( @chans, $chan );
380                 delete $chanconf{$chan}{$opt};
381             }
382
383             if ( scalar @chans ) {
384                 &DEBUG(
385 "Removed config $opt to @chans since it's defiend in '_default'"
386                 );
387             }
388         }
389
390         ### Process 2: if defined in all chans but _default, set in
391         ###             _default and remove all others.
392         my ( %optsval, %opts );
393         foreach ( keys %chanconf ) {
394             $chan = $_;
395             next if ( $chan eq '_default' );
396             my $opt;
397
398             foreach ( keys %{ $chanconf{$chan} } ) {
399                 $opt = $_;
400                 if ( exists $optsval{$opt}
401                     and $optsval{$opt} eq $chanconf{$chan}{$opt} )
402                 {
403                     $opts{$opt}++;
404                     next;
405                 }
406                 $optsval{$opt} = $chanconf{$chan}{$opt};
407                 $opts{$opt}    = 1;
408             }
409         }
410
411         foreach ( keys %opts ) {
412             next unless ( $opts{$_} > 2 );
413             &DEBUG("  opts{$_} => $opts{$_}");
414         }
415
416         ### other optimizations are in UserDCC.pl
417     }
418
419     ### lets do it...
420     foreach ( sort keys %chanconf ) {
421         $chan = $_;
422
423         print OUT "$chan\n";
424
425         foreach ( sort keys %{ $chanconf{$chan} } ) {
426             my $val = $chanconf{$chan}{$_};
427
428             if ( $val =~ /^0$/ ) {    # bool, false.
429                 print OUT "    -$_\n";
430
431             }
432             elsif ( $val =~ /^1$/ ) {    # bool, true.
433                 print OUT "    +$_\n";
434
435             }
436             else {                       # what = val.
437                 print OUT "    $_ $val\n";
438
439             }
440
441         }
442         print OUT "\n";
443     }
444
445     close OUT;
446
447     $wtime_chanfile = time();
448     &status('--- Saved CHANFILE ('
449           . scalar( keys %chanconf )
450           . " chans) at $time" );
451
452     if ( defined $msgType and $msgType =~ /^chat$/ ) {
453         &performStrictReply('--- Writing chan file...');
454     }
455 }
456
457 #####
458 ##### USER COMMANDS.
459 #####
460
461 # TODO: support multiple flags.
462 # TODO: return all flags for opers
463 sub IsFlag {
464     my $flags = shift;
465     my ( $ret, $f, $o ) = '';
466
467     &verifyUser( $who, $nuh );
468
469     foreach $f ( split //, $users{$userHandle}{FLAGS} ) {
470         foreach $o ( split //, $flags ) {
471             next unless ( $f eq $o );
472
473             $ret = $f;
474             last;
475         }
476     }
477
478     $ret;
479 }
480
481 sub verifyUser {
482     my ( $nick, $lnuh ) = @_;
483     my ( $user, $m );
484
485     if ( $userHandle = $dcc{'CHATvrfy'}{$who} ) {
486         &VERB( "vUser: cached auth for $who.", 2 );
487         return $userHandle;
488     }
489
490     $userHandle = '';
491
492     foreach $user ( keys %users ) {
493         next if ( $user eq '_default' );
494
495         foreach $m ( keys %{ $users{$user}{HOSTS} } ) {
496             $m =~ s/\?/./g;
497             $m =~ s/\*/.*?/g;
498             $m =~ s/([\@\(\)\[\]])/\\$1/g;
499
500             next unless ( $lnuh =~ /^$m$/i );
501
502             if ( $user !~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user} )
503             {
504                 &status("vU: host matched but diff nick ($nick != $user).");
505                 $cache{VUSERWARN}{$user} = 1;
506             }
507
508             $userHandle = $user;
509             last;
510         }
511
512         last if ( $userHandle ne '' );
513
514         if ( $user =~ /^\Q$nick\E$/i and !exists $cache{VUSERWARN}{$user} ) {
515             &status("vU: nick matched but host is not in list ($lnuh).");
516             $cache{VUSERWARN}{$user} = 1;
517         }
518     }
519
520     $userHandle ||= '_default';
521
522     # what's talkchannel for?
523     $talkWho{$talkchannel} = $who if ( defined $talkchannel );
524     $talkWho = $who;
525
526     return $userHandle;
527 }
528
529 sub ckpasswd {
530
531     # returns true if arg1 encrypts to arg2
532     my ( $plain, $encrypted ) = @_;
533     if ( $encrypted eq '' ) {
534         ( $plain, $encrypted ) = split( /\s+/, $plain, 2 );
535     }
536     return 0 unless ( $plain ne '' and $encrypted ne '' );
537
538     # MD5 // DES. Bobby Billingsley++.
539     my $salt;
540     if ( $encrypted =~ /^(\S{2})/ and length $encrypted == 13 ) {
541         $salt = $1;
542     }
543     elsif ( $encrypted =~ /^\$\d\$(\w\w)\$/ ) {
544         $salt = $1;
545     }
546     else {
547         &DEBUG("unknown salt from $encrypted.");
548         return 0;
549     }
550
551     return ( $encrypted eq crypt( $plain, $salt ) );
552 }
553
554 # mainly for dcc chat... hrm.
555 sub hasFlag {
556     my ($flag) = @_;
557
558     if ( &IsFlag($flag) eq $flag ) {
559         return 1;
560     }
561     else {
562         &status("DCC CHAT: <$who> $message -- not enough flags.");
563         &performStrictReply(
564             "error: you do not have enough flags for that. ($flag required)");
565         return 0;
566     }
567 }
568
569 # expire is time in minutes
570 sub ignoreAdd {
571     my ( $mask, $chan, $expire, $comment ) = @_;
572
573     $chan     ||= '*';    # global if undefined.
574     $comment  ||= '';     # optional.
575     $expire   ||= 0;      # permament.
576     my $count ||= 0;
577
578     if ( $expire > 0 ) {
579         $expire = ( $expire * 60 ) + time();
580     }
581     else {
582         $expire = 0;
583     }
584
585     my $exist = 0;
586     $exist++ if ( exists $ignore{$chan}{$mask} );
587
588     $ignore{$chan}{$mask} = [ $expire, time(), $who, $comment ];
589
590     # TODO: improve this.
591     if ( $expire == 0 ) {
592         &status(
593 "ignore: Added $mask for $chan to NEVER expire, by $who, for $comment"
594         );
595     }
596     else {
597         &status(
598 "ignore: Added $mask for $chan to expire $expire mins, by $who, for $comment"
599         );
600     }
601
602     if ($exist) {
603         $utime_userfile = time();
604         $ucount_userfile++;
605
606         return 2;
607     }
608     else {
609         return 1;
610     }
611 }
612
613 sub ignoreDel {
614     my ($mask) = @_;
615     my @match;
616
617     ### TODO: support wildcards.
618     foreach ( keys %ignore ) {
619         my $chan = $_;
620
621         foreach ( grep /^\Q$mask\E$/i, keys %{ $ignore{$chan} } ) {
622             delete $ignore{$chan}{$mask};
623             push( @match, $chan );
624         }
625
626         &DEBUG( 'iD: scalar => ' . scalar( keys %{ $ignore{$chan} } ) );
627     }
628
629     if ( scalar @match ) {
630         $utime_userfile = time();
631         $ucount_userfile++;
632     }
633
634     return @match;
635 }
636
637 sub userAdd {
638     my ( $nick, $mask ) = @_;
639
640     if ( exists $users{$nick} ) {
641         return 0;
642     }
643
644     $utime_userfile = time();
645     $ucount_userfile++;
646
647     if ( defined $mask and $mask !~ /^\s*$/ ) {
648         &DEBUG("userAdd: mask => $mask");
649         $users{$nick}{HOSTS}{$mask} = 1;
650     }
651
652     $users{$nick}{FLAGS} ||= $users{_default}{FLAGS};
653
654     return 1;
655 }
656
657 sub userDel {
658     my ($nick) = @_;
659
660     if ( !exists $users{$nick} ) {
661         return 0;
662     }
663
664     $utime_userfile = time();
665     $ucount_userfile++;
666
667     delete $users{$nick};
668
669     return 1;
670 }
671
672 sub banAdd {
673     my ( $mask, $chan, $expire, $reason ) = @_;
674
675     $chan   ||= '*';
676     $expire ||= 0;
677
678     if ( $expire > 0 ) {
679         $expire = $expire * 60 + time();
680     }
681
682     my $exist = 1;
683     $exist++ if ( exists $bans{$chan}{$mask}
684         or exists $bans{'*'}{$mask} );
685     $bans{$chan}{$mask} = [ $expire, time(), 0, $who, $reason ];
686
687     my @chans = ( $chan eq '*' ) ? keys %channels : $chan;
688     my $m = $mask;
689     $m =~ s/\?/\\./g;
690     $m =~ s/\*/\\S*/g;
691     foreach (@chans) {
692         my $chan = $_;
693         foreach ( keys %{ $channels{$chan}{''} } ) {
694             next unless ( exists $nuh{ lc $_ } );
695             next unless ( $nuh{ lc $_ } =~ /^$m$/i );
696             &FIXME("nuh{$_} =~ /$m/");
697         }
698     }
699
700     if ( $exist == 1 ) {
701         $utime_userfile = time();
702         $ucount_userfile++;
703     }
704
705     return $exist;
706 }
707
708 sub banDel {
709     my ($mask) = @_;
710     my @match;
711
712     foreach ( keys %bans ) {
713         my $chan = $_;
714
715         foreach ( grep /^\Q$mask\E$/i, keys %{ $bans{$chan} } ) {
716             delete $bans{$chan}{$_};
717             push( @match, $chan );
718         }
719
720         &DEBUG( 'bans: scalar => ' . scalar( keys %{ $bans{$chan} } ) );
721     }
722
723     if ( scalar @match ) {
724         $utime_userfile = time();
725         $ucount_userfile++;
726     }
727
728     return @match;
729 }
730
731 sub IsUser {
732     my ($user) = @_;
733
734     if ( &getUser($user) ) {
735         return 1;
736     }
737     else {
738         return 0;
739     }
740 }
741
742 sub getUser {
743     my ($user) = @_;
744
745     if ( !defined $user ) {
746         &WARN('getUser: user == NULL.');
747         return;
748     }
749
750     if ( my @retval = grep /^\Q$user\E$/i, keys %users ) {
751         if ( $retval[0] ne $user ) {
752             &WARN("getUser: retval[0] ne user ($retval[0] ne $user)");
753         }
754         my $count = scalar keys %{ $users{ $retval[0] } };
755         &DEBUG("count => $count.");
756
757         return $retval[0];
758     }
759     else {
760         return;
761     }
762 }
763
764 sub chanSet {
765     my ( $cmd, $chan, $what, $val ) = @_;
766
767     if ( $cmd eq 'chanadd' ) {
768         if ( exists $chanconf{$chan} ) {
769             &performStrictReply("chan $chan already exists.");
770             return;
771         }
772         $chanconf{$chan}{_time_added} = time();
773         $chanconf{$chan}{autojoin}    = $conn->nick();
774
775         &performStrictReply("Joining $chan...");
776         &joinchan($chan);
777
778         return;
779     }
780
781     if ( !exists $chanconf{$chan} ) {
782         &performStrictReply("no such channel $chan");
783         return;
784     }
785
786     my $update = 0;
787
788     if ( defined $what and $what =~ s/^([+-])(\S+)/$2/ ) {
789         ### '.chanset +blah'
790         ### '.chanset +blah 10'         -- error.
791
792         my $set = ( $1 eq '+' ) ? 1 : 0;
793         my $was = $chanconf{$chan}{$what};
794
795         if ($set) {    # add/set.
796             if ( defined $was and $was eq '1' ) {
797                 &performStrictReply("setting $what for $chan already 1.");
798                 return;
799             }
800
801             $val = 1;
802
803         }
804         else {         # delete/unset.
805             if ( !defined $was ) {
806                 &performStrictReply("setting $what for $chan is not set.");
807                 return;
808             }
809
810             $val = 0;
811         }
812
813         # alter for cosmetic (print out) reasons only.
814         $was = ( defined $was ) ? "; was '$was'" : '';
815
816         if ( $val eq '0' ) {
817             &performStrictReply("Unsetting $what for $chan$was.");
818             delete $chanconf{$chan}{$what};
819             delete $cache{ircTextCounters} if $what eq 'ircTextCounters';
820         }
821         else {
822             &performStrictReply("Setting $what for $chan to '$val'$was.");
823             $chanconf{$chan}{$what} = $val;
824             delete $cache{ircTextCounters} if $what eq 'ircTextCounters';
825         }
826
827         $update++;
828
829     }
830     elsif ( defined $val ) {
831         ### '.chanset blah testing'
832
833         my $was = $chanconf{$chan}{$what};
834         if ( defined $was and $was eq $val ) {
835             &performStrictReply("setting $what for $chan already '$val'.");
836             return;
837         }
838         $was = ($was) ? "; was '$was'" : '';
839         &performStrictReply("Setting $what for $chan to '$val'$was.");
840
841         $chanconf{$chan}{$what} = $val;
842         delete $cache{ircTextCounters} if $what eq 'ircTextCounters';
843
844         $update++;
845
846     }
847     else {    # read only.
848         ### '.chanset'
849         ### '.chanset blah'
850
851         if ( !defined $what ) {
852             &WARN('chanset/DC: what == undefine.');
853             return;
854         }
855
856         if ( exists $chanconf{$chan}{$what} ) {
857             &performStrictReply("$what for $chan is '$chanconf{$chan}{$what}'");
858         }
859         else {
860             &performStrictReply("$what for $chan is not set.");
861         }
862     }
863
864     if ($update) {
865         $utime_chanfile = time();
866         $ucount_chanfile++;
867     }
868
869     return;
870 }
871
872 sub rehashConfVars {
873
874     # this is an attempt to fix where an option is enabled but the module
875     # has been not loaded. it also can be used for other things.
876
877     foreach ( keys %{ $cache{confvars} } ) {
878         my $i = $cache{confvars}{$_};
879         &DEBUG("rehashConfVars: _ => $_");
880
881         if ( /^news$/ and $i ) {
882             &loadMyModule('News');
883             delete $cache{confvars}{$_};
884         }
885
886         if ( /^uptime$/ and $i ) {
887             &loadMyModule('Uptime');
888             delete $cache{confvars}{$_};
889         }
890
891         if ( /^rootwarn$/i and $i ) {
892             &loadMyModule('RootWarn');
893             delete $cache{confvars}{$_};
894         }
895
896         if ( /^onjoin$/i and $i ) {
897             &loadMyModule('OnJoin');
898             delete $cache{confvars}{$_};
899         }
900     }
901
902     &DEBUG('end of rehashConfVars');
903
904     delete $cache{confvars};
905 }
906
907 1;
908
909 # vim:ts=4:sw=4:expandtab:tw=80