]> git.proxmox.com Git - pmg-api.git/blame - src/PMG/RuleDB/Notify.pm
ruledb: properly substitute prox_vars in headers
[pmg-api.git] / src / PMG / RuleDB / Notify.pm
CommitLineData
758c7b6b
DM
1package PMG::RuleDB::Notify;
2
3use strict;
4use warnings;
758c7b6b
DM
5use DBI;
6use MIME::Body;
7use MIME::Head;
8use MIME::Entity;
6296d93f 9use MIME::Words qw(encode_mimewords);
7c142311 10use Encode qw(decode encode);
758c7b6b
DM
11
12use PVE::SafeSyslog;
13
14use PMG::Utils;
15use PMG::ModGroup;
16use PMG::RuleDB::Object;
74d48a9a 17use PMG::MailQueue;
758c7b6b
DM
18
19use base qw(PMG::RuleDB::Object);
20
21sub otype {
22 return 4002;
23}
24
25sub oclass {
26 return 'action';
27}
28
29sub otype_text {
30 return 'Notification';
31}
32
758c7b6b
DM
33sub final {
34 return 0;
35}
36
37sub priority {
38 return 89;
39}
40
41sub new {
42 my ($type, $to, $subject, $body, $attach, $ogroup) = @_;
8d39c4fc 43
758c7b6b 44 my $class = ref($type) || $type;
8d39c4fc 45
7a2cf7e6 46 my $self = $class->SUPER::new($class->otype(), $ogroup);
758c7b6b
DM
47
48 $to //= '__ADMIN__';
49 $attach //= 'N';
50 $subject //= 'Notification: __SUBJECT__';
8d39c4fc 51
758c7b6b
DM
52 if (!defined($body)) {
53 $body = <<EOB;
54Proxmox Notification:
55
56Sender: __SENDER__
57Receiver: __RECEIVERS__
58Targets: __TARGETS__
59
60Subject: __SUBJECT__
61
62Matching Rule: __RULE__
63
64__RULE_INFO__
65
66__VIRUS_INFO__
67__SPAM_INFO__
68EOB
69 }
70 $self->{to} = $to;
71 $self->{subject} = $subject;
72 $self->{body} = $body;
73 $self->{attach} = $attach;
74
75 return $self;
76}
77
78sub load_attr {
79 my ($type, $ruledb, $id, $ogroup, $value) = @_;
8d39c4fc 80
758c7b6b
DM
81 my $class = ref($type) || $type;
82
9ef3f143 83 defined($value) || die "undefined object attribute: ERROR";
8d39c4fc 84
7c142311 85 my ($raw_subject, $raw_body, $attach);
758c7b6b
DM
86
87 my $sth = $ruledb->{dbh}->prepare(
88 "SELECT * FROM Attribut WHERE Object_ID = ?");
89
90 $sth->execute($id);
91
92 while (my $ref = $sth->fetchrow_hashref()) {
7c142311
DM
93 $raw_subject = $ref->{value} if $ref->{name} eq 'subject';
94 $raw_body = $ref->{value} if $ref->{name} eq 'body';
758c7b6b
DM
95 $attach = $ref->{value} if $ref->{name} eq 'attach';
96 }
8d39c4fc 97
758c7b6b 98 $sth->finish();
7c142311
DM
99
100 # Note: db stores binary (ascii) data, so we need to convert to UTF-8 manually
101 my $obj = $class->new($value,
102 decode('UTF-8', $raw_subject),
103 decode('UTF-8', $raw_body),
104 $attach, $ogroup);
758c7b6b
DM
105 $obj->{id} = $id;
106
7c142311 107 # Note: Digest::SHA does not work with wide UTF8 characters
758c7b6b 108 $obj->{digest} = Digest::SHA::sha1_hex(
7c142311 109 $id, $obj->{to}, $raw_subject, $raw_body, $obj->{attach}, $ogroup);
758c7b6b
DM
110
111 return $obj;
112}
113
114sub save {
115 my ($self, $ruledb, $no_trans) = @_;
116
9ef3f143
DM
117 defined($self->{ogroup}) || die "undefined object attribute: ERROR";
118 defined($self->{to}) || die "undefined object attribute: ERROR";
119 defined($self->{subject}) || die "undefined object attribute: ERROR";
120 defined($self->{body}) || die "undefined object attribute: ERROR";
758c7b6b 121
aa376168
DM
122 $self->{attach} //= 'N';
123
7c142311
DM
124 # Note: db stores binary (ascii) data, so we need to convert from UTF-8 manually
125
758c7b6b
DM
126 if (defined ($self->{id})) {
127 # update
8d39c4fc 128
758c7b6b
DM
129 eval {
130 $ruledb->{dbh}->begin_work if !$no_trans;
131
132 $ruledb->{dbh}->do(
8d39c4fc 133 "UPDATE Object SET Value = ? WHERE ID = ?",
758c7b6b
DM
134 undef, $self->{to}, $self->{id});
135
136 $ruledb->{dbh}->do(
137 "UPDATE Attribut SET Value = ? " .
8d39c4fc 138 "WHERE Name = ? and Object_ID = ?",
7c142311 139 undef, encode('UTF-8', $self->{subject}), 'subject', $self->{id});
758c7b6b
DM
140
141 $ruledb->{dbh}->do(
142 "UPDATE Attribut SET Value = ? " .
8d39c4fc 143 "WHERE Name = ? and Object_ID = ?",
7c142311 144 undef, encode('UTF-8', $self->{body}), 'body', $self->{id});
758c7b6b
DM
145
146 $ruledb->{dbh}->do(
147 "UPDATE Attribut SET Value = ? " .
8d39c4fc 148 "WHERE Name = ? and Object_ID = ?",
758c7b6b 149 undef, $self->{attach}, 'attach', $self->{id});
8d39c4fc 150
758c7b6b
DM
151 $ruledb->{dbh}->commit if !$no_trans;
152 };
153 if (my $err = $@) {
154 die $err if !$no_trans;
155 $ruledb->{dbh}->rollback;
156 syslog('err', $err);
157 return undef;
158 }
159
160 } else {
161 # insert
162
163 $ruledb->{dbh}->begin_work if !$no_trans;
164
165 eval {
166
167 my $sth = $ruledb->{dbh}->prepare(
168 "INSERT INTO Object (Objectgroup_ID, ObjectType, Value) " .
169 "VALUES (?, ?, ?);");
170
171 $sth->execute($self->ogroup, $self->otype, $self->{to});
172
8d39c4fc
TL
173 $self->{id} = PMG::Utils::lastid($ruledb->{dbh}, 'object_id_seq');
174
758c7b6b
DM
175 $sth->finish();
176
8d39c4fc 177 $ruledb->{dbh}->do("INSERT INTO Attribut " .
758c7b6b
DM
178 "(Object_ID, Name, Value) " .
179 "VALUES (?, ?, ?)", undef,
7c142311 180 $self->{id}, 'subject', encode('UTF-8', $self->{subject}));
8d39c4fc 181 $ruledb->{dbh}->do("INSERT INTO Attribut " .
758c7b6b
DM
182 "(Object_ID, Name, Value) " .
183 "VALUES (?, ?, ?)", undef,
7c142311 184 $self->{id}, 'body', encode('UTF-8', $self->{body}));
8d39c4fc 185 $ruledb->{dbh}->do("INSERT INTO Attribut " .
758c7b6b
DM
186 "(Object_ID, Name, Value) " .
187 "VALUES (?, ?, ?)", undef,
188 $self->{id}, 'attach', $self->{attach});
189
190 $ruledb->{dbh}->commit if !$no_trans;
191 };
192 if (my $err = $@) {
193 die $err if !$no_trans;
194 $ruledb->{dbh}->rollback;
195 syslog('err', $err);
196 return undef;
197 }
198 }
8d39c4fc 199
758c7b6b
DM
200 return $self->{id};
201}
202
203sub execute {
8d39c4fc 204 my ($self, $queue, $ruledb, $mod_group, $targets,
758c7b6b
DM
205 $msginfo, $vars, $marks) = @_;
206
207 my $original;
208
209 my $from = 'postmaster';
210
6c65ab40 211 my $rulename = $vars->{RULE} // 'unknown';
365d5b95 212
758c7b6b 213 my $body = PMG::Utils::subst_values($self->{body}, $vars);
3650871c
SI
214 my $subject = PMG::Utils::subst_values_for_header($self->{subject}, $vars);
215 my $to = PMG::Utils::subst_values_for_header($self->{to}, $vars);
758c7b6b
DM
216
217 if ($to =~ m/^\s*$/) {
218 # this happens if a notification is triggered by bounce mails
219 # which notifies the sender <> - we just log and then ignore it
365d5b95 220 syslog('info', "%s: notify <> (rule: %s, ignored)", $queue->{logid}, $rulename);
758c7b6b
DM
221 return;
222 }
223
224 $to =~ s/[;,]/ /g;
225 $to =~ s/\s+/,/g;
226
227 my $top = MIME::Entity->build(
45102dfd
SI
228 Encoding => 'quoted-printable',
229 Charset => 'UTF-8',
758c7b6b
DM
230 From => $from,
231 To => $to,
6296d93f 232 Subject => encode_mimewords(encode('UTF-8', $subject), "Charset" => "UTF-8"),
45102dfd 233 Data => encode('UTF-8', $body));
758c7b6b
DM
234
235 if ($self->{attach} eq 'O') {
236 # attach original mail
74d48a9a
DM
237 my $spooldir = $PMG::MailQueue::spooldir;
238 my $path = "$spooldir/active/$queue->{uid}";
758c7b6b
DM
239 $original = $top->attach(
240 Path => $path,
241 Filename => "original_message.eml",
242 Type => "message/rfc822",);
243 }
244
245 if ($msginfo->{testmode}) {
246 my $fh = $msginfo->{test_fh};
247 print $fh "notify: $self->{to}\n";
248 print $fh "notify content:\n";
8d39c4fc 249
758c7b6b 250 if ($self->{attach} eq 'O') {
1359baef 251 # make result reproducible for regression testing
8d39c4fc 252 $top->head->replace('content-type',
758c7b6b
DM
253 'multipart/mixed; boundary="---=_1234567"');
254 }
255 $top->print ($fh);
256 print $fh "notify end\n";
257 } else {
258 my @targets = split(/\s*,\s*/, $to);
259 my $qid = PMG::Utils::reinject_mail(
260 $top, $from, \@targets, undef, $msginfo->{fqdn});
261 foreach (@targets) {
262 if ($qid) {
365d5b95 263 syslog('info', "%s: notify <%s> (rule: %s, %s)", $queue->{logid}, $_, $rulename, $qid);
758c7b6b 264 } else {
365d5b95 265 syslog ('err', "%s: notify <%s> (rule: %s) failed", $queue->{logid}, $_, $rulename);
758c7b6b
DM
266 }
267 }
268 }
269}
270
758c7b6b
DM
271sub short_desc {
272 my $self = shift;
273
274 return "notify $self->{to}";
275}
276
4cb22484
DC
277sub properties {
278 my ($class) = @_;
279
280 return {
281 to => {
282 description => "The Receiver E-Mail address",
283 type => 'string',
284 maxLength => 200,
285 },
286 subject => {
287 description => "The Notification subject",
288 type => 'string',
289 maxLength => 100,
290 },
291 attach => {
292 description => "Attach original E-Mail",
293 type => 'boolean',
294 optional => 1,
295 default => 0,
296 },
297 body => {
298 description => "The Notification Body",
299 type => 'string',
300 maxLength => 2048
301 }
302 };
303}
304
305sub get {
306 my ($self) = @_;
307
308 return {
309 to => $self->{to},
310 subject => $self->{subject},
311 body => $self->{body},
312 attach => ($self->{attach} eq 'O') ? 1 : 0,
313 };
314}
315
316sub update {
317 my ($self, $param) = @_;
318
319 $self->{to} = $param->{to};
320 $self->{subject} = $param->{subject};
321 $self->{body} = $param->{body};
aa376168 322 $self->{attach} = $param->{attach} ? 'O' : 'N';
4cb22484
DC
323}
324
758c7b6b
DM
3251;
326
327__END__
328
329=head1 PMG::RuleDB::Notify
330
331Notifications.