]>
Commit | Line | Data |
---|---|---|
758c7b6b DM |
1 | package PMG::RuleDB::Notify; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
758c7b6b DM |
5 | use DBI; |
6 | use MIME::Body; | |
7 | use MIME::Head; | |
8 | use MIME::Entity; | |
6296d93f | 9 | use MIME::Words qw(encode_mimewords); |
7c142311 | 10 | use Encode qw(decode encode); |
758c7b6b DM |
11 | |
12 | use PVE::SafeSyslog; | |
13 | ||
14 | use PMG::Utils; | |
15 | use PMG::ModGroup; | |
16 | use PMG::RuleDB::Object; | |
74d48a9a | 17 | use PMG::MailQueue; |
758c7b6b DM |
18 | |
19 | use base qw(PMG::RuleDB::Object); | |
20 | ||
21 | sub otype { | |
22 | return 4002; | |
23 | } | |
24 | ||
25 | sub oclass { | |
26 | return 'action'; | |
27 | } | |
28 | ||
29 | sub otype_text { | |
30 | return 'Notification'; | |
31 | } | |
32 | ||
758c7b6b DM |
33 | sub final { |
34 | return 0; | |
35 | } | |
36 | ||
37 | sub priority { | |
38 | return 89; | |
39 | } | |
40 | ||
41 | sub 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; | |
54 | Proxmox Notification: | |
55 | ||
56 | Sender: __SENDER__ | |
57 | Receiver: __RECEIVERS__ | |
58 | Targets: __TARGETS__ | |
59 | ||
60 | Subject: __SUBJECT__ | |
61 | ||
62 | Matching Rule: __RULE__ | |
63 | ||
64 | __RULE_INFO__ | |
65 | ||
66 | __VIRUS_INFO__ | |
67 | __SPAM_INFO__ | |
68 | EOB | |
69 | } | |
70 | $self->{to} = $to; | |
71 | $self->{subject} = $subject; | |
72 | $self->{body} = $body; | |
73 | $self->{attach} = $attach; | |
74 | ||
75 | return $self; | |
76 | } | |
77 | ||
78 | sub 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 | ||
114 | sub 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 | ||
203 | sub 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 |
271 | sub short_desc { |
272 | my $self = shift; | |
273 | ||
274 | return "notify $self->{to}"; | |
275 | } | |
276 | ||
4cb22484 DC |
277 | sub 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 | ||
305 | sub 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 | ||
316 | sub 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 |
325 | 1; |
326 | ||
327 | __END__ | |
328 | ||
329 | =head1 PMG::RuleDB::Notify | |
330 | ||
331 | Notifications. |