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