-
Notifications
You must be signed in to change notification settings - Fork 0
/
dansguardian-lib.pl
executable file
·1921 lines (1705 loc) · 85.6 KB
/
dansguardian-lib.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/perl -w
# dansguardian-lib.pl
# Note: Every sub-command execution used to be protected by a sequence that restored the Environment
# just in case the sub-command had screwed it up. But according to the Webmin documentation (?),
# the protection didn't actually work right. So, since the protection attempt seemed to take
# extra time and risked breaking something else (particularly with its errors?-) and seemed to be
# an attempt to "fix something that wasn't broken", I just removed it (March 2009). If there's
# ever a problem with sub-commands polluting the Environment, restore the protection and make it
# work right.
# by declaring all the globals we'll reference from libraries
# _before_ pulling in libraries, and by adding the 'use ...' _after_
# pulling in libraries, we can 'use strict' for our own code without
# generating any messages about the less-than-clean code in the very old
# Webmin libraries themselves
our (%text, %config, %in, $module_name, $current_lang);
do '../web-lib.pl';
do '../ui-lib.pl';
use Time::HiRes qw( time alarm sleep );
use warnings;
use strict;
# All Webmin initialization is now done here for everybody just once the same way
# rather than doing it in each .cgi file. (March 2009)
# Note we do NOT &clean_environment(), because we utilize some of the webserver environment
# variables ourselves, which wouldn't work if they were gone.
if ($ENV{'SCRIPT_FILENAME'} =~ 'dansguardian') {
# things we can't do in case we're called from a foreign user, for example ACL processing
&ReadParse();
}
&init_config();
our %access = &get_module_acl();
our $dg_version;
#################################################################
#
# global flag setting -
# change this line of source code to 1 during development
# change it back to 0 for production
#
our $debug = 0;
#
#################################################################
open DEBUGLOG,">>/var/log/dansguardian/debuglog" if $debug;
print DEBUGLOG "at almost very top of dansguardian-lib.pl\n" if $debug;
# useful constants
# typically the same as the hash "key" - can use for backtracking if necessary
use constant DETAILSNAME => 0;
# usually either same as the name, or "undef"
# "undef" will problably be interpreted as generic for all vars of same type
use constant DETAILSHELP => 1;
# kind of variable (roughly -but not exactly- corresponds to HTML GUI
# constructs: type=checkbox, type=radio, type=text, type=textarea, nomatch.)
use constant DETAILSVAR => 2;
use constant VARNONE => 0;
use constant VARONOFF => 1;
use constant VARCHOICE => 2;
use constant VARTEXT => 3;
use constant VARPARA => 4;
use constant VARLINE => 5;
# GUI details for various variable types (reused)
# for type=radio (or <select>), subarray lists choices,
# each subarray entry is a subsubarray of [ external name, internal value ]
use constant DETAILSCHOICE => 3;
# for type=text, numeric representation (1-9) of size class
use constant DETAILSTEXT => 3;
# has a compiled-in default
use constant DETAILSHASDEFAULT => 4;
use constant HASDEFAULTYES => 1;
use constant HASDEFAULTNO => 0;
# compiled-in default value (if known)
use constant DETAILSDEFAULTVAL => 5;
# number of repetitions allowed
# (if reality doesn't match this, the current configuration is erroneous)
# can occur only once, or can occur multiple times,
# or individual lines can be commented in or out (like 'authplugin')
use constant DETAILSREP => 6;
use constant REPSINGLE => 1;
# REPMULTI isn't fully implemented! We say it's okay for this input to be multi-valued,
# but then we have no allowance for properly displaying it or for changing its value
# (it is fully implemented for variable type LINE though)
use constant REPMULTI => 2;
use constant REPLINES => 3;
# where the option is allowed to occur (NOT where it actually typically occurs)
# a few options can occur only in filter group config files
# a few options can occur only in the main system-wide config file
# most options can occur both places
# (if present the individual filter group setting will take precedence,
# so the system-wide setting becomes a sort of "all groups" local default)
use constant DETAILSWHERE => 7;
use constant WHEREMAIN => 1;
use constant WHEREGROUP => 2;
use constant WHEREOVERRIDE => 3;
use constant WHERESHARED => 4;
# read-only and statics
# (use these freely, but don't write to them)
# Perl doesn't actually _enforce_ treating them as read-only
# this is internal data used by &read_file_lines_just_once (and &flush_file_lines)
# it MUST not be corrupted
our %filesread = ();
# note these are hashes rather than arrays so we can test for membership very quickly with just "exists"
# (the downside is there's no "order", so uses require rather sophisticated sorts)
our %mainconfigurationlists = (
filtergroupslist => 1,
exceptioniplist => 1,
bannediplist => 1,
);
our %groupsconfigurationlists = (
exceptionsitelist => 1,
bannedsitelist => 1,
greysitelist => 1,
logsitelist => 1,
exceptionurllist => 1,
bannedurllist => 1,
greyurllist => 1,
logurllist => 1,
bannedregexpurllist => 1,
exceptionregexpurllist => 1,
logregexpurllist => 1,
weightedphraselist => 1,
exceptionphraselist => 1,
bannedphraselist => 1,
bannedextensionlist => 1,
bannedmimetypelist => 1,
picsfile => 1,
exceptionextensionlist => 1,
exceptionmimetypelist => 1,
exceptionfilesitelist => 1,
exceptionfileurllist => 1,
contentregexplist => 1,
urlregexplist => 1,
headerregexplist => 1,
bannedregexpheaderlist => 1,
);
our %configurationlists = (%mainconfigurationlists, %groupsconfigurationlists);
# next two lists are constructed at run-time, as they vary from one environment to the next
our %sharedgroupsconfigurationlists = ();
our %notsharedgroupsconfigurationlists = ();
# fixed lists of a few lists which can ONLY be shared for each scheme
our %sharedonlynestedconfigurationlists = (
logsitelist => 1,
logurllist => 1,
logregexpurllist => 1,
picsfile => 1,
urlregexplist => 1,
contentregexplist => 1,
headerregexplist => 1,
);
our %sharedonlyseparateconfigurationlists = ();
our %sharedonlycommonconfigurationlists = ();
#################################################################
#
# this program is almost entirely DATA-DRIVEN
#
# to fix a variable or add a new variable (or delete a variable),
# just change the follow table
#
# there will very seldom be any need to change the code
#
# for example to delete a variable just change its type to
# VARNONE - it will be handled correctly and appropriate
# warning messages will be generated if it appears
#
# (implementation note: as a side effect of implementing this
# as a Perl 'hash', the one thing it canNOT do is specify an
# order - that's why separate arrays of option names [generally
# in other modules] exist)
#
# (implementation note: this should be a "read-only" variable -
# however Perl does not enforce this so you can accidentally
# change this - doing so will almost certainly screw things up)
#
#################################################################
our %options2details = (
loglevel => [ 'loglevel', 'loglevel', VARCHOICE, [ [ 0, 'conf_loglevel_radio_1' ], [ 1, 'conf_loglevel_radio_2' ], [ 2, 'conf_loglevel_radio_3' ], [ 3, 'conf_loglevel_radio_4' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
logfileformat => [ 'logfileformat', 'logfileformat', VARCHOICE, [ [ 1, 'conf_logfileformat_radio_1' ], [ 2, 'conf_logfileformat_radio_2' ], [ 3, 'conf_logfileformat_radio_3' ], [ 4, 'conf_logfileformat_radio_4' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
logclienthostnames => [ 'logclienthostnames', 'logclienthostnames', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
logconnectionhandlingerrors => [ 'logconnectionhandlingerrors', 'logconnectionhandlingerrors', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
languagedir => [ 'languagedir', 'filepaths', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
language => [ 'language', 'language', VARTEXT, 6, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
groupname => [ 'groupname', 'groupname', VARTEXT, 6, HASDEFAULTYES, '', REPSINGLE, WHEREGROUP ],
# next option exists in the code but isn't reference ANYwhere, so effectively it doesn't exist
groupnamelist => [ 'groupnamelist', 'groupnamelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
naughtynesslimit => [ 'naughtynesslimit', 'naughtynesslimit', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
daemonuser => [ 'daemonuser', 'daemonid', VARTEXT, 6, HASDEFAULTYES, 'PROXYUSER', REPSINGLE, WHEREMAIN ],
daemongroup => [ 'daemongroup', 'daemonid', VARTEXT, 6, HASDEFAULTYES, 'PROXYGROUP', REPSINGLE, WHEREMAIN ],
# note REPMULTI (used in following) is unfortunately NOT fully implemented so it won't work in some cases
filterip => [ 'filterip', 'filterip', VARTEXT, 6, HASDEFAULTNO, undef, REPMULTI, WHEREMAIN ],
filterport => [ 'filterport', 'filterport', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
proxyip => [ 'proxyip', 'proxyip', VARTEXT, 6, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
proxyport => [ 'proxyport', 'proxyport', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxchildren => [ 'maxchildren', 'children', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
minchildren => [ 'minchildren', 'children', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
minsparechildren => [ 'minsparechildren', 'children', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
preforkchildren => [ 'preforkchildren', 'children', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxsparechildren => [ 'maxsparechildren', 'children', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxagechildren => [ 'maxagechildren', 'children', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxips => [ 'maxips', 'maxips', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
reverseaddresslookups => [ 'reverseaddresslookups', 'reverseaddresslookups', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
reverseclientiplookups => [ 'reverseclientiplookups', 'reverseclientiplookups', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
forwardedfor => [ 'forwardedfor', 'forwardedfor', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
usexforwardedfor => [ 'usexforwardedfor', 'usexforwardedfor', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
ipcfilename => [ 'ipcfilename', 'ipc', VARTEXT, 7, HASDEFAULTYES, '/tmp/.dguardianipc', REPSINGLE, WHEREMAIN ],
urlipcfilename => [ 'urlipcfilename', 'ipc', VARTEXT, 7, HASDEFAULTYES, '/tmp/.dguardianurlipc', REPSINGLE, WHEREMAIN ],
ipipcfilename => [ 'ipipcfilename', 'ipc', VARTEXT, 7, HASDEFAULTYES, '/tmp/.dguardianipipc', REPSINGLE, WHEREMAIN ],
pidfilename => [ 'pidfilename', 'pidfilename', VARTEXT, 7, HASDEFAULTYES, 'PREFIX/var/run/dansguardian.pid', REPSINGLE, WHEREMAIN ],
nodaemon => [ 'nodaemon', 'nodaemon', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
nologger => [ 'nologger', 'nologger', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
softrestart => [ 'softrestart', 'softrestart', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
authplugin => [ 'authplugin', 'authplugin', VARLINE, undef, HASDEFAULTNO, undef, REPMULTI, WHEREMAIN ],
downloadmanager => [ 'downloadmanager', 'downloadmanager', VARLINE, undef, HASDEFAULTNO, undef, REPMULTI, WHEREMAIN ],
contentscanner => [ 'contentscanner', 'contentscanner', VARLINE, undef, HASDEFAULTNO, undef, REPMULTI, WHEREMAIN ],
maxlogitemlength => [ 'maxlogitemlength', 'maxlogitemlength', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
anonymizelogs => [ 'anonymizelogs', 'anonymizelogs', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
# next option exists in the code but isn't reference ANYwhere, so effectively it doesn't exist
logtimestamp => [ 'logtimestamp', 'logtimestamp', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
logsyslog => [ 'logsyslog', 'logsyslog', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHEREMAIN ],
loglocation => [ 'loglocation', 'filepaths', VARTEXT, 8, HASDEFAULTYES, 'PREFIX/var/log/dansguardian/access.log', REPSINGLE, WHEREMAIN ],
statlocation => [ 'statlocation', 'filepaths', VARTEXT, 8, HASDEFAULTYES, 'PREFIX/var/log/dansguardian/stats', REPSINGLE, WHEREMAIN ],
logexceptionhits => [ 'logexceptionhits', 'logexceptionhits', VARCHOICE, [ [ 0, 'conf_logexceptionhits_radio_1' ], [ 1, 'conf_logexceptionhits_radio_2' ], [ 2, 'conf_logexceptionhits_radio_3' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
showweightedfound => [ 'showweightedfound', 'showweightedfound', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
weightedphrasemode => [ 'weightedphrasemode', 'weightedphrasemode', VARCHOICE, [ [ 0, 'conf_weightedphrasemode_radio_1' ], [ 1, 'conf_weightedphrasemode_radio_2' ], [ 2, 'conf_weightedphrasemode_radio_3' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
reportinglevel => [ 'reportinglevel', 'reporterrors', VARCHOICE, [ [ -1, 'conf_reportinglevel_radio_1' ], [ 0, 'conf_reportinglevel_radio_2' ], [ 1, 'conf_reportinglevel_radio_3' ], [ 2, 'conf_reportinglevel_radio_4' ], [ 3, 'conf_reportinglevel_radio_5' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREOVERRIDE ],
accessdeniedaddress => [ 'accessdeniedaddress', 'reporterrors', VARTEXT, 7, HASDEFAULTNO, undef, REPSINGLE, WHEREOVERRIDE ],
htmltemplate => [ 'htmltemplate', 'reporterrors', VARTEXT, 8, HASDEFAULTYES, 'LANGUAGEDIR/LANGUAGE/template.html', REPSINGLE, WHEREOVERRIDE ],
logchildprocesshandling => [ 'logchildprocesshandling', 'logchildprocesshandling', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
logadblocks => [ 'logadblocks', 'logadblocks', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
loguseragent => [ 'loguseragent', 'loguseragent', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
bannediplist => [ 'bannediplist', 'iplists', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
exceptioniplist => [ 'exceptioniplist', 'iplists', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxuploadsize => [ 'maxuploadsize', 'maxuploadsize', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
urlcachenumber => [ 'urlcachenumber', 'urlcachenumber', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
urlcacheage => [ 'urlcacheage', 'urlcacheage', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxcontentfiltersize => [ 'maxcontentfiltersize', 'maxcontentfiltersize', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxcontentramcachescansize => [ 'maxcontentramcachescansize', 'maxcontentramcachescansize', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
maxcontentfilecachescansize => [ 'maxcontentfilecachescansize', 'maxcontentfilecachescansize', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
filecachedir => [ 'filecachedir', 'filecachedir', VARTEXT, 7, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
deletedownloadedtempfiles => [ 'deletedownloadedtempfiles', 'deletedownloadedtempfiles', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHEREMAIN ],
initialtrickledelay => [ 'initialtrickledelay', 'initialtrickledelay', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
trickledelay => [ 'trickledelay', 'trickledelay', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
phrasefiltermode => [ 'phrasefiltermode', 'phrasefiltermode', VARCHOICE, [ [ 0, 'conf_phrasefiltermode_radio_1' ], [ 1, 'conf_phrasefiltermode_radio_2' ], [ 2, 'conf_phrasefiltermode_radio_3' ], [ 3, 'conf_phrasefiltermode_radio_4' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
preservecase => [ 'preservecase', 'preservecase', VARCHOICE, [ [ 0, 'conf_preservecase_radio_1' ], [ 1, 'conf_preservecase_radio_2' ], [ 2, 'conf_preservecase_radio_3' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
hexdecodecontent => [ 'hexdecodecontent', 'hexdecodecontent', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
forcequicksearch => [ 'forcequicksearch', 'forcequicksearch', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
nonstandarddelimiter => [ 'nonstandarddelimiter', 'nonstandarddelimiter', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHEREMAIN ],
filtergroups => [ 'filtergroups', 'filtergroups', VARTEXT, 2, HASDEFAULTYES, 1, REPSINGLE, WHEREMAIN ],
filtergroupslist => [ 'filtergroupslist', 'filtergroupslist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
usecustombannedimage => [ 'usecustombannedimage', 'usecustombannedimage', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHEREMAIN ],
custombannedimagefile => [ 'custombannedimagefile', 'custombannedimagefile', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
scancleancache => [ 'scancleancache', 'scancleancache', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHEREMAIN ],
createlistcachefiles => [ 'createlistcachefiles', 'createlistcachefiles', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHEREMAIN ],
contentscannertimeout => [ 'contentscannertimeout', 'contentscannertimeout', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
contentscanexceptions => [ 'contentscanexceptions', 'contentscanexceptions', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
recheckreplacedurls => [ 'recheckreplacedurls', 'recheckreplacedurls', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHEREMAIN ],
groupmode => [ 'groupmode', 'groupmode', VARCHOICE, [ [ 0, 'conf_groupmode_radio_1' ], [ 1, 'conf_groupmode_radio_2' ], [ 2, 'conf_groupmode_radio_3' ] ], HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
blockdownloads => [ 'blockdownloads', 'blockdownloads', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
categorydisplaythreshold => [ 'categorydisplaythreshold', 'categorydisplaythreshold', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
embeddedurlweight => [ 'embeddedurlweight', 'embeddedurlweight', VARTEXT, 3, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
enablepics => [ 'enablepics', 'enablepics', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
bypass => [ 'bypass', 'bypass', VARTEXT, 3, HASDEFAULTYES, 0, REPSINGLE, WHERESHARED ],
bypasskey => [ 'bypasskey', 'bypasskey', VARTEXT, 6, HASDEFAULTYES, '', REPSINGLE, WHERESHARED ],
infectionbypass => [ 'infectionbypass', 'bypass', VARTEXT, 3, HASDEFAULTYES, 0, REPSINGLE, WHERESHARED ],
infectionbypasskey => [ 'infectionbypasskey', 'bypasskey', VARTEXT, 6, HASDEFAULTYES, '', REPSINGLE, WHERESHARED ],
infectionbypasserrorsonly => [ 'infectionbypasserrorsonly', 'infectionbypasserrorsonly', VARONOFF, undef, HASDEFAULTYES, 'on', REPSINGLE, WHERESHARED ],
disablecontentscan => [ 'disablecontentscan', 'disablecontentscan', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
deepurlanalysis => [ 'deepurlanalysis', 'deepurlanalysis', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
filtergroupsschemelists => [ 'filtergroupsschemelists' , 'filtergroupsschemelists', VARCHOICE, [ [ 1, 'conf_filtergroupsschemelists_radio_1' ], [ 2, 'conf_filtergroupsschemelists_radio_2' ], [ 3, 'conf_filtergroupsschemelists_radio_3' ] ], HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ], # this is _Webmin's_ variable, although stored in dansguardian.conf it means _nothing_ to DansGuardian itself
filtergroupsdefaultnoweb => [ 'filtergroupsdefaultnoweb' , 'filtergroupsdefaultnoweb', VARONOFF, undef, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ], # this is _Webmin's_ variable, although stored in dansguardian.conf it means _nothing_ to DansGuardian itself
filtergroupshighestallweb => [ 'filtergroupshighestallweb' , 'filtergroupshighestallweb', VARONOFF, undef, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ], # this is _Webmin's_ variable, although stored in dansguardian.conf it means _nothing_ to DansGuardian itself
exceptionsitelist => [ 'exceptionsitelist', 'exceptionsitelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedsitelist => [ 'bannedsitelist', 'bannedsitelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
greysitelist => [ 'greysitelist', 'greysitelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
logsitelist => [ 'logsitelist', 'logsitelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionurllist => [ 'exceptionurllist', 'exceptionurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedurllist => [ 'bannedurllist', 'bannedurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
greyurllist => [ 'greyurllist', 'greyurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
logurllist => [ 'logurllist', 'logurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionregexpurllist => [ 'exceptionregexpurllist', 'exceptionregexpurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedregexpurllist => [ 'bannedregexpurllist', 'bannedregexpurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
logregexpurllist => [ 'logregexpurllist', 'logregexpurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
weightedphraselist => [ 'weightedphraselist', 'weightedphraselist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedphraselist => [ 'bannedphraselist', 'bannedphraselist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionphraselist => [ 'exceptionphraselist', 'exceptionphraselist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
picsfile => [ 'picsfile', 'picsfile', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedextensionlist => [ 'bannedextensionlist', 'bannedextensionlist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedmimetypelist => [ 'bannedmimetypelist', 'bannedmimetypelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionextensionlist => [ 'exceptionextensionlist', 'exceptionextensionlist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionmimetypelist => [ 'exceptionmimetypelist', 'exceptionmimetypelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionfilesitelist => [ 'exceptionfilesitelist', 'exceptionfilesitelist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
exceptionfileurllist => [ 'exceptionfileurllist', 'exceptionfileurllist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
contentregexplist => [ 'contentregexplist', 'contentregexplist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
urlregexplist => [ 'urlregexplist', 'urlregexplist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
headerregexplist => [ 'headerregexplist', 'headerregexplist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
bannedregexpheaderlist => [ 'bannedregexpheaderlist', 'bannedregexpheaderlist', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
mailer => [ 'mailer', 'emailnotify', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHEREMAIN ],
# all the rest of the email-notify options are group-only
usesmtp => [ 'usesmtp', 'emailnotify', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
mailfrom => [ 'mailfrom', 'emailnotify', VARTEXT, 7, HASDEFAULTYES, '', REPSINGLE, WHERESHARED ],
avadmin => [ 'avadmin', 'emailnotify', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
contentadmin => [ 'contentadmin', 'emailnotify', VARTEXT, 8, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
avsubject => [ 'avsubject', 'emailnotify', VARTEXT, 7, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
contentsubject => [ 'contentsubject', 'emailnotify', VARTEXT, 7, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
notifyav => [ 'notifyav', 'emailnotify', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
notifycontent => [ 'notifycontent', 'emailnotify', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
thresholdbyuser => [ 'thresholdbyuser', 'emailnotify', VARONOFF, undef, HASDEFAULTYES, 'off', REPSINGLE, WHERESHARED ],
violations => [ 'violations', 'emailnotify', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
threshold => [ 'threshold', 'emailnotify', VARTEXT, 4, HASDEFAULTNO, undef, REPSINGLE, WHERESHARED ],
# there are a whole bunch of per-filter-group options that set the score levels for various PICS categories,
# however they are only mentioned in the source code, the config files avoid them as though they didn't exist
# we don't list them here
);
# these are FIXED data which may be reference various places
# do _not_ change them (note that Perl does _not_ enforce the read-only restriction; you must be nice)
our @varnum2vartext = ( 'obsolete', 'checkbox', 'radio', 'text', 'textarea', 'line' );
# input is a "size class", outputs are HTML-specific size= and maxsize=
our %sizeclass2specifics = (
1 => [ 1, 1 ],
2 => [ 2, 2 ],
3 => [ 3, 3 ],
4 => [ 6, 6 ],
5 => [ 12, 12 ],
6 => [ 20, 20 ],
7 => [ 20, 56 ],
8 => [ 40, 128 ],
9 => [ 56, 200 ],
);
###################
sub validateoptions
###################
{
return if ! $debug;
print DEBUGLOG "beginning validating options table\n" if $debug;
while ( my ($optionname, $entryref) = each %options2details) {
print DEBUGLOG "OOPS? dangling help pointer help/$$entryref[DETAILSHELP].html (for option $optionname)\n" if ((! -e "help/$$entryref[DETAILSHELP].html") && $debug);
die "OOPS! - wrong size of array describing option (@$entryref)\n" if scalar @$entryref != 8;
# following will always fail because we have one use of REPMULTI despite its not being implemented
#die "OOPS! - REPMULTI not fully implemented yet specified (@$entryref)\n" if (($$entryref[DETAILSREP] == REPMULTI) && ($$entryref[DETAILSVAR] != VARLINE));
die "OOPS! - out of range variable type (@$entryref)\n" if (($$entryref[DETAILSVAR] < 1) || ($$entryref[DETAILSVAR] > 5));
die "OOPS! - option (@$entryref) supposedly has default yet no value is specified\n" if (($$entryref[DETAILSHASDEFAULT] == HASDEFAULTYES) && (! defined $$entryref[DETAILSDEFAULTVAL]));
die "OOPS! - option (@$entryref) default value yet it will never be used\n" if ((defined $$entryref[DETAILSDEFAULTVAL]) && ($$entryref[DETAILSHASDEFAULT] != HASDEFAULTYES));
die "OOPS! - option (@$entryref) detailsname is not same as key\n" if $optionname ne $$entryref[DETAILSNAME];
}
}
# Get the DG version
&checkdgver(\$dg_version);
&validateoptions();
&adjustdefaultpaths();
&adjustvariablelocations();
&constructsharedgroupsconfigurationlists();
our $cfref;
if ($ENV{'SCRIPT_FILENAME'} =~ 'dansguardian') {
# these things don't make sense except if we're invoked on behalf of one of the DG .cgi's
# Read in the dansguardian.conf file
$cfref = &read_file_lines_just_once(&group2conffilepath(0));
# get our own version (we're given the global $module_name)
our %moduleinfo = &get_module_info($module_name);
our $modulever = $moduleinfo{'version'};
}
############################################################################################
#
# subroutines only for local use
#
############################################################################################
######################
sub adjustdefaultpaths
######################
{
my $executable = $config{'binary_file'};
# exit silently if we can't continue,
# as we have no way to display an error message yet
# and callers are not expecting any failure at this point
return if ! $executable;
return if (! -e $executable);
my $output = `$executable -v`;
my $prefix = '/usr/local';
if ($output =~ m/--prefix=([^']*)'/) { $prefix = $1; }
my $proxyuser = 'anonymous';
if ($output =~ m/--with-proxyuser=([^']*)'/) { $proxyuser = $1; }
my $proxygroup = 'anonymous';
if ($output =~ m/--with-proxygroup=([^']*)'/) { $proxygroup = $1; }
my $languagedir = &getconfigvalue('languagedir');
my $language = &getconfigvalue('language');
foreach my $entryref (values %options2details) {
next if !($$entryref[DETAILSDEFAULTVAL]);
$$entryref[DETAILSDEFAULTVAL] =~ s/PREFIX/$prefix/;
$$entryref[DETAILSDEFAULTVAL] = &canonicalizefilepath($$entryref[DETAILSDEFAULTVAL]);
$$entryref[DETAILSDEFAULTVAL] =~ s/PROXYUSER/$proxyuser/;
$$entryref[DETAILSDEFAULTVAL] =~ s/PROXYGROUP/$proxygroup/;
$$entryref[DETAILSDEFAULTVAL] =~ s/LANGUAGEDIR/$languagedir/;
$$entryref[DETAILSDEFAULTVAL] =~ s/LANGUAGE/$language/;
}
}
###########################
sub adjustvariablelocations
###########################
{
# if "shared" variables supported, nothing to adjust in option descriptions
return if &sharedoptionsavailable();
# no "shared" variables in earlier versions, so adjust option descriptions
foreach my $entryref (values %options2details) {
$$entryref[DETAILSWHERE] = WHEREGROUP if $$entryref[DETAILSWHERE] == WHERESHARED;
}
}
##########################################
sub constructsharedgroupsconfigurationlists
##########################################
{
my @values;
foreach my $list (keys %groupsconfigurationlists) {
my $currentvalue = &readconfigoptionlimited($list);
if ($currentvalue) {
$sharedgroupsconfigurationlists{$list} = 1;
} else {
$notsharedgroupsconfigurationlists{$list} = 1;
}
}
}
############################################################################################
#
# subroutines for callers (and also this module) to use
#
############################################################################################
###############
sub checkwbconf
###############
{
## checkwbconf();
## Check for existence of Webmin-DG config file
# (admittedly this is quite paranoid [yet easily fooled] and may not be necessary..
# but it doesn't hurt anything)
if ((scalar keys %config) <= 0) {
return 0;
} else {
return 1;
}
}
###############
sub checkdgconf
###############
{
## checkdgconf();
## Check for existence of DG config file
my $conffilepath = &group2conffilepath(0);
if (!(-f $conffilepath)) {
return 0;
} else {
return 1;
}
}
#################
sub checkdgbinary
#################
{
## checkdgbinary();
## Check for existence and executable state of DG binary
if (!(-x $config{'binary_file'})) {
return 0;
} else {
return 1;
}
}
##########################
sub sharedoptionsavailable
##########################
{
&checkdgver(\$dg_version) if (! $dg_version);
#################################################################################
#
# literal in following line is VERY IMPORTANT
#
# change it to be the first version in which "shared" (one setting
# in dansguardian.conf for all groups) options were supported
#
my $firstsharedversionstring = '99.99.99.99';
#########$firstsharedversionstring = '2.9.8.5';
# (current setting of '99.99.99.99' means "we don't know, we're assuming NONE")
#
#################################################################################
my $currentversionnum = &versionstring2versionnum($dg_version);
my $firstsharedversionnum = &versionstring2versionnum($firstsharedversionstring);
return ($currentversionnum >= $firstsharedversionnum) ? 1 : 0;
}
############################
sub versionstring2versionnum
############################
{
my ($stringversion) = @_;
my ($major, $minor, $sub, $subsub) = ($stringversion =~ m/^\D*(\d+)\D+(\d+)(?:\D+(\d+)(?:\D+(\d+))?)?\D*$/);
$major = 0 if !$major;
$minor = 0 if !$minor;
$sub = 0 if !$sub;
$subsub = 0 if !$subsub;
my $numericversion = $subsub + (100 * ($sub + (100 * ($minor + (100 * $major)))));
return $numericversion;
}
##############
sub checkdgver
##############
{
## checkdgver();
## Check DG version for compatibility
# optional argument is where to return actual version
# ($retver is a dummy for $retverrref to point at if argument NOT passed)
my ($ver, $retver, $retverref);
if ($#_ >= 0) { ($retverref) = @_; } else { $retverref = \$retver; }
if (&checkdgbinary) {
$ver = `$config{'binary_file'} -v 2>&1`;
'' =~ /()/;
$ver =~ m/\b(\d+\.\d+(?:\.\d+(?:\.\d+)?)?)\b/;
$ver = $1;
} else {
$ver = '?';
}
$$retverref = $ver;
return(($ver =~ m/^2\.(?:9|10)/) ? $ver : "");
}
################
sub checkmodauth
################
{
## checkmodauth(ACLNAME);
## Check webmin ACL's for ACLNAME against username
my ($section) = @_;
if (!$access{$section}) {
print "<p><span style='color: red'>$text{'index_notauth'}</span><br>\n";
&ui_print_footer("index.cgi", $text{'index_return'});
exit;
}
}
################################################################
#
# there are several different variations
# on routines to read configuration variables
#
# (note that -unlike in the past- main-vs.-group is NOT
# one of the the variations - group number is an argument
# to all routines, and in all cases group=0 means the
# main group
#
# available routines are:
#
# 1) readconfigoptionlimited(optionname, groupnumber)
# returns a single naked value that's the first value
# found in the specified config file
# -defaults and overrides are NOT accounted for
# -this is very simple if you just want to know whether
# a particular option is defined in a particular file
# -very similar to getconfigvalue, the difference is
# one accounts for overrides and defaults while
# the other doesn't
# 2) getconfigvalue(optionname, groupnumber)
# returns only a single naked value
# -DOES account for overrides and defaults,
# so will sometimes return a value even if that
# option is not explicitly set in the specified file
# -this is useful to get the option value that's
# actually in effect
# -very similar to readconfigoptionlimited, the
# difference is one accounts for overrides and defaults
# while the other doesn't
# 3) readconfigoptiondetails(optionname, groupnumber)
# returns a complicated structure that includes lots of
# information and can be used directly for editing
# -not convenient for some uses though, as the caller
# has to watch out for 'undef' at various levels and
# for multiple values
# -if you're not sure what you want, and you're willing
# to pick through the complicated returned structure,
# this is the most complete option
# -the complicated return structure isn't as arbitrary
# as it may look, as it can be used DIRECTLY to set up
# display and editing of config options
# 4) readconfiglinedetails(optionname)
# this finds whole lines associated with the option,
# even if they are currently commented out
# (for example 'authplugin')
# -group is not an implemented option because such
# options only occur legitimately in the main config
# -useful mainly for a few plugin options
# -this is a special purpose routine that's probably
# not useful for anything else
#
################################################################
###########################
sub readconfigoptionlimited
###########################
{
## Read a name=value ONLY from the specified DansGuardian config
## #1 of 4 variants, see earlier comments in this file about ALL config reading routines
## also, only return the first value found no matter what
## (not even a suggestion of an indication there are multiple values)
# you can think of this as just a thin wrapper around &readconfigoptiondetails_inner
# so the functionality is easily available to callers even though it can't be called directly
my ($option, $group) = @_;
$option = lc $option;
$group = &canonicalizegroupnumber($group);
my ($valueslocalref, $valuesallgroupsref, $valuesdefaultref);
$cfref = &read_file_lines_just_once(&group2conffilepath($group));
$valueslocalref = &readconfigoptiondetails_inner($option, $cfref);
return $$valueslocalref[0];
}
##################
sub getconfigvalue
##################
{
## #2 of 4 variants, see earlier comments in this file about ALL config reading routines
# this is a wrapper for &readconfigoptiondetails that gets the one currently active value,
# even if it's a shared setting or a default
# (rather than just an explicit setting in the outermost config file)
my ($option, $group) = @_;
$group = &canonicalizegroupnumber($group);
my ($valueslocalref, $valuesallgroupsref, $valuesdefaultref) = &readconfigoptiondetails($option, $group);
return $$valueslocalref[0] if defined $valueslocalref;
return $$valuesallgroupsref[0] if ((defined $valuesallgroupsref) && (($options2details{$option}[DETAILSWHERE] == WHERESHARED) || ($options2details{$option}[DETAILSWHERE] == WHEREOVERRIDE)));
return $$valuesdefaultref[0] if defined $valuesdefaultref;
}
###########################
sub readconfigoptiondetails
###########################
{
## Read a name=value from DansGuardian configs
## #3 of 4 variants, see earlier comments in this file about ALL config reading routines
# this is the guts of much of the "read current setting ..." functionality
# note although we (sorta) handle the case of multiple occurrences, i.e. foo=bar1
# foo=bar2
# foo=bar3
# we do NOT handle (not hardly at all) the alternate syntax of multiple values on one line, i.e.
# foo=bar1 bar2 bar3
# also note though that even the bits of functionality that do already exist have
# NOT been tested, and may very well not entirely work correctly.
my ($option, $group) = @_;
$option = lc $option;
$group = &canonicalizegroupnumber($group);
return [ undef, undef, undef ] if $group < 0;
my ($valueslocalref, $valuesallgroupsref, $valuesdefaultref);
my (@result);
$cfref = &read_file_lines_just_once(&group2conffilepath($group));
$valueslocalref = &readconfigoptiondetails_inner($option, $cfref);
our $foo = $valueslocalref ? "[ @$valueslocalref ]" : 'undef';
$result[0] = $valueslocalref;
if ($group != 0) {
$cfref = &read_file_lines_just_once(&group2conffilepath(0));
$valuesallgroupsref = &readconfigoptiondetails_inner($option, $cfref);
$result[1] = $valuesallgroupsref;
} else {
$result[1] = undef;
}
my $detailsref = $options2details{$option};
if ($$detailsref[DETAILSHASDEFAULT] == HASDEFAULTYES) {
my $defaultval = $$detailsref[DETAILSDEFAULTVAL];
$result[2] = [ $defaultval ];
} else {
$result[2] = undef;
}
return @result;
}
#--------------------------------
sub readconfigoptiondetails_inner
#--------------------------------
{
# Only called internally, never to be called directly by a user
# (if you want this functionality, call &readconfigoptionlimited,
# which is available to users and is a thin wrapper around this functionality)
# this subroutine is defined at file scope rather than inside another subroutine
# for a couple reasons - first, we don't want a "closure" - second, this
# is called by more than one subroutine so it wouldn't work to have it inside any one of them
# note this assumes ONE value per line and _probably_won't_work_correctly_
# with lines of the form foo=bar1 bar2 bar3
# this at bottom provides much of the functionality exposed by &readconfigoptiondetails,
# and also by &readconfigoptionlimited
my ($option, $cfref) = @_;
my ($line, $name, $value, $valuealt);
my @result = ();
my $valuesfound = 0;
CONFIGLINE: foreach my $protoline (@$cfref) {
next if $protoline =~ m/^\s*$/; # skip blank lines
next if $protoline =~ m/^\s*#+\s*[^!][^!]/; # skip comment-only lines (except !! warnings)
#($line) = ($protoline =~ m/^\s*((?:#[#\s]*)?[^#]*[^#\s])/); # copy data part but not partial comment part
# used above line to get rid of display of inactivated config files, but this also screwed up conf display
# so reimplemented it in a higher subroutine (edit_file_row) so it wouldn't have such drastic effects all over
($line = $protoline) =~ s/\s*#(?!!!).*$//; # trim off partial comment part (except !! warnings)
next if $line !~ m/=/; # skip anything without an equals sign in the part we kept
($valuealt, $name, $value) = ($line =~ m/^\s*(?:#(?>[\s#]*)(.*?(?<=\S)))?\s*\b(\w+)\s*=\s*(\S.*(?<=\S))?\s*$/);
$valuealt = '' if ! defined $valuealt; # can happen if parse failure
$name = '' if ! defined $name; # can happen if parse failure
$value = '' if ! defined $value; # can happen if parse failure
$name = lc $name;
if ($name eq $option) {
if ($options2details{$option}[DETAILSVAR] == VARONOFF) {
$value = ($value =~ m/on|t[r\b]|y[e\b]|[1-9]/i) ? 'on' : 'off';
}
$value = $valuealt if $valuealt =~ m/!!/;
# no need to trim leading/trailing white space because regexp above already did it!
$value =~ s/^(['"])([^\1]*)\1.*$/$2/; # strip quotes (also strip any second value on same line)
++$valuesfound;
push @result, $value;
next CONFIGLINE;
}
}
my $resultref = ($valuesfound > 0) ? \@result : undef;
return $resultref;
}
#########################
sub readconfiglinedetails
#########################
{
## Read a name=value from DansGuardian config even if commented out and/or multivalued
## #4 of 4 variants, see earlier comments in this file about ALL config reading routines
# use this only for things like "authplugin" and other things that may be "commented out"
# mostly use &readconfigoptiondetails
my ($option) = @_;
$option = lc $option;
my @result = ();
my ($name, $value, $comment, $line);
CONFIGLINE: foreach my $protoline (@$cfref) {
next if $protoline =~ m/^\s*$/; # skip blank lines
($line) = ($protoline =~ m/^\s*((?:#[#\s]*)?[^#]*[^#\s])/); # copy data part but not partial comment part
next if $line !~ m/=/; # skip anything without an equals sign in the non-comment part
$line =~ s/^\s*//; # trim white space from line
$line =~ s/\s*$//;
($name, $value) = split /\s*=\s*/, $line;
($value, $comment) = split /\s*#\s*/, $value;
$name = lc $name;
if ($name =~ m/\b$option$/) {
# no need to trim leading/trailing white space because already done above!
$value =~ s/^(['"])([^\1]*)\1$/$2/; # strip quotes
push @result, [ $name, $value ];
next CONFIGLINE;
}
}
our $foo0 = $result[0] ? "[ @{$result[0]} ]" : 'undef';
our $foo1 = $result[1] ? "[ @{$result[1]} ]" : 'undef';
our $foo2 = $result[2] ? "[ @{$result[2]} ]" : 'undef';
our $foo3 = $result[3] ? "[ @{$result[3]} ]" : 'undef';
our $foo4 = $result[4] ? "[ @{$result[4]} ]" : 'undef';
our $foo5 = $result[5] ? "[ @{$result[5]} ]" : 'undef';
our $foo6 = $result[6] ? "[ @{$result[6]} ]" : 'undef';
return @result;
}
###################
sub getbesthelpfile
###################
{
my ($option, $helpfile) = @_;
my $result = $helpfile;
$result = $option if ! defined $helpfile;
if ((! -e "./help/$result.html") && (! -e "./help/$result.$current_lang.html")) {
# no such helpfile exists, so try to select an appropriate generic one
if ($option =~ m/(path|dir|filename)/) {
$result = 'filepaths';
} elsif (($option =~ m/list[s\s]*$/ ) || ($option =~ m/pics/)) {
$result = 'lists';
} else {
my $detailsref = $options2details{$option};
$result = "helpvar$varnum2vartext[($$detailsref[DETAILSVAR])]";
}
}
return $result;
}
{
# variable shared between outer and inner routines
our %filepathslisted;
our $showdupsflag;
#################
sub edit_file_row
#################
{
# edit_file_row(<configfileoptionname>, <groupnumber>, <helpfile>, <wheretoreturnto>)
# third argument defaults more intelligently than in previous version
# second and fourth arguments are new
my ($option, $group, $helpfile, $return, $showdupsflagarg) = @_;
$showdupsflag = $showdupsflagarg;
$group = &canonicalizegroupnumber($group);
$helpfile = &getbesthelpfile($option, $helpfile);
my $label = $text{"conf_$option"};
$label = $option if ! $label;
my ($valuesref, $valuesmainref, $defaultsref) = &readconfigoptiondetails($option, $group);
# ignore any settings found in the main config file if they can't have any effect
$valuesmainref = undef if ! &sharedoptionsavailable();
# (note the following only works if &readconfigoptiondetails returns 'undef' [rather than ( )]
# for missing sets of values - as the assumption is indeed true, this works,
# but changing style of &readconfigoptiondetails to be more "standard Perl" would probably break this)
my @values = (defined $valuesref) ? @$valuesref : ((defined $valuesmainref) ? @$valuesmainref : ((defined $defaultsref) ? @$defaultsref : () ));
if (($#values < 0) && ($option =~ m/(list|pics)/)) {
# if no value at all was found, create a reasonable (filepath) value and use that
$values[0] = &canonicalizefilepath("$config{'conf_dir'}/lists/$option");
}
foreach my $value (@values) {
# readconfigoptiondetails may return a value like "!! Not Compiled!!"
# just skip any such lines
next if $value =~ m/!!/;
$value = ' ' if ! $value; # avoid displaying line with screwy background colors (even if data error)
%filepathslisted = (); # (re)set to nothing
# actually do the work (and anything below it too)
&edit_file_row_inner($option, $value, $helpfile, 0, $return);
}
#-----------------------
sub edit_file_row_inner
#-----------------------
{
my ($name, $filepath, $helpfile, $level, $return) = @_;
# list a file only once even if there are multiple .Includes for it in either same/different files
return if ((exists $filepathslisted{$filepath}) && (!$showdupsflag));
++$filepathslisted{$filepath};
my ($formatlink, $formatnolink, $recurse);
my ($label, $sublabel, $newlevel, $globalname);
my ($fileref, $file_path, $file_name, $file_line, $proto_file_line);
$filepath = &canonicalizefilepath($filepath);
my $filepath2 = readlink $filepath;
$label = $text{"conf_$name"};
$newlevel = $level + 1;
if (($filepath =~ m/\b(blacklists|phraselists)/) || ($filepath2 =~ m/\b(blacklists|phraselists)/)) {
# lists from outside mechanisms (treat as read-only)
# display them as follows (or just return to not display them)
return if ! $config{'show_fixedlists'};
$recurse = 0;
$formatnolink = "style='font-size: 90%; font-variant: small-caps; color: #808080'";
$formatlink = $formatnolink;
($label, $sublabel) = ($filepath =~ m{/([^/]+)/(?:[^/_]*|[^/]*_([^/]*))$});
$label = "$label/$sublabel" if $sublabel;
} elsif ((($filepath =~ m{lists/[^/]*$}) || ($filepath =~ m{lists/local}))
|| (($filepath2 =~ m{lists/[^/]*$}) || ($filepath2 =~ m{lists/local}))) {
#($label in @sharedgroupsconfigurationlists)# MIGHT need to code subroutine to test this explicitly, unlikely
# shared lists (only edit carefully, widespread effects)
$formatnolink = "style='font-size: 85%; font-style: italic; color: #707070'";
$formatlink = "style='font-size: 90%; font-style: italic; color: #50507c'";
$recurse = 1;
} elsif ($level > 0) {
# an included list, probably for a different filter group
$formatnolink = "'style='font-size: 85%; color: #404040'";
$formatlink = "style='font-size: 90%; color: #404070'";
($globalname) = ($name =~ m/^(.*)f\d+/);
$label = $text{"conf_$globalname"} if ! $label;
$recurse = 1;
} else {
# un-special top-level list
$formatnolink = '';
$formatlink = '';
($globalname) = ($name =~ m/^(.*)f\d+/);
$label = $text{"conf_$globalname"} if ! $label;
$recurse = 1;
}
$label = $name if ! $label;
$label = (($level > 0) ? ' ' : '') . ('+ ' x $level) . $label;
my $helplink = $helpfile ? &hlink("<span $formatlink>[$text{'button_help'}]</span>", $helpfile) : '';
print &ui_columns_row( [ "<span $formatnolink>$label</span>", "<a href='./editfile.cgi?file=$filepath&return=$return'><span $formatlink>$filepath</span></a>", $helplink ] );
if ($recurse) {
# search the file for '.Include<...>'-like directives
# and list any files found
# (except do NOT scan through blacklist/phraselist files,
# as it will take a very large amount of CPU time and will never find anything useful)
$fileref = &read_file_lines_just_once($filepath);
foreach $proto_file_line (@$fileref) {
next if $proto_file_line =~ m/^#/; # m/^\s*#/ smarter, but doesn't perform as well
next if $proto_file_line =~ m/^$/; # m/^\s*$/ smarter, but doesn't perform as well
($file_line = $proto_file_line) =~ s/#.*$//; # dumb stripping of partial line comment,
# but we're very concerned with performance
next if $file_line =~ m/^\s*(#|$)/; # finish ignoring uninteresting lines,
# but only later so we don't impact performance so much
my ($subname, $subvalue) = '';
if (($file_line =~ m/^\s*(\S*include\S*)\s*<(.*)>\s*$/i) || ($file_line =~ m/^\s*(\S+)\s*=\s*(\S.*\S)\s*$/)) {
# note regexps not only isolate values but also trim them
$subname = $1;
$subvalue = $2;
} else {
# we now know for sure this is not a line of interest
next;
}
# conditions (_reversed_ in 'if') on $subname that say definitely include this line
if ($subname !~ m/include/i) {
# not certain yet, so see if $subvalue looks like a filenamepath
next if $subvalue !~ m{['"<]?/};
next if $subvalue !~ m{/.*/.*/};
}
# we now know this line is definitely to be processed, so strip quotes
($file_path = $subvalue) =~ s/^(['"])(.*)\1$/$2/;
if ($subname eq '.Include') { ($file_name = $file_path) =~ s{^.*/}{}; } else { $file_name = $subname; }
# recursive calls
&edit_file_row_inner($file_name, $file_path, undef, $newlevel, $return);
}
}
}
}
}
our $errorsonform; # "global" because referenced by several different subroutines,
# but not intended to be referenced by callers
################################
sub edit_options_formtable_start
################################
{
my ($tabref) = @_;
my $dest = $$tabref[2];
print &ui_form_start($dest, 'post');
print "<table border=0 bgcolor=#d8d8d8 cellpadding=2 cellspacing=3><tr bgcolor=#6080ff><td align=center><span style='color: white; font-size: larger; font-weight: 800'> $$tabref[3] </span></td></tr><tr bgcolor=#e8e8e8><td><table cellpadding=4 cellspacing=0 border=0>\n";
$errorsonform = 0;