Handle hostnames with upper-case letters
[webmin.git] / updown / updown-lib.pl
1 # updown-lib.pl
2
3 BEGIN { push(@INC, ".."); };
4 use WebminCore;
5 &init_config();
6
7 if ($module_info{'usermin'}) {
8         # Running under Usermin
9         &switch_to_remote_user();
10         &create_user_config_dirs();
11         $downloads_dir = "$user_module_config_directory/downloads";
12         $atjob_cmd = "$user_module_config_directory/download.pl";
13
14         $can_upload = $config{'upload'};
15         $can_download = $config{'download'};
16         $can_fetch = $config{'fetch'};
17         $can_schedule = $config{'background'} && &foreign_check("at");
18         $can_background = $config{'background'};
19         if ($config{'home_only'}) {
20                 @can_dirs = ( &resolve_links($remote_user_info[7]),
21                               split(/\s+/, $config{'root'}) );
22                 }
23         else {
24                 @can_dirs = ( "/" );
25                 }
26         $can_mode = 3;
27
28         $download_dir = $userconfig{'ddir'};
29         $download_dir = $remote_user_info[7] if ($download_dir eq "~");
30         $upload_dir = $userconfig{'dir'};
31         $upload_dir = $remote_user_info[7] if ($upload_dir eq "~");
32         $upload_max = $config{'max'};
33         $fetch_file = $userconfig{'fetch'};
34         $fetch_show = $userconfig{'show'} || 0;
35         }
36 else {
37         # Running under Webmin
38         $downloads_dir = "$module_config_directory/downloads";
39         $atjob_cmd = "$module_config_directory/download.pl";
40
41         %access = &get_module_acl();
42         $can_upload = $access{'upload'};
43         $can_download = $access{'download'};
44         $can_fetch = $access{'fetch'} && !&is_readonly_mode();
45         if ($access{'download'} != 2) {
46                 $can_schedule = &foreign_check("at");
47                 $can_background = 1;
48                 }
49         if (&supports_users()) {
50                 $can_mode = $access{'mode'};
51                 }
52         else {
53                 $can_mode = 3;
54                 }
55         @can_users = split(/\s+/, $access{'users'});
56         @can_dirs = split(/\s+/, $access{'dirs'});
57         if ($access{'home'}) {
58                 local @uinfo = getpwnam($remote_user);
59                 push(@can_dirs, $uinfo[7]) if ($uinfo[7]);
60                 }
61
62         $download_dir = $config{'ddir_'.$remote_user} || $config{'ddir'};
63         $upload_dir = $config{'dir_'.$remote_user} || $config{'dir'};
64         $upload_user = $config{'user_'.$remote_user} || $config{'user'};
65         $upload_group = $config{'group_'.$remote_group} || $config{'group'};
66         $upload_max = $access{'max'};
67         $download_user = $config{'duser_'.$remote_user} || $config{'duser'};
68         $download_group = $config{'dgroup_'.$remote_group} || $config{'dgroup'};
69         $fetch_file = $config{'fetch_'.$remote_user};
70         $fetch_show = $config{'show_'.$remote_user} || 0;
71         }
72
73 # list_downloads()
74 # Returns a list of downloads currently in progress
75 sub list_downloads
76 {
77 local (@rv, $f);
78 opendir(DIR, $downloads_dir);
79 foreach $f (readdir(DIR)) {
80         next if ($f !~ /^(\S+)\.down$/);
81         local $down = &get_download("$1");
82         push(@rv, $down) if ($down);
83         }
84 closedir(DIR);
85 return @rv;
86 }
87
88 # get_download(id)
89 sub get_download
90 {
91 local %down;
92 &read_file("$downloads_dir/$_[0].down", \%down) || return undef;
93 $down{'user'} = getpwuid($down{'uid'});
94 return \%down;
95 }
96
97 # save_download(&download)
98 sub save_download
99 {
100 $_[0]->{'id'} = time().$$ if (!$_[0]->{'id'});
101 &lock_file($downloads_dir);
102 mkdir($downloads_dir, 0755);
103 &unlock_file($downloads_dir);
104 &lock_file("$downloads_dir/$_[0]->{'id'}.down");
105 &write_file("$downloads_dir/$_[0]->{'id'}.down", $_[0]);
106 &unlock_file("$downloads_dir/$_[0]->{'id'}.down");
107 }
108
109 # delete_download(&download)
110 sub delete_download
111 {
112 &lock_file("$downloads_dir/$_[0]->{'id'}.down");
113 unlink("$downloads_dir/$_[0]->{'id'}.down");
114 &unlock_file("$downloads_dir/$_[0]->{'id'}.down");
115 }
116
117 # do_download(&download, &callback, &dests)
118 # Actually download one or more files, and return undef or any error message
119 sub do_download
120 {
121 local ($i, $error, $msg);
122 for($i=0; $_[0]->{"url_$i"}; $i++) {
123         $error = undef;
124         $progress_callback_url = $_[0]->{"url_$i"};
125         $progress_callback_count = $i;
126         local $path;
127         if (-d $_[0]->{'dir'}) {
128                 local $page = $_[0]->{"page_$i"};
129                 $page =~ s/\?.*$//;
130                 if ($page =~ /([^\/]+)$/) {
131                         $path = "$_[0]->{'dir'}/$1";
132                         }
133                 else {
134                         $path = "$_[0]->{'dir'}/index.html";
135                         }
136                 }
137         else {
138                 $path = $_[0]->{'dir'};
139                 }
140         &switch_uid_to($_[0]->{'uid'}, $_[0]->{'gid'});
141         $down->{'upto'} = $progress_callback_count;
142         if ($_[0]->{"proto_$i"} eq "http") {
143                 &http_download($_[0]->{"host_$i"},
144                                $_[0]->{"port_$i"},
145                                $_[0]->{"page_$i"},
146                                $path,
147                                \$error,
148                                $_[1],
149                                $_[0]->{"ssl_$i"},
150                                $_[0]->{"user_$i"},
151                                $_[0]->{"pass_$i"});
152                 }
153         else {
154                 &ftp_download($_[0]->{"host_$i"},
155                               $_[0]->{"page_$i"},
156                                $path,
157                                \$error,
158                                $_[1],
159                                $_[0]->{"user_$i"},
160                                $_[0]->{"pass_$i"});
161                 }
162         unlink($path) if ($error);
163         &switch_uid_back();
164
165         # Add to email message
166         $msg .= &text('email_downurl', $_[0]->{"url_$i"})."\n";
167         if ($error) {
168                 $msg .= &text('email_downerr', $error)."\n";
169                 }
170         else {
171                 local @st = stat($path);
172                 $msg .= &text('email_downpath', $path)."\n";
173                 $msg .= &text('email_downsize',&nice_size($st[7]))."\n";
174                 }
175         $msg .= "\n";
176
177         last if ($error);
178         push(@{$_[2]}, $path);
179         }
180
181 # Send status email
182 if ($down->{'email'}) {
183         # Send email when done
184         $msg = $text{'email_downmsg'}."\n\n".$msg;
185         &send_email_notification(
186                         $down->{'email'}, $text{'email_subjectd'}, $msg);
187         }
188
189 return $error;
190 }
191
192 # can_write_file(file)
193 # Returns 1 if some path can be written to, 0 if not
194 sub can_write_file
195 {
196 local $d;
197 foreach $d (@can_dirs) {
198         return 1 if (&is_under_directory($d, $_[0]));
199         }
200 return 0;
201 }
202
203 # can_as_user(username)
204 # Returns 1 if uploading or downloading can be done as some user
205 sub can_as_user
206 {
207 if ($can_mode == 0) {
208         return 1;
209         }
210 elsif ($can_mode == 1) {
211         return &indexof($_[0], @can_users) != -1;
212         }
213 elsif ($can_mode == 2) {
214         return &indexof($_[0], @can_users) == -1;
215         }
216 elsif ($can_mode == 3) {
217         return $_[0] eq $remote_user;
218         }
219 else {
220         return 0;       # shouldn't happen
221         }
222 }
223
224 # in_group(&uinfo, &ginfo)
225 sub in_group
226 {
227 return 1 if ($_[0]->[3] == $_[1]->[2]);
228 foreach $s (&other_groups($_[0]->[0])) {
229         return 1 if ($s eq $_[1]->[2]);
230         }
231 return 0;
232 }
233
234 # switch_uid_to(uid, gid)
235 # Temporarily sets the effective UID and GID, if appropriate
236 sub switch_uid_to
237 {
238 if ($< == 0 && ($_[0] || $_[1]) && &supports_users()) {
239         $old_uid = $>;
240         $old_gid = $);
241         $) = "$_[1] $_[1]";
242         $> = $_[0];
243         }
244 }
245
246 # switch_uid_back()
247 # Undo the switch made by switch_uid_to
248 sub switch_uid_back
249 {
250 if (defined($old_uid)) {
251         $> = $old_uid;
252         $) = $old_gid;
253         $old_uid = $old_gid = undef;
254         }
255 }
256
257 # send_email_notification(address, subject, message)
258 # Send email when some download or upload is complete
259 sub send_email_notification
260 {
261 local ($to, $subject, $msg) = @_;
262 if ($module_info{'usermin'}) {
263         &foreign_require("mailbox", "mailbox-lib.pl");
264         local $from = &mailbox::get_preferred_from_address();
265         &mailbox::send_text_mail($from, $to, undef, $subject, $msg);
266         }
267 else {
268         &foreign_require("mailboxes", "mailboxes-lib.pl");
269         local $from = &mailboxes::get_from_address();
270         &mailboxes::send_text_mail($from, $to, undef, $subject, $msg);
271         }
272 }
273
274 # webmin_command_as_user(user, env, command)
275 # Return a command as some user with su if this is webmin, or un-changed for
276 # usermin
277 sub webmin_command_as_user
278 {
279 my ($user, $env, @args) = @_;
280 if ($module_info{'usermin'}) {
281         return join(" ", @args);
282         }
283 else {
284         return &command_as_user($user, $env, @args);
285         }
286 }
287
288 1;
289