]>
git.proxmox.com Git - pmg-api.git/blob - src/PMG/API2/MailTracker.pm
1 package PMG
::API2
::MailTracker
;
12 use PVE
::Exception
qw(raise_param_exc);
13 use PVE
::JSONSchema
qw(get_standard_option);
18 use PMG
::RESTEnvironment
;
20 use base
qw(PVE::RESTHandler);
22 my $get_start_end_time = sub {
24 my $start = $param->{starttime
} // (time - 86400);
25 my $end = $param->{endtime
} // ($start + 86400);
26 raise_param_exc
({'endtime' => "must be newer than 'starttime'"}) if $start > $end;
27 return ($start, $end);
41 my $run_pmg_log_tracker = sub {
42 my ($args, $includelog) = @_;
46 my $timezone = tz_local_offset
();;
48 if (defined(my $id = $includelog)) {
49 if ($id =~ m/^Q([a-f0-9]+)R([a-f0-9]+)$/i) {
52 push @$args, '-q', $1, '-q', $2;
55 push @$args, '-q', $id;
69 # assume syslog is UTF-8 encoded
70 $line = decode
('UTF-8', $line);
72 if ($state eq 'start') {
74 return if $line =~ m/^\#/;
75 return if $line =~ m/^\s*$/;
77 if ($line =~ m/^STATUS: (.*)$/) {
83 if ($line =~ m/^SMTPD:\s+(T[0-9A-F]+L[0-9A-F]+)$/) {
85 $entry = { id
=> $1 };
89 if ($line =~ m/^QENTRY:\s+([0-9A-F]+)$/) {
91 $entry = { qid
=> $1 };
95 die "got unexpected data: $line";
96 } elsif ($state eq 'end') {
97 die "got unexpected data after status: $line";
98 } elsif ($state eq 'skiplogs') {
99 if ($line =~ m/^\s*$/) {
105 } elsif ($state eq 'logs') {
106 if ($line =~ m/^\s*$/) {
109 } elsif ($line =~ m/^(SMTP|FILTER|QMGR):/) {
111 } elsif ($line =~ m/^(L[A-F0-9]+)\s(.*)$/) {
112 push @$logs, { linenr
=> $1, text
=> $2 };
114 die "got unexpected data: $line";
116 } elsif ($state eq 'qentry') {
117 if ($line =~ m/^\s*$/) {
120 } elsif ($line =~ m/^SIZE:\s+(\d+)$/) {
122 } elsif ($line =~ m/^CLIENT:\s+(\S+)$/) {
123 $entry->{client
} = $1;
124 } elsif ($line =~ m/^MSGID:\s+(\S+)$/) {
125 $entry->{msgid
} = $1;
126 } elsif ($line =~ m/^CTIME:\s+([0-9A-F]+)$/) {
128 } elsif ($line =~ m/^TO:([0-9A-F]+):([0-9A-F]+):([0-9A-Z]):\s+from <([^>]*)>\s+to\s+<([^>]+)>\s+\((\S+)\)$/) {
130 size
=> $entry->{size
} // 0,
131 time => hex($1) - $timezone,
138 $new->{client
} = $entry->{client
} if defined($entry->{client
});
139 $new->{msgid
} = $entry->{msgid
} if defined($entry->{msgid
});
141 my $dstatus = $new->{dstatus
};
142 if ($dstatus =~ /P|D|R/) {
143 my $before_queue_status = {
148 $new->{dstatus
} = 'A';
149 $new->{rstatus
} = $before_queue_status->{$dstatus};
154 my ($qid, $to) = $new->@{'qid', 'to'};
155 $lookup_hash->{$qid}->{$to} = $new;
156 } elsif ($line =~ m/^(SMTP|FILTER|QMGR):/) {
157 if ($logids->{$entry->{qid
}}) {
163 die "got unexpected data: $line";
165 } elsif ($state eq 'smtp') {
167 if ($line =~ m/^\s*$/) {
170 } elsif ($line =~ m/^CLIENT:\s+(\S+)$/) {
171 $entry->{client
} = $1;
172 } elsif ($line =~ m/^CTIME:\s+([0-9A-F]+)$/) {
174 } elsif ($line =~ m/^TO:([0-9A-F]+):(T[0-9A-F]+L[0-9A-F]+):([0-9A-Z]):\s+from <([^>]*)>\s+to\s+<([^>]*)>$/) {
176 $e->{client
} = $entry->{client
} if defined($entry->{client
});
177 $e->{time} = hex($1) - $timezone;
181 die "empty to address only allowed in NOQUEUE case\n" if !$5 && $e->{dstatus
} ne 'N';
184 } elsif ($line =~ m/^LOGS:$/) {
185 if ($logids->{$entry->{id
}}) {
191 die "got unexpected data: $line";
194 die "unknown state '$state'\n";
198 my $cmd = ['/usr/bin/pmg-log-tracker', '-v', '-l', 2000];
200 PVE
::Tools
::run_command
([@$cmd, @$args], timeout
=> 25, outfunc
=> $parser);
202 my $sorted_logs = [];
203 foreach my $le (sort {$a->{linenr
} cmp $b->{linenr
}} @$logs) {
204 push @$sorted_logs, $le->{text
};
207 foreach my $e (@$list) {
208 if (my $id = $e->{qid
}) {
209 if (my $relay = $e->{relay
}) {
210 if (my $ref = $lookup_hash->{$relay}->{$e->{to
}}) {
211 $ref->{is_relay
} = 1;
212 $id = 'Q' . $e->{qid
} . 'R' . $e->{relay
};
213 if ($e->{dstatus
} eq 'A') {
214 $e->{rstatus
} = $ref->{dstatus
};
220 if ($includelog && ($e->{id
} eq $includelog)) {
221 $e->{logs
} = $sorted_logs;
225 return wantarray ?
($list, $status) : $list;
228 my $email_log_property_desc = {
230 description
=> "Unique ID.",
234 description
=> "Sender email address.",
238 description
=> "Receiver email address.",
242 description
=> "Postfix qmgr ID.",
247 description
=> "Delivery timestamp.",
251 description
=> "Delivery status.",
257 description
=> "Delivery status of relayed mail.",
264 description
=> "ID of relayed mail.",
269 description
=> "The size of the raw email.",
274 description
=> "Client address",
279 description
=> "SMTP message ID.",
285 __PACKAGE__-
>register_method({
286 name
=> 'list_mails',
289 description
=> "Read mail list.",
292 permissions
=> { check
=> [ 'admin', 'audit' ] },
294 additionalProperties
=> 0,
296 node
=> get_standard_option
('pve-node'),
297 starttime
=> get_standard_option
('pmg-starttime'),
298 endtime
=> get_standard_option
('pmg-endtime'),
300 description
=> "Only include mails containing this filter string.",
307 description
=> "Sender email address filter.",
314 description
=> "Receiver email address filter.",
321 description
=> "Include NDRs (non delivery reports).",
327 description
=> "Include Greylisted entries.",
338 properties
=> $email_log_property_desc,
340 links
=> [ { rel
=> 'child', href
=> "{id}" } ],
345 my $restenv = PMG
::RESTEnvironment-
>get();
349 my ($start, $end) = $get_start_end_time->($param);
351 push @$args, '-s', $start;
352 push @$args, '-e', $end;
354 push @$args, '-n' if !$param->{ndr
};
356 push @$args, '-g' if !$param->{greylist
};
358 push @$args, '-x', $param->{xfilter
} if defined($param->{xfilter
});
360 if (defined($param->{from
})) {
361 push @$args, '-f', $param->{from
};
363 if (defined($param->{target
})) {
364 push @$args, '-t', $param->{target
};
367 my ($list, $status) = $run_pmg_log_tracker->($args);
370 foreach my $e (@$list) {
371 push @$res, $e if !$e->{is_relay
};
374 # hack: return status message in 'changes' attribute
375 $restenv->set_result_attrib('changes', $status) if defined($status);
380 __PACKAGE__-
>register_method({
384 description
=> "Get the detailed syslog entries for a specific mail ID.",
387 permissions
=> { check
=> [ 'admin', 'audit' ] },
389 additionalProperties
=> 0,
391 node
=> get_standard_option
('pve-node'),
392 starttime
=> get_standard_option
('pmg-starttime'),
393 endtime
=> get_standard_option
('pmg-endtime'),
395 description
=> "Mail ID (as returned by the list API).",
405 %$email_log_property_desc,
408 items
=> { type
=> "string" },
415 my $restenv = PMG
::RESTEnvironment-
>get();
419 my ($start, $end) = $get_start_end_time->($param);
421 push @$args, '-s', $start;
422 push @$args, '-e', $end;
424 my $list = $run_pmg_log_tracker->($args, $param->{id
});
427 foreach my $e (@$list) {
428 $res = $e if $e->{id
} eq $param->{id
};
431 die "entry '$param->{id}' not found\n" if !defined($res);