Handle hostnames with upper-case letters
[webmin.git] / feedback.cgi
1 #!/usr/local/bin/perl
2 # Send the webmin feedback form
3
4 BEGIN { push(@INC, ".."); };
5 use WebminCore;
6
7 &init_config();
8 if (&get_product_name() eq 'usermin') {
9         &switch_to_remote_user();
10         }
11 &ReadParseMime();
12 &error_setup($text{'feedback_err'});
13 %access = &get_module_acl();
14 $access{'feedback'} || &error($text{'feedback_ecannot'});
15
16 # Construct the email body
17 $in{'text'} =~ s/\r//g;
18 $date = localtime(time());
19 $ver = &get_webmin_version();
20 if ($in{'name'} && $in{'email'}) {
21         $from = "$in{'name'} <$in{'email'}>";
22         $email = $in{'email'};
23         }
24 elsif ($in{'email'}) {
25         $email = $from = $in{'email'};
26         }
27 else {
28         $email = $from = "feedback\@".&get_system_hostname();
29         }
30 local $m = $in{'module'};
31 $m || !$in{'config'} || &error($text{'feedback_emodule'});
32 &check_os_support($m) && $m !~ /\.\./ || &error($text{'feedback_emodule2'});
33 if ($m) {
34         %minfo = &get_module_info($m);
35         $ver .= " (Module: $minfo{'version'})" if ($minfo{'version'});
36         $module = "$m ($minfo{'desc'})";
37         }
38 else {
39         $module = "None";
40         }
41 if ($gconfig{'nofeedbackcc'}) {
42         @tolist = ( $gconfig{'feedback_to'} ||
43                     $minfo{'feedback'} ||
44                     $webmin_feedback_address );
45         }
46 else {
47         @tolist = split(/\s+/, $in{'to'});
48         }
49 @tolist || &error($text{'feedback_enoto'});
50 foreach $t (@tolist) {
51         $headers .= "To: $t\n";
52         }
53 $headers .= "From: $from\n";
54 $headers .= "Subject: $text{'feedback_title'}\n";
55
56 $attach[0] = <<EOF;
57 Content-Type: text/plain
58 Content-Transfer-Encoding: 7bit
59
60 Name:           $in{'name'}
61 Email address:  $in{'email'}
62 Date:           $date
63 Webmin version: $ver
64 Perl version:   $]
65 Module:         $module
66 Browser:        $ENV{'HTTP_USER_AGENT'}
67 EOF
68
69 if ($in{'os'}) {
70         $uname = `uname -a`;
71         $attach[0] .= <<EOF;
72 OS from webmin: $gconfig{'real_os_type'} $gconfig{'real_os_version'}
73 OS code:        $gconfig{'os_type'} $gconfig{'os_version'}
74 Uname output:   $uname
75 EOF
76         }
77
78 $attach[0] .= "\n".$in{'text'}."\n";
79
80 if ($in{'config'} && !$gconfig{'nofeedbackconfig'}) {
81         # Check if this user has full rights to the module
82         $access{'feedback'} >= 2 || &error($text{'feedback_ecannot2'});
83         local %uacl = &get_module_acl(undef, $m);
84         local %defacl;
85         local $mdir = &module_root_directory($m);
86         &read_file("$mdir/defaultacl", \%defacl);
87         if ($access{'feedback'} != 3) {
88                 foreach $k (keys %uacl) {
89                         if ($defacl{$k} ne $uacl{$k}) {
90                                 &error($text{'feedback_econfig'});
91                                 }
92                         }
93                 }
94
95         # Attach all the text file from the module's config
96         local %mconfig = &foreign_config($m);
97         if (keys %mconfig) {
98                 local $a;
99                 $a .= "Content-Type: text/plain; name=\"config\"\n";
100                 $a .= "Content-Transfer-Encoding: 7bit\n";
101                 $a .= "\n";
102                 foreach $k (keys %mconfig) {
103                         $a .= "$k=$mconfig{$k}\n";
104                         }
105                 push(@attach, $a);
106                 }
107
108         # Find out what config files the module uses
109         local @files;
110         if (-r "$mdir/feedback_files.pl") {
111                 # Ask the module for it's files
112                 &foreign_require($m, "feedback_files.pl");
113                 @files = &foreign_call($m, "feedback_files", $m);
114                 }
115
116         # Use all the path in the config
117         foreach $k (keys %mconfig) {
118                 push(@files, $mconfig{$k}) if ($mconfig{$k} =~ /^\//);
119                 }
120         @files = &unique(@files);
121
122         # Attach those config files that are plain text (less than 5%
123         # non-ascii characters). Also skip logfiles.
124         foreach $f (@files) {
125                 next if (!$f || -d $f);
126                 next if ($f =~ /\/var\/log\//);
127                 local $/ = undef;
128                 open(FILE, $f) || next;
129                 local $data = <FILE>;
130                 close(FILE);
131                 local $count = ($data =~ tr/[\000-\176]/[\000-\176]/);
132                 if (!length($data) || 100*$count / length($data) > 95) {
133                         # File is text
134                         local $a;
135                         local $sf = &short_name($f);
136                         $a .= "Content-Type: text/plain; name=\"$sf\"\n";
137                         $a .= "Content-Transfer-Encoding: 7bit\n";
138                         $a .= "\n";
139                         $a .= $data;
140                         push(@attach, $a);
141                         }
142                 }
143         }
144
145 # Include uploaded attached files
146 foreach $u ('attach0', 'attach1') {
147         if ($in{$u} ne '') {
148                 local $a;
149                 local $name = &short_name($in{"${u}_filename"});
150                 local $type = $in{"${u}_content_type"};
151                 $type = &guess_mime_type($name) if (!$type);
152                 $a .= "Content-type: $type; name=\"$name\"\n";
153                 $a .= "Content-Transfer-Encoding: base64\n";
154                 $a .= "\n\n";
155                 $a .= &encode_base64($in{$u});
156                 push(@attach, $a);
157                 }
158         }
159
160 # Build the MIME email
161 $bound = "bound".time();
162 $mail = $headers;
163 $mail .= "Content-Type: multipart/mixed; boundary=\"$bound\"\n";
164 $mail .= "MIME-Version: 1.0\n";
165 $mail .= "\n";
166 $mail .= "This is a multi-part message in MIME format.\n";
167 foreach $a (@attach) {
168         $mail .= "\n--".$bound."\n";
169         $mail .= $a;
170         }
171 $mail .= "\n--".$bound."--\n";
172
173 if (!$in{'mailserver_def'}) {
174         $ok = &send_via_smtp($in{'mailserver'});
175         $sent = 3 if ($ok);
176         }
177
178 if (!$sent) {
179         # Try to send the email by calling sendmail -t
180         %sconfig = &foreign_config("sendmail");
181         $sendmail = $sconfig{'sendmail_path'} ? $sconfig{'sendmail_path'}
182                                               : &has_command("sendmail");
183         if (-x $sendmail && open(MAIL, "| $sendmail -t")) {
184                 print MAIL $mail;
185                 if (close(MAIL)) {
186                         $sent = 2;
187                         }
188                 }
189         }
190
191 if (!$sent) {
192         # Try to connect to a local SMTP server
193         $ok = &send_via_smtp("localhost");
194         $sent = 1 if ($ok);
195         }
196
197 if ($sent) {
198         # Tell the user that it was sent OK
199         &ui_print_header(undef, $text{'feedback_title'}, "", undef, 0, 1);
200         if ($sent == 3) {
201                 print &text('feedback_via', join(",", @tolist),
202                             "<tt>$in{'mailserver'}</tt>"),"\n";
203                 }
204         elsif ($sent == 2) {
205                 print &text('feedback_prog', join(",", @tolist),
206                             "<tt>$sendmail</tt>"),"\n";
207                 }
208         else {
209                 print &text('feedback_via', join(",", @tolist),
210                             "<tt>localhost</tt>"),"\n";
211                 }
212         print "<p>\n";
213         &ui_print_footer("/?cat=$minfo{'category'}", $text{'index'});
214
215         # Save settings in config
216         $gconfig{'feedback_name'} = $in{'name'};
217         $gconfig{'feedback_email'} = $in{'email'};
218         $gconfig{'feedback_mailserver'} =
219                 $in{'mailserver_def'} ? undef : $in{'mailserver'};
220         &write_file("$config_directory/config", \%gconfig);
221         }
222 else {
223         # Give up! Tell the user ..
224         &error($text{'feedback_esend'});
225         }
226
227 sub send_via_smtp
228 {
229 local $error;
230 &open_socket($_[0], 25, MAIL, \$error);
231 return 0 if ($error);
232 &smtp_command(MAIL) || return 0;
233 &smtp_command(MAIL, "helo ".&get_system_hostname()."\r\n") || return 0;
234 &smtp_command(MAIL, "mail from: <$email>\r\n") || return 0;
235 foreach $t (@tolist) {
236         &smtp_command(MAIL, "rcpt to: <$t>\r\n") || return 0;
237         }
238 &smtp_command(MAIL, "data\r\n");
239 $mail =~ s/\r//g;
240 $mail =~ s/\n/\r\n/g;
241 print MAIL $mail;
242 &smtp_command(MAIL, ".\r\n");
243 &smtp_command(MAIL, "quit\r\n");
244 close(MAIL);
245 return 1;
246 }
247
248 # smtp_command(handle, command)
249 sub smtp_command
250 {
251 local ($m, $c) = @_;
252 print $m $c;
253 local $r = <$m>;
254 return $r =~ /^[23]\d+/;
255 }
256
257 sub short_name
258 {
259 $_[0] =~ /([^\\\/]+)$/;
260 return $1;
261 }
262