]> git.proxmox.com Git - pmg-api.git/blame - PMG/DBTools.pm
cleanup: move more helpers to ObjectGroupHelpers.pm
[pmg-api.git] / PMG / DBTools.pm
CommitLineData
a355f100
DM
1package PMG::DBTools;
2
3use strict;
4use warnings;
5
6use POSIX ":sys_wait_h";
7use POSIX ':signal_h';
8use DBI;
9
10use PVE::Tools;
11
0a580593
DM
12use PMG::RuleDB;
13
a355f100
DM
14sub open_ruledb {
15 my ($database, $host, $port) = @_;
16
17 $port = 5432 if !$port;
18
19 $database = "Proxmox_ruledb" if !$database;
20
21 if ($host) {
22
23 my $dsn = "dbi:Pg:dbname=$database;host=$host;port=$port;";
24
25 my $timeout = 5;
26 # only low level alarm interface works for DBI->connect
27 my $mask = POSIX::SigSet->new(SIGALRM);
28 my $action = POSIX::SigAction->new(sub { die "connect timeout\n" }, $mask);
29 my $oldaction = POSIX::SigAction->new();
30 sigaction(SIGALRM, $action, $oldaction);
31
32 my $rdb;
33
34 eval {
35 alarm($timeout);
36 $rdb = DBI->connect($dsn, "postgres", undef,
37 { PrintError => 0, RaiseError => 1 });
38 alarm(0);
39 };
40 alarm(0);
41 sigaction(SIGALRM, $oldaction); # restore original handler
f1bf78ff 42
a355f100
DM
43 die $@ if $@;
44
45 return $rdb;
46 } else {
47 my $dsn = "DBI:Pg:dbname=$database";
48
f1bf78ff 49 my $dbh = DBI->connect($dsn, "postgres", undef,
a355f100
DM
50 { PrintError => 0, RaiseError => 1 });
51
52 return $dbh;
53 }
54}
55
56sub delete_ruledb {
57 my ($dbname) = @_;
58
59 PVE::Tools::run_command(['dropdb', '-U', 'postgres', $dbname]);
60}
61
f1bf78ff
DM
62sub database_list {
63
64 my $database_list = {};
65
66 my $parser = sub {
67 my $line = shift;
68
69 my ($name, $owner) = map { PVE::Tools::trim($_) } split(/\|/, $line);
70 return if !$name || !$owner;
71
72 $database_list->{$name} = { owner => $owner };
73 };
74
75 my $cmd = ['psql', '-U', 'postgres', '--list', '--quiet', '--tuples-only'];
76
77 PVE::Tools::run_command($cmd, outfunc => $parser);
78
79 return $database_list;
80}
81
82my $dbfunction_maxint = <<__EOD;
83 CREATE OR REPLACE FUNCTION maxint (INTEGER, INTEGER) RETURNS INTEGER AS
84 'BEGIN IF \$1 > \$2 THEN RETURN \$1; ELSE RETURN \$2; END IF; END;' LANGUAGE plpgsql;
85__EOD
86
87my $dbfunction_minint = <<__EOD;
88 CREATE OR REPLACE FUNCTION minint (INTEGER, INTEGER) RETURNS INTEGER AS
89 'BEGIN IF \$1 < \$2 THEN RETURN \$1; ELSE RETURN \$2; END IF; END;' LANGUAGE plpgsql;
90__EOD
91
92# merge function to avoid update/insert race condition
93# see: http://www.postgresql.org/docs/9.1/static/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING
94my $dbfunction_merge_greylist = <<__EOD;
95 CREATE OR REPLACE FUNCTION merge_greylist (in_ipnet VARCHAR, in_host INTEGER, in_sender VARCHAR,
96 in_receiver VARCHAR, in_instance VARCHAR,
97 in_rctime INTEGER, in_extime INTEGER, in_delay INTEGER,
98 in_blocked INTEGER, in_passed INTEGER, in_mtime INTEGER,
99 in_cid INTEGER) RETURNS INTEGER AS
100 'BEGIN
101 LOOP
102 UPDATE CGreylist SET Host = CASE WHEN MTime >= in_mtime THEN Host ELSE in_host END,
103 CID = maxint (CID, in_cid), RCTime = minint (rctime, in_rctime),
104 ExTime = maxint (extime, in_extime),
105 Delay = maxint (delay, in_delay),
106 Blocked = maxint (blocked, in_blocked),
107 Passed = maxint (passed, in_passed)
108 WHERE IPNet = in_ipnet AND Sender = in_sender AND Receiver = in_receiver;
109
110 IF found THEN
111 RETURN 0;
112 END IF;
113
114 BEGIN
115 INSERT INTO CGREYLIST (IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, MTime, CID)
116 VALUES (in_ipnet, in_host, in_sender, in_receiver, in_instance, in_rctime, in_extime,
117 in_delay, in_blocked, in_passed, in_mtime, in_cid);
118 RETURN 1;
119 EXCEPTION WHEN unique_violation THEN
120 -- do nothing - continue loop
121 END;
122 END LOOP;
123 END;' LANGUAGE plpgsql;
124__EOD
125
126my $cgreylist_ctablecmd = <<__EOD;
127 CREATE TABLE CGreylist
128 (IPNet VARCHAR(16) NOT NULL,
129 Host INTEGER NOT NULL,
130 Sender VARCHAR(255) NOT NULL,
131 Receiver VARCHAR(255) NOT NULL,
132 Instance VARCHAR(255),
133 RCTime INTEGER NOT NULL,
134 ExTime INTEGER NOT NULL,
135 Delay INTEGER NOT NULL DEFAULT 0,
136 Blocked INTEGER NOT NULL,
137 Passed INTEGER NOT NULL,
138 CID INTEGER NOT NULL,
139 MTime INTEGER NOT NULL,
140 PRIMARY KEY (IPNet, Sender, Receiver));
141
142 CREATE INDEX CGreylist_Instance_Sender_Index ON CGreylist (Instance, Sender);
143
144 CREATE INDEX CGreylist_ExTime_Index ON CGreylist (ExTime);
145
146 CREATE INDEX CGreylist_MTime_Index ON CGreylist (MTime);
147__EOD
148
149my $clusterinfo_ctablecmd = <<__EOD;
150 CREATE TABLE ClusterInfo
151 (CID INTEGER NOT NULL,
152 Name VARCHAR NOT NULL,
153 IValue INTEGER,
154 SValue VARCHAR,
155 PRIMARY KEY (CID, Name))
156__EOD
157
158my $daily_stat_ctablecmd = <<__EOD;
159 CREATE TABLE DailyStat
160 (Time INTEGER NOT NULL UNIQUE,
161 CountIn INTEGER NOT NULL,
162 CountOut INTEGER NOT NULL,
163 BytesIn REAL NOT NULL,
164 BytesOut REAL NOT NULL,
165 VirusIn INTEGER NOT NULL,
166 VirusOut INTEGER NOT NULL,
167 SpamIn INTEGER NOT NULL,
168 SpamOut INTEGER NOT NULL,
169 BouncesIn INTEGER NOT NULL,
170 BouncesOut INTEGER NOT NULL,
171 GreylistCount INTEGER NOT NULL,
172 SPFCount INTEGER NOT NULL,
173 PTimeSum REAL NOT NULL,
174 MTime INTEGER NOT NULL,
175 RBLCount INTEGER DEFAULT 0 NOT NULL,
176 PRIMARY KEY (Time));
177
178 CREATE INDEX DailyStat_MTime_Index ON DailyStat (MTime);
179
180__EOD
181
182my $domain_stat_ctablecmd = <<__EOD;
183 CREATE TABLE DomainStat
184 (Time INTEGER NOT NULL,
185 Domain VARCHAR(255) NOT NULL,
186 CountIn INTEGER NOT NULL,
187 CountOut INTEGER NOT NULL,
188 BytesIn REAL NOT NULL,
189 BytesOut REAL NOT NULL,
190 VirusIn INTEGER NOT NULL,
191 VirusOut INTEGER NOT NULL,
192 SpamIn INTEGER NOT NULL,
193 SpamOut INTEGER NOT NULL,
194 BouncesIn INTEGER NOT NULL,
195 BouncesOut INTEGER NOT NULL,
196 PTimeSum REAL NOT NULL,
197 MTime INTEGER NOT NULL,
198 PRIMARY KEY (Time, Domain));
199
200 CREATE INDEX DomainStat_MTime_Index ON DomainStat (MTime);
201__EOD
202
203my $statinfo_ctablecmd = <<__EOD;
204 CREATE TABLE StatInfo
205 (Name VARCHAR(255) NOT NULL UNIQUE,
206 IValue INTEGER,
207 SValue VARCHAR(255),
208 PRIMARY KEY (Name))
209__EOD
210
211my $virusinfo_stat_ctablecmd = <<__EOD;
212 CREATE TABLE VirusInfo
213 (Time INTEGER NOT NULL,
214 Name VARCHAR NOT NULL,
215 Count INTEGER NOT NULL,
216 MTime INTEGER NOT NULL,
217 PRIMARY KEY (Time, Name));
218
219 CREATE INDEX VirusInfo_MTime_Index ON VirusInfo (MTime);
220
221__EOD
222
223# mail storage stable
224# QTypes
225# V - Virus quarantine
226# S - Spam quarantine
227# D - Delayed Mails - not implemented
228# A - Held for Audit - not implemented
229# Status
230# N - new
231# D - deleted
232
233my $cmailstore_ctablecmd = <<__EOD;
234 CREATE TABLE CMailStore
235 (CID INTEGER DEFAULT 0 NOT NULL,
236 RID INTEGER NOT NULL,
237 ID SERIAL UNIQUE,
238 Time INTEGER NOT NULL,
239 QType "char" NOT NULL,
240 Bytes INTEGER NOT NULL,
241 Spamlevel INTEGER NOT NULL,
242 Info VARCHAR NULL,
243 Sender VARCHAR(255) NOT NULL,
244 Header VARCHAR NOT NULL,
245 File VARCHAR(255) NOT NULL,
246 PRIMARY KEY (CID, RID));
247 CREATE INDEX CMailStore_Time_Index ON CMailStore (Time);
248
249 CREATE TABLE CMSReceivers
250 (CMailStore_CID INTEGER NOT NULL,
251 CMailStore_RID INTEGER NOT NULL,
252 PMail VARCHAR(255) NOT NULL,
253 Receiver VARCHAR(255),
254 TicketID INTEGER NOT NULL,
255 Status "char" NOT NULL,
256 MTime INTEGER NOT NULL);
257
258 CREATE INDEX CMailStore_ID_Index ON CMSReceivers (CMailStore_CID, CMailStore_RID);
259
260 CREATE INDEX CMSReceivers_MTime_Index ON CMSReceivers (MTime);
261
262__EOD
263
264my $cstatistic_ctablecmd = <<__EOD;
265 CREATE TABLE CStatistic
266 (CID INTEGER DEFAULT 0 NOT NULL,
267 RID INTEGER NOT NULL,
268 ID SERIAL UNIQUE,
269 Time INTEGER NOT NULL,
270 Bytes INTEGER NOT NULL,
271 Direction Boolean NOT NULL,
272 Spamlevel INTEGER NOT NULL,
273 VirusInfo VARCHAR(255) NULL,
274 PTime INTEGER NOT NULL,
275 Sender VARCHAR(255) NOT NULL,
276 PRIMARY KEY (CID, RID));
277
278 CREATE INDEX CStatistic_Time_Index ON CStatistic (Time);
279
280 CREATE TABLE CReceivers
281 (CStatistic_CID INTEGER NOT NULL,
282 CStatistic_RID INTEGER NOT NULL,
283 Receiver VARCHAR(255) NOT NULL,
284 Blocked Boolean NOT NULL);
285
286 CREATE INDEX CStatistic_ID_Index ON CReceivers (CStatistic_CID, CStatistic_RID);
287__EOD
288
289# user preferences (black an whitelists, ...)
290# Name: perference name ('BL' -> blacklist, 'WL' -> whitelist)
291# Data: arbitrary data
292my $userprefs_ctablecmd = <<__EOD;
293 CREATE TABLE UserPrefs
294 (PMail VARCHAR,
295 Name VARCHAR(255),
296 Data VARCHAR,
297 MTime INTEGER NOT NULL,
298 PRIMARY KEY (PMail, Name));
299
300 CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime);
301
302__EOD
758c7b6b 303
0a580593
DM
304sub cond_create_dbtable {
305 my ($dbh, $name, $ctablecmd) = @_;
306
307 eval {
308 $dbh->begin_work;
309
310 my $cmd = "SELECT tablename FROM pg_tables " .
311 "WHERE tablename = lower ('$name')";
312
313 my $sth = $dbh->prepare ($cmd);
758c7b6b 314
0a580593
DM
315 $sth->execute();
316
317 if (!(my $ref = $sth->fetchrow_hashref())) {
318 $dbh->do ($ctablecmd);
319 }
758c7b6b 320
0a580593
DM
321 $sth->finish();
322
323 $dbh->commit;
324 };
325 if (my $err = $@) {
326 $dbh->rollback;
9ef3f143 327 die $err;
0a580593
DM
328 }
329}
f1bf78ff
DM
330
331sub create_ruledb {
332 my ($dbname) = @_;
333
334 $dbname = "Proxmox_ruledb" if !$dbname;
335
336 # use sql_ascii to avoid any character set conversions, and be compatible with
337 # older postgres versions (update from 8.1 must be possible)
338 my $cmd = [ 'createdb', '-U', 'postgres', '-E', 'sql_ascii',
339 '-T', 'template0', '--lc-collate=C', '--lc-ctype=C', $dbname ];
340
341 PVE::Tools::run_command($cmd);
342
343 my $dbh = open_ruledb($dbname);
344
345 #$dbh->do ($dbloaddrivers_sql);
346 #$dbh->do ($dbfunction_update_modtime);
347
348 $dbh->do ($dbfunction_minint);
349
350 $dbh->do ($dbfunction_maxint);
351
352 $dbh->do ($dbfunction_merge_greylist);
353
354 $dbh->do (
355<<EOD
356 CREATE TABLE Attribut
357 (Object_ID INTEGER NOT NULL,
358 Name VARCHAR(20) NOT NULL,
359 Value BYTEA NULL,
360 PRIMARY KEY (Object_ID, Name));
361
362 CREATE INDEX Attribut_Object_ID_Index ON Attribut(Object_ID);
363
364 CREATE TABLE Object
365 (ID SERIAL UNIQUE,
366 ObjectType INTEGER NOT NULL,
367 Objectgroup_ID INTEGER NOT NULL,
368 Value BYTEA NULL,
369 PRIMARY KEY (ID));
370
371 CREATE TABLE Objectgroup
372 (ID SERIAL UNIQUE,
373 Name VARCHAR(255) NOT NULL,
374 Info VARCHAR(255) NULL,
375 Class VARCHAR(10) NOT NULL,
376 PRIMARY KEY (ID));
377
378 CREATE TABLE Rule
379 (ID SERIAL UNIQUE,
380 Name VARCHAR(255) NULL,
381 Priority INTEGER NOT NULL,
382 Active INTEGER NOT NULL DEFAULT 0,
383 Direction INTEGER NOT NULL DEFAULT 2,
384 Count INTEGER NOT NULL DEFAULT 0,
385 PRIMARY KEY (ID));
386
387 CREATE TABLE RuleGroup
388 (Objectgroup_ID INTEGER NOT NULL,
389 Rule_ID INTEGER NOT NULL,
390 Grouptype INTEGER NOT NULL,
391 PRIMARY KEY (Objectgroup_ID, Rule_ID, Grouptype));
392
393 $cgreylist_ctablecmd;
394
395 $clusterinfo_ctablecmd;
396
397 $daily_stat_ctablecmd;
398
399 $domain_stat_ctablecmd;
400
401 $statinfo_ctablecmd;
402
403 $cmailstore_ctablecmd;
404
405 $cstatistic_ctablecmd;
406
407 $userprefs_ctablecmd;
408
409 $virusinfo_stat_ctablecmd;
410EOD
411 );
412
413 return $dbh;
414}
415
0a580593
DM
416sub cond_create_action_quarantine {
417 my ($ruledb) = @_;
418
419 my $dbh = $ruledb->{dbh};
420
421 eval {
422 my $sth = $dbh->prepare(
423 "SELECT * FROM Objectgroup, Object " .
424 "WHERE Object.ObjectType = ? AND Objectgroup.Class = ? " .
425 "AND Object.objectgroup_id = Objectgroup.id");
426
427 my $otype = PMG::RuleDB::Quarantine::otype();
428 if ($sth->execute($otype, 'action') <= 0) {
429 my $obj = PMG::RuleDB::Quarantine->new ();
430 my $txt = decode_entities(PMG::RuleDB::Quarantine->otype_text);
431 my $quarantine = $ruledb->create_group_with_obj
432 ($obj, $txt, PMG::RuleDB::Quarantine->oinfo);
433 }
434 };
435}
436
437sub cond_create_std_actions {
438 my ($ruledb) = @_;
439
440 cond_create_action_quarantine($ruledb);
441
442 #cond_create_action_report_spam($ruledb);
443}
444
445
e7c865af
DM
446sub upgrade_mailstore_db {
447 my ($dbh) = @_;
448
449 eval {
450 $dbh->begin_work;
451
452 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('MailStore')";
453
454 my $sth = $dbh->prepare($cmd);
455 $sth->execute();
456 my $ref = $sth->fetchrow_hashref();
457 $sth->finish();
458
459 if ($ref) { # table exists
460
461 $cmd = "INSERT INTO CMailStore " .
462 "(CID, RID, ID, Time, QType, Bytes, Spamlevel, Info, Header, Sender, File) " .
463 "SELECT 0, ID, ID, Time, QType, Bytes, Spamlevel, Info, Header, Sender, File FROM MailStore";
464
465 $dbh->do($cmd);
466
467 $cmd = "INSERT INTO CMSReceivers " .
468 "(CMailStore_CID, CMailStore_RID, PMail, Receiver, TicketID, Status, MTime) " .
469 "SELECT 0, MailStore_ID, PMail, Receiver, TicketID, Status, 0 FROM MSReceivers";
470
471 $dbh->do($cmd);
472
473 $dbh->do("SELECT setval ('cmailstore_id_seq', nextval ('mailstore_id_seq'))");
474
475 $dbh->do("DROP TABLE MailStore");
476 $dbh->do("DROP TABLE MSReceivers");
477 }
478
479 $dbh->commit;
480 };
481 if (my $err = $@) {
482 $dbh->rollback;
483 die $err;
484 }
485}
486
487sub upgrade_dailystat_db {
488 my ($dbh) = @_;
489
490 eval { # make sure we have MTime
491 $dbh->do("ALTER TABLE DailyStat ADD COLUMN MTime INTEGER;" .
492 "UPDATE DailyStat SET MTime = EXTRACT (EPOCH FROM now());");
493 };
494
495 eval { # make sure we have correct constraints for MTime
496 $dbh->do ("ALTER TABLE DailyStat ALTER COLUMN MTime SET NOT NULL;");
497 };
498
499 eval { # make sure we have RBLCount
500 $dbh->do ("ALTER TABLE DailyStat ADD COLUMN RBLCount INTEGER;" .
501 "UPDATE DailyStat SET RBLCount = 0;");
502 };
503
504 eval { # make sure we have correct constraints for RBLCount
505 $dbh->do ("ALTER TABLE DailyStat ALTER COLUMN RBLCount SET DEFAULT 0;" .
506 "ALTER TABLE DailyStat ALTER COLUMN RBLCount SET NOT NULL;");
507 };
508
509 eval {
510 $dbh->begin_work;
511
512 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('DailyStat_MTime_Index')";
513
514 my $sth = $dbh->prepare($cmd);
515 $sth->execute();
516 my $ref = $sth->fetchrow_hashref();
517 $sth->finish();
518
519 if (!$ref) { # index does not exist
520 $dbh->do ("CREATE INDEX DailyStat_MTime_Index ON DailyStat (MTime)");
521 }
522
523 $dbh->commit;
524 };
525 if (my $err = $@) {
526 $dbh->rollback;
527 die $err;
528 }
529}
530
531sub upgrade_domainstat_db {
532 my ($dbh) = @_;
533
534 eval { # make sure we have MTime
535 $dbh->do("ALTER TABLE DomainStat ADD COLUMN MTime INTEGER;" .
536 "UPDATE DomainStat SET MTime = EXTRACT (EPOCH FROM now());" .
537 "ALTER TABLE DomainStat ALTER COLUMN MTime SET NOT NULL;");
538 };
539
540 eval {
541 $dbh->begin_work;
542
543 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('DomainStat_MTime_Index')";
544
545 my $sth = $dbh->prepare($cmd);
546 $sth->execute();
547 my $ref = $sth->fetchrow_hashref();
548 $sth->finish();
549
550 if (!$ref) { # index does not exist
551 $dbh->do ("CREATE INDEX DomainStat_MTime_Index ON DomainStat (MTime)");
552 }
553
554 $dbh->commit;
555 };
556 if (my $err = $@) {
557 $dbh->rollback;
558 die $@;
559 }
560}
561
562sub upgrade_statistic_db {
563 my ($dbh) = @_;
564
565 eval {
566 $dbh->begin_work;
567
568 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('Statistic')";
569
570 my $sth = $dbh->prepare($cmd);
571 $sth->execute();
572 my $ref = $sth->fetchrow_hashref();
573 $sth->finish();
574
575 if ($ref) { # old table exists
576
577 my $timezone = tz_local_offset();;
578
579 $dbh->do("INSERT INTO VirusInfo (Time, Name, Count, MTime) " .
580 "SELECT ((time + $timezone) / 86400) * 86400 as day, virusinfo, " .
581 "count (virusinfo), max (Time) FROM Statistic " .
582 "WHERE virusinfo IS NOT NULL GROUP BY day, virusinfo");
583
584 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time());
585 my $end = timelocal(0, 0, 0, $mday, $mon, $year);
586 my $start = $end - 3600*24*7; # / days
587
588 $cmd = "INSERT INTO CStatistic " .
589 "(CID, RID, ID, Time, Bytes, Direction, Spamlevel, VirusInfo, PTime, Sender) " .
590 "SELECT 0, ID, ID, Time, Bytes, Direction, Spamlevel, VirusInfo, PTime, Sender FROM Statistic " .
591 "WHERE time >= $start";
592
593 $dbh->do($cmd);
594
595 $dbh->do("SELECT setval ('cstatistic_id_seq', nextval ('statistic_id_seq'))");
596
597 $dbh->do("INSERT INTO StatInfo (name, ivalue) VALUES ('virusinfo_index', " .
598 "nextval ('statistic_id_seq'))");
599
600 $cmd = "INSERT INTO CReceivers (CStatistic_CID, CStatistic_RID, Receiver, Blocked) " .
601 "SELECT 0, Mail_ID, Receiver, Blocked FROM Receivers " .
602 "WHERE EXISTS (SELECT * FROM CStatistic WHERE CID = 0 AND RID = Mail_ID)";
603
604 $dbh->do($cmd);
605
606 $dbh->do("DROP TABLE Statistic");
607 $dbh->do("DROP TABLE Receivers");
608 }
609
610 $dbh->commit;
611 };
612 if (my $err = $@) {
613 $dbh->rollback;
614 die $err;
615 }
616}
617
618sub upgrade_greylist_db {
619 my ($dbh) = @_;
620
621 eval {
622 $dbh->begin_work;
623
624 my $cmd = "SELECT tablename FROM pg_tables WHERE tablename = lower ('Greylist')";
625
626 my $sth = $dbh->prepare($cmd);
627 $sth->execute();
628 my $ref = $sth->fetchrow_hashref();
629 $sth->finish();
630
631 if ($ref) { # table exists
632
633 $cmd = "INSERT INTO CGreylist " .
634 "(IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, MTime, CID) " .
635 "SELECT IPNet, Host, Sender, Receiver, Instance, RCTime, ExTime, Delay, Blocked, Passed, RCTime, 0 FROM Greylist";
636
637 $dbh->do($cmd);
638
639 $dbh->do("DROP TABLE Greylist");
640 }
641
642 $dbh->commit;
643 };
644 if (my $err = $@) {
645 $dbh->rollback;
646 die $err;
647 }
648}
649
650sub upgrade_userprefs_db {
651 my ($dbh) = @_;
652
653 eval {
654 $dbh->do("ALTER TABLE UserPrefs ADD COLUMN MTime INTEGER;" .
655 "UPDATE UserPrefs SET MTime = EXTRACT (EPOCH FROM now());" .
656 "ALTER TABLE UserPrefs ALTER COLUMN MTime SET NOT NULL;");
657 };
658
659
660 eval {
661 $dbh->begin_work;
662
663 my $cmd = "SELECT indexname FROM pg_indexes WHERE indexname = lower ('UserPrefs_MTime_Index')";
664
665 my $sth = $dbh->prepare($cmd);
666 $sth->execute();
667 my $ref = $sth->fetchrow_hashref();
668 $sth->finish();
669
670 if (!$ref) { # index does not exist
671 $dbh->do("CREATE INDEX UserPrefs_MTime_Index ON UserPrefs (MTime)");
672 }
673
674 $dbh->commit;
675 };
676 if ($@) {
677 $dbh->rollback;
678 die $@;
679 }
680}
681
0a580593
DM
682sub upgradedb {
683 my ($ruledb) = @_;
684
685 my $dbh = $ruledb->{dbh};
686
e7c865af 687 $dbh->do($dbfunction_minint);
0a580593 688
e7c865af 689 $dbh->do($dbfunction_maxint);
0a580593 690
e7c865af 691 $dbh->do($dbfunction_merge_greylist);
0a580593 692
758c7b6b 693 # make sure we do not use slow sequential scans when upgraing
0a580593
DM
694 # database (before analyze can gather statistics)
695 $dbh->do("set enable_seqscan = false");
696
e7c865af
DM
697 cond_create_dbtable($dbh, 'DailyStat', $daily_stat_ctablecmd);
698 cond_create_dbtable($dbh, 'DomainStat', $domain_stat_ctablecmd);
699 cond_create_dbtable($dbh, 'StatInfo', $statinfo_ctablecmd);
700 cond_create_dbtable($dbh, 'CMailStore', $cmailstore_ctablecmd);
701 cond_create_dbtable($dbh, 'UserPrefs', $userprefs_ctablecmd);
702 cond_create_dbtable($dbh, 'CGreylist', $cgreylist_ctablecmd);
703 cond_create_dbtable($dbh, 'CStatistic', $cstatistic_ctablecmd);
704 cond_create_dbtable($dbh, 'ClusterInfo', $clusterinfo_ctablecmd);
705 cond_create_dbtable($dbh, 'VirusInfo', $virusinfo_stat_ctablecmd);
0a580593 706
e7c865af 707 cond_create_std_actions($ruledb);
0a580593 708
e7c865af 709 upgrade_mailstore_db($dbh);
0a580593 710
e7c865af 711 upgrade_statistic_db($dbh);
0a580593 712
e7c865af 713 upgrade_userprefs_db($dbh);
0a580593 714
e7c865af 715 upgrade_greylist_db($dbh);
0a580593 716
e7c865af 717 upgrade_dailystat_db($dbh);
0a580593 718
e7c865af 719 upgrade_domainstat_db($dbh);
0a580593
DM
720
721 # update obsolete content type names
722 eval {
e7c865af
DM
723 $dbh->do("UPDATE Object " .
724 "SET value = 'content-type:application/java-vm' ".
725 "WHERE objecttype = 3003 " .
726 "AND value = 'content-type:application/x-java-vm';");
0a580593
DM
727 };
728
729 eval {
730 $dbh->do ("ANALYZE");
731 };
732}
733
734sub init_ruledb {
735 my ($ruledb, $reset, $testmode) = @_;
736
737 my $dbh = $ruledb->{dbh};
738
739 if (!$reset) {
740 # Greylist Objectgroup
741 my $greylistgroup = PMG::RuleDB::Group->new
742 ("GreyExclusion", "-", "greylist");
743 $ruledb->save_group ($greylistgroup);
744
745 } else {
746 # we do not touch greylist objects
747 my $glids = "SELECT object.ID FROM Object, Objectgroup WHERE " .
748 "objectgroup_id = objectgroup.id and class = 'greylist'";
749
750 $dbh->do ("DELETE FROM Rule; " .
751 "DELETE FROM RuleGroup; " .
752 "DELETE FROM Attribut WHERE Object_ID NOT IN ($glids); " .
753 "DELETE FROM Object WHERE ID NOT IN ($glids); " .
754 "DELETE FROM Objectgroup WHERE class != 'greylist';");
755 }
756
757 # WHO Objects
758
759 # Blacklist
760 my $obj = PMG::RuleDB::EMail->new ('nomail@fromthisdomain.com');
761 my $blacklist = $ruledb->create_group_with_obj(
762 $obj, 'Blacklist', 'Global blacklist');
758c7b6b 763
0a580593
DM
764 # Whitelist
765 $obj = PMG::RuleDB::EMail->new('mail@fromthisdomain.com');
766 my $whitelist = $ruledb->create_group_with_obj(
767 $obj, 'Whitelist', 'Global whitelist');
768
769 # WHEN Objects
770
771 # Working hours
a29b9649 772 $obj = PMG::RuleDB::TimeFrame->new(8*60, 16*60);
0a580593
DM
773 my $working_hours =$ruledb->create_group_with_obj($obj, 'Office Hours' ,
774 'Usual office hours');
775
776 # WHAT Objects
777
0a580593 778 # Images
a29b9649
DM
779 $obj = PMG::RuleDB::ContentTypeFilter->new('image/.*');
780 my $img_content = $ruledb->create_group_with_obj(
781 $obj, 'Images', 'All kinds of graphic files');
782
0a580593 783 # Multimedia
a29b9649
DM
784 $obj = PMG::RuleDB::ContentTypeFilter->new('audio/.*');
785 my $mm_content = $ruledb->create_group_with_obj(
786 $obj, 'Multimedia', 'Audio and Video');
0a580593 787
a29b9649
DM
788 $obj = PMG::RuleDB::ContentTypeFilter->new('video/.*');
789 $ruledb->group_add_object($mm_content, $obj);
0a580593
DM
790
791 # Office Files
a29b9649
DM
792 $obj = PMG::RuleDB::ContentTypeFilter->new('application/vnd\.ms-excel');
793 my $office_content = $ruledb->create_group_with_obj(
794 $obj, 'Office Files', 'Common Office Files');
758c7b6b 795
a29b9649
DM
796 $obj = PMG::RuleDB::ContentTypeFilter->new(
797 'application/vnd\.ms-powerpoint');
758c7b6b 798
a29b9649 799 $ruledb->group_add_object($office_content, $obj);
758c7b6b 800
a29b9649 801 $obj = PMG::RuleDB::ContentTypeFilter->new('application/msword');
0a580593 802 $ruledb->group_add_object ($office_content, $obj);
758c7b6b 803
a29b9649
DM
804 $obj = PMG::RuleDB::ContentTypeFilter->new(
805 'application/vnd\.openxmlformats-officedocument\..*');
806 $ruledb->group_add_object($office_content, $obj);
758c7b6b 807
a29b9649
DM
808 $obj = PMG::RuleDB::ContentTypeFilter->new(
809 'application/vnd\.oasis\.opendocument\..*');
810 $ruledb->group_add_object($office_content, $obj);
0a580593 811
a29b9649
DM
812 $obj = PMG::RuleDB::ContentTypeFilter->new(
813 'application/vnd\.stardivision\..*');
814 $ruledb->group_add_object($office_content, $obj);
758c7b6b 815
a29b9649
DM
816 $obj = PMG::RuleDB::ContentTypeFilter->new(
817 'application/vnd\.sun\.xml\..*');
818 $ruledb->group_add_object($office_content, $obj);
758c7b6b 819
0a580593 820 # Dangerous Content
a29b9649
DM
821 $obj = PMG::RuleDB::ContentTypeFilter->new(
822 'application/x-ms-dos-executable');
823 my $exe_content = $ruledb->create_group_with_obj(
824 $obj, 'Dangerous Content', 'executable files and partial messages');
758c7b6b 825
a29b9649
DM
826 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-java');
827 $ruledb->group_add_object($exe_content, $obj);
828 $obj = PMG::RuleDB::ContentTypeFilter->new('application/javascript');
829 $ruledb->group_add_object($exe_content, $obj);
830 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-executable');
831 $ruledb->group_add_object($exe_content, $obj);
79ef2452
DM
832 $obj = PMG::RuleDB::ContentTypeFilter->new('application/x-ms-dos-executable');
833 $ruledb->group_add_object($exe_content, $obj);
a29b9649
DM
834 $obj = PMG::RuleDB::ContentTypeFilter->new('message/partial');
835 $ruledb->group_add_object($exe_content, $obj);
836 $obj = PMG::RuleDB::MatchFilename->new('.*\.(vbs|pif|lnk|shs|shb)');
837 $ruledb->group_add_object($exe_content, $obj);
be6c5e4a 838 $obj = PMG::RuleDB::MatchFilename->new('.*\.\{.+\}');
a29b9649 839 $ruledb->group_add_object($exe_content, $obj);
0a580593
DM
840
841 # Virus
758c7b6b
DM
842 $obj = PMG::RuleDB::Virus->new();
843 my $virus = $ruledb->create_group_with_obj(
844 $obj, 'Virus', 'Matches virus infected mail');
845
0a580593
DM
846 # WHAT Objects
847
848 # Spam
758c7b6b
DM
849 $obj = PMG::RuleDB::Spam->new(3);
850 my $spam3 = $ruledb->create_group_with_obj(
851 $obj, 'Spam (Level 3)', 'Matches possible spam mail');
e7c865af 852
758c7b6b
DM
853 $obj = PMG::RuleDB::Spam->new(5);
854 my $spam5 = $ruledb->create_group_with_obj(
855 $obj, 'Spam (Level 5)', 'Matches possible spam mail');
e7c865af 856
758c7b6b
DM
857 $obj = PMG::RuleDB::Spam->new(10);
858 my $spam10 = $ruledb->create_group_with_obj(
859 $obj, 'Spam (Level 10)', 'Matches possible spam mail');
0a580593
DM
860
861 # ACTIONS
862
758c7b6b
DM
863 # Mark Spam
864 $obj = PMG::RuleDB::ModField->new('X-SPAM-LEVEL', '__SPAM_INFO__');
865 my $mod_spam_level = $ruledb->create_group_with_obj(
e7c865af 866 $obj, 'Modify Spam Level',
758c7b6b 867 'Mark mail as spam by adding a header tag.');
0a580593
DM
868
869 # Mark Spam
758c7b6b
DM
870 $obj = PMG::RuleDB::ModField->new('subject', 'SPAM: __SUBJECT__');
871 my $mod_spam_subject = $ruledb->create_group_with_obj(
e7c865af 872 $obj, 'Modify Spam Subject',
758c7b6b 873 'Mark mail as spam by modifying the subject.');
e7c865af 874
0a580593 875 # Remove matching attachments
758c7b6b
DM
876 $obj = PMG::RuleDB::Remove->new(0);
877 my $remove = $ruledb->create_group_with_obj(
878 $obj, 'Remove attachments', 'Remove matching attachments');
e7c865af 879
0a580593 880 # Remove all attachments
758c7b6b
DM
881 $obj = PMG::RuleDB::Remove->new(1);
882 my $remove_all = $ruledb->create_group_with_obj(
883 $obj, 'Remove all attachments', 'Remove all attachments');
0a580593
DM
884
885 # Accept
758c7b6b
DM
886 $obj = PMG::RuleDB::Accept->new();
887 my $accept = $ruledb->create_group_with_obj(
888 $obj, 'Accept', 'Accept mail for Delivery');
0a580593
DM
889
890 # Block
758c7b6b
DM
891 $obj = PMG::RuleDB::Block->new ();
892 my $block = $ruledb->create_group_with_obj($obj, 'Block', 'Block mail');
0a580593
DM
893
894 # Quarantine
758c7b6b
DM
895 $obj = PMG::RuleDB::Quarantine->new();
896 my $quarantine = $ruledb->create_group_with_obj(
897 $obj, 'Quarantine', 'Move mail to quarantine');
0a580593
DM
898
899 # Notify Admin
758c7b6b
DM
900 $obj = PMG::RuleDB::Notify->new('__ADMIN__');
901 my $notify_admin = $ruledb->create_group_with_obj(
902 $obj, 'Notify Admin', 'Send notification');
0a580593
DM
903
904 # Notify Sender
758c7b6b
DM
905 $obj = PMG::RuleDB::Notify->new('__SENDER__');
906 my $notify_sender = $ruledb->create_group_with_obj(
907 $obj, 'Notify Sender', 'Send notification');
0a580593
DM
908
909 # Add Disclaimer
758c7b6b
DM
910 $obj = PMG::RuleDB::Disclaimer->new ();
911 my $add_discl = $ruledb->create_group_with_obj(
912 $obj, 'Disclaimer', 'Add Disclaimer');
0a580593
DM
913
914 # Attach original mail
915 #$obj = Proxmox::RuleDB::Attach->new ();
758c7b6b 916 #my $attach_orig = $ruledb->create_group_with_obj ($obj, 'Attach Original Mail',
0a580593
DM
917 # 'Attach Original Mail');
918
919 ####################### RULES ##################################
920
921 ## Block Dangerous Files
a29b9649 922 my $rule = PMG::RuleDB::Rule->new ('Block Dangerous Files', 93, 1, 0);
0a580593
DM
923 $ruledb->save_rule ($rule);
924
925 $ruledb->rule_add_what_group ($rule, $exe_content);
926 $ruledb->rule_add_action ($rule, $remove);
927
928 ## Block Viruses
a29b9649 929 $rule = PMG::RuleDB::Rule->new ('Block Viruses', 96, 1, 0);
0a580593
DM
930 $ruledb->save_rule ($rule);
931
932 $ruledb->rule_add_what_group ($rule, $virus);
933 $ruledb->rule_add_action ($rule, $notify_admin);
758c7b6b 934
0a580593
DM
935 if ($testmode) {
936 $ruledb->rule_add_action ($rule, $block);
937 } else {
938 $ruledb->rule_add_action ($rule, $quarantine);
939 }
940
941 ## Virus Alert
a29b9649 942 $rule = PMG::RuleDB::Rule->new ('Virus Alert', 96, 1, 1);
0a580593
DM
943 $ruledb->save_rule ($rule);
944
945 $ruledb->rule_add_what_group ($rule, $virus);
946 $ruledb->rule_add_action ($rule, $notify_sender);
947 $ruledb->rule_add_action ($rule, $notify_admin);
948 $ruledb->rule_add_action ($rule, $block);
758c7b6b 949
0a580593 950 ## Blacklist
a29b9649 951 $rule = PMG::RuleDB::Rule->new ('Blacklist', 98, 1, 0);
0a580593
DM
952 $ruledb->save_rule ($rule);
953
954 $ruledb->rule_add_from_group ($rule, $blacklist);
955 $ruledb->rule_add_action ($rule, $block);
956
957 ## Modify header
958 if (!$testmode) {
a29b9649 959 $rule = PMG::RuleDB::Rule->new ('Modify Header', 90, 1, 0);
0a580593
DM
960 $ruledb->save_rule ($rule);
961 $ruledb->rule_add_action ($rule, $mod_spam_level);
962 }
963
964 ## Whitelist
a29b9649 965 $rule = PMG::RuleDB::Rule->new ('Whitelist', 85, 1, 0);
0a580593
DM
966 $ruledb->save_rule ($rule);
967
968 $ruledb->rule_add_from_group ($rule, $whitelist);
969 $ruledb->rule_add_action ($rule, $accept);
970
971 if ($testmode) {
a29b9649 972 $rule = PMG::RuleDB::Rule->new ('Mark Spam', 80, 1, 0);
0a580593
DM
973 $ruledb->save_rule ($rule);
974
975 $ruledb->rule_add_what_group ($rule, $spam10);
976 $ruledb->rule_add_action ($rule, $mod_spam_level);
977 $ruledb->rule_add_action ($rule, $mod_spam_subject);
978 } else {
979 # Quarantine/Mark Spam (Level 3)
a29b9649 980 $rule = PMG::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 3)', 80, 1, 0);
0a580593
DM
981 $ruledb->save_rule ($rule);
982
983 $ruledb->rule_add_what_group ($rule, $spam3);
984 $ruledb->rule_add_action ($rule, $mod_spam_subject);
985 $ruledb->rule_add_action ($rule, $quarantine);
986 #$ruledb->rule_add_action ($rule, $count_spam);
987 }
988
989 # Quarantine/Mark Spam (Level 5)
a29b9649 990 $rule = PMG::RuleDB::Rule->new ('Quarantine/Mark Spam (Level 5)', 79, 0, 0);
0a580593
DM
991 $ruledb->save_rule ($rule);
992
993 $ruledb->rule_add_what_group ($rule, $spam5);
994 $ruledb->rule_add_action ($rule, $mod_spam_subject);
995 $ruledb->rule_add_action ($rule, $quarantine);
996
997 ## Block Spam Level 10
a29b9649 998 $rule = PMG::RuleDB::Rule->new ('Block Spam (Level 10)', 78, 0, 0);
0a580593
DM
999 $ruledb->save_rule ($rule);
1000
1001 $ruledb->rule_add_what_group ($rule, $spam10);
1002 $ruledb->rule_add_action ($rule, $block);
1003
1004 ## Block Outgoing Spam
a29b9649 1005 $rule = PMG::RuleDB::Rule->new ('Block outgoing Spam', 70, 0, 1);
0a580593
DM
1006 $ruledb->save_rule ($rule);
1007
1008 $ruledb->rule_add_what_group ($rule, $spam3);
1009 $ruledb->rule_add_action ($rule, $notify_admin);
1010 $ruledb->rule_add_action ($rule, $notify_sender);
1011 $ruledb->rule_add_action ($rule, $block);
1012
1013 ## Add disclaimer
a29b9649 1014 $rule = PMG::RuleDB::Rule->new ('Add Disclaimer', 60, 0, 1);
0a580593
DM
1015 $ruledb->save_rule ($rule);
1016 $ruledb->rule_add_action ($rule, $add_discl);
1017
1018 # Block Multimedia Files
a29b9649 1019 $rule = PMG::RuleDB::Rule->new ('Block Multimedia Files', 87, 0, 2);
0a580593
DM
1020 $ruledb->save_rule ($rule);
1021
1022 $ruledb->rule_add_what_group ($rule, $mm_content);
1023 $ruledb->rule_add_action ($rule, $remove);
1024
1025 #$ruledb->rule_add_from_group ($rule, $anybody);
1026 #$ruledb->rule_add_from_group ($rule, $trusted);
1027 #$ruledb->rule_add_to_group ($rule, $anybody);
1028 #$ruledb->rule_add_what_group ($rule, $ct_filter);
1029 #$ruledb->rule_add_action ($rule, $add_discl);
1030 #$ruledb->rule_add_action ($rule, $remove);
1031 #$ruledb->rule_add_action ($rule, $bcc);
1032 #$ruledb->rule_add_action ($rule, $storeq);
1033 #$ruledb->rule_add_action ($rule, $accept);
1034
1035 cond_create_std_actions ($ruledb);
1036}
1037
a355f100 10381;