]>
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 $new->{size
} = $entry->{size
} // 0,
131 $new->{client
} = $entry->{client
} if defined($entry->{client
});
132 $new->{msgid
} = $entry->{msgid
} if defined($entry->{msgid
});
133 $new->{time} = hex($1) - $timezone;
135 $new->{dstatus
} = $3;
141 $lookup_hash->{$2}->{$5} = $new;
142 } elsif ($line =~ m/^(SMTP|FILTER|QMGR):/) {
143 if ($logids->{$entry->{qid
}}) {
149 die "got unexpected data: $line";
151 } elsif ($state eq 'smtp') {
153 if ($line =~ m/^\s*$/) {
156 } elsif ($line =~ m/^CLIENT:\s+(\S+)$/) {
157 $entry->{client
} = $1;
158 } elsif ($line =~ m/^CTIME:\s+([0-9A-F]+)$/) {
160 } elsif ($line =~ m/^TO:([0-9A-F]+):(T[0-9A-F]+L[0-9A-F]+):([0-9A-Z]):\s+from <([^>]*)>\s+to\s+<([^>]+)>$/) {
162 $e->{client
} = $entry->{client
} if defined($entry->{client
});
163 $e->{time} = hex($1) - $timezone;
169 } elsif ($line =~ m/^LOGS:$/) {
170 if ($logids->{$entry->{id
}}) {
176 die "got unexpected data: $line";
179 die "unknown state '$state'\n";
183 my $cmd = ['/usr/bin/pmg-log-tracker', '-v', '-l', 2000];
185 PVE
::Tools
::run_command
([@$cmd, @$args], timeout
=> 25, outfunc
=> $parser);
187 my $sorted_logs = [];
188 foreach my $le (sort {$a->{linenr
} cmp $b->{linenr
}} @$logs) {
189 push @$sorted_logs, $le->{text
};
192 foreach my $e (@$list) {
193 if (my $id = $e->{qid
}) {
194 if (my $relay = $e->{relay
}) {
195 if (my $ref = $lookup_hash->{$relay}->{$e->{to
}}) {
196 $ref->{is_relay
} = 1;
197 $id = 'Q' . $e->{qid
} . 'R' . $e->{relay
};
198 if ($e->{dstatus
} eq 'A') {
199 $e->{rstatus
} = $ref->{dstatus
};
205 if ($includelog && ($e->{id
} eq $includelog)) {
206 $e->{logs
} = $sorted_logs;
210 return wantarray ?
($list, $status) : $list;
213 my $email_log_property_desc = {
215 description
=> "Unique ID.",
219 description
=> "Sender email address.",
223 description
=> "Receiver email address.",
227 description
=> "Postfix qmgr ID.",
232 description
=> "Delivery timestamp.",
236 description
=> "Delivery status.",
242 description
=> "Delivery status of relayed mail.",
249 description
=> "ID of relayed mail.",
254 description
=> "The size of the raw email.",
259 description
=> "Client address",
264 description
=> "SMTP message ID.",
270 __PACKAGE__-
>register_method({
271 name
=> 'list_mails',
274 description
=> "Read mail list.",
277 permissions
=> { check
=> [ 'admin', 'audit' ] },
279 additionalProperties
=> 0,
281 node
=> get_standard_option
('pve-node'),
282 starttime
=> get_standard_option
('pmg-starttime'),
283 endtime
=> get_standard_option
('pmg-endtime'),
285 description
=> "Only include mails containing this filter string.",
292 description
=> "Sender email address filter.",
299 description
=> "Receiver email address filter.",
306 description
=> "Include NDRs (non delivery reports).",
312 description
=> "Include Greylisted entries.",
323 properties
=> $email_log_property_desc,
325 links
=> [ { rel
=> 'child', href
=> "{id}" } ],
330 my $restenv = PMG
::RESTEnvironment-
>get();
334 my ($start, $end) = $get_start_end_time->($param);
336 push @$args, '-s', $start;
337 push @$args, '-e', $end;
339 push @$args, '-n' if !$param->{ndr
};
341 push @$args, '-g' if !$param->{greylist
};
343 push @$args, '-x', $param->{xfilter
} if defined($param->{xfilter
});
345 if (defined($param->{from
})) {
346 push @$args, '-f', $param->{from
};
348 if (defined($param->{target
})) {
349 push @$args, '-t', $param->{target
};
352 my ($list, $status) = $run_pmg_log_tracker->($args);
355 foreach my $e (@$list) {
356 push @$res, $e if !$e->{is_relay
};
359 # hack: return status message in 'changes' attribute
360 $restenv->set_result_attrib('changes', $status) if defined($status);
365 __PACKAGE__-
>register_method({
369 description
=> "Get the detailed syslog entries for a specific mail ID.",
372 permissions
=> { check
=> [ 'admin', 'audit' ] },
374 additionalProperties
=> 0,
376 node
=> get_standard_option
('pve-node'),
377 starttime
=> get_standard_option
('pmg-starttime'),
378 endtime
=> get_standard_option
('pmg-endtime'),
380 description
=> "Mail ID (as returend by the list API).",
390 %$email_log_property_desc,
393 items
=> { type
=> "string" },
400 my $restenv = PMG
::RESTEnvironment-
>get();
404 my ($start, $end) = $get_start_end_time->($param);
406 push @$args, '-s', $start;
407 push @$args, '-e', $end;
409 my $list = $run_pmg_log_tracker->($args, $param->{id
});
412 foreach my $e (@$list) {
413 $res = $e if $e->{id
} eq $param->{id
};
416 die "entry '$param->{id}' not found\n" if !defined($res);