/src/logging-log4cxx/src/main/cpp/smtpappender.cpp
Line | Count | Source |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
3 | | * contributor license agreements. See the NOTICE file distributed with |
4 | | * this work for additional information regarding copyright ownership. |
5 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
6 | | * (the "License"); you may not use this file except in compliance with |
7 | | * the License. You may obtain a copy of the License at |
8 | | * |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | * |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | #include <log4cxx/net/smtpappender.h> |
18 | | #include <log4cxx/level.h> |
19 | | #include <log4cxx/helpers/loglog.h> |
20 | | #include <log4cxx/helpers/optionconverter.h> |
21 | | #include <log4cxx/spi/loggingevent.h> |
22 | | #include <log4cxx/private/string_c11.h> |
23 | | #include <log4cxx/helpers/stringhelper.h> |
24 | | #include <log4cxx/helpers/stringtokenizer.h> |
25 | | #include <log4cxx/helpers/transcoder.h> |
26 | | #include <log4cxx/helpers/loader.h> |
27 | | #if !defined(LOG4CXX) |
28 | | #define LOG4CXX 1 |
29 | | #endif |
30 | | #include <log4cxx/private/log4cxx_private.h> |
31 | | #include <log4cxx/private/appenderskeleton_priv.h> |
32 | | |
33 | | |
34 | | #include <apr_strings.h> |
35 | | #include <vector> |
36 | | |
37 | | using namespace LOG4CXX_NS; |
38 | | using namespace LOG4CXX_NS::helpers; |
39 | | using namespace LOG4CXX_NS::net; |
40 | | using namespace LOG4CXX_NS::spi; |
41 | | |
42 | | #if LOG4CXX_HAVE_LIBESMTP |
43 | | #include <auth-client.h> |
44 | | #include <libesmtp.h> |
45 | | #endif |
46 | | |
47 | | namespace |
48 | | { |
49 | | // RFC 5322 §2.1 defines header fields as CRLF-terminated lines, so an embedded |
50 | | // CR or LF in a configured Subject/From/To/Cc/Bcc value would split it across |
51 | | // header boundaries on the wire — a caller who controls a configured field |
52 | | // (e.g. through ${...} property substitution from an environment variable) |
53 | | // could inject arbitrary additional headers such as Bcc. The library already |
54 | | // owns SMTP wire-format sanitization (see SMTPSession::toAscii, which silently |
55 | | // rewrites non-ASCII to '?'); strip CR/LF in the public setters so the same |
56 | | // boundary is enforced regardless of how the value reaches the appender. |
57 | | LogString stripSmtpControl(const LogString& value, const logchar* field) |
58 | 0 | { |
59 | 0 | if (value.find_first_of(LOG4CXX_STR("\r\n")) == LogString::npos) |
60 | 0 | { |
61 | 0 | return value; |
62 | 0 | } |
63 | 0 | LogString warning(LOG4CXX_STR("SMTPAppender ")); |
64 | 0 | warning.append(field); |
65 | 0 | warning.append(LOG4CXX_STR(" contains CR or LF; stripping to prevent SMTP header injection.")); |
66 | 0 | LogLog::warn(warning); |
67 | 0 | LogString out; |
68 | 0 | out.reserve(value.size()); |
69 | 0 | for (auto ch : value) |
70 | 0 | { |
71 | 0 | if (ch != 0x0D && ch != 0x0A) |
72 | 0 | { |
73 | 0 | out.append(1, ch); |
74 | 0 | } |
75 | 0 | } |
76 | 0 | return out; |
77 | 0 | } |
78 | | } // namespace |
79 | | |
80 | | namespace LOG4CXX_NS |
81 | | { |
82 | | namespace net |
83 | | { |
84 | | // |
85 | | // The following two classes implement an C++ SMTP wrapper over libesmtp. |
86 | | // The same signatures could be implemented over different SMTP implementations |
87 | | // or libesmtp could be combined with libgmime to enable support for non-ASCII |
88 | | // content. |
89 | | |
90 | | #if LOG4CXX_HAVE_LIBESMTP |
91 | | /** |
92 | | * SMTP Session. |
93 | | */ |
94 | | class SMTPSession |
95 | | { |
96 | | public: |
97 | | /** |
98 | | * Create new instance. |
99 | | */ |
100 | | SMTPSession(const LogString& smtpHost, |
101 | | int smtpPort, |
102 | | const LogString& smtpUsername, |
103 | | const LogString& smtpPassword, |
104 | | Pool& p) : session(0), authctx(0), |
105 | | user(toAscii(smtpUsername, p)), |
106 | | pwd(toAscii(smtpPassword, p)) |
107 | | { |
108 | | auth_client_init(); |
109 | | session = smtp_create_session(); |
110 | | |
111 | | if (session == 0) |
112 | | { |
113 | | throw Exception("Could not initialize session."); |
114 | | } |
115 | | |
116 | | std::string host(toAscii(smtpHost, p)); |
117 | | host.append(1, ':'); |
118 | | host.append(p.itoa(smtpPort)); |
119 | | smtp_set_server(session, host.c_str()); |
120 | | |
121 | | authctx = auth_create_context(); |
122 | | auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0); |
123 | | auth_set_interact_cb(authctx, authinteract, (void*) this); |
124 | | |
125 | | if (*user || *pwd) |
126 | | { |
127 | | smtp_auth_set_context(session, authctx); |
128 | | } |
129 | | } |
130 | | |
131 | | ~SMTPSession() |
132 | | { |
133 | | smtp_destroy_session(session); |
134 | | auth_destroy_context(authctx); |
135 | | } |
136 | | |
137 | | void send(Pool& p) |
138 | | { |
139 | | int status = smtp_start_session(session); |
140 | | |
141 | | if (!status) |
142 | | { |
143 | | size_t bufSize = 128; |
144 | | char* buf = p.pstralloc(bufSize); |
145 | | smtp_strerror(smtp_errno(), buf, bufSize); |
146 | | throw Exception(buf); |
147 | | } |
148 | | } |
149 | | |
150 | | operator smtp_session_t() |
151 | | { |
152 | | return session; |
153 | | } |
154 | | |
155 | | static char* toAscii(const LogString& str, Pool& p) |
156 | | { |
157 | | char* buf = p.pstralloc(str.length() + 1); |
158 | | char* current = buf; |
159 | | |
160 | | for (unsigned int c : str) |
161 | | { |
162 | | if (c > 0x7F) |
163 | | { |
164 | | c = '?'; |
165 | | } |
166 | | |
167 | | *current++ = c; |
168 | | } |
169 | | |
170 | | *current = 0; |
171 | | return buf; |
172 | | } |
173 | | |
174 | | private: |
175 | | SMTPSession(SMTPSession&); |
176 | | SMTPSession& operator=(SMTPSession&); |
177 | | smtp_session_t session; |
178 | | auth_context_t authctx; |
179 | | char* user; |
180 | | char* pwd; |
181 | | |
182 | | /** |
183 | | * This method is called if the SMTP server requests authentication. |
184 | | */ |
185 | | static int authinteract(auth_client_request_t request, char** result, int fields, |
186 | | void* arg) |
187 | | { |
188 | | SMTPSession* pThis = (SMTPSession*) arg; |
189 | | |
190 | | for (int i = 0; i < fields; i++) |
191 | | { |
192 | | int flag = request[i].flags & 0x07; |
193 | | |
194 | | if (flag == AUTH_USER) |
195 | | { |
196 | | result[i] = pThis->user; |
197 | | } |
198 | | else if (flag == AUTH_PASS) |
199 | | { |
200 | | result[i] = pThis->pwd; |
201 | | } |
202 | | } |
203 | | |
204 | | return 1; |
205 | | } |
206 | | |
207 | | |
208 | | }; |
209 | | |
210 | | /** |
211 | | * A message in an SMTP session. |
212 | | */ |
213 | | class SMTPMessage |
214 | | { |
215 | | public: |
216 | | SMTPMessage(SMTPSession& session, |
217 | | const LogString& from, |
218 | | const LogString& to, |
219 | | const LogString& cc, |
220 | | const LogString& bcc, |
221 | | const LogString& subject, |
222 | | const LogString msg, Pool& p) |
223 | | { |
224 | | message = smtp_add_message(session); |
225 | | current_len = msg.length(); |
226 | | body = current = toMessage(msg, p); |
227 | | messagecbState = 0; |
228 | | smtp_set_reverse_path(message, toAscii(from, p)); |
229 | | addRecipients(to, "To", p); |
230 | | addRecipients(cc, "Cc", p); |
231 | | addRecipients(bcc, "Bcc", p); |
232 | | |
233 | | if (!subject.empty()) |
234 | | { |
235 | | smtp_set_header(message, "Subject", toAscii(subject, p)); |
236 | | } |
237 | | |
238 | | smtp_set_messagecb(message, messagecb, this); |
239 | | } |
240 | | ~SMTPMessage() |
241 | | { |
242 | | } |
243 | | |
244 | | private: |
245 | | SMTPMessage(const SMTPMessage&); |
246 | | SMTPMessage& operator=(const SMTPMessage&); |
247 | | smtp_message_t message; |
248 | | const char* body; |
249 | | const char* current; |
250 | | size_t current_len; |
251 | | int messagecbState; |
252 | | void addRecipients(const LogString& addresses, const char* field, Pool& p) |
253 | | { |
254 | | if (!addresses.empty()) |
255 | | { |
256 | | char* str = p.pstrdup(toAscii(addresses, p));; |
257 | | smtp_set_header(message, field, NULL, str); |
258 | | char* last; |
259 | | |
260 | | for (char* next = apr_strtok(str, ",", &last); |
261 | | next; |
262 | | next = apr_strtok(NULL, ",", &last)) |
263 | | { |
264 | | smtp_add_recipient(message, next); |
265 | | } |
266 | | } |
267 | | } |
268 | | static const char* toAscii(const LogString& str, Pool& p) |
269 | | { |
270 | | return SMTPSession::toAscii(str, p); |
271 | | } |
272 | | |
273 | | /** |
274 | | * Message bodies can only contain US-ASCII characters and |
275 | | * CR and LFs can only occur together. |
276 | | */ |
277 | | static const char* toMessage(const LogString& str, Pool& p) |
278 | | { |
279 | | // |
280 | | // count the number of carriage returns and line feeds |
281 | | // |
282 | | int feedCount = 0; |
283 | | |
284 | | for (size_t pos = str.find_first_of(LOG4CXX_STR("\n\r")); |
285 | | pos != LogString::npos; |
286 | | pos = str.find_first_of(LOG4CXX_STR("\n\r"), ++pos)) |
287 | | { |
288 | | feedCount++; |
289 | | } |
290 | | |
291 | | // |
292 | | // allocate sufficient space for the modified message |
293 | | char* retval = p.pstralloc(str.length() + feedCount + 1); |
294 | | char* current = retval; |
295 | | char* startOfLine = current; |
296 | | unsigned int ignoreChar = 0; |
297 | | |
298 | | // |
299 | | // iterator through message |
300 | | // |
301 | | for (unsigned int c : str) |
302 | | { |
303 | | // |
304 | | // replace non-ASCII characters with '?' |
305 | | // |
306 | | if (c > 0x7F) |
307 | | { |
308 | | *current++ = 0x3F; // '?' |
309 | | } |
310 | | else if (c == 0x0A || c == 0x0D) |
311 | | { |
312 | | // |
313 | | // replace any stray CR or LF with CRLF |
314 | | // reset start of line |
315 | | if (c == ignoreChar && current == startOfLine) |
316 | | ignoreChar = 0; |
317 | | else |
318 | | { |
319 | | *current++ = 0x0D; |
320 | | *current++ = 0x0A; |
321 | | startOfLine = current; |
322 | | ignoreChar = (c == 0x0A ? 0x0D : 0x0A); |
323 | | } |
324 | | } |
325 | | else |
326 | | { |
327 | | // |
328 | | // truncate any lines to 1000 characters (including CRLF) |
329 | | // as required by RFC. |
330 | | if (current < startOfLine + 998) |
331 | | { |
332 | | *current++ = (char) c; |
333 | | } |
334 | | } |
335 | | } |
336 | | |
337 | | *current = 0; |
338 | | return retval; |
339 | | } |
340 | | |
341 | | /** |
342 | | * Callback for message. |
343 | | */ |
344 | | static const char* messagecb(void** ctx, int* len, void* arg) |
345 | | { |
346 | | *ctx = 0; |
347 | | const char* retval = 0; |
348 | | SMTPMessage* pThis = (SMTPMessage*) arg; |
349 | | |
350 | | // rewind message |
351 | | if (len == NULL) |
352 | | { |
353 | | pThis->current = pThis->body; |
354 | | } |
355 | | else |
356 | | { |
357 | | // we are asked for headers, but we don't have any |
358 | | if ((pThis->messagecbState)++ == 0) |
359 | | { |
360 | | return NULL; |
361 | | } |
362 | | |
363 | | if (pThis->current) |
364 | | { |
365 | | *len = strnlen_s(pThis->current, pThis->current_len); |
366 | | } |
367 | | |
368 | | retval = pThis->current; |
369 | | pThis->current = 0; |
370 | | } |
371 | | |
372 | | return retval; |
373 | | } |
374 | | |
375 | | }; |
376 | | #endif |
377 | | |
378 | | class LOG4CXX_EXPORT DefaultEvaluator |
379 | | #if LOG4CXX_ABI_VERSION <= 15 |
380 | | : public virtual spi::TriggeringEventEvaluator |
381 | | , public virtual helpers::Object |
382 | | #else |
383 | | : public spi::TriggeringEventEvaluator |
384 | | #endif |
385 | | { |
386 | | public: |
387 | | DECLARE_LOG4CXX_OBJECT(DefaultEvaluator) |
388 | 0 | BEGIN_LOG4CXX_CAST_MAP() |
389 | 0 | LOG4CXX_CAST_ENTRY(DefaultEvaluator) |
390 | 0 | LOG4CXX_CAST_ENTRY(spi::TriggeringEventEvaluator) |
391 | 0 | END_LOG4CXX_CAST_MAP() |
392 | | |
393 | | DefaultEvaluator(); |
394 | | |
395 | | /** |
396 | | Is this <code>event</code> the e-mail triggering event? |
397 | | <p>This method returns <code>true</code>, if the event level |
398 | | has ERROR level or higher. Otherwise it returns |
399 | | <code>false</code>. |
400 | | */ |
401 | | bool isTriggeringEvent(const spi::LoggingEventPtr& event) override; |
402 | | private: |
403 | | DefaultEvaluator(const DefaultEvaluator&); |
404 | | DefaultEvaluator& operator=(const DefaultEvaluator&); |
405 | | }; // class DefaultEvaluator |
406 | | |
407 | | } |
408 | | } |
409 | | |
410 | | IMPLEMENT_LOG4CXX_OBJECT(DefaultEvaluator) |
411 | | IMPLEMENT_LOG4CXX_OBJECT(SMTPAppender) |
412 | | |
413 | | struct SMTPAppender::SMTPPriv : public AppenderSkeletonPrivate |
414 | | { |
415 | | SMTPPriv() : |
416 | 0 | AppenderSkeletonPrivate(), |
417 | 0 | smtpPort(25), |
418 | 0 | bufferSize(512), |
419 | 0 | locationInfo(false), |
420 | 0 | cb(bufferSize), |
421 | 0 | evaluator(new DefaultEvaluator()) {} |
422 | | |
423 | | SMTPPriv(spi::TriggeringEventEvaluatorPtr evaluator) : |
424 | 0 | AppenderSkeletonPrivate(), |
425 | 0 | smtpPort(25), |
426 | 0 | bufferSize(512), |
427 | 0 | locationInfo(false), |
428 | 0 | cb(bufferSize), |
429 | 0 | evaluator(evaluator) {} |
430 | | |
431 | | LogString to; |
432 | | LogString cc; |
433 | | LogString bcc; |
434 | | LogString from; |
435 | | LogString subject; |
436 | | LogString smtpHost; |
437 | | LogString smtpUsername; |
438 | | LogString smtpPassword; |
439 | | int smtpPort; |
440 | | int bufferSize; // 512 |
441 | | bool locationInfo; |
442 | | helpers::CyclicBuffer cb; |
443 | | spi::TriggeringEventEvaluatorPtr evaluator; |
444 | | }; |
445 | | |
446 | 0 | #define _priv static_cast<SMTPPriv*>(m_priv.get()) |
447 | | |
448 | | DefaultEvaluator::DefaultEvaluator() |
449 | 0 | { |
450 | 0 | } Unexecuted instantiation: log4cxx::net::DefaultEvaluator::DefaultEvaluator() Unexecuted instantiation: log4cxx::net::DefaultEvaluator::DefaultEvaluator() |
451 | | |
452 | | bool DefaultEvaluator::isTriggeringEvent(const spi::LoggingEventPtr& event) |
453 | 0 | { |
454 | 0 | return event->getLevel()->isGreaterOrEqual(Level::getError()); |
455 | 0 | } |
456 | | |
457 | | SMTPAppender::SMTPAppender() |
458 | 0 | : AppenderSkeleton (std::make_unique<SMTPPriv>()) |
459 | 0 | { |
460 | 0 | } Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender() Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender() |
461 | | |
462 | | /** |
463 | | Use <code>evaluator</code> passed as parameter as the |
464 | | TriggeringEventEvaluator for this SMTPAppender. */ |
465 | | SMTPAppender::SMTPAppender(spi::TriggeringEventEvaluatorPtr evaluator) |
466 | 0 | : AppenderSkeleton (std::make_unique<SMTPPriv>(evaluator)) |
467 | 0 | { |
468 | 0 | } Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender(std::__1::shared_ptr<log4cxx::spi::TriggeringEventEvaluator>) Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender(std::__1::shared_ptr<log4cxx::spi::TriggeringEventEvaluator>) |
469 | | |
470 | | SMTPAppender::~SMTPAppender() |
471 | 0 | { |
472 | 0 | _priv->setClosed(); |
473 | 0 | } |
474 | | |
475 | | bool SMTPAppender::requiresLayout() const |
476 | 0 | { |
477 | 0 | return true; |
478 | 0 | } |
479 | | |
480 | | LogString SMTPAppender::getFrom() const |
481 | 0 | { |
482 | 0 | return _priv->from; |
483 | 0 | } |
484 | | |
485 | | void SMTPAppender::setFrom(const LogString& newVal) |
486 | 0 | { |
487 | 0 | _priv->from = stripSmtpControl(newVal, LOG4CXX_STR("From")); |
488 | 0 | } |
489 | | |
490 | | |
491 | | LogString SMTPAppender::getSubject() const |
492 | 0 | { |
493 | 0 | return _priv->subject; |
494 | 0 | } |
495 | | |
496 | | void SMTPAppender::setSubject(const LogString& newVal) |
497 | 0 | { |
498 | 0 | _priv->subject = stripSmtpControl(newVal, LOG4CXX_STR("Subject")); |
499 | 0 | } |
500 | | |
501 | | LogString SMTPAppender::getSMTPHost() const |
502 | 0 | { |
503 | 0 | return _priv->smtpHost; |
504 | 0 | } |
505 | | |
506 | | void SMTPAppender::setSMTPHost(const LogString& newVal) |
507 | 0 | { |
508 | 0 | _priv->smtpHost = newVal; |
509 | 0 | } |
510 | | |
511 | | int SMTPAppender::getSMTPPort() const |
512 | 0 | { |
513 | 0 | return _priv->smtpPort; |
514 | 0 | } |
515 | | |
516 | | void SMTPAppender::setSMTPPort(int newVal) |
517 | 0 | { |
518 | 0 | _priv->smtpPort = newVal; |
519 | 0 | } |
520 | | |
521 | | bool SMTPAppender::getLocationInfo() const |
522 | 0 | { |
523 | 0 | return _priv->locationInfo; |
524 | 0 | } |
525 | | |
526 | | void SMTPAppender::setLocationInfo(bool newVal) |
527 | 0 | { |
528 | 0 | _priv->locationInfo = newVal; |
529 | 0 | } |
530 | | |
531 | | LogString SMTPAppender::getSMTPUsername() const |
532 | 0 | { |
533 | 0 | return _priv->smtpUsername; |
534 | 0 | } |
535 | | |
536 | | void SMTPAppender::setSMTPUsername(const LogString& newVal) |
537 | 0 | { |
538 | 0 | _priv->smtpUsername = newVal; |
539 | 0 | } |
540 | | |
541 | | LogString SMTPAppender::getSMTPPassword() const |
542 | 0 | { |
543 | 0 | return _priv->smtpPassword; |
544 | 0 | } |
545 | | |
546 | | void SMTPAppender::setSMTPPassword(const LogString& newVal) |
547 | 0 | { |
548 | 0 | _priv->smtpPassword = newVal; |
549 | 0 | } |
550 | | |
551 | | |
552 | | |
553 | | |
554 | | |
555 | | void SMTPAppender::setOption(const LogString& option, |
556 | | const LogString& value) |
557 | 0 | { |
558 | 0 | if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize"))) |
559 | 0 | { |
560 | 0 | setBufferSize(OptionConverter::toInt(value, 512)); |
561 | 0 | } |
562 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("EVALUATORCLASS"), LOG4CXX_STR("evaluatorclass"))) |
563 | 0 | { |
564 | 0 | setEvaluatorClass(value); |
565 | 0 | } |
566 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FROM"), LOG4CXX_STR("from"))) |
567 | 0 | { |
568 | 0 | setFrom(value); |
569 | 0 | } |
570 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPHOST"), LOG4CXX_STR("smtphost"))) |
571 | 0 | { |
572 | 0 | setSMTPHost(value); |
573 | 0 | } |
574 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPUSERNAME"), LOG4CXX_STR("smtpusername"))) |
575 | 0 | { |
576 | 0 | setSMTPUsername(value); |
577 | 0 | } |
578 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPASSWORD"), LOG4CXX_STR("smtppassword"))) |
579 | 0 | { |
580 | 0 | setSMTPPassword(value); |
581 | 0 | } |
582 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SUBJECT"), LOG4CXX_STR("subject"))) |
583 | 0 | { |
584 | 0 | setSubject(value); |
585 | 0 | } |
586 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("TO"), LOG4CXX_STR("to"))) |
587 | 0 | { |
588 | 0 | setTo(value); |
589 | 0 | } |
590 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CC"), LOG4CXX_STR("cc"))) |
591 | 0 | { |
592 | 0 | setCc(value); |
593 | 0 | } |
594 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BCC"), LOG4CXX_STR("bcc"))) |
595 | 0 | { |
596 | 0 | setBcc(value); |
597 | 0 | } |
598 | 0 | else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPORT"), LOG4CXX_STR("smtpport"))) |
599 | 0 | { |
600 | 0 | setSMTPPort(OptionConverter::toInt(value, 25)); |
601 | 0 | } |
602 | 0 | else |
603 | 0 | { |
604 | 0 | AppenderSkeleton::setOption(option, value); |
605 | 0 | } |
606 | 0 | } |
607 | | |
608 | | |
609 | | bool SMTPAppender::asciiCheck(const LogString& value, const LogString& field) |
610 | 0 | { |
611 | 0 | for (unsigned int item : value) |
612 | 0 | { |
613 | 0 | if (0x7F < item) |
614 | 0 | { |
615 | 0 | LogLog::warn(field + LOG4CXX_STR(" contains non-ASCII character")); |
616 | 0 | return false; |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | 0 | return true; |
621 | 0 | } |
622 | | |
623 | | /** |
624 | | Activate the specified options, such as the smtp host, the |
625 | | recipient, from, etc. */ |
626 | | void SMTPAppender::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS ) |
627 | 0 | { |
628 | 0 | if (_priv->layout == 0) |
629 | 0 | { |
630 | 0 | _priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("].")); |
631 | 0 | } |
632 | |
|
633 | 0 | if (_priv->evaluator == 0) |
634 | 0 | { |
635 | 0 | _priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") + |
636 | 0 | _priv->name + LOG4CXX_STR("].")); |
637 | 0 | } |
638 | |
|
639 | 0 | if (_priv->smtpHost.empty()) |
640 | 0 | { |
641 | 0 | _priv->errorHandler->error(LOG4CXX_STR("No smtpHost is set for appender [") + |
642 | 0 | _priv->name + LOG4CXX_STR("].")); |
643 | 0 | } |
644 | |
|
645 | 0 | if (_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty()) |
646 | 0 | { |
647 | 0 | _priv->errorHandler->error(LOG4CXX_STR("No recipient address is set for appender [") + |
648 | 0 | _priv->name + LOG4CXX_STR("].")); |
649 | 0 | } |
650 | |
|
651 | 0 | asciiCheck(_priv->to, LOG4CXX_STR("to")); |
652 | 0 | asciiCheck(_priv->cc, LOG4CXX_STR("cc")); |
653 | 0 | asciiCheck(_priv->bcc, LOG4CXX_STR("bcc")); |
654 | 0 | asciiCheck(_priv->from, LOG4CXX_STR("from")); |
655 | |
|
656 | 0 | #if !LOG4CXX_HAVE_LIBESMTP |
657 | 0 | _priv->errorHandler->error(LOG4CXX_STR("log4cxx built without SMTP support.")); |
658 | 0 | #endif |
659 | 0 | } |
660 | | |
661 | | /** |
662 | | Perform SMTPAppender specific appending actions, mainly adding |
663 | | the event to a cyclic buffer and checking if the event triggers |
664 | | an e-mail to be sent. */ |
665 | | void SMTPAppender::append( LOG4CXX_APPEND_FORMAL_PARAMETERS ) |
666 | 0 | { |
667 | 0 | if (!checkEntryConditions()) |
668 | 0 | { |
669 | 0 | return; |
670 | 0 | } |
671 | | |
672 | | // Get a copy of this thread's diagnostic context |
673 | 0 | event->LoadDC(); |
674 | |
|
675 | 0 | _priv->cb.add(event); |
676 | |
|
677 | 0 | if (_priv->evaluator->isTriggeringEvent(event)) |
678 | 0 | { |
679 | 0 | Pool p; |
680 | 0 | sendBuffer(p); |
681 | 0 | } |
682 | 0 | } |
683 | | |
684 | | /** |
685 | | This method determines if there is a sense in attempting to append. |
686 | | <p>It checks whether there is a set output target and also if |
687 | | there is a set layout. If these checks fail, then the boolean |
688 | | value <code>false</code> is returned. */ |
689 | | bool SMTPAppender::checkEntryConditions() |
690 | 0 | { |
691 | | #if LOG4CXX_HAVE_LIBESMTP |
692 | | |
693 | | if ((_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty()) || _priv->from.empty() || _priv->smtpHost.empty()) |
694 | | { |
695 | | _priv->errorHandler->error(LOG4CXX_STR("Message not configured.")); |
696 | | return false; |
697 | | } |
698 | | |
699 | | if (_priv->evaluator == 0) |
700 | | { |
701 | | _priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") + |
702 | | _priv->name + LOG4CXX_STR("].")); |
703 | | return false; |
704 | | } |
705 | | |
706 | | |
707 | | if (_priv->layout == 0) |
708 | | { |
709 | | _priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("].")); |
710 | | return false; |
711 | | } |
712 | | |
713 | | return true; |
714 | | #else |
715 | 0 | return false; |
716 | 0 | #endif |
717 | 0 | } |
718 | | |
719 | | |
720 | | |
721 | | void SMTPAppender::close() |
722 | 0 | { |
723 | 0 | _priv->setClosed(); |
724 | 0 | } |
725 | | |
726 | | LogString SMTPAppender::getTo() const |
727 | 0 | { |
728 | 0 | return _priv->to; |
729 | 0 | } |
730 | | |
731 | | void SMTPAppender::setTo(const LogString& addressStr) |
732 | 0 | { |
733 | 0 | _priv->to = stripSmtpControl(addressStr, LOG4CXX_STR("To")); |
734 | 0 | } |
735 | | |
736 | | LogString SMTPAppender::getCc() const |
737 | 0 | { |
738 | 0 | return _priv->cc; |
739 | 0 | } |
740 | | |
741 | | void SMTPAppender::setCc(const LogString& addressStr) |
742 | 0 | { |
743 | 0 | _priv->cc = stripSmtpControl(addressStr, LOG4CXX_STR("Cc")); |
744 | 0 | } |
745 | | |
746 | | LogString SMTPAppender::getBcc() const |
747 | 0 | { |
748 | 0 | return _priv->bcc; |
749 | 0 | } |
750 | | |
751 | | void SMTPAppender::setBcc(const LogString& addressStr) |
752 | 0 | { |
753 | 0 | _priv->bcc = stripSmtpControl(addressStr, LOG4CXX_STR("Bcc")); |
754 | 0 | } |
755 | | |
756 | | /** |
757 | | Send the contents of the cyclic buffer as an e-mail message. |
758 | | */ |
759 | | void SMTPAppender::sendBuffer(Pool& p) |
760 | 0 | { |
761 | | #if LOG4CXX_HAVE_LIBESMTP |
762 | | |
763 | | // Note: this code already owns the monitor for this |
764 | | // appender. This frees us from needing to synchronize on 'cb'. |
765 | | try |
766 | | { |
767 | | LogString sbuf; |
768 | | _priv->layout->appendHeader(sbuf); |
769 | | |
770 | | int len = _priv->cb.length(); |
771 | | |
772 | | for (int i = 0; i < len; i++) |
773 | | { |
774 | | LoggingEventPtr event = _priv->cb.get(); |
775 | | _priv->layout->format(sbuf, event); |
776 | | } |
777 | | |
778 | | _priv->layout->appendFooter(sbuf); |
779 | | |
780 | | SMTPSession session(_priv->smtpHost, _priv->smtpPort, _priv->smtpUsername, _priv->smtpPassword, p); |
781 | | |
782 | | SMTPMessage message(session, _priv->from, _priv->to, _priv->cc, |
783 | | _priv->bcc, _priv->subject, sbuf, p); |
784 | | |
785 | | session.send(p); |
786 | | |
787 | | } |
788 | | catch (std::exception& e) |
789 | | { |
790 | | _priv->errorHandler->error(LOG4CXX_STR("Error occured while sending e-mail to [") + _priv->smtpHost + LOG4CXX_STR("]."), e, 0); |
791 | | } |
792 | | |
793 | | #endif |
794 | 0 | } |
795 | | |
796 | | /** |
797 | | Returns value of the <b>EvaluatorClass</b> option. |
798 | | */ |
799 | | LogString SMTPAppender::getEvaluatorClass() |
800 | 0 | { |
801 | 0 | return _priv->evaluator == 0 ? LogString() : _priv->evaluator->getClass().getName(); |
802 | 0 | } |
803 | | |
804 | | LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr SMTPAppender::getEvaluator() const |
805 | 0 | { |
806 | 0 | return _priv->evaluator; |
807 | 0 | } |
808 | | |
809 | | void SMTPAppender::setEvaluator(LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr& trigger) |
810 | 0 | { |
811 | 0 | _priv->evaluator = trigger; |
812 | 0 | } |
813 | | |
814 | | /** |
815 | | The <b>BufferSize</b> option takes a positive integer |
816 | | representing the maximum number of logging events to collect in a |
817 | | cyclic buffer. When the <code>BufferSize</code> is reached, |
818 | | oldest events are deleted as new events are added to the |
819 | | buffer. By default the size of the cyclic buffer is 512 events. |
820 | | */ |
821 | | void SMTPAppender::setBufferSize(int sz) |
822 | 0 | { |
823 | 0 | if (sz < 1) |
824 | 0 | { |
825 | 0 | sz = 1; |
826 | 0 | } |
827 | |
|
828 | 0 | _priv->bufferSize = sz; |
829 | 0 | _priv->cb.resize(sz); |
830 | 0 | } |
831 | | |
832 | | /** |
833 | | The <b>EvaluatorClass</b> option takes a string value |
834 | | representing the name of the class implementing the {@link |
835 | | TriggeringEventEvaluator} interface. A corresponding object will |
836 | | be instantiated and assigned as the triggering event evaluator |
837 | | for the SMTPAppender. |
838 | | */ |
839 | | void SMTPAppender::setEvaluatorClass(const LogString& value) |
840 | 0 | { |
841 | 0 | ObjectPtr obj = ObjectPtr(Loader::loadClass(value).newInstance()); |
842 | 0 | _priv->evaluator = LOG4CXX_NS::cast<TriggeringEventEvaluator>(obj); |
843 | 0 | } |
844 | | |
845 | | int SMTPAppender::getBufferSize() const |
846 | 0 | { |
847 | 0 | return _priv->bufferSize; |
848 | 0 | } |