]> git.proxmox.com Git - pmg-api.git/blob - PMG/Config.pm
PVE::Config: remove type ldap (use PVE::LDAPConfig instead)
[pmg-api.git] / PMG / Config.pm
1 package PMG::Config::Base;
2
3 use strict;
4 use warnings;
5 use Data::Dumper;
6
7 use PVE::Tools;
8 use PVE::JSONSchema qw(get_standard_option);
9 use PVE::SectionConfig;
10
11 use base qw(PVE::SectionConfig);
12
13 my $defaultData = {
14 propertyList => {
15 type => { description => "Section type." },
16 section => {
17 description => "Secion ID.",
18 type => 'string', format => 'pve-configid',
19 },
20 },
21 };
22
23 sub private {
24 return $defaultData;
25 }
26
27 sub format_section_header {
28 my ($class, $type, $sectionId) = @_;
29
30 die "internal error ($type ne $sectionId)" if $type ne $sectionId;
31
32 return "section: $type\n";
33 }
34
35
36 sub parse_section_header {
37 my ($class, $line) = @_;
38
39 if ($line =~ m/^section:\s*(\S+)\s*$/) {
40 my $section = $1;
41 my $errmsg = undef; # set if you want to skip whole section
42 eval { PVE::JSONSchema::pve_verify_configid($section); };
43 $errmsg = $@ if $@;
44 my $config = {}; # to return additional attributes
45 return ($section, $section, $errmsg, $config);
46 }
47 return undef;
48 }
49
50 package PMG::Config::Admin;
51
52 use strict;
53 use warnings;
54
55 use base qw(PMG::Config::Base);
56
57 sub type {
58 return 'admin';
59 }
60
61 sub properties {
62 return {
63 dailyreport => {
64 description => "Send daily reports.",
65 type => 'boolean',
66 default => 1,
67 },
68 demo => {
69 description => "Demo mode - do not start SMTP filter.",
70 type => 'boolean',
71 default => 0,
72 },
73 email => {
74 description => "Administrator E-Mail address.",
75 type => 'string', format => 'email',
76 default => 'admin@domain.tld',
77 },
78 proxyport => {
79 description => "HTTP proxy port.",
80 type => 'integer',
81 minimum => 1,
82 default => 8080,
83 },
84 proxyserver => {
85 description => "HTTP proxy server address.",
86 type => 'string',
87 },
88 proxyuser => {
89 description => "HTTP proxy user name.",
90 type => 'string',
91 },
92 proxypassword => {
93 description => "HTTP proxy password.",
94 type => 'string',
95 },
96 };
97 }
98
99 sub options {
100 return {
101 dailyreport => { optional => 1 },
102 demo => { optional => 1 },
103 proxyport => { optional => 1 },
104 proxyserver => { optional => 1 },
105 proxyuser => { optional => 1 },
106 proxypassword => { optional => 1 },
107 };
108 }
109
110 package PMG::Config::Spam;
111
112 use strict;
113 use warnings;
114
115 use base qw(PMG::Config::Base);
116
117 sub type {
118 return 'spam';
119 }
120
121 sub properties {
122 return {
123 languages => {
124 description => "This option is used to specify which languages are considered OK for incoming mail.",
125 type => 'string',
126 pattern => '(all|([a-z][a-z])+( ([a-z][a-z])+)*)',
127 default => 'all',
128 },
129 use_bayes => {
130 description => "Whether to use the naive-Bayesian-style classifier.",
131 type => 'boolean',
132 default => 1,
133 },
134 use_awl => {
135 description => "Use the Auto-Whitelist plugin.",
136 type => 'boolean',
137 default => 1,
138 },
139 use_razor => {
140 description => "Whether to use Razor2, if it is available.",
141 type => 'boolean',
142 default => 1,
143 },
144 use_ocr => {
145 description => "Enable OCR to scan pictures.",
146 type => 'boolean',
147 default => 0,
148 },
149 wl_bounce_relays => {
150 description => "Whitelist legitimate bounce relays.",
151 type => 'string',
152 },
153 bounce_score => {
154 description => "Additional score for bounce mails.",
155 type => 'integer',
156 minimum => 0,
157 maximum => 1000,
158 default => 0,
159 },
160 rbl_checks => {
161 description => "Enable real time blacklists (RBL) checks.",
162 type => 'boolean',
163 default => 1,
164 },
165 maxspamsize => {
166 description => "Maximum size of spam messages in bytes.",
167 type => 'integer',
168 minimum => 64,
169 default => 200*1024,
170 },
171 };
172 }
173
174 sub options {
175 return {
176 use_awl => { optional => 1 },
177 use_razor => { optional => 1 },
178 use_ocr => { optional => 1 },
179 wl_bounce_relays => { optional => 1 },
180 languages => { optional => 1 },
181 use_bayes => { optional => 1 },
182 bounce_score => { optional => 1 },
183 rbl_checks => { optional => 1 },
184 maxspamsize => { optional => 1 },
185 };
186 }
187
188 package PMG::Config::ClamAV;
189
190 use strict;
191 use warnings;
192
193 use base qw(PMG::Config::Base);
194
195 sub type {
196 return 'clamav';
197 }
198
199 sub properties {
200 return {
201 dbmirror => {
202 description => "ClamAV database mirror server.",
203 type => 'string',
204 default => 'database.clamav.net',
205 },
206 archiveblockencrypted => {
207 description => "Wether to block encrypted archives. Mark encrypted archives as viruses.",
208 type => 'boolean',
209 default => 0,
210 },
211 archivemaxrec => {
212 description => "Nested archives are scanned recursively, e.g. if a ZIP archive contains a TAR file, all files within it will also be scanned. This options specifies how deeply the process should be continued. Warning: setting this limit too high may result in severe damage to the system.",
213 type => 'integer',
214 minimum => 1,
215 default => 5,
216 },
217 archivemaxfiles => {
218 description => "Number of files to be scanned within an archive, a document, or any other kind of container. Warning: disabling this limit or setting it too high may result in severe damage to the system.",
219 type => 'integer',
220 minimum => 0,
221 default => 1000,
222 },
223 archivemaxsize => {
224 description => "Files larger than this limit won't be scanned.",
225 type => 'integer',
226 minimum => 1000000,
227 default => 25000000,
228 },
229 maxscansize => {
230 description => "Sets the maximum amount of data to be scanned for each input file.",
231 type => 'integer',
232 minimum => 1000000,
233 default => 100000000,
234 },
235 maxcccount => {
236 description => "This option sets the lowest number of Credit Card or Social Security numbers found in a file to generate a detect.",
237 type => 'integer',
238 minimum => 0,
239 default => 0,
240 },
241 };
242 }
243
244 sub options {
245 return {
246 archiveblockencrypted => { optional => 1 },
247 archivemaxrec => { optional => 1 },
248 archivemaxfiles => { optional => 1 },
249 archivemaxsize => { optional => 1 },
250 maxscansize => { optional => 1 },
251 dbmirror => { optional => 1 },
252 maxcccount => { optional => 1 },
253 };
254 }
255
256 package PMG::Config::Mail;
257
258 use strict;
259 use warnings;
260
261 use PVE::ProcFSTools;
262
263 use base qw(PMG::Config::Base);
264
265 sub type {
266 return 'mail';
267 }
268
269 my $physicalmem = 0;
270 sub physical_memory {
271
272 return $physicalmem if $physicalmem;
273
274 my $info = PVE::ProcFSTools::read_meminfo();
275 my $total = int($info->{memtotal} / (1024*1024));
276
277 return $total;
278 }
279
280 sub get_max_filters {
281 # estimate optimal number of filter servers
282
283 my $max_servers = 5;
284 my $servermem = 120;
285 my $memory = physical_memory();
286 my $add_servers = int(($memory - 512)/$servermem);
287 $max_servers += $add_servers if $add_servers > 0;
288 $max_servers = 40 if $max_servers > 40;
289
290 return $max_servers - 2;
291 }
292
293 sub get_max_smtpd {
294 # estimate optimal number of smtpd daemons
295
296 my $max_servers = 25;
297 my $servermem = 20;
298 my $memory = physical_memory();
299 my $add_servers = int(($memory - 512)/$servermem);
300 $max_servers += $add_servers if $add_servers > 0;
301 $max_servers = 100 if $max_servers > 100;
302 return $max_servers;
303 }
304
305
306 sub properties {
307 return {
308 relay => {
309 description => "The default mail delivery transport (incoming mails).",
310 type => 'string',
311 },
312 relayport => {
313 description => "SMTP port number for relay host.",
314 type => 'integer',
315 minimum => 1,
316 maximum => 65535,
317 default => 25,
318 },
319 relaynomx => {
320 description => "Disable MX lookups for default relay.",
321 type => 'boolean',
322 default => 0,
323 },
324 smarthost => {
325 description => "When set, all outgoing mails are deliverd to the specified smarthost.",
326 type => 'string',
327 },
328 banner => {
329 description => "ESMTP banner.",
330 type => 'string',
331 maxLength => 1024,
332 default => 'ESMTP Proxmox',
333 },
334 max_filters => {
335 description => "Maximum number of filter processes.",
336 type => 'integer',
337 minimum => 3,
338 maximum => 40,
339 default => get_max_filters(),
340 },
341 max_smtpd_in => {
342 description => "Maximum number of SMTP daemon processes (in).",
343 type => 'integer',
344 minimum => 3,
345 maximum => 100,
346 default => get_max_smtpd(),
347 },
348 max_smtpd_out => {
349 description => "Maximum number of SMTP daemon processes (out).",
350 type => 'integer',
351 minimum => 3,
352 maximum => 100,
353 default => get_max_smtpd(),
354 },
355 conn_count_limit => {
356 description => "How many simultaneous connections any client is allowed to make to this service. To disable this feature, specify a limit of 0.",
357 type => 'integer',
358 minimum => 0,
359 default => 50,
360 },
361 conn_rate_limit => {
362 description => "The maximal number of connection attempts any client is allowed to make to this service per minute. To disable this feature, specify a limit of 0.",
363 type => 'integer',
364 minimum => 0,
365 default => 0,
366 },
367 message_rate_limit => {
368 description => "The maximal number of message delivery requests that any client is allowed to make to this service per minute.To disable this feature, specify a limit of 0.",
369 type => 'integer',
370 minimum => 0,
371 default => 0,
372 },
373 hide_received => {
374 description => "Hide received header in outgoing mails.",
375 type => 'boolean',
376 default => 0,
377 },
378 maxsize => {
379 description => "Maximum email size. Larger mails are rejected.",
380 type => 'integer',
381 minimum => 1024,
382 default => 1024*1024*10,
383 },
384 dwarning => {
385 description => "SMTP delay warning time (in hours).",
386 type => 'integer',
387 minimum => 0,
388 default => 4,
389 },
390 use_rbl => {
391 description => "Use Realtime Blacklists.",
392 type => 'boolean',
393 default => 1,
394 },
395 tls => {
396 description => "Use TLS.",
397 type => 'boolean',
398 default => 0,
399 },
400 spf => {
401 description => "Use Sender Policy Framework.",
402 type => 'boolean',
403 default => 1,
404 },
405 greylist => {
406 description => "Use Greylisting.",
407 type => 'boolean',
408 default => 1,
409 },
410 helotests => {
411 description => "Use SMTP HELO tests.",
412 type => 'boolean',
413 default => 0,
414 },
415 rejectunknown => {
416 description => "Reject unknown clients.",
417 type => 'boolean',
418 default => 0,
419 },
420 rejectunknownsender => {
421 description => "Reject unknown senders.",
422 type => 'boolean',
423 default => 0,
424 },
425 verifyreceivers => {
426 description => "Enable receiver verification. The value (if greater than 0) spefifies the numerical reply code when the Postfix SMTP server rejects a recipient address (450 or 550).",
427 type => 'integer',
428 minimum => 0,
429 maximum => 599,
430 default => 0,
431 },
432 dnsbl_sites => {
433 description => "Optional list of DNS white/blacklist domains (see postscreen_dnsbl_sites parameter).",
434 type => 'string',
435 },
436 };
437 }
438
439 sub options {
440 return {
441 relay => { optional => 1 },
442 relayport => { optional => 1 },
443 relaynomx => { optional => 1 },
444 dwarning => { optional => 1 },
445 max_smtpd_in => { optional => 1 },
446 max_smtpd_out => { optional => 1 },
447 greylist => { optional => 1 },
448 helotests => { optional => 1 },
449 use_rbl => { optional => 1 },
450 tls => { optional => 1 },
451 spf => { optional => 1 },
452 maxsize => { optional => 1 },
453 banner => { optional => 1 },
454 max_filters => { optional => 1 },
455 hide_received => { optional => 1 },
456 rejectunknown => { optional => 1 },
457 rejectunknownsender => { optional => 1 },
458 conn_count_limit => { optional => 1 },
459 conn_rate_limit => { optional => 1 },
460 message_rate_limit => { optional => 1 },
461 verifyreceivers => { optional => 1 },
462 dnsbl_sites => { optional => 1 },
463 };
464 }
465 package PMG::Config;
466
467 use strict;
468 use warnings;
469 use IO::File;
470 use Data::Dumper;
471 use Template;
472
473 use PVE::SafeSyslog;
474 use PVE::Tools;
475 use PVE::INotify;
476
477 PMG::Config::Admin->register();
478 PMG::Config::Mail->register();
479 PMG::Config::Spam->register();
480 PMG::Config::ClamAV->register();
481
482 # initialize all plugins
483 PMG::Config::Base->init();
484
485
486 sub new {
487 my ($type) = @_;
488
489 my $class = ref($type) || $type;
490
491 my $cfg = PVE::INotify::read_file("pmg.conf");
492
493 return bless $cfg, $class;
494 }
495
496 sub write {
497 my ($self) = @_;
498
499 PVE::INotify::write_file("pmg.conf", $self);
500 }
501
502 my $lockfile = "/var/lock/pmgconfig.lck";
503
504 sub lock_config {
505 my ($code, $errmsg) = @_;
506
507 my $p = PVE::Tools::lock_file($lockfile, undef, $code);
508 if (my $err = $@) {
509 $errmsg ? die "$errmsg: $err" : die $err;
510 }
511 }
512
513 # set section values
514 sub set {
515 my ($self, $section, $key, $value) = @_;
516
517 my $pdata = PMG::Config::Base->private();
518
519 my $plugin = $pdata->{plugins}->{$section};
520 die "no such section '$section'" if !$plugin;
521
522 if (defined($value)) {
523 my $tmp = PMG::Config::Base->check_value($section, $key, $value, $section, 0);
524 $self->{ids}->{$section} = { type => $section } if !defined($self->{ids}->{$section});
525 $self->{ids}->{$section}->{$key} = PMG::Config::Base->decode_value($section, $key, $tmp);
526 } else {
527 if (defined($self->{ids}->{$section})) {
528 delete $self->{ids}->{$section}->{$key};
529 }
530 }
531
532 return undef;
533 }
534
535 # get section value or default
536 sub get {
537 my ($self, $section, $key) = @_;
538
539 my $pdata = PMG::Config::Base->private();
540 return undef if !defined($pdata->{options}->{$section});
541 return undef if !defined($pdata->{options}->{$section}->{$key});
542 my $pdesc = $pdata->{propertyList}->{$key};
543 return undef if !defined($pdesc);
544
545 if (defined($self->{ids}->{$section}) &&
546 defined(my $value = $self->{ids}->{$section}->{$key})) {
547 return $value;
548 }
549
550 return $pdesc->{default};
551 }
552
553 # get a whole section with default value
554 sub get_section {
555 my ($self, $section) = @_;
556
557 my $pdata = PMG::Config::Base->private();
558 return undef if !defined($pdata->{options}->{$section});
559
560 my $res = {};
561
562 foreach my $key (keys %{$pdata->{options}->{$section}}) {
563
564 my $pdesc = $pdata->{propertyList}->{$key};
565
566 if (defined($self->{ids}->{$section}) &&
567 defined(my $value = $self->{ids}->{$section}->{$key})) {
568 $res->{$key} = $value;
569 next;
570 }
571 $res->{$key} = $pdesc->{default};
572 }
573
574 return $res;
575 }
576
577 # get a whole config with default values
578 sub get_config {
579 my ($self) = @_;
580
581 my $pdata = PMG::Config::Base->private();
582
583 my $res = {};
584
585 foreach my $type (keys %{$pdata->{plugins}}) {
586 my $plugin = $pdata->{plugins}->{$type};
587 $res->{$type} = $self->get_section($type);
588 }
589
590 return $res;
591 }
592
593 sub read_pmg_conf {
594 my ($filename, $fh) = @_;
595
596 local $/ = undef; # slurp mode
597
598 my $raw = <$fh>;
599
600 return PMG::Config::Base->parse_config($filename, $raw);
601 }
602
603 sub write_pmg_conf {
604 my ($filename, $fh, $cfg) = @_;
605
606 my $raw = PMG::Config::Base->write_config($filename, $cfg);
607
608 PVE::Tools::safe_print($filename, $fh, $raw);
609 }
610
611 PVE::INotify::register_file('pmg.conf', "/etc/proxmox/pmg.conf",
612 \&read_pmg_conf,
613 \&write_pmg_conf);
614
615 # parsers/writers for other files
616
617 my $domainsfilename = "/etc/proxmox/domains";
618
619 sub read_pmg_domains {
620 my ($filename, $fh) = @_;
621
622 my $domains = [];
623
624 if (defined($fh)) {
625 while (defined(my $line = <$fh>)) {
626 if ($line =~ m/^\s*(\S+)\s*$/) {
627 my $domain = $1;
628 push @$domains, $domain;
629 }
630 }
631 }
632
633 return $domains;
634 }
635
636 sub write_pmg_domains {
637 my ($filename, $fh, $domain) = @_;
638
639 foreach my $domain (sort @$domain) {
640 PVE::Tools::safe_print($filename, $fh, "$domain\n");
641 }
642 }
643
644 PVE::INotify::register_file('domains', $domainsfilename,
645 \&read_pmg_domains,
646 \&write_pmg_domains,
647 undef, always_call_parser => 1);
648
649 my $transport_map_filename = "/etc/postfix/transport";
650
651 sub read_transport_map {
652 my ($filename, $fh) = @_;
653
654 return [] if !defined($fh);
655
656 my $res = {};
657
658 while (defined(my $line = <$fh>)) {
659 chomp $line;
660 next if $line =~ m/^\s*$/;
661 next if $line =~ m/^\s*\#/;
662
663 if ($line =~ m/^(\S+)\s+smtp:([^\s:]+):(\d+)\s*$/) {
664 my $domain = $1;
665 my $host = $2;
666 my $port =$3;
667 my $nomx;
668
669 if ($host =~ m/^\[(.*)\]$/) {
670 $host = $1;
671 $nomx = 1;
672 }
673
674 my $key = "$host:$port";
675
676 $res->{$key}->{nomx} = $nomx;
677 $res->{$key}->{host} = $host;
678 $res->{$key}->{port} = $port;
679 $res->{$key}->{transport} = $key;
680
681 push @{$res->{$key}->{domains}}, $domain;
682 }
683 }
684
685 my $ta = [];
686
687 foreach my $t (sort keys %$res) {
688 push @$ta, $res->{$t};
689 }
690
691 return $ta;
692 }
693
694 sub write_ransport_map {
695 my ($filename, $fh, $tmap) = @_;
696
697 return if !$tmap;
698
699 foreach my $t (sort { $a->{transport} cmp $b->{transport} } @$tmap) {
700 my $domains = $t->{domains};
701
702 foreach my $d (sort @$domains) {
703 if ($t->{nomx}) {
704 PVE::Tools::safe_print($filename, $fh, "$d smtp:[$t->{host}]:$t->{port}\n");
705 } else {
706 PVE::Tools::safe_print($filename, $fh, "$d smtp:$t->{host}:$t->{port}\n");
707 }
708 }
709 }
710 }
711
712 PVE::INotify::register_file('transport', $transport_map_filename,
713 \&read_transport_map,
714 \&write_ransport_map,
715 undef, always_call_parser => 1);
716
717 # config file generation using templates
718
719 sub get_template_vars {
720 my ($self) = @_;
721
722 my $vars = { pmg => $self->get_config() };
723
724 my $nodename = PVE::INotify::nodename();
725 my $int_ip = PMG::Cluster::remote_node_ip($nodename);
726 my $int_net_cidr = PMG::Utils::find_local_network_for_ip($int_ip);
727
728 $vars->{ipconfig}->{int_ip} = $int_ip;
729 # $vars->{ipconfig}->{int_net_cidr} = $int_net_cidr;
730 $vars->{ipconfig}->{int_port} = 26;
731 $vars->{ipconfig}->{ext_port} = 25;
732
733 my $transportnets = []; # fixme
734 $vars->{postfix}->{transportnets} = join(' ', @$transportnets);
735
736 my $mynetworks = [ '127.0.0.0/8', '[::1]/128' ];
737 push @$mynetworks, @$transportnets;
738 push @$mynetworks, $int_net_cidr;
739
740 # add default relay to mynetworks
741 if (my $relay = $self->get('mail', 'relay')) {
742 if (Net::IP::ip_is_ipv4($relay)) {
743 push @$mynetworks, "$relay/32";
744 } elsif (Net::IP::ip_is_ipv6($relay)) {
745 push @$mynetworks, "[$relay]/128";
746 } else {
747 warn "unable to detect IP version of relay '$relay'";
748 }
749 }
750
751 $vars->{postfix}->{mynetworks} = join(' ', @$mynetworks);
752
753 my $usepolicy = 0;
754 $usepolicy = 1 if $self->get('mail', 'greylist') ||
755 $self->get('mail', 'spf') || $self->get('mail', 'use_rbl');
756 $vars->{postfix}->{usepolicy} = $usepolicy;
757
758 my $resolv = PVE::INotify::read_file('resolvconf');
759 $vars->{dns}->{hostname} = $nodename;
760 $vars->{dns}->{domain} = $resolv->{search};
761
762 return $vars;
763 }
764
765 # rewrite file from template
766 # return true if file has changed
767 sub rewrite_config_file {
768 my ($self, $tmplname, $dstfn) = @_;
769
770 my $demo = $self->get('admin', 'demo');
771
772 my $srcfn = ($tmplname =~ m|^.?/|) ?
773 $tmplname : "/var/lib/pmg/templates/$tmplname";
774
775 if ($demo) {
776 my $demosrc = "$srcfn.demo";
777 $srcfn = $demosrc if -f $demosrc;
778 }
779
780 my ($perm, $uid, $gid);
781
782 my $srcfd = IO::File->new ($srcfn, "r")
783 || die "cant read template '$srcfn' - $!: ERROR";
784
785 if ($dstfn eq '/etc/fetchmailrc') {
786 (undef, undef, $uid, $gid) = getpwnam('fetchmail');
787 $perm = 0600;
788 } elsif ($dstfn eq '/etc/clamav/freshclam.conf') {
789 # needed if file contains a HTTPProxyPasswort
790
791 $uid = getpwnam('clamav');
792 $gid = getgrnam('adm');
793 $perm = 0600;
794 }
795
796 my $template = Template->new({});
797
798 my $vars = $self->get_template_vars();
799
800 my $output = '';
801
802 $template->process($srcfd, $vars, \$output) ||
803 die $template->error();
804
805 $srcfd->close();
806
807 my $old = PVE::Tools::file_get_contents($dstfn, 128*1024) if -f $dstfn;
808
809 return 0 if defined($old) && ($old eq $output); # no change
810
811 PVE::Tools::file_set_contents($dstfn, $output, $perm);
812
813 if (defined($uid) && defined($gid)) {
814 chown($uid, $gid, $dstfn);
815 }
816
817 return 1;
818 }
819
820 # rewrite spam configuration
821 sub rewrite_config_spam {
822 my ($self) = @_;
823
824 my $use_awl = $self->get('spam', 'use_awl');
825 my $use_bayes = $self->get('spam', 'use_bayes');
826 my $use_razor = $self->get('spam', 'use_razor');
827
828 my $changes = 0;
829
830 # delete AW and bayes databases if those features are disabled
831 if (!$use_awl) {
832 $changes = 1 if unlink '/root/.spamassassin/auto-whitelist';
833 }
834
835 if (!$use_bayes) {
836 $changes = 1 if unlink '/root/.spamassassin/bayes_journal';
837 $changes = 1 if unlink '/root/.spamassassin/bayes_seen';
838 $changes = 1 if unlink '/root/.spamassassin/bayes_toks';
839 }
840
841 # make sure we have a custom.cf file (else cluster sync fails)
842 IO::File->new('/etc/mail/spamassassin/custom.cf', 'a', 0644);
843
844 $changes = 1 if $self->rewrite_config_file(
845 'local.cf.in', '/etc/mail/spamassassin/local.cf');
846
847 $changes = 1 if $self->rewrite_config_file(
848 'init.pre.in', '/etc/mail/spamassassin/init.pre');
849
850 $changes = 1 if $self->rewrite_config_file(
851 'v310.pre.in', '/etc/mail/spamassassin/v310.pre');
852
853 $changes = 1 if $self->rewrite_config_file(
854 'v320.pre.in', '/etc/mail/spamassassin/v320.pre');
855
856 if ($use_razor) {
857 mkdir "/root/.razor";
858
859 $changes = 1 if $self->rewrite_config_file(
860 'razor-agent.conf.in', '/root/.razor/razor-agent.conf');
861
862 if (! -e '/root/.razor/identity') {
863 eval {
864 my $timeout = 30;
865 PVE::Tools::run_command(['razor-admin', '-discover'], timeout => $timeout);
866 PVE::Tools::run_command(['razor-admin', '-register'], timeout => $timeout);
867 };
868 my $err = $@;
869 syslog('info', msgquote ("registering razor failed: $err")) if $err;
870 }
871 }
872
873 return $changes;
874 }
875
876 # rewrite ClamAV configuration
877 sub rewrite_config_clam {
878 my ($self) = @_;
879
880 return $self->rewrite_config_file(
881 'clamd.conf.in', '/etc/clamav/clamd.conf');
882 }
883
884 sub rewrite_config_freshclam {
885 my ($self) = @_;
886
887 return $self->rewrite_config_file(
888 'freshclam.conf.in', '/etc/clamav/freshclam.conf');
889 }
890
891 sub rewrite_config_postgres {
892 my ($self) = @_;
893
894 my $pgconfdir = "/etc/postgresql/9.6/main";
895
896 my $changes = 0;
897
898 $changes = 1 if $self->rewrite_config_file(
899 'pg_hba.conf.in', "$pgconfdir/pg_hba.conf");
900
901 $changes = 1 if $self->rewrite_config_file(
902 'postgresql.conf.in', "$pgconfdir/postgresql.conf");
903
904 return $changes;
905 }
906
907 # rewrite /root/.forward
908 sub rewrite_dot_forward {
909 my ($self) = @_;
910
911 my $dstfn = '/root/.forward';
912
913 my $email = $self->get('administration', 'email');
914
915 my $output = '';
916 if ($email && $email =~ m/\s*(\S+)\s*/) {
917 $output = "$1\n";
918 } else {
919 # empty .forward does not forward mails (see man local)
920 }
921
922 my $old = PVE::Tools::file_get_contents($dstfn, 128*1024) if -f $dstfn;
923
924 return 0 if defined($old) && ($old eq $output); # no change
925
926 PVE::Tools::file_set_contents($dstfn, $output);
927
928 return 1;
929 }
930
931 # rewrite /etc/postfix/*
932 sub rewrite_config_postfix {
933 my ($self) = @_;
934
935 # make sure we have required files (else postfix start fails)
936 IO::File->new($domainsfilename, 'a', 0644);
937 IO::File->new($transport_map_filename, 'a', 0644);
938
939 my $changes = 0;
940
941 if ($self->get('mail', 'tls')) {
942 eval {
943 my $resolv = PVE::INotify::read_file('resolvconf');
944 my $domain = $resolv->{search};
945
946 my $company = $domain; # what else ?
947 my $cn = "*.$domain";
948 PMG::Utils::gen_proxmox_tls_cert(0, $company, $cn);
949 };
950 syslog ('info', msgquote ("generating certificate failed: $@")) if $@;
951 }
952
953 $changes = 1 if $self->rewrite_config_file(
954 'main.cf.in', '/etc/postfix/main.cf');
955
956 $changes = 1 if $self->rewrite_config_file(
957 'master.cf.in', '/etc/postfix/master.cf');
958
959 #rewrite_config_transports ($class);
960 #rewrite_config_whitelist ($class);
961 #rewrite_config_tls_policy ($class);
962
963 # make sure aliases.db is up to date
964 system('/usr/bin/newaliases');
965
966 return $changes;
967 }
968
969 sub rewrite_config {
970 my ($self, $restart_services) = @_;
971
972 if ($self->rewrite_config_postfix() && $restart_services) {
973 PMG::Utils::service_cmd('postfix', 'restart');
974 }
975
976 if ($self->rewrite_dot_forward() && $restart_services) {
977 # no need to restart anything
978 }
979
980 if ($self->rewrite_config_postgres() && $restart_services) {
981 # do nothing (too many side effects)?
982 # does not happen anyways, because config does not change.
983 }
984
985 if ($self->rewrite_config_spam() && $restart_services) {
986 PMG::Utils::service_cmd('pmg-smtp-filter', 'restart');
987 }
988
989 if ($self->rewrite_config_clam() && $restart_services) {
990 PMG::Utils::service_cmd('clamd', 'restart');
991 }
992
993 if ($self->rewrite_config_freshclam() && $restart_services) {
994 PMG::Utils::service_cmd('freshclam', 'restart');
995 }
996
997 }
998
999 1;