]>
Commit | Line | Data |
---|---|---|
e84bf942 DM |
1 | package PMG::Quarantine; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use Net::SMTP; | |
6 | ||
7 | use PVE::SafeSyslog; | |
8 | use PVE::Tools; | |
9 | ||
10 | use PMG::Utils; | |
11 | use PMG::RuleDB; | |
12 | use PMG::MailQueue; | |
13 | ||
14 | sub add_to_blackwhite { | |
15 | my ($dbh, $username, $listname, $addrs, $delete) = @_; | |
16 | ||
17 | my $name = $listname eq 'BL' ? 'BL' : 'WL'; | |
18 | my $oname = $listname eq 'BL' ? 'WL' : 'BL'; | |
19 | my $qu = $dbh->quote ($username); | |
20 | ||
21 | my $sth = $dbh->prepare( | |
22 | "SELECT * FROM UserPrefs WHERE pmail = $qu AND (Name = 'BL' OR Name = 'WL')"); | |
23 | $sth->execute(); | |
24 | ||
25 | my $list = { 'WL' => {}, 'BL' => {} }; | |
26 | ||
27 | while (my $ref = $sth->fetchrow_hashref()) { | |
28 | my $data = $ref->{data}; | |
29 | $data =~ s/[,;]/ /g; | |
30 | my @alist = split('\s+', $data); | |
31 | ||
32 | my $tmp = {}; | |
33 | foreach my $a (@alist) { | |
34 | if ($a =~ m/^[[:ascii:]]+$/) { | |
35 | $tmp->{$a} = 1; | |
36 | } | |
37 | } | |
38 | ||
39 | $list->{$ref->{name}} = $tmp; | |
40 | } | |
41 | ||
42 | $sth->finish; | |
43 | ||
44 | if ($addrs) { | |
45 | ||
46 | foreach my $v (@$addrs) { | |
47 | die "email address '$v' is too long (> 512 characters)\n" | |
48 | if length($v) > 512; | |
49 | ||
50 | if ($delete) { | |
51 | delete($list->{$name}->{$v}); | |
52 | } else { | |
53 | if ($v =~ m/[[:^ascii:]]/) { | |
54 | die "email address '$v' contains invalid characters\n"; | |
55 | } | |
56 | $list->{$name}->{$v} = 1; | |
57 | delete ($list->{$oname}->{$v}); | |
58 | } | |
59 | } | |
60 | ||
61 | my $wlist = $dbh->quote(join (',', keys %{$list->{WL}}) || ''); | |
62 | my $blist = $dbh->quote(join (',', keys %{$list->{BL}}) || ''); | |
63 | ||
64 | if (!$delete) { | |
65 | my $maxlen = 200000; | |
66 | die "whitelist size exceeds limit (> $maxlen bytes)\n" | |
67 | if length($wlist) > $maxlen; | |
68 | die "blacklist size exceeds limit (> $maxlen bytes)\n" | |
69 | if length($blist) > $maxlen; | |
70 | } | |
71 | ||
570be5b2 DC |
72 | my $queries = "DELETE FROM UserPrefs WHERE pmail = $qu AND (Name = 'WL' OR Name = 'BL');"; |
73 | if (scalar(keys %{$list->{WL}})) { | |
74 | $queries .= | |
e84bf942 | 75 | "INSERT INTO UserPrefs (PMail, Name, Data, MTime) " . |
570be5b2 DC |
76 | "VALUES ($qu, 'WL', $wlist, EXTRACT (EPOCH FROM now()));"; |
77 | } | |
78 | if (scalar(keys %{$list->{BL}})) { | |
79 | $queries .= | |
e84bf942 | 80 | "INSERT INTO UserPrefs (PMail, Name, Data, MTime) " . |
570be5b2 DC |
81 | "VALUES ($qu, 'BL', $blist, EXTRACT (EPOCH FROM now()));"; |
82 | } | |
83 | $dbh->do($queries); | |
e84bf942 DM |
84 | } |
85 | ||
86 | my $values = [ keys %{$list->{$name}} ]; | |
87 | ||
88 | return $values; | |
89 | } | |
90 | ||
91 | sub deliver_quarantined_mail { | |
c7e20f13 | 92 | my ($dbh, $ref, $receiver) = @_; |
e84bf942 DM |
93 | |
94 | my $filename = $ref->{file}; | |
95 | my $spooldir = $PMG::MailQueue::spooldir; | |
96 | my $path = "$spooldir/$filename"; | |
97 | ||
666b5e8f | 98 | my $id = 'C' . $ref->{cid} . 'R' . $ref->{rid} . 'T' . $ref->{ticketid};; |
e84bf942 DM |
99 | |
100 | my $sender = 'postmaster'; # notify postmaster if something fails | |
101 | ||
102 | my $smtp; | |
103 | ||
104 | eval { | |
105 | my $smtp = Net::SMTP->new ('127.0.0.1', Port => 10025, Hello => 'quarantine') || | |
106 | die "unable to connect to localhost at port 10025\n"; | |
107 | ||
108 | my $resid; | |
109 | ||
110 | if (!$smtp->mail($sender)) { | |
111 | die sprintf("smtp from error - got: %s %s\n", $smtp->code, $smtp->message); | |
112 | } | |
113 | ||
c7e20f13 | 114 | if (!$smtp->to($receiver)) { |
e84bf942 DM |
115 | die sprintf("smtp to error - got: %s %s\n", $smtp->code, $smtp->message); |
116 | } | |
117 | ||
118 | $smtp->data(); | |
119 | ||
120 | my $header = 1; | |
121 | ||
122 | open(my $fh, '<', $path) || die "unable to open file '$path' - $!\n"; | |
123 | ||
124 | while (defined(my $line = <$fh>)) { | |
125 | chomp $line; | |
126 | if ($header && ($line =~ m/^\s*$/)) { | |
127 | $header = 0; | |
128 | } | |
129 | ||
130 | # skip Delivered-To and Return-Path (avoid problem with postfix | |
131 | # forwarding loop detection (man local)) | |
132 | next if ($header && (($line =~ m/^Delivered-To:/i) || ($line =~ m/^Return-Path:/i))); | |
133 | ||
134 | # rfc821 requires this | |
135 | $line =~ s/^\./\.\./mg; | |
136 | $smtp->datasend("$line\n"); | |
137 | } | |
138 | close($fh); | |
139 | ||
140 | if ($smtp->dataend()) { | |
141 | my (@msgs) = $smtp->message; | |
142 | my ($last_msg) = $msgs[$#msgs]; | |
143 | ($resid) = $last_msg =~ m/Ok: queued as ([0-9A-Z]+)/; | |
144 | if (!$resid) { | |
145 | die sprintf("smtp error - got: %s %s\n", $smtp->code, $smtp->message); | |
146 | } | |
147 | } else { | |
148 | die sprintf("sending data failed - got: %s %s\n", $smtp->code, $smtp->message); | |
149 | } | |
150 | ||
151 | my $sth = $dbh->prepare( | |
152 | "UPDATE CMSReceivers SET Status='D', MTime = ? " . | |
666b5e8f DM |
153 | "WHERE CMailStore_CID = ? AND CMailStore_RID = ? AND TicketID = ?"); |
154 | $sth->execute(time(), $ref->{cid}, $ref->{rid}, $ref->{ticketid}); | |
e84bf942 DM |
155 | $sth->finish; |
156 | }; | |
157 | my $err = $@; | |
158 | ||
159 | $smtp->quit if $smtp; | |
160 | ||
161 | if ($err) { | |
162 | my $msg = "deliver quarantined mail '$id' ($path) failed: $err"; | |
163 | syslog('err', $msg); | |
164 | die "$msg\n"; | |
165 | } | |
166 | ||
167 | syslog('info', "delivered quarantined mail '$id' ($path)"); | |
168 | ||
169 | return 1; | |
170 | } | |
171 | ||
172 | sub delete_quarantined_mail { | |
666b5e8f | 173 | my ($dbh, $ref) = @_; |
e84bf942 DM |
174 | |
175 | my $filename = $ref->{file}; | |
176 | my $spooldir = $PMG::MailQueue::spooldir; | |
177 | my $path = "$spooldir/$filename"; | |
178 | ||
666b5e8f | 179 | my $id = 'C' . $ref->{cid} . 'R' . $ref->{rid} . 'T' . $ref->{ticketid};; |
e84bf942 DM |
180 | |
181 | eval { | |
182 | my $sth = $dbh->prepare( | |
183 | "UPDATE CMSReceivers SET Status='D', MTime = ? WHERE " . | |
666b5e8f DM |
184 | "CMailStore_CID = ? AND CMailStore_RID = ? AND TicketID = ?"); |
185 | $sth->execute (time(), $ref->{cid}, $ref->{rid}, $ref->{ticketid}); | |
e84bf942 DM |
186 | $sth->finish; |
187 | }; | |
188 | if (my $err = $@) { | |
189 | my $msg = "delete quarantined mail '$id' ($path) failed: $err"; | |
190 | syslog ('err', $msg); | |
191 | die "$msg\n"; | |
192 | } | |
193 | ||
194 | syslog ('info', "marked quarantined mail '$id' as deleted ($path)"); | |
195 | ||
196 | return 1; | |
197 | } | |
198 | ||
199 | ||
200 | 1; |