]> git.proxmox.com Git - pmg-api.git/blame - PMG/API2/Quarantine.pm
read_raw_email: add maxbytes parameter
[pmg-api.git] / PMG / API2 / Quarantine.pm
CommitLineData
b66faa68
DM
1package PMG::API2::Quarantine;
2
3use strict;
4use warnings;
5use Time::Local;
6use Time::Zone;
7use Data::Dumper;
8
dae021a8
DM
9use Mail::Header;
10
b66faa68 11use PVE::SafeSyslog;
6e8886d4 12use PVE::Exception qw(raise_param_exc raise_perm_exc);
b66faa68
DM
13use PVE::Tools qw(extract_param);
14use PVE::JSONSchema qw(get_standard_option);
15use PVE::RESTHandler;
16use PVE::INotify;
17
dae021a8 18use PMG::Utils;
b66faa68
DM
19use PMG::AccessControl;
20use PMG::DBTools;
21
22use base qw(PVE::RESTHandler);
23
24
dae021a8
DM
25my $parse_header_info = sub {
26 my ($ref) = @_;
27
28 my $res = { subject => '', from => '' };
29
30 my @lines = split('\n', $ref->{header});
31 my $head = Mail::Header->new(\@lines);
32
33 $res->{subject} = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('subject'))) // '';
34
35 my @fromarray = split('\s*,\s*', $head->get('from') || $ref->{sender});
36
37 $res->{from} = PMG::Utils::decode_rfc1522(PVE::Tools::trim ($fromarray[0])) // '';
38
39 my $sender = PMG::Utils::decode_rfc1522(PVE::Tools::trim($head->get('sender')));
40 $res->{sender} = $sender if $sender;
41
42 $res->{envelope_sender} = $ref->{sender};
43 $res->{receiver} = $ref->{receiver};
6e8886d4 44 $res->{id} = 'C' . $ref->{cid} . 'R' . $ref->{rid};
dae021a8
DM
45 $res->{time} = $ref->{time};
46 $res->{bytes} = $ref->{bytes};
47
48 return $res;
49};
50
6e8886d4
DM
51my $read_email_raw = sub {
52 my ($path) = @_;
53
54 open (my $fh, '<', $path) || die "unable to open '$path' - $!\n";
55
56 my $data = '';
57 my $raw_header = '';
58
59 # read header
60 my $header;
61 while (defined(my $line = <$fh>)) {
62 $raw_header .= $line;
63 chomp $line;
64 push @$header, $line;
65 last if $line =~ m/^\s*$/;
66 }
67
68 my $head = MIME::Head->new($header);
69
70 my $cs = $head->mime_attr("content-type.charset");
71
72 while (defined(my $line = <$fh>)) {
73 if ($cs) {
74 $data .= decode($cs, $line);
75 } else {
76 $data .= $line;
77 }
78 }
79
80 close($fh);
81
82 return ($raw_header, $data);
83};
84
85
b66faa68
DM
86__PACKAGE__->register_method ({
87 name => 'index',
88 path => '',
89 method => 'GET',
90 permissions => { user => 'all' },
91 description => "Directory index.",
92 parameters => {
93 additionalProperties => 0,
94 properties => {},
95 },
96 returns => {
97 type => 'array',
98 items => {
99 type => "object",
100 properties => {},
101 },
102 links => [ { rel => 'child', href => "{name}" } ],
103 },
104 code => sub {
105 my ($param) = @_;
106
107 my $result = [
108 { name => 'deliver' },
6e8886d4 109 { name => 'content' },
b66faa68
DM
110 { name => 'spam' },
111 { name => 'virus' },
112 ];
113
114 return $result;
115 }});
116
117__PACKAGE__->register_method ({
118 name => 'spam',
119 path => 'spam',
120 method => 'GET',
121 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
122 description => "Show spam mails distribution (per day).",
123 parameters => {
124 additionalProperties => 0,
125 properties => {
126 starttime => {
127 description => "Only consider entries newer than 'startime' (unix epoch).",
128 type => 'integer',
129 minimum => 0,
130 optional => 1,
131 },
132 endtime => {
133 description => "Only consider entries older than 'endtime' (unix epoch).",
134 type => 'integer',
135 minimum => 1,
136 optional => 1,
137 },
138 pmail => {
ded33c7c 139 description => "List entries for the user with this primary email address. Quarantine users cannot speficy this parameter, but it is required for all other roles.",
b66faa68
DM
140 type => 'string', format => 'email',
141 optional => 1,
142 },
143 },
144 },
145 returns => {
146 type => 'array',
147 items => {
148 type => "object",
149 properties => {
150 day => {
151 description => "Day (as unix epoch).",
152 type => 'integer',
153 },
bc1ebe25 154 count => {
b66faa68
DM
155 description => "Number of quarantine entries.",
156 type => 'integer',
157 },
158 spamavg => {
159 description => "Average spam level.",
160 type => 'number',
bc1ebe25 161 },
b66faa68
DM
162 },
163 },
ded33c7c 164 links => [ { rel => 'child', href => "{day}" } ],
b66faa68
DM
165 },
166 code => sub {
167 my ($param) = @_;
168
169 my $rpcenv = PMG::RESTEnvironment->get();
170 my $authuser = $rpcenv->get_user();
171 my $role = $rpcenv->get_role();
172
173 my $pmail = $param->{pmail};
174
175 if ($role eq 'quser') {
176 raise_param_exc({ pmail => "paramater not allwed with role '$role'"})
177 if defined($pmail);
178 $pmail = $authuser;
ded33c7c
DM
179 } else {
180 raise_param_exc({ pmail => "paramater required with role '$role'"})
181 if !defined($pmail);
b66faa68
DM
182 }
183
184 my $res = [];
bc1ebe25 185
b66faa68
DM
186 my $dbh = PMG::DBTools::open_ruledb();
187
ec7035c2 188 my $start = $param->{starttime};
b66faa68
DM
189 my $end = $param->{endtime};
190
191 my $timezone = tz_local_offset();
192
193 my $sth = $dbh->prepare(
194 "SELECT " .
195 "((time + $timezone) / 86400) * 86400 - $timezone as day, " .
196 "count (ID) as count, avg (Spamlevel) as spamavg " .
197 "FROM CMailStore, CMSReceivers WHERE " .
ec7035c2
DM
198 (defined($start) ? "time >= $start AND " : '') .
199 (defined($end) ? "time < $end AND " : '') .
ded33c7c 200 "pmail = ? AND " .
b66faa68
DM
201 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
202 "AND Status = 'N' " .
203 "GROUP BY day " .
204 "ORDER BY day DESC");
205
ded33c7c
DM
206 $sth->execute($pmail);
207
208 while (my $ref = $sth->fetchrow_hashref()) {
209 push @$res, $ref;
210 }
211
212 return $res;
213 }});
214
215__PACKAGE__->register_method ({
216 name => 'spamlist',
217 path => 'spam/{starttime}',
218 method => 'GET',
219 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
220 description => "Show spam mails distribution (per day).",
221 parameters => {
222 additionalProperties => 0,
223 properties => {
224 starttime => {
225 description => "Only consider entries newer than 'starttime' (unix epoch).",
226 type => 'integer',
227 minimum => 0,
228 },
229 endtime => {
230 description => "Only consider entries older than 'endtime' (unix epoch). This is set to '<start> + 1day' by default.",
231 type => 'integer',
232 minimum => 1,
233 optional => 1,
234 },
235 pmail => {
236 description => "List entries for the user with this primary email address. Quarantine users cannot speficy this parameter, but it is required for all other roles.",
237 type => 'string', format => 'email',
238 optional => 1,
239 },
240 },
241 },
242 returns => {
243 type => 'array',
244 items => {
245 type => "object",
dae021a8
DM
246 properties => {
247 id => {
248 description => 'Unique ID',
249 type => 'string',
250 },
251 bytes => {
252 description => "Size of raw email.",
253 type => 'integer' ,
254 },
255 envelope_sender => {
256 description => "SMTP envelope sender.",
257 type => 'string',
258 },
259 from => {
260 description => "Header 'From' field.",
261 type => 'string',
262 },
263 sender => {
264 description => "Header 'Sender' field.",
265 type => 'string',
266 optional => 1,
267 },
268 receiver => {
269 description => "Receiver email address",
270 type => 'string',
271 },
272 subject => {
273 description => "Header 'Subject' field.",
274 type => 'string',
275 },
276 time => {
277 description => "Receive time stamp",
278 type => 'integer',
279 },
280 },
ded33c7c 281 },
6e8886d4 282 links => [ { rel => 'child', href => "{day}" } ],
ded33c7c
DM
283 },
284 code => sub {
285 my ($param) = @_;
286
287 my $rpcenv = PMG::RESTEnvironment->get();
288 my $authuser = $rpcenv->get_user();
289 my $role = $rpcenv->get_role();
290
291 my $pmail = $param->{pmail};
292
293 if ($role eq 'quser') {
294 raise_param_exc({ pmail => "paramater not allwed with role '$role'"})
295 if defined($pmail);
296 $pmail = $authuser;
b66faa68 297 } else {
ded33c7c
DM
298 raise_param_exc({ pmail => "paramater required with role '$role'"})
299 if !defined($pmail);
b66faa68
DM
300 }
301
ded33c7c
DM
302 my $res = [];
303
304 my $dbh = PMG::DBTools::open_ruledb();
305
306 my $start = $param->{starttime};
307 my $end = $param->{endtime} // ($start + 86400);
308
309 my $sth = $dbh->prepare(
310 "SELECT * " .
311 "FROM CMailStore, CMSReceivers WHERE " .
312 "pmail = ? AND time >= $start AND time < $end AND " .
313 "QType = 'S' AND CID = CMailStore_CID AND RID = CMailStore_RID " .
314 "AND Status = 'N' ORDER BY pmail, time, receiver");
315
316 $sth->execute($pmail);
317
b66faa68 318 while (my $ref = $sth->fetchrow_hashref()) {
dae021a8
DM
319 my $data = $parse_header_info->($ref);
320 push @$res, $data;
b66faa68
DM
321 }
322
323 return $res;
324 }});
325
6e8886d4
DM
326__PACKAGE__->register_method ({
327 name => 'content',
328 path => 'content',
329 method => 'GET',
330 permissions => { check => [ 'admin', 'qmanager', 'audit', 'quser'] },
331 description => "Get email data.",
332 parameters => {
333 additionalProperties => 0,
334 properties => {
335 id => {
336 description => 'Unique ID',
337 type => 'string',
338 pattern => 'C\d+R\d+',
339 maxLength => 40,
340 },
341 },
342 },
343 returns => {
344 type => "object",
345 properties => {},
346 },
347 code => sub {
348 my ($param) = @_;
349
350 my $rpcenv = PMG::RESTEnvironment->get();
351 my $authuser = $rpcenv->get_user();
352 my $role = $rpcenv->get_role();
353
354 my ($cid, $rid) = $param->{id} =~ m/^C(\d+)R(\d+)$/;
355 $cid = int($cid);
356 $rid = int($rid);
357
358 my $dbh = PMG::DBTools::open_ruledb();
359
360 my $ref = PMG::DBTools::load_mail_data($dbh, $cid, $rid);
361
362 if ($role eq 'quser') {
363 raise_perm_exc("mail does not belong to user '$authuser'")
364 if $authuser ne $ref->{pmail};
365 }
366
367 my $res = $parse_header_info->($ref);
368
369 foreach my $k (qw(info file spamlevel)) {
370 $res->{$k} = $ref->{$k} if defined($ref->{$k});
371 }
372
373 my $filename = $ref->{file};
374 my $spooldir = $PMG::MailQueue::spooldir;
375
376 my $path = "$spooldir/$filename";
377
378 my ($header, $content) = $read_email_raw->($path);
379
380 $res->{header} = $header;
381 $res->{content} = $content;
382
383 return $res;
384
385 }});
386
b66faa68 3871;