]>
git.proxmox.com Git - pmg-api.git/blob - src/PMG/API2/MailTracker.pm
d512dd99d52fd0c79f5e2c899f7f54495516de47
1 package PMG
::API2
::MailTracker
;
14 use PVE
::Exception
qw(raise_param_exc);
15 use PVE
::JSONSchema
qw(get_standard_option);
17 use PMG
::RESTEnvironment
;
19 use base
qw(PVE::RESTHandler);
21 my $get_start_end_time = sub {
23 my $start = $param->{starttime
} // (time - 86400);
24 my $end = $param->{endtime
} // ($start + 86400);
25 raise_param_exc
({'endtime' => "must be newer than 'starttime'"}) if $start > $end;
26 return ($start, $end);
40 my $run_pmg_log_tracker = sub {
41 my ($args, $includelog) = @_;
45 my $timezone = tz_local_offset
();;
47 if (defined(my $id = $includelog)) {
48 if ($id =~ m/^Q([a-f0-9]+)R([a-f0-9]+)$/i) {
51 push @$args, '-q', $1, '-q', $2;
54 push @$args, '-q', $id;
68 # assume syslog is UTF-8 encoded
69 $line = decode
('UTF-8', $line);
71 if ($state eq 'start') {
73 return if $line =~ m/^\#/;
74 return if $line =~ m/^\s*$/;
76 if ($line =~ m/^STATUS: (.*)$/) {
82 if ($line =~ m/^SMTPD:\s+(T[0-9A-F]+L[0-9A-F]+)$/) {
84 $entry = { id
=> $1 };
88 if ($line =~ m/^QENTRY:\s+([0-9A-F]+)$/) {
90 $entry = { qid
=> $1 };
94 die "got unexpected data: $line";
95 } elsif ($state eq 'end') {
96 die "got unexpected data after status: $line";
97 } elsif ($state eq 'skiplogs') {
98 if ($line =~ m/^\s*$/) {
104 } elsif ($state eq 'logs') {
105 if ($line =~ m/^\s*$/) {
108 } elsif ($line =~ m/^(SMTP|FILTER|QMGR):/) {
110 } elsif ($line =~ m/^(L[A-F0-9]+)\s(.*)$/) {
111 push @$logs, { linenr
=> $1, text
=> $2 };
113 die "got unexpected data: $line";
115 } elsif ($state eq 'qentry') {
116 if ($line =~ m/^\s*$/) {
119 } elsif ($line =~ m/^SIZE:\s+(\d+)$/) {
121 } elsif ($line =~ m/^CLIENT:\s+(\S+)$/) {
122 $entry->{client
} = $1;
123 } elsif ($line =~ m/^MSGID:\s+(\S+)$/) {
124 $entry->{msgid
} = $1;
125 } elsif ($line =~ m/^CTIME:\s+([0-9A-F]+)$/) {
127 } elsif ($line =~ m/^TO:([0-9A-F]+):([0-9A-F]+):([0-9A-Z]):\s+from <([^>]*)>\s+to\s+<([^>]+)>\s+\((\S+)\)$/) {
129 $new->{size
} = $entry->{size
} // 0,
130 $new->{client
} = $entry->{client
} if defined($entry->{client
});
131 $new->{msgid
} = $entry->{msgid
} if defined($entry->{msgid
});
132 $new->{time} = hex($1) - $timezone;
134 $new->{dstatus
} = $3;
140 $lookup_hash->{$2}->{$5} = $new;
141 } elsif ($line =~ m/^(SMTP|FILTER|QMGR):/) {
142 if ($logids->{$entry->{qid
}}) {
148 die "got unexpected data: $line";
150 } elsif ($state eq 'smtp') {
152 if ($line =~ m/^\s*$/) {
155 } elsif ($line =~ m/^CLIENT:\s+(\S+)$/) {
156 $entry->{client
} = $1;
157 } elsif ($line =~ m/^CTIME:\s+([0-9A-F]+)$/) {
159 } elsif ($line =~ m/^TO:([0-9A-F]+):(T[0-9A-F]+L[0-9A-F]+):([0-9A-Z]):\s+from <([^>]*)>\s+to\s+<([^>]+)>$/) {
161 $e->{client
} = $entry->{client
} if defined($entry->{client
});
162 $e->{time} = hex($1) - $timezone;
168 } elsif ($line =~ m/^LOGS:$/) {
169 if ($logids->{$entry->{id
}}) {
175 die "got unexpected data: $line";
178 die "unknown state '$state'\n";
182 my $cmd = ['/usr/bin/pmg-log-tracker', '-v', '-l', 2000];
184 PVE
::Tools
::run_command
([@$cmd, @$args], timeout
=> 25, outfunc
=> $parser);
186 my $sorted_logs = [];
187 foreach my $le (sort {$a->{linenr
} cmp $b->{linenr
}} @$logs) {
188 push @$sorted_logs, $le->{text
};
191 foreach my $e (@$list) {
192 if (my $id = $e->{qid
}) {
193 if (my $relay = $e->{relay
}) {
194 if (my $ref = $lookup_hash->{$relay}->{$e->{to
}}) {
195 $ref->{is_relay
} = 1;
196 $id = 'Q' . $e->{qid
} . 'R' . $e->{relay
};
197 if ($e->{dstatus
} eq 'A') {
198 $e->{rstatus
} = $ref->{dstatus
};
204 if ($includelog && ($e->{id
} eq $includelog)) {
205 $e->{logs
} = $sorted_logs;
209 return wantarray ?
($list, $status) : $list;
212 my $email_log_property_desc = {
214 description
=> "Unique ID.",
218 description
=> "Sender email address.",
222 description
=> "Receiver email address.",
226 description
=> "Postfix qmgr ID.",
231 description
=> "Delivery timestamp.",
235 description
=> "Delivery status.",
241 description
=> "Delivery status of relayed mail.",
248 description
=> "ID of relayed mail.",
253 description
=> "The size of the raw email.",
258 description
=> "Client address",
263 description
=> "SMTP message ID.",
269 __PACKAGE__-
>register_method({
270 name
=> 'list_mails',
273 description
=> "Read mail list.",
276 permissions
=> { check
=> [ 'admin', 'audit' ] },
278 additionalProperties
=> 0,
280 node
=> get_standard_option
('pve-node'),
281 starttime
=> get_standard_option
('pmg-starttime'),
282 endtime
=> get_standard_option
('pmg-endtime'),
284 description
=> "Only include mails containing this filter string.",
291 description
=> "Sender email address filter.",
298 description
=> "Receiver email address filter.",
305 description
=> "Include NDRs (non delivery reports).",
311 description
=> "Include Greylisted entries.",
322 properties
=> $email_log_property_desc,
324 links
=> [ { rel
=> 'child', href
=> "{id}" } ],
329 my $restenv = PMG
::RESTEnvironment-
>get();
333 my ($start, $end) = $get_start_end_time->($param);
335 push @$args, '-s', $start;
336 push @$args, '-e', $end;
338 push @$args, '-n' if !$param->{ndr
};
340 push @$args, '-g' if !$param->{greylist
};
342 push @$args, '-x', $param->{xfilter
} if defined($param->{xfilter
});
344 if (defined($param->{from
})) {
345 push @$args, '-f', $param->{from
};
347 if (defined($param->{target
})) {
348 push @$args, '-t', $param->{target
};
351 my ($list, $status) = $run_pmg_log_tracker->($args);
354 foreach my $e (@$list) {
355 push @$res, $e if !$e->{is_relay
};
358 # hack: return status message in 'changes' attribute
359 $restenv->set_result_attrib('changes', $status) if defined($status);
364 __PACKAGE__-
>register_method({
368 description
=> "Get the detailed syslog entries for a specific mail ID.",
371 permissions
=> { check
=> [ 'admin', 'audit' ] },
373 additionalProperties
=> 0,
375 node
=> get_standard_option
('pve-node'),
376 starttime
=> get_standard_option
('pmg-starttime'),
377 endtime
=> get_standard_option
('pmg-endtime'),
379 description
=> "Mail ID (as returend by the list API).",
389 %$email_log_property_desc,
392 items
=> { type
=> "string" },
399 my $restenv = PMG
::RESTEnvironment-
>get();
403 my ($start, $end) = $get_start_end_time->($param);
405 push @$args, '-s', $start;
406 push @$args, '-e', $end;
408 my $list = $run_pmg_log_tracker->($args, $param->{id
});
411 foreach my $e (@$list) {
412 $res = $e if $e->{id
} eq $param->{id
};
415 die "entry '$param->{id}' not found\n" if !defined($res);