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