File Coverage

blib/lib/XMail/Install.pm
Criterion Covered Total %
statement 28 30 93.3
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 38 40 95.0


line stmt bran cond sub pod time code
1             package XMail::Install;
2            
3             # Documentation:
4             # POD-style documentation is at the end. Extract it with pod2html.*.
5             #
6             # Note:
7             # tab = 4 spaces || die.
8             #
9             # History Info:
10             # Rev Author Date Comment
11             # 1.00 Ron Savage 20061106 Initial version
12            
13 1     1   33340 use strict;
  1         1  
  1         36  
14 1     1   4 use warnings;
  1         2  
  1         27  
15            
16 1     1   6 use Carp;
  1         2  
  1         162  
17 1     1   824 use Email::Send;
  1         26008  
  1         5  
18 1     1   779 use File::Copy; # For copy().
  1         2216  
  1         57  
19 1     1   769 use File::Copy::Recursive qw(dircopy);
  1         3482  
  1         75  
20 1     1   8 use File::Path; # For rmtree().
  1         1  
  1         63  
21 1     1   1194 use Mail::POP3Client;
  1         35891  
  1         63  
22 1     1   777 use Path::Class; # For dir() and file().
  1         33726  
  1         54  
23 1     1   397 use Win32;
  0            
  0            
24             use Win32::Process;
25             use Win32::Process::List;
26             use Win32::Service;
27             use Win32::TieRegistry (Delimiter => '/');
28            
29             our $VERSION = '1.01';
30            
31             # -----------------------------------------------
32            
33             # Encapsulated class data.
34            
35             {
36             my(%_attr_data) =
37             ( # Alphabetical order.
38             _options => '',
39             );
40            
41             sub _default_for
42             {
43             my($self, $attr_name) = @_;
44            
45             return $_attr_data{$attr_name};
46             }
47            
48             sub _standard_keys
49             {
50             return keys %_attr_data;
51             }
52             }
53            
54             # -----------------------------------------------
55            
56             sub copy_dirs_and_files
57             {
58             my($self) = @_;
59            
60             $self -> info("Removing directory $$self{'_target_dir'}");
61            
62             rmtree("$$self{'_target_dir'}"); # ($$self{'_target_dir'}) dies with: Not an ARRAY reference at C:/Perl/lib/File/Path.pm line 191.
63            
64             $self -> info("Recursively copying directory $$self{'_source_dir'} to $$self{'_target_dir'}");
65            
66             my(@result) = dircopy($$self{'_source_dir'}, $$self{'_target_dir'});
67            
68             $self -> info("Copied $result[0] files and directories");
69            
70             my($dir) = $$self{'_target_dir'} -> subdir('bin');
71            
72             my($file);
73            
74             for (qw/ctrlclnt mkusers sendmail XMail xmcrypt/)
75             {
76             $file = $$self{'_options'}{'in_dir'} -> file("$_.exe");
77            
78             $self -> info("Copying $file to $dir");
79            
80             copy($file, $dir -> file("$_.exe") );
81             }
82            
83             } # End of copy_dirs_and_files.
84            
85             # -----------------------------------------------
86            
87             sub create_account
88             {
89             my($self) = @_;
90             my($file) = $$self{'_target_dir'} -> file('ctrlaccounts.tab');
91            
92             $self -> info("Updating $file to contain the xmailuser's account");
93             $self -> info('o This account is used to create the domain and the user account');
94             $self -> info("o Later, the postmaster's account overwrites the xmailuser's account");
95            
96             open(OUT, "> $file") || die "Can't open(> $file): $!";
97             print OUT qq|"xmailuser"\t"1d08040c09"\n|; # Password is xmail.
98             close OUT;
99            
100             } # End of create_account.
101            
102             # -----------------------------------------------
103            
104             sub create_domain
105             {
106             my($self) = @_;
107             my($file) = $$self{'_target_dir'} -> file('domains.tab');
108             my($command) = "$$self{'_ctrlclnt'} -s $$self{'_options'}{'server'} -u xmailuser -p xmail domainadd $$self{'_options'}{'domain_name'}";
109            
110             $self -> info("Running ctrlclnt to create the domain $$self{'_options'}{'domain_name'}");
111             $self -> info("o Run: $command");
112             $self -> info("o This will update $file");
113            
114             $self -> execute($command);
115             $self -> sleep($$self{'_options'}{'short_delay'});
116            
117             my($dir) = $$self{'_target_dir'} -> subdir('domains', $$self{'_options'}{'domain_name'});
118            
119             if (-d $dir)
120             {
121             $self -> info("Created domain directory $dir");
122             }
123             else
124             {
125             Carp::croak "Failed to create domain directory $dir";
126             }
127            
128             } # End of create_domain.
129            
130             # -----------------------------------------------
131            
132             sub create_postmaster_account
133             {
134             my($self) = @_;
135             my($command) = "$$self{'_xmcrypt'} $$self{'_options'}{'postmaster_password'}";
136            
137             $self -> info("Running xmcrypt to encrypt the postmaster's password");
138             $self -> info("o Run: $command");
139            
140             my($password) = `$command`;
141            
142             chomp $password;
143            
144             $self -> info("o Password: $password");
145            
146             my($file) = $$self{'_target_dir'} -> file('ctrlaccounts.tab');
147            
148             $self -> info("Updating $file to contain the postmaster's account");
149            
150             open(OUT, "> $file") || die "Can't open(> $file): $!";
151             print OUT qq|"postmaster"\t"$password"\n|;
152             close OUT;
153            
154             } # End of create_postmaster_account.
155            
156             # -----------------------------------------------
157            
158             sub create_user_account
159             {
160             my($self) = @_;
161             my($command) = "$$self{'_xmcrypt'} $$self{'_options'}{'user_password'}";
162            
163             $self -> info("Running xmcrypt to encrypt $$self{'_options'}{'user_name'}'s password");
164             $self -> info("o Run: $command");
165            
166             my($password) = `$command`;
167            
168             chomp $password;
169            
170             $self -> info("o Password: $password");
171            
172             $command = "$$self{'_ctrlclnt'} -s $$self{'_options'}{'server'} -u xmailuser -p xmail useradd $$self{'_options'}{'domain_name'} $$self{'_options'}{'user_name'} $$self{'_options'}{'user_password'} U";
173             my($file) = $$self{'_target_dir'} -> file('mailusers.tab');
174            
175             $self -> info("Running ctrlclnt to create the user $$self{'_options'}{'user_name'}");
176             $self -> info("o Run: $command");
177             $self -> info("o This will update $file");
178            
179             $self -> execute($command);
180            
181             $self -> info('Created user account');
182            
183             } # End of create_user_account.
184            
185             # -----------------------------------------------
186            
187             sub execute
188             {
189             my($self, $command) = @_;
190             my(@result) = `$command`;
191            
192             chomp @result;
193            
194             @result = '(nothing)' if (! @result);
195            
196             $self -> info("Output of that command: $_") for @result;
197            
198             } # End of execute.
199            
200             # -----------------------------------------------
201            
202             sub info
203             {
204             my($self, $message) = @_;
205            
206             print "$message\n" if ($$self{'_options'}{'verbose'});
207            
208             } # End of info.
209            
210             # -----------------------------------------------
211            
212             sub install_and_start_service
213             {
214             my($self) = @_;
215             my($xmail) = $$self{'_target_dir'} -> file('bin', 'XMail.exe');
216             my($command) = "$xmail --install-auto";
217            
218             $self -> info("Installing XMail as a service");
219             $self -> info("o Run: $command");
220            
221             $self -> execute($command);
222             $self -> info('Starting the XMail service');
223            
224             Win32::Service::StartService('', 'XMail');
225            
226             } # End of install_and_start_service.
227            
228             # -----------------------------------------------
229            
230             sub new
231             {
232             my($class, %arg) = @_;
233             my($self) = bless({}, $class);
234            
235             for my $attr_name ($self -> _standard_keys() )
236             {
237             my($arg_name) = $attr_name =~ /^_(.*)/;
238            
239             if (exists($arg{$arg_name}) )
240             {
241             $$self{$attr_name} = $arg{$arg_name};
242             }
243             else
244             {
245             $$self{$attr_name} = $self -> _default_for($attr_name);
246             }
247             }
248            
249             $$self{'_options'} = {} if (! $$self{'_options'});
250             $$self{'_options'}{'domain_name'} = 'xmail.net' if (! $$self{'_options'}{'domain_name'});
251             $$self{'_options'}{'server'} = '127.0.0.1' if (! $$self{'_options'}{'server'});
252             $$self{'_options'}{'in_dir'} = dir('c:', 'xmail-1.25') if (! $$self{'_options'}{'in_dir'});
253             $$self{'_options'}{'long_delay'} = 20;
254             $$self{'_options'}{'out_dir'} = dir('c:') if (! $$self{'_options'}{'out_dir'});
255             $$self{'_options'}{'postmaster_password'} = 'richness-of-martens' if (! $$self{'_options'}{'postmaster_password'});
256             $$self{'_options'}{'short_delay'} = 5;
257             $$self{'_options'}{'user_name'} = 'rsavage' if (! $$self{'_options'}{'user_name'});
258             $$self{'_options'}{'user_password'} = 'skulk-of-foxes' if (! $$self{'_options'}{'user_password'});
259             $$self{'_options'}{'verbose'} = 0 if (! $$self{'_options'}{'verbose'});
260            
261             $self -> info("Program: $0");
262             $self -> info("Version: $VERSION");
263             $self -> info("domain_name: $$self{'_options'}{'domain_name'}");
264             $self -> info("server: $$self{'_options'}{'server'}");
265             $self -> info("in_dir: $$self{'_options'}{'in_dir'}");
266             $self -> info("out_dir: $$self{'_options'}{'out_dir'}");
267             $self -> info("postmaster_password: $$self{'_options'}{'postmaster_password'}");
268             $self -> info("user_name: $$self{'_options'}{'user_name'}");
269             $self -> info("user_password: $$self{'_options'}{'user_password'}");
270             $self -> info("verbose: $$self{'_options'}{'verbose'}");
271             $self -> info('-' x 50);
272            
273             return $self;
274            
275             } # End of new.
276            
277             # -----------------------------------------------
278            
279             sub receive_test_message
280             {
281             my($self) = @_;
282             my($pop) = Mail::POP3Client -> new
283             (
284             USER => $$self{'_options'}{'user_name'},
285             PASSWORD => $$self{'_options'}{'user_password'},
286             HOST => $$self{'_options'}{'server'},
287             AUTH_MODE => 'PASS',
288             );
289             my($count) = $pop -> Count();
290            
291             $self -> info("Server has $count message(s) waiting to be read");
292             $self -> info('-' x 50);
293            
294             my(@body);
295             my(@head);
296             my($i);
297            
298             for ($i = 1; $i <= $count; $i++)
299             {
300             @head = $pop -> Head($i);
301            
302             $self -> info("Received head: $_") for @head;
303             $self -> info('');
304            
305             @body = $pop -> Body($i);
306            
307             $self -> info("Received body: $_") for @body;
308             $self -> info('-' x 50);
309            
310             $pop -> Delete($i);
311             }
312            
313             $pop -> Close();
314            
315             } # End of receive_test_message.
316            
317             # -----------------------------------------------
318            
319             sub run
320             {
321             my($self) = @_;
322             $$self{'_source_dir'} = $$self{'_options'}{'in_dir'} -> subdir('MailRoot');
323             $$self{'_target_dir'} = $$self{'_options'}{'out_dir'} -> subdir('MailRoot');
324             $$self{'_ctrlclnt'} = $$self{'_target_dir'} -> file('bin', 'ctrlclnt.exe');
325             $$self{'_xmail'} = $$self{'_target_dir'} -> file('bin', 'XMail.exe');
326             $$self{'_xmail_debug'} = 'XMail --debug';
327             $$self{'_xmcrypt'} = $$self{'_target_dir'} -> file('bin', 'xmcrypt.exe');
328            
329             $self -> stop_and_remove_service();
330             $self -> update_the_registry();
331             $self -> copy_dirs_and_files();
332             $self -> create_account();
333             $self -> info("Starting '$$self{'_xmail_debug'}' using the config files shipped with XMail");
334             $self -> info('o Server must be up to run ctrlclnt');
335             $self -> info("o Creating the domain $$self{'_options'}{'domain_name'}");
336             $self -> info("o Creating the account for the user $$self{'_options'}{'user_name'}");
337            
338             my($process) = $self -> start_server();
339            
340             =pod
341            
342             $self -> create_domain();
343             $self -> create_user_account();
344             $self -> stop_server();
345            
346             $self -> create_postmaster_account();
347             $self -> update_dirs_and_files();
348             $self -> info("Starting '$$self{'_xmail_debug'}' with the new config files");
349             $self -> info('o Server must be up for it to receive mail');
350             $self -> info("o Sending a test message to $$self{'_options'}{'user_name'}");
351            
352             $process = $self -> start_server();
353            
354             $self -> send_test_message();
355             $self -> receive_test_message();
356             $self -> stop_server();
357             $self -> install_and_start_service();
358            
359             =cut
360            
361             $self -> info('Finished');
362            
363             return 0;
364            
365             } # End of run.
366            
367             # -----------------------------------------------
368            
369             sub run_run
370             {
371             my($self) = @_;
372             $$self{'_source_dir'} = $$self{'_options'}{'in_dir'} -> subdir('MailRoot');
373             $$self{'_target_dir'} = $$self{'_options'}{'out_dir'} -> subdir('MailRoot');
374             $$self{'_ctrlclnt'} = $$self{'_target_dir'} -> file('bin', 'ctrlclnt.exe');
375             $$self{'_xmail'} = $$self{'_target_dir'} -> file('bin', 'XMail.exe');
376             $$self{'_xmail_debug'} = 'XMail --debug';
377             $$self{'_xmcrypt'} = $$self{'_target_dir'} -> file('bin', 'xmcrypt.exe');
378            
379             $self -> create_domain();
380             $self -> create_user_account();
381             $self -> stop_server();
382            
383             $self -> create_postmaster_account();
384             $self -> update_dirs_and_files();
385             $self -> info("Starting '$$self{'_xmail_debug'}' with the new config files");
386             $self -> info('o Server must be up for it to receive mail');
387             $self -> info("o Sending a test message to $$self{'_options'}{'user_name'}");
388            
389             my($process) = $self -> start_server();
390            
391             $self -> send_test_message();
392             $self -> receive_test_message();
393             $self -> stop_server();
394             $self -> install_and_start_service();
395             $self -> info('Finished');
396            
397             return 0;
398            
399             } # End of run_run.
400            
401             # -----------------------------------------------
402            
403             sub sleep
404             {
405             my($self, $timeout) = @_;
406            
407             $self -> info("Sleeping $timeout seconds");
408            
409             sleep $timeout;
410            
411             } # End of sleep.
412            
413             # -----------------------------------------------
414            
415             sub send_test_message
416             {
417             my($self) = @_;
418             my($subject) = 'Testing installation of XMail';
419             my($message) = <
420             To: $$self{'_options'}{'user_name'}\@$$self{'_options'}{'domain_name'}
421             From: ron\@savage.net.au
422             Subject: $subject
423            
424             An implausibility of gnus.
425             An impossibility of platypuses.
426             EOS
427             $self -> info('Message reads...');
428             $self -> info('-' x 50);
429             $self -> info($message);
430             $self -> info('-' x 50);
431            
432             my($sender) = Email::Send->new({mailer => 'SMTP'});
433            
434             $sender->mailer_args([Host => $$self{'_options'}{'server'}]);
435             $sender->send($message);
436             $self -> info('Sent test message');
437             $self -> sleep($$self{'_options'}{'short_delay'});
438            
439             } # End of send_test_message.
440            
441             # -----------------------------------------------
442            
443             sub start_server
444             {
445             my($self) = @_;
446            
447             my($process);
448            
449             Win32::Process::Create($process, $$self{'_xmail'}, $$self{'_xmail_debug'}, 0, NORMAL_PRIORITY_CLASS, '.') || die "Can't start process $$self{'_xmail'}. \n" . win32_error();
450            
451             $self -> info("Started $$self{'_xmail'} as a process, not as a service");
452            
453             $self -> sleep($$self{'_options'}{'long_delay'});
454            
455             return $process;
456            
457             } # End of start_server.
458            
459             # -----------------------------------------------
460            
461             sub stop_and_remove_service
462             {
463             my($self) = @_;
464             my($command) = $$self{'_options'}{'in_dir'} -> file('XMail.exe');
465             $command .= ' --remove';
466            
467             $self -> info('Stopping the XMail service');
468            
469             Win32::Service::StopService('', 'XMail');
470            
471             $self -> info("Running XMail to remove the service");
472             $self -> info("o Run: $command");
473            
474             $self -> execute($command);
475             $self -> sleep(2);
476             $self -> info('Service removed');
477            
478             } # End of stop_and_remove_service.
479            
480             # -----------------------------------------------
481            
482             sub stop_server
483             {
484             my($self) = @_;
485            
486             # We do things this way, rather than using the $process returned from start_server()
487             # so that we can run start_server() and stop_server() via run() and run_run()
488             # respectively, and in the latter case $process is not available.
489            
490             $self -> info("Stopping the $$self{'_xmail'} process");
491            
492             my($processor) = Win32::Process::List -> new();
493             my(%process) = $processor -> GetProcesses();
494            
495             my($p, $pid);
496            
497             for $p (keys %process)
498             {
499             $pid = $process{$p} if ($p eq 'XMail.exe');
500             }
501            
502             if ($pid)
503             {
504             Win32::Process::KillProcess($pid, 0);
505            
506             $self -> info('Stopped XMail');
507             }
508             else
509             {
510             $self -> info("Can't stop XMail. It is not running");
511             }
512            
513             } # End of stop_server.
514            
515             # -----------------------------------------------
516            
517             sub update_dirs_and_files
518             {
519             my($self) = @_;
520            
521             my($dir);
522            
523             for (qw/home.bogus xmailserver.test/)
524             {
525             $dir = $$self{'_target_dir'} -> subdir('domains', $_);
526            
527             $self -> info("Removing obsolete directory $dir");
528            
529             rmtree("$dir"); # ($dir) dies with: Not an ARRAY reference at C:/Perl/lib/File/Path.pm line 191.
530             }
531            
532             my($file);
533            
534             for (qw/aliases/)
535             {
536             $file = $$self{'_target_dir'} -> file("$_.tab");
537            
538             $self -> info("Removing contents of $file");
539             $self -> info('o It refers to the obsolete account xmailserver.test');
540            
541             open(OUT, "> $file") || die "Can't open(> $file): $!";
542             close OUT;
543             }
544            
545             $file = $$self{'_target_dir'} -> file('server.tab');
546            
547             $self -> info("Updating $file");
548             $self -> info("o Converting xmailserver.test to $$self{'_options'}{'domain_name'}");
549             $self -> info("o Converting root\@$$self{'_options'}{'domain_name'} to postmaster\@$$self{'_options'}{'domain_name'}");
550            
551             open(INX, $file) || die "Can't open($file): $!";
552             my(@line) = map
553             {
554             s/xmailserver.test/$$self{'_options'}{'domain_name'}/;
555             s/root/postmaster/ if (/^"ErrorsAdmin/);
556             s/root/postmaster/ if (/^"PostMaster/);
557             $_;
558             } ;
559             close INX;
560            
561             open(OUT, "> $file") || die "Can't open(> $file): $!";
562             print OUT @line;
563             close OUT;
564            
565             $file = $$self{'_target_dir'} -> file('domains.tab');
566            
567             $self -> info("Updating $file");
568             $self -> info('o Remove the domain shipped with XMail');
569             $self -> info("o Leave only the new domain $$self{'_options'}{'domain_name'}");
570            
571             open(INX, $file) || die "Can't open($file): $!";
572             @line = grep{/"$$self{'_options'}{'domain_name'}"/} ;
573             close INX;
574            
575             open(OUT, "> $file") || die "Can't open(> $file): $!";
576             print OUT $line[0];
577             close OUT;
578            
579             $file = $$self{'_target_dir'} -> file('mailusers.tab');
580            
581             $self -> info("Updating $file");
582             $self -> info('o Remove the account shipped with XMail');
583             $self -> info("o Leave only the new account for $$self{'_options'}{'user_name'}");
584            
585             open(INX, $file) || die "Can't open($file): $!";
586             @line = grep{/"$$self{'_options'}{'domain_name'}"/} ;
587             close INX;
588            
589             open(OUT, "> $file") || die "Can't open(> $file): $!";
590             print OUT $line[0];
591             close OUT;
592            
593             for (qw/ctrl smtp/)
594             {
595             $file = $$self{'_target_dir'} -> file("$_.ipmap.tab");
596            
597             $self -> info("Updating $file");
598             $self -> info('o Convert original line (Allow all) to Deny all');
599             $self -> info("o Add a line to allow admin only from $$self{'_options'}{'server'}");
600            
601             open(OUT, "> $file") || die "Can't open(> $file): $!";
602             print OUT qq|"0.0.0.0"\t"0.0.0.0"\t"DENY"\t1\n|;
603             print OUT qq|"$$self{'_options'}{'server'}"\t"255.255.255.0"\t"ALLOW"\t2\n|;
604             close OUT;
605             }
606            
607             } # End of update_dirs_and_files.
608            
609             # -----------------------------------------------
610            
611             sub update_the_registry
612             {
613             my($self) = @_;
614            
615             $self -> info('Checking for registry keys under HKEY_LOCAL_MACHINE/SOFTWARE/GNU/XMail');
616            
617             my($root) = 'HKEY_LOCAL_MACHINE/SOFTWARE';
618             my($hash) = $$Registry{"$root/"};
619            
620             if (! $$Registry{"$root/GNU"})
621             {
622             $self -> info("Creating key $root/GNU");
623             $hash -> CreateKey('GNU');
624             }
625            
626             $root = "$root/GNU";
627             $hash = $$Registry{"$root/"};
628            
629             if (! $$Registry{"$root/XMail"})
630             {
631             $self -> info("Creating key $root/XMail");
632             $hash -> CreateKey('XMail');
633             }
634            
635             $root = "$root/XMail";
636             $hash = $$Registry{"$root/"};
637            
638             my($result);
639            
640             if ($$Registry{"$root/MAIL_CMD_LINE"})
641             {
642             $result = delete $$Registry{"$root//MAIL_CMD_LINE"};
643            
644             $self -> info("Deleted existing $root/MAIL_CMD_LINE: $result");
645             }
646            
647             my($parameters) = '-Pl -Sl -Ql -Yl -Fl -Cl -Ll';
648            
649             $self -> info("Creating value $root/MAIL_CMD_LINE => $parameters");
650            
651             $result = $hash -> SetValue('MAIL_CMD_LINE', $parameters);
652            
653             if ($$Registry{"$root/MAIL_ROOT"})
654             {
655             $result = delete $$Registry{"$root//MAIL_ROOT"};
656            
657             $self -> info("Deleted existing $root/MAIL_ROOT: $result");
658             }
659            
660             $self -> info("Creating value $root/MAIL_ROOT => $$self{'_target_dir'}");
661            
662             $result = $hash -> SetValue('MAIL_ROOT', $$self{'_target_dir'});
663            
664             $self -> info("Values under $root:");
665             $self -> info("o $_ => " . $hash -> GetValue($_) ) for sort $hash -> ValueNames();
666            
667             } # End of update_the_registry.
668            
669             # -----------------------------------------------
670            
671             sub win32_error
672             {
673             return Win32::FormatMessage(Win32::GetLastError() );
674            
675             } # End of win32_error.
676            
677             # -----------------------------------------------
678            
679             1;
680            
681             =head1 NAME
682            
683             C - A module to install the MS Windows mail server XMail
684            
685             =head1 Synopsis
686            
687             #!/usr/bin/perl
688            
689             use strict;
690             use warnings;
691            
692             use XMail::Install;
693            
694             # -----------------
695            
696             my(%option) = (...);
697            
698             XMail::Install -> new(options => \%option) -> run();
699            
700             See the next section for details.
701            
702             =head1 Description
703            
704             C is a pure Perl module. It only runs under MS Windows.
705            
706             It will read an unpacked distro of the XMail mail server, and install, configure and test it.
707            
708             Also, it will stop and remove the service if it is already running.
709            
710             So, download xmail-1.25.win32bin.zip from http://xmailserver.org/ and unpack it into c:\. This creates c:\xmail-1.25.
711            
712             Then:
713             Unpack the distro.
714             shell>cd examples
715             shell>perl install-xmail-1.pl -h
716             shell>perl install-xmail-1.pl -v -other -options
717             shell>perl install-xmail-2.pl -v -other -options
718            
719             The reason for having 2 install programs is that I could not get 1 to work properly, neither under Win2FK nor WinXFP.
720             Sometimes it would work, and sometimes it would not.
721            
722             =head1 Distributions
723            
724             This module is available as a Unix-style distro (*.tgz).
725            
726             See http://savage.net.au/Perl-modules.html for details.
727            
728             See http://savage.net.au/Perl-modules/html/installing-a-module.html for
729             help on unpacking and installing.
730            
731             =head1 Constructor and initialization
732            
733             new(...) returns an object of type C.
734            
735             This is the class's contructor.
736            
737             Usage: XMail::Install -> new().
738            
739             This method takes a hashref of options. There are no mandatory options.
740            
741             Call C as new(options => {key_1 => value_1, key_2 => value_2, ...}).
742            
743             =over 4
744            
745             =item domain_name
746            
747             This is the name of your mail domain.
748            
749             The default is xmail.net.
750            
751             =item in_dir
752            
753             This is the name of the directory into which you unpacked XMail.
754            
755             The default is c:\xmail-1.25.
756            
757             =item out_dir
758            
759             This is the name of the directory into which XMail's default directory MailRoot will be installed.
760            
761             The default is c:\, so XMail will be installed into c:\MailRoot.
762            
763             Also, executables in the distro dir c:\xmail-1.25\bin will be copied to c:\MailRoot\bin.
764            
765             =item postmaster_password
766            
767             This is the password of the postmaster (admin) account.
768            
769             The default is 'richness-of-martens'.
770            
771             =item server
772            
773             This is the IP address, or name, of the host on which the XMail service will be running.
774            
775             The default is 127.0.0.1.
776            
777             =item user_name
778            
779             This is the name of a user (non-admin) account.
780            
781             The default is 'rsavage'.
782            
783             =item user_password
784            
785             This is the password of the user account.
786            
787             The default is 'skulk-of-foxes'.
788            
789             =item verbose
790            
791             This is the flag which controls the amount of progress messages printed.
792            
793             Values are 0 or 1.
794            
795             The default is 0.
796            
797             =back
798            
799             =head1 Method: copy_dirs_and_files
800            
801             A convenience method which makes the main line code in method C simpler.
802            
803             Actually, except for C and C, all methods in the class are convenience methods.
804            
805             =head1 Method: create_account
806            
807             Update ctrlaccounts.tab with the details of the user 'xmailuser'.
808            
809             =head1 Method: create_domain
810            
811             Use the C C program to create a mail domain.
812            
813             =head1 Method: create_postmaster_account
814            
815             Create C's postmaster account and password.
816            
817             =head1 Method: create_user_account
818            
819             Create an C user account and password.
820            
821             =head1 Method: info
822            
823             Print progress messages, while checking the verbose switch.
824            
825             =head1 Method: install_and_start_service
826            
827             This installs and starts the XMail service.
828            
829             =head1 Method: receive_test_message
830            
831             Receive and print the test message sent by method C.
832            
833             =head1 Method: run
834            
835             Do all the work required to install C.
836            
837             This is achieved by calling all the convenience methods in the class.
838            
839             =head1 Method: send_test_message
840            
841             Send a test message, which will be received by method C.
842            
843             =head1 Method: start_server
844            
845             Start the C program as a process, not as a service.
846            
847             =head1 Method: stop_and_remove_service
848            
849             Stop the C service, and then remove it.
850            
851             =head1 Method: stop_server
852            
853             Stop the C program.
854            
855             =head1 Method: update_dirs_and_files
856            
857             Update various directories and files.
858            
859             =head1 Method: update_the_registry
860            
861             Update the registry, if necessary, being careful to preserve data in the immediate vicinity of the new keys.
862            
863             =head1 Method: win32_error
864            
865             Return the last error available from the OS.
866            
867             =head1 FAQ
868            
869             =over
870            
871             =item Why did you write this module?
872            
873             To explicitly document a minimum set of steps I believe are required to install XMail.
874            
875             This allows to me very simply install XMail on more than one system and, in the same way, it allows anyone
876             to very simply set up a mail server to experiment with.
877            
878             Email me if you have any suggestions regarding the steps I've implemented.
879            
880             =item How secure is C?
881            
882             Well, you'll need to investigate C itself to answer that question. See http://www.xmailserver.org/
883            
884             But we can say mail server security is a complex issue, and installing a mail server should not be done lightly.
885            
886             At the absolute minimum, you should C use the default passwords shipped with this module.
887            
888             =item Why did you use passwords such as 'richness-of-martens' anyway?
889            
890             Firstly, as a way of drawing you attention to the problem of choosing good passwords, and secondly because
891             I like playing with the English language.
892            
893             And yes, 'a richness of martens' is correct English, where martens refers to a type of bird, and richness is the
894             corresponding collective noun. The same goes for 'skulk-of-foxes'.
895            
896             One source of passwords is https://www.grc.com/passwords.htm
897            
898             =item Which versions of C did you test this module against?
899            
900             V 1.22 and V 1.24.
901            
902             =item Is C your primary mail server?
903            
904             No. I use a commercial web hosting company, http://www.quadrahosting.com.au/
905            
906             The way I use C is by restricing the clients which can talk to it to be clients with IP addresses in
907             the ranges 192.168.*.* and 10.*.*.*.
908            
909             =item Why don't you use the module XMail::Ctrl?
910            
911             I examined it, and decided it wasn't quite relevant.
912            
913             =item What's with this word daemon?
914            
915             A daemon is what Microsoft, and others, call a service.
916            
917             See http://en.wikipedia.org/wiki/Daemon_%28computer_software%29 for an explanation.
918            
919             =back
920            
921             =head1 Required Modules
922            
923             =over 4
924            
925             =item Carp
926            
927             =item Email::Send
928            
929             =item File::Copy
930            
931             =item File::Copy::Recursive
932            
933             =item File::Path
934            
935             =item Mail::POP3Client
936            
937             =item Path::Class
938            
939             =item Win32
940            
941             =item Win32::Process
942            
943             =item Win32::Process::List
944            
945             =item Win32::Service
946            
947             =item Win32::TieRegistry
948            
949             =back
950            
951             =head1 Author
952            
953             C was written by Ron Savage in 2007. [ron@savage.net.au]
954            
955             Home page: http://savage.net.au/index.html
956            
957             =head1 Copyright
958            
959             Australian copyright (c) 2007, Ron Savage. All rights reserved.
960            
961             All Programs of mine are 'OSI Certified Open Source Software';
962             you can redistribute them and/or modify them under the terms of
963             The Artistic License, a copy of which is available at:
964             http://www.opensource.org/licenses/index.html
965            
966             =cut