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