-
Notifications
You must be signed in to change notification settings - Fork 0
/
savefile.cgi
executable file
·233 lines (187 loc) · 8.38 KB
/
savefile.cgi
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
#!/usr/bin/perl
# savefile.cgi
# by declaring all the globals we'll reference (including some in our own
# libraries)_before_ pulling in libraries and 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, %access, %config, %in, $module_name, $modulever, $moduleinfo);
our ($debug, $dg_version, $current_lang, $module_config_directory);
our (%mainconfigurationlists, %groupsconfigurationlists);
our (%sharedgroupsconfigurationlists, %notsharedgroupsconfigurationlists);
our (%sharedonlynestedconfigurationlists, %sharedonlyseparateconfigurationlists, %sharedonlycommonconfigurationlists);
our ($EFFECTIVE_USER_ID);
require './dansguardian-lib.pl';
use POSIX;
use warnings;
use strict qw(subs vars);
our $filepath = $in{'filepath'};
&bailifcallercancelled();
&webminheader();
# error checks
if (! &accessfile($filepath)) {
print "<p><span style='color: magenta'>$text{'error_notauthfile'}<br>$text{'index_location'}: $filepath</span><p>\n";
&showackandret();
&webminfooterandexit();
}
# Check to see if the file we are editing is in the DG config dir
if (!(&is_under_directory($config{'conf_path'}, $filepath))) {
print "$text{'error_filenotindir'}";
}
&finish_manually_editing_file(\%in, 'showmessages');
&touchwholechain($filepath);
&handlerefresh($filepath);
&showackandret();
&webminfooterandexit();
{
# "shared"/"global" variables for all the following subroutines - yucky/ugly, but...
our $confdir;
our $listdir;
our $includesearchfiles;
our $explodedincludesearchfiles;
our %upperfilepaths = ();
our $filtergroups = 0;
###########################################################################
#
# Largeish subroutines for tracking and touching a whole .Include chain
#
# These subroutines all work together. Rather than totally isolating
# their varible namespace with "my", they rely on a bunch of "shared"
# global varilables as above.
#
###########################################################################
###################
sub touchwholechain
###################
{
my ($changedfilepath) = @_;
return if $changedfilepath !~ m/phrase/;
# record information about date changed from file we changed
# (get latest time even if this was a symlink of some sort)
my (undef, undef, undef, undef, undef, undef, undef, undef, $atime, $mtime,undef, undef, undef) = stat($changedfilepath);
$changedfilepath = readlink $changedfilepath if -l $changedfilepath; # get real file
my (undef, undef, undef, undef, undef, undef, undef, undef, $atime2, $mtime2,undef, undef, undef) = stat($changedfilepath);
$atime = $atime2 if $atime2 > $atime;
$mtime = $mtime2 if $mtime2 > $mtime;
$filtergroups = &readconfigoptionlimited('filtergroups');
(my $changedfilename = $changedfilepath) =~ s{^.*/}{};
(my $basechangedfilename) = ($changedfilename =~ m/^(.*list).*$/);
$confdir = &canonicalizefilepath($config{'conf_dir'});
$listdir = "$confdir/lists";
# following could probably use "$basechangedfilename*" rather than "*phrase*"
# this searches a lot more files than really necessary, but doing so may be prudent
$includesearchfiles = "$listdir/*phrase*";
for (my $i=1; $i<=$filtergroups; ++$i) {
$includesearchfiles .= " $listdir/f$i/*phrase*";
}
$explodedincludesearchfiles = &explodefilepaths($includesearchfiles, 'dontremovesymdests');
# see if we can find any other names for (i.e. symlinks that point at) this file
our @changedfilepaths = ($changedfilepath);
our @suspectfilepaths = split ' ', $explodedincludesearchfiles;
for my $suspectfilepath (@suspectfilepaths) {
push(@changedfilepaths, $suspectfilepath) if (&significantconfigfilepath(readlink $suspectfilepath)) eq &significantconfigfilepath($changedfilepath);
}
for $changedfilepath (@changedfilepaths) {
# back up as necessary, result is filled in %upperfilepaths
&trackchainback(1, $changedfilepath);
# repeat the information into all files higher in the chain
foreach my $upperfilepath (keys %upperfilepaths) {
next if ! $upperfilepath; # skip over empty entries
# skip over ourselves if we're in the list
next if &significantconfigfilepath($upperfilepath) eq &significantconfigfilepath($changedfilepath);
utime $atime, $mtime, $upperfilepath;
}
}
}
##################
sub trackchainback
##################
{
# this level uses a lot of RECURSION
my ($level, @filepaths) = @_;
return undef if $#filepaths < 0; # stop recursion at full depth (nothing more to do)
return undef if $level > ($debug ? 10 : 100); # protection against recursion loop
my @oneback = @filepaths;
my $newlevel = $level + 1;
foreach my $filepath (@filepaths) {
if (($filepath =~ m/^\s*$/) || (! $filepath)) {
# empty search, not sure how we got here, but just ignore it and go on
next;
} elsif ($filepath =~ m/conf$/) {
# search produced an irrelevant file, not sure how we got here, but just ignore it and go on
next;
} else {
push @oneback, &trackchainback($newlevel, &backuponelevel($filepath));
}
}
foreach my $filepath (@oneback) {
next if ! $filepath;
# canonicalize everything, (this is probably over-cautious)
# be absolutely certain typos in files can't pollute what we're doing
$filepath = &canonicalizefilepath($filepath);
# this has the (desirable) side effect of listing each file only once
# the count is only interesting for debugging, it's not actually used for anything
$upperfilepaths{$filepath} += 1;
}
return (@oneback);
}
##################
sub backuponelevel
##################
{
# lowest level, does one bit of work for caller who's in charge
my ($filepath) = @_;
my ($line, $file, $value, $outlinesref);
# strip quotes and remove other junk
$filepath = &canonicalizefilepath($filepath);
return () if ! $filepath;
my @results = ();
# run search command and collect output
my $cmd = "grep -FiH '$filepath' $explodedincludesearchfiles";
my $temp = &tempname();
&system_logged("$cmd >$temp 2>&1 </dev/null");
my $outlinesref = &read_file_lines_just_once($temp);
my $findcount = scalar @$outlinesref;
unlink($temp);
foreach my $line (@$outlinesref) {
# we didn't skip over comment lines above because a straight search is faster
# so now we have to throw out any that snuck in
next if $line =~ m/^\s*(?:^[^:]*:\s*#|$)/;
($file, $value) = split(/\s*:\s*/, $line, 2);
# we can get extra hits because of a partial name match
# we didn't skip when searching for performance, so we re-check and skip now
# we omit &canonicalizefilepath for performance on the assumption that grep NEVER produces funky results
next if $value !~ m/$filepath\b/;
$file = &canonicalizefilepath($file);
# try to avoid loops if file refers to itself
next if &significantconfigfilepath($file) eq &significantconfigfilepath($filepath);
# normal condition
push @results, $file;
}
return @results;
}
} # end block that contains "global" vars
###########################################################################
#
# UTILITY SUBROUTINES
#
###########################################################################
################
sub webminheader
################
{
&ui_print_header($text{'savefile_title'}, $text{'index_title'}, undef, undef, 0, 1, 1, &restart_button, undef, undef, "$text{'index_version'} $dg_version <small>($text{'index_modulever'} $modulever)</small>");
}
#######################
sub webminfooterandexit
#######################
{
if (exists $in{'return'}) {
# we have a specific return, issue a message then don't display any manual returns
&ui_print_footer(); # entirely omit/suppress the return arrow
} else {
# no particular return specified, so supply user with lots of manual choices
&ui_print_footer('index.cgi', $text{'index_return'}, 'editplugconf.cgi', "$text{'index_viewedit'} $text{'index_editplugconf'}", 'editlists.cgi', "$text{'index_viewedit'} $text{'index_editlists'}", 'editgroupslists.cgi', "$text{'index_viewedit'} $text{'index_editgroupslists'}" );
}
exit;
}