File Coverage

lib/Mail/Toaster/Setup/Config.pm
Criterion Covered Total %
statement 24 216 11.1
branch 1 100 1.0
condition 0 55 0.0
subroutine 8 24 33.3
pod 0 17 0.0
total 33 412 8.0


line stmt bran cond sub pod time code
1             package Mail::Toaster::Setup::Config;
2 1     1   4 use strict;
  1         1  
  1         34  
3 1     1   3 use warnings;
  1         2  
  1         55  
4              
5             our $VERSION = '5.50';
6              
7             #use Carp;
8             #use Config;
9             #use Cwd;
10             #use Data::Dumper;
11             #use File::Copy;
12             #use File::Path;
13 1     1   4 use English '-no_match_vars';
  1         2  
  1         12  
14 1     1   426 use Params::Validate ':all';
  1         2  
  1         201  
15 1     1   6 use Sys::Hostname;
  1         1  
  1         54  
16              
17 1     1   3 use lib 'lib';
  1         1  
  1         11  
18 1     1   134 use parent 'Mail::Toaster::Base';
  1         1  
  1         7  
19              
20             sub config {
21 2     2 0 3 my $self = shift;
22 2         6 my %p = validate( @_, { $self->get_std_opts } );
23              
24 2 50       19 return $p{test_ok} if defined $p{test_ok}; # for testing only
25              
26             # apply the platform specific changes to the config file
27 0           $self->tweaks();
28              
29 0           my $file_name = "toaster-watcher.conf";
30 0           my $file_path = $self->util->find_config( $file_name );
31 0 0         $self->setup->refresh_config( $file_path ) or return;
32              
33             ### start questions
34 0           $self->config_hostname();
35 0           $self->postmaster();
36 0           $self->test_email();
37 0           $self->test_email_pass();
38 0           $self->vpopmail_mysql_pass();
39 0           $self->openssl();
40 0           $self->webmail_passwords();
41             ### end questions
42             ### don't forget to add changed fields to the list in save_changes
43              
44 0           $self->save_changes( $file_path );
45 0           $self->install($file_name, $file_path );
46             };
47              
48             sub apply_tweaks {
49 0     0 0   my $self = shift;
50 0           my %p = validate( @_,
51             { file => { type => SCALAR },
52             changes => { type => ARRAYREF },
53             $self->get_std_opts
54             },
55             );
56              
57             # changes is a list (array) of changes to apply to a text file
58             # each change is a hash with two elements: search, replace. the contents of
59             # file is searched for lines that matches search. Matches are replaced by the
60             # replace string. If search2 is also supplied and search does not match,
61             # search2 will be replaced.
62             # Ex:
63             # $changes = (
64             # { search => '#ssl_cert = /etc/ssl/certs/server.pem',
65             # replace => 'ssl_cert = /etc/ssl/certs/server.pem',
66             # },
67             # );
68             #
69             # read in file
70 0 0         my @lines = $self->util->file_read( $p{file} ) or return;
71              
72 0           my $total_found = 0;
73 0           foreach my $e ( @{ $p{changes} } ) {
  0            
74 0 0         my $search = $e->{search} or next;
75 0 0         my $replace = $e->{replace} or next;
76 0           my $search2 = $e->{search2};
77 0           my $found = 0;
78              
79 0 0 0       if ( $search2 && $search2 eq 'section' ) {
80             # look for a multiline pattern such as: protocol manageseive { .... }
81 0           my (@after, $in);
82 0           foreach my $line ( @lines ) {
83 0 0         if ( $in ) {
84 0 0         next if $line !~ /^ \s* \} \s* $/xms;
85 0           $in = 0;
86 0           next;
87             }
88 0 0         if ( $search eq $line ) {
89 0           $found++;
90 0           $in++;
91 0           next;
92             };
93 0 0         push @after, $line if ! $in;
94             };
95 0           @lines = @after;
96             };
97             # search entire file for $search string
98 0           for ( my $i = 0; $i < scalar @lines; $i++ ) {
99 0 0         if ( $lines[$i] eq $search ) {
100 0           $lines[$i] = $replace;
101 0           $found++;
102             };
103             }
104             # search entire file for $search2 string
105 0 0 0       if ( ! $found && $search2 ) {
106 0           for ( my $i = 0; $i < scalar @lines; $i++ ) {
107 0 0         if ( $lines[$i] eq $search2 ) {
108 0           $lines[$i] = $replace;
109 0           $found++;
110             };
111             }
112             };
113             $self->error( "attempt to replace\n$search\n\twith\n$replace\n\tfailed",
114             frames => 1,
115 0 0 0       fatal => 0) if ( ! $found && ! $e->{nowarn} );
116 0           $total_found += $found;
117             };
118              
119 0           $self->audit( "config tweaks replaced $total_found lines",verbose=>$p{verbose} );
120              
121 0           $self->util->file_write( $p{file}, lines => \@lines );
122             };
123              
124             sub config_hostname {
125 0     0 0   my $self = shift;
126              
127 0 0 0       return if ( $self->conf->{'toaster_hostname'} && $self->conf->{'toaster_hostname'} ne "mail.example.com" );
128              
129 0           $self->conf->{'toaster_hostname'} = $self->util->ask(
130             "the hostname of this mail server",
131             default => hostname,
132             );
133 0           chomp $self->conf->{'toaster_hostname'};
134              
135 0           $self->audit( "toaster hostname set to " . $self->conf->{'toaster_hostname'} );
136             };
137              
138             sub install {
139 0     0 0   my $self = shift;
140 0           my ($file_name, $file_path) = @_;
141              
142             # install $file_path in $prefix/etc/toaster-watcher.conf if it doesn't exist
143             # already
144 0   0       my $config_dir = $self->conf->{'system_config_dir'} || '/usr/local/etc';
145              
146             # if $config_dir is missing, create it
147 0 0         $self->util->mkdir_system( dir => $config_dir ) if ! -e $config_dir;
148              
149 0           my @configs = (
150             { newfile => $file_path, existing => "$config_dir/$file_name", mode => '0640', overwrite => 0 },
151             { newfile => $file_path, existing => "$config_dir/$file_name-dist", mode => '0640', overwrite => 1 },
152             { newfile => 'toaster.conf-dist', existing => "$config_dir/toaster.conf", mode => '0644', overwrite => 0 },
153             { newfile => 'toaster.conf-dist', existing => "$config_dir/toaster.conf-dist", mode => '0644', overwrite => 1 },
154             );
155              
156 0           foreach ( @configs ) {
157 0 0 0       next if -e $_->{existing} && ! $_->{overwrite};
158             $self->util->install_if_changed(
159             newfile => $_->{newfile},
160             existing => $_->{existing},
161             mode => $_->{mode},
162 0           clean => 0,
163             notify => 1,
164             verbose => 0,
165             );
166             };
167             }
168              
169             sub openssl {
170 0     0 0   my $self = shift;
171             # OpenSSL certificate settings
172              
173             # country
174 0 0         if ( $self->conf->{'ssl_country'} eq "SU" ) {
175 0           print " SSL certificate defaults\n";
176 0           $self->conf->{'ssl_country'} =
177             uc( $self->util->ask( "your 2 digit country code (US)", default => "US" )
178             );
179             }
180 0           $self->audit( "config: ssl_country, (" . $self->conf->{'ssl_country'} . ")" );
181              
182             # state
183 0 0         if ( $self->conf->{'ssl_state'} eq "saxeT" ) {
184 0           $self->conf->{'ssl_state'} =
185             $self->util->ask( "the name (non abbreviated) of your state" );
186             }
187 0           $self->audit( "config: ssl_state, (" . $self->conf->{'ssl_state'} . ")" );
188              
189             # locality (city)
190 0 0         if ( $self->conf->{'ssl_locality'} eq "dnalraG" ) {
191 0           $self->conf->{'ssl_locality'} =
192             $self->util->ask( "the name of your locality/city" );
193             }
194 0           $self->audit( "config: ssl_locality, (" . $self->conf->{'ssl_locality'} . ")" );
195              
196             # organization
197 0 0         if ( $self->conf->{'ssl_organization'} eq "moc.elpmaxE" ) {
198 0           $self->conf->{'ssl_organization'} = $self->util->ask( "the name of your organization" );
199             }
200 0           $self->audit( "config: ssl_organization, (" . $self->conf->{'ssl_organization'} . ")" );
201             };
202              
203             sub webmail_passwords {
204 0     0 0   my $self = shift;
205              
206 0 0 0       if ( $self->conf->{install_squirrelmail} &&
      0        
207             $self->conf->{install_squirrelmail_sql} &&
208             $self->conf->{install_squirrelmail_sql_pass} eq 'chAnge7his' ) {
209             $self->conf->{install_squirrelmail_sql_pass} =
210 0           $self->util->ask("squirrelmail database password");
211             };
212              
213 0 0 0       if ( $self->conf->{install_roundcube} &&
214             $self->conf->{install_roundcube_db_pass} eq 'To4st3dR0ndc@be' ) {
215             $self->conf->{install_roundcube_db_pass} =
216 0           $self->util->ask("roundcube database password");
217             };
218              
219 0 0 0       if ( $self->conf->{install_spamassassin} &&
      0        
220             $self->conf->{install_spamassassin_sql} &&
221             $self->conf->{install_spamassassin_dbpass} eq 'assSPAMing' ) {
222             $self->conf->{install_spamassassin_dbpass} =
223 0           $self->util->ask("spamassassin database password");
224             };
225              
226 0 0 0       if ( $self->conf->{install_phpmyadmin} &&
227             $self->conf->{phpMyAdmin_controlpassword} eq 'pmapass') {
228             $self->conf->{phpMyAdmin_controlpassword} =
229 0           $self->util->ask("phpMyAdmin control password");
230             };
231              
232 0           return 1;
233             };
234              
235             sub postmaster {
236 0     0 0   my $self = shift;
237              
238 0 0 0       return if ( $self->conf->{'toaster_admin_email'} && $self->conf->{'toaster_admin_email'} ne "postmaster\@example.com" );
239              
240 0   0       $self->conf->{'toaster_admin_email'} = $self->util->ask(
241             "the email address for administrative emails and notices\n".
242             " (probably yours!)",
243             default => "postmaster",
244             ) || 'root';
245              
246             $self->audit(
247 0           "toaster admin emails sent to " . $self->conf->{'toaster_admin_email'} );
248             };
249              
250             sub save_changes {
251 0     0 0   my $self = shift;
252 0           my ($file_path) = @_;
253              
254 0           my @fields = qw/ toaster_hostname toaster_admin_email toaster_test_email
255             toaster_test_email_pass vpopmail_mysql_pass ssl_country ssl_state
256             ssl_locality ssl_organization install_squirrelmail_sql_pass
257             install_roundcube_db_pass install_spamassassin_dbpass
258             phpMyAdmin_controlpassword
259             /;
260 0 0         push @fields, 'vpopmail_mysql_pass' if $self->conf->{'vpopmail_mysql'};
261              
262 0           my @lines = $self->util->file_read( $file_path, verbose => 0 );
263 0           foreach my $key ( @fields ) {
264 0           foreach my $line (@lines) {
265 0 0         if ( $line =~ /^$key\s*=/ ) {
266             # format the config entries to match config file format
267 0           $line = sprintf( '%-34s = %s', $key, $self->conf->{$key} );
268             }
269             };
270             };
271              
272 0           $self->util->file_write( "/tmp/toaster-watcher.conf", lines => \@lines );
273              
274 0 0         my $r = $self->util->install_if_changed(
    0          
275             newfile => "/tmp/toaster-watcher.conf",
276             existing => $file_path,
277             mode => '0640',
278             clean => 1,
279             notify => -e $file_path ? 1 : 0,
280             )
281             or return $self->error( "installing /tmp/toaster-watcher.conf to $file_path failed!" );
282              
283 0 0         my $status = $r == 1 ? "ok" : "ok (current)";
284 0           $self->audit( "config: updating $file_path, $status" );
285 0           return $r;
286             };
287              
288             sub test_email {
289 0     0 0   my $self = shift;
290              
291 0 0         return if $self->conf->{'toaster_test_email'} ne "test\@example.com";
292              
293             $self->conf->{'toaster_test_email'} = $self->util->ask(
294             "an email account for running tests",
295 0           default => "postmaster\@" . $self->conf->{'toaster_hostname'}
296             );
297              
298 0           $self->audit( "toaster test account set to ".$self->conf->{'toaster_test_email'} );
299             };
300              
301             sub test_email_pass {
302 0     0 0   my $self = shift;
303              
304 0 0 0       return if ( $self->conf->{'toaster_test_email_pass'} && $self->conf->{'toaster_test_email_pass'} ne "cHanGeMe" );
305              
306 0           $self->conf->{'toaster_test_email_pass'} = $self->util->ask( "the test email account password" );
307              
308             $self->audit(
309 0           "toaster test password set to ".$self->conf->{'toaster_test_email_pass'} );
310             };
311              
312             sub tweaks {
313 0     0 0   my $self = shift;
314 0           my %p = validate( @_, { $self->get_std_opts } );
315              
316 0           my $status = "ok";
317              
318 0           my $file = $self->util->find_config( 'toaster-watcher.conf' );
319              
320             # verify that find_config worked and $file is readable
321             return $self->error( "tweaks: read test on $file, FAILED",
322 0 0         fatal => $p{fatal} ) if ! -r $file;
323              
324 0           my %changes;
325 0 0         %changes = $self->config_tweaks_freebsd() if $OSNAME eq 'freebsd';
326 0 0         %changes = $self->config_tweaks_darwin() if $OSNAME eq 'darwin';
327 0 0         %changes = $self->config_tweaks_linux() if $OSNAME eq 'linux';
328              
329 0           %changes = $self->config_tweaks_testing(%changes);
330 0           %changes = $self->config_tweaks_mysql(%changes);
331              
332             # foreach key of %changes, apply to $conf
333 0           my @lines = $self->util->file_read( $file );
334 0           foreach my $line (@lines) {
335 0 0         next if $line =~ /^#/; # comment lines
336 0 0         next if $line !~ /=/; # not a key = value
337              
338 0           my ( $key, $val ) = $self->util->parse_line( $line, strip => 0 );
339              
340 0 0 0       if ( defined $changes{$key} && $changes{$key} ne $val ) {
341 0           $status = "changed";
342             #print "\t setting $key to ". $changes{$key} . "\n";
343 0           $line = sprintf( '%-34s = %s', $key, $changes{$key} );
344 0           print "\t$line\n";
345             }
346             }
347 0 0 0       return 1 unless ( $status && $status eq "changed" );
348              
349             # ask the user for permission to install
350 0 0         return 1
351             if ! $self->util->yes_or_no(
352             'config tweaks: The changes shown above are recommended for use on your system.
353             May I apply the changes for you?',
354             timeout => 30,
355             );
356              
357             # write $conf to temp file
358 0           $self->util->file_write( "/tmp/toaster-watcher.conf", lines => \@lines );
359              
360             # if the file ends with -dist, then save it back with out the -dist suffix
361             # the find_config sub will automatically prefer the newer non-suffixed one
362 0 0         if ( $file =~ m/(.*)-dist\z/ ) {
363 0           $file = $1;
364             };
365              
366             # update the file if there are changes
367 0           my $r = $self->util->install_if_changed(
368             newfile => "/tmp/toaster-watcher.conf",
369             existing => $file,
370             clean => 1,
371             notify => 0,
372             verbose => 0,
373             );
374              
375 0 0         return 0 unless $r;
376 0 0         $r == 1 ? $r = "ok" : $r = "ok (current)";
377 0           $self->audit( "config tweaks: updated $file, $r" );
378             }
379              
380             sub config_tweaks_darwin {
381 0     0 0   my $self = shift;
382              
383 0           $self->audit( "config tweaks: applying Darwin tweaks" );
384              
385             return (
386 0           toaster_http_base => '/Library/WebServer',
387             toaster_http_docs => '/Library/WebServer/Documents',
388             toaster_cgi_bin => '/Library/WebServer/CGI-Executables',
389             toaster_prefix => '/opt/local',
390             toaster_src_dir => '/opt/local/src',
391             system_config_dir => '/opt/local/etc',
392             vpopmail_valias => '0',
393             install_mysql => '0 # 0, 1, 2, 3, 40, 41, 5',
394             install_portupgrade => '0',
395             filtering_maildrop_filter_file => '/opt/local/etc/mail/mailfilter',
396             qmail_mysql_include => '/opt/local/lib/mysql/libmysqlclient.a',
397             vpopmail_home_dir => '/opt/local/vpopmail',
398             vpopmail_mysql => '0',
399             smtpd_use_mysql_relay_table => '0',
400             qmailadmin_spam_command => '| /opt/local/bin/maildrop /opt/local/etc/mail/mailfilter',
401             qmailadmin_http_images => '/Library/WebServer/Documents/images',
402             );
403             };
404              
405             sub config_tweaks_freebsd {
406 0     0 0   my $self = shift;
407 0           $self->audit( "config tweaks: applying FreeBSD tweaks" );
408              
409             return (
410 0           install_squirrelmail => 'port # 0, ver, port',
411             install_autorespond => 'port # 0, ver, port',
412             install_ezmlm => 'port # 0, ver, port',
413             install_courier_imap => '0 # 0, ver, port',
414             install_dovecot => 'port # 0, ver, port',
415             install_clamav => 'port # 0, ver, port',
416             install_ripmime => 'port # 0, ver, port',
417             install_cronolog => 'port # ver, port',
418             install_daemontools => 'port # ver, port',
419             install_qmailadmin => 'port # 0, ver, port',
420             install_djbdns => 'port # ver, port',
421             )
422             }
423              
424             sub config_tweaks_linux {
425 0     0 0   my $self = shift;
426 0           $self->audit( "config tweaks: applying Linux tweaks " );
427              
428             return (
429 0           toaster_http_base => '/var/www',
430             toaster_http_docs => '/var/www',
431             toaster_cgi_bin => '/usr/lib/cgi-bin',
432             vpopmail_valias => '0',
433             install_mysql => '0 # 0, 1, 2, 3, 40, 41, 5',
434             vpopmail_mysql => '0',
435             smtpd_use_mysql_relay_table => '0',
436             qmailadmin_http_images => '/var/www/images',
437             install_dovecot => '1.0.2',
438             )
439             }
440              
441             sub config_tweaks_mysql {
442 0     0 0   my ($self, %changes) = @_;
443              
444 0 0         return %changes if $self->conf->{install_mysql};
445 0 0         return %changes if ! $self->util->yes_or_no("Enable MySQL support?");
446              
447 0           $self->audit( "config tweaks: applying MT testing tweaks" );
448              
449 0           $changes{'install_mysql'} = '55 # 0, 1, 2, 3, 40, 41, 5, 55';
450 0           $changes{'install_mysqld'} = '1 # 0, 1';
451 0           $changes{'vpopmail_mysql'} = '1 # enables all mysql options';
452 0           $changes{'smtpd_use_mysql_relay_table'} = 0;
453 0           $changes{'install_squirrelmail_sql'} = 1;
454 0           $changes{'install_spamassassin_sql'} = 1;
455              
456 0           return %changes;
457             };
458              
459             sub config_tweaks_testing {
460 0     0 0   my ($self, %changes) = @_;
461              
462 0           my $hostname = hostname;
463 0 0 0       return %changes if ( ! $hostname || $hostname ne 'jail.simerson.net' );
464              
465 0           $self->audit( "config tweaks: applying MT testing tweaks" );
466              
467 0           $changes{'toaster_hostname'} = 'jail.simerson.net';
468 0           $changes{'toaster_admin_email'} = 'postmaster@jail.simerson.net';
469 0           $changes{'toaster_test_email'} = 'test@jail.simerson.net';
470 0           $changes{'toaster_test_email_pass'} = 'sdfsdf';
471 0           $changes{'install_squirrelmail_sql'} = '1';
472 0           $changes{'install_phpmyadmin'} = '1';
473 0           $changes{'install_sqwebmail'} = 'port';
474 0           $changes{'install_vqadmin'} = 'port';
475 0           $changes{'install_openldap_client'} = '1';
476 0           $changes{'install_ezmlm_cgi'} = '1';
477 0           $changes{'install_dspam'} = '1';
478 0           $changes{'install_pyzor'} = '1';
479 0           $changes{'install_bogofilter'} = '1';
480 0           $changes{'install_dcc'} = '1';
481 0           $changes{'install_lighttpd'} = '1';
482 0           $changes{'install_courier_imap'} = 'port';
483 0           $changes{'install_gnupg'} = 'port';
484 0           $changes{'vpopmail_default_domain'} = 'jail.simerson.net';
485 0           $changes{'pop3_ssl_daemon'} = 'qpop3d';
486 0           $changes{'install_spamassassin_flags'}= '-v -u spamd -q -A 10.0.1.67 -H /var/spool/spamd -x';
487 0           $changes{'install_isoqlog'} = 'port # 0, ver, port';
488              
489 0           return %changes;
490             }
491              
492             sub vpopmail_mysql_pass {
493 0     0 0   my $self = shift;
494              
495 0 0         return if ! $self->conf->{'vpopmail_mysql'};
496             return if ( $self->conf->{'vpopmail_mysql_pass'}
497 0 0 0       && $self->conf->{'vpopmail_mysql_pass'} ne "supersecretword" );
498              
499 0           $self->conf->{'vpopmail_mysql_pass'} =
500             $self->util->ask( "the password for securing vpopmails "
501             . "database connection. You MUST enter a password here!",
502             );
503              
504 0           $self->audit( "vpopmail MySQL password set to ".$self->conf->{'vpopmail_mysql_pass'});
505             }
506              
507             1;
508