Coverage Report

Created: 2026-09-01 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
79
} // namespace
80
81
namespace LOG4CXX_NS
82
{
83
namespace net
84
{
85
//
86
//   The following two classes implement an C++ SMTP wrapper over libesmtp.
87
//   The same signatures could be implemented over different SMTP implementations
88
//   or libesmtp could be combined with libgmime to enable support for non-ASCII
89
//   content.
90
91
#if LOG4CXX_HAVE_LIBESMTP
92
/**
93
 *   SMTP Session.
94
 */
95
class SMTPSession
96
{
97
  public:
98
    /**
99
    *   Create new instance.
100
    */
101
    SMTPSession(const LogString& smtpHost,
102
      int smtpPort,
103
      const LogString& smtpUsername,
104
      const LogString& smtpPassword,
105
      bool allowPlainTextAuth,
106
      Pool& p
107
      )
108
      : user{toAscii(smtpUsername, p)}
109
      , pwd{toAscii(smtpPassword, p)}
110
    {
111
      auth_client_init();
112
      session = smtp_create_session();
113
      if (session == 0)
114
      {
115
        throw Exception("Could not initialize session.");
116
      }
117
118
      std::string host(toAscii(smtpHost, p));
119
      host.append(1, ':');
120
      host.append(p.itoa(smtpPort));
121
      smtp_set_server(session, host.c_str());
122
      smtp_set_monitorcb(session, monitor_cb, (void*)this, 1);
123
      smtp_set_eventcb(session, event_cb, (void*)this);
124
125
      authctx = auth_create_context();
126
      auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
127
      auth_set_interact_cb(authctx, authinteract, (void*) this);
128
129
      if (*user || *pwd)
130
      {
131
        // Secure by default: never send AUTH credentials over an
132
        // unencrypted connection. Require STARTTLS before
133
        // authenticating unless the operator explicitly opted in
134
        // to plain-text authentication via the
135
        // AllowPlainTextAuthentication option.
136
        if (!allowPlainTextAuth && !smtp_starttls_enable(session, Starttls_REQUIRED))
137
        {
138
          // The destructor does not run when a constructor throws
139
          smtp_destroy_session(session);
140
          auth_destroy_context(authctx);
141
          throw Exception("SMTPAppender: STARTTLS is unavailable in this libESMTP build;"
142
            " refusing to send SMTP credentials in clear text."
143
            " Set AllowPlainTextAuthentication=true to override.");
144
        }
145
        smtp_auth_set_context(session, authctx);
146
      }
147
    }
148
149
    ~SMTPSession()
150
    {
151
      smtp_destroy_session(session);
152
      auth_destroy_context(authctx);
153
    }
154
155
    void send()
156
    {
157
      int status = smtp_start_session(session);
158
159
      if (!status)
160
      {
161
        static const size_t smtp_msgSize = 128;
162
        char smtp_msg[smtp_msgSize];
163
        smtp_strerror(smtp_errno(), smtp_msg, smtp_msgSize);
164
        char msg[2 * smtp_msgSize];
165
        snprintf(msg, sizeof (msg), "%s (sessionState %d isActive? %d tlsStarted? %d)"
166
          , smtp_msg, sessionState, isActive, tlsStarted);
167
        throw Exception(msg);
168
      }
169
      else if (incorrectAuthentication)
170
        throw Exception("Incorrect authentication data");
171
      else if (relayDenied)
172
        throw Exception("Relay Denied");
173
      else if (!certificateProblem.empty())
174
        throw Exception(("X509 error: " + certificateProblem).c_str());
175
    }
176
177
    operator smtp_session_t()
178
    {
179
      return session;
180
    }
181
182
    static char* toAscii(const LogString& str, Pool& p)
183
    {
184
      char* buf = p.pstralloc(str.length() + 1);
185
      char* current = buf;
186
187
      for (unsigned int c : str)
188
      {
189
        if (c > 0x7F)
190
        {
191
          c = '?';
192
        }
193
194
        *current++ = c;
195
      }
196
197
      *current = 0;
198
      return buf;
199
    }
200
201
  private:
202
    SMTPSession(SMTPSession&);
203
    SMTPSession& operator=(SMTPSession&);
204
    smtp_session_t session{0};
205
    auth_context_t authctx{0};
206
    char* user;
207
    char* pwd;
208
    int sessionState{0};
209
    bool isActive{false};
210
    bool tlsStarted{false};
211
    bool incorrectAuthentication{false};
212
    bool relayDenied{false};
213
    std::string certificateProblem;
214
215
    /**
216
     *   This method is called if the SMTP server requests authentication.
217
     */
218
    static int authinteract(auth_client_request_t request, char** result, int fields,
219
      void* arg)
220
    {
221
      auto pThis = static_cast<SMTPSession*>(arg);
222
223
      for (int i = 0; i < fields; i++)
224
      {
225
        int flag = request[i].flags & 0x07;
226
227
        if (flag == AUTH_USER)
228
        {
229
          result[i] = pThis->user;
230
        }
231
        else if (flag == AUTH_PASS)
232
        {
233
          result[i] = pThis->pwd;
234
        }
235
      }
236
237
      return 1;
238
    }
239
240
    static void monitor_cb(const char *buf, int buflen, int writing, void *arg)
241
    {
242
      auto pThis = static_cast<SMTPSession*>(arg);
243
      if (writing)
244
        pThis->isActive = true;
245
      else if (auto smtp_response = atoi(buf))
246
      {
247
        if (535 == smtp_response)
248
          pThis->incorrectAuthentication = true;
249
        if (550 == smtp_response)
250
          pThis->relayDenied = true;
251
      }
252
253
      if (LogLog::isDebugEnabled())
254
      {
255
        while (0 < buflen && std::isspace(buf[buflen - 1]))
256
          --buflen;
257
        std::string data(buf, buflen);
258
        LOG4CXX_DECODE_CHAR(lsData, data);
259
        LogString type = writing ? LOG4CXX_STR("send") : LOG4CXX_STR("recv");
260
        LogLog::debug(LOG4CXX_STR("SMTP ") + type + LOG4CXX_STR(" [") + lsData + LOG4CXX_STR("]"));
261
      }
262
    }
263
264
    static void event_cb (smtp_session_t session /* unused */, int event_no, void *arg,...)
265
    {
266
      auto pThis = static_cast<SMTPSession*>(arg);
267
      va_list alist;
268
      va_start(alist, arg);
269
      switch (event_no)
270
      {
271
      case SMTP_EV_CONNECT:
272
      case SMTP_EV_MAILSTATUS:
273
      case SMTP_EV_RCPTSTATUS:
274
      case SMTP_EV_MESSAGEDATA:
275
      case SMTP_EV_MESSAGESENT:
276
      case SMTP_EV_DISCONNECT:
277
        pThis->sessionState = event_no;
278
        break;
279
      case SMTP_EV_WEAK_CIPHER:
280
      {
281
        auto bitsRequired = va_arg(alist, long);
282
        pThis->certificateProblem = "weak cipher";
283
        if (auto ok = va_arg(alist, int*))
284
          *ok = 1; // Accept the problem
285
        break;
286
      }
287
      case SMTP_EV_STARTTLS_OK:
288
        pThis->tlsStarted = true;
289
        break;
290
      case SMTP_EV_INVALID_PEER_CERTIFICATE:
291
        pThis->certificateProblem = get_X509_error(va_arg(alist, long));
292
        if (auto ok = va_arg(alist, int*))
293
          *ok = 1; // Accept the problem
294
        break;
295
      case SMTP_EV_NO_PEER_CERTIFICATE:
296
        pThis->certificateProblem = "no peer certificate";
297
        if (auto ok = va_arg(alist, int*))
298
          *ok = 1;
299
        break;
300
      case SMTP_EV_WRONG_PEER_CERTIFICATE:
301
        pThis->certificateProblem = "wrong peer certificate";
302
        if (auto ok = va_arg(alist, int*))
303
          *ok = 1; // Accept the problem
304
        break;
305
      case SMTP_EV_NO_CLIENT_CERTIFICATE:
306
        pThis->certificateProblem = "no client certificate";
307
        if (auto ok = va_arg(alist, int*))
308
          *ok = 1; // Accept the problem
309
        break;
310
      }
311
      va_end(alist);
312
    }
313
314
    static std::string get_X509_error(long verifyResult)
315
    {
316
      switch (verifyResult)
317
      {
318
      case 2: return "unable to get issuer cert";
319
      case 3: return "unable to get crl";
320
      case 4: return "unable to decrypt cert signature";
321
      case 5: return "unable to decrypt crl signature";
322
      case 6: return "unable to decode issuer public key";
323
      case 7: return "cert signature failure";
324
      case 8: return "crl signature failure";
325
      case 9: return "cert not yet valid";
326
      case 10: return "cert has expired";
327
      case 11: return "crl not yet valid";
328
      case 12: return "crl has expired";
329
      case 13: return "error in cert not before field";
330
      case 14: return "error in cert not after field";
331
      case 15: return "error in crl last update field";
332
      case 16: return "error in crl next update field";
333
      case 17: return "out of mem";
334
      case 18: return "depth zero self signed cert";
335
      case 19: return "self signed cert in chain";
336
      case 20: return "unable to get issuer cert locally";
337
      case 21: return "unable to verify leaf signature";
338
      case 22: return "cert chain too long";
339
      case 23: return "cert revoked";
340
      case 24: return "invalid ca";
341
      case 25: return "path length exceeded";
342
      case 26: return "invalid purpose";
343
      case 27: return "cert untrusted";
344
      case 28: return "cert rejected";
345
      case 29: return "subject issuer mismatch";
346
      case 30: return "akid skid mismatch";
347
      case 31: return "akid issuer serial mismatch";
348
      case 32: return "keyusage no certsign";
349
      case 33: return "unable to get crl issuer";
350
      case 34: return "unhandled critical extension";
351
      case 35: return "keyusage no crl sign";
352
      case 36: return "unhandled critical crl extension";
353
      case 37: return "invalid non ca";
354
      case 38: return "proxy path length exceeded";
355
      case 39: return "keyusage no digital signature";
356
      case 40: return "proxy certificates not allowed";
357
      case 41: return "invalid extension";
358
      case 42: return "invalid policy extension";
359
      case 43: return "no explicit policy";
360
      case 44: return "different crl scope";
361
      case 45: return "unsupported extension feature";
362
      case 46: return "unnested resource";
363
      case 47: return "permitted violation";
364
      case 48: return "excluded violation";
365
      case 49: return "subtree minmax";
366
      case 51: return "unsupported constraint type";
367
      case 52: return "unsupported constraint syntax";
368
      case 53: return "unsupported name syntax";
369
      case 54: return "crl path validation error";
370
      case 50: return "application verification";
371
      }
372
      return "unknown";
373
    }
374
};
375
376
/**
377
 *  A message in an SMTP session.
378
 */
379
class SMTPMessage
380
{
381
  public:
382
    SMTPMessage(SMTPSession& session,
383
      const LogString& from,
384
      const LogString& to,
385
      const LogString& cc,
386
      const LogString& bcc,
387
      const LogString& subject,
388
      const LogString msg, Pool& p)
389
    {
390
      message = smtp_add_message(session);
391
      body = current = toMessage(msg, p, current_len);
392
      messagecbState = 0;
393
      smtp_set_reverse_path(message, toAscii(from, p));
394
      addRecipients(to, "To", p);
395
      addRecipients(cc, "Cc", p);
396
      addRecipients(bcc, "Bcc", p);
397
398
      if (!subject.empty())
399
      {
400
        smtp_set_header(message, "Subject", toAscii(subject, p));
401
      }
402
403
      smtp_set_messagecb(message, messagecb, this);
404
    }
405
    ~SMTPMessage()
406
    {
407
    }
408
409
  private:
410
    SMTPMessage(const SMTPMessage&);
411
    SMTPMessage& operator=(const SMTPMessage&);
412
    smtp_message_t message;
413
    const char* body;
414
    const char* current;
415
    size_t current_len;
416
    int messagecbState;
417
    void addRecipients(const LogString& addresses, const char* field, Pool& p)
418
    {
419
      if (!addresses.empty())
420
      {
421
        char* str = p.pstrdup(toAscii(addresses, p));;
422
        smtp_set_header(message, field, NULL, str);
423
        char* last;
424
425
        for (char* next = apr_strtok(str, ",", &last);
426
          next;
427
          next = apr_strtok(NULL, ",", &last))
428
        {
429
          smtp_add_recipient(message, next);
430
        }
431
      }
432
    }
433
    static const char* toAscii(const LogString& str, Pool& p)
434
    {
435
      return SMTPSession::toAscii(str, p);
436
    }
437
438
    /**
439
     *   Message bodies can only contain US-ASCII characters and
440
     *   CR and LFs can only occur together.
441
     *   On return \c lenOut holds the length of the converted body.
442
     */
443
    static const char* toMessage(const LogString& str, Pool& p, size_t& lenOut)
444
    {
445
      //
446
      //    count the number of carriage returns and line feeds
447
      //
448
      int feedCount = 0;
449
450
      for (size_t pos = str.find_first_of(LOG4CXX_STR("\n\r"));
451
        pos != LogString::npos;
452
        pos = str.find_first_of(LOG4CXX_STR("\n\r"), ++pos))
453
      {
454
        feedCount++;
455
      }
456
457
      //
458
      //   allocate sufficient space for the modified message
459
      char* retval = p.pstralloc(str.length() + feedCount + 1);
460
      char* current = retval;
461
      char* startOfLine = current;
462
      unsigned int ignoreChar = 0;
463
464
      //
465
      //    iterator through message
466
      //
467
      for (unsigned int c : str)
468
      {
469
        //
470
        //   replace non-ASCII characters and embedded NULs with '?'
471
        //   (a NUL octet must never act as a body terminator)
472
        //
473
        if (c > 0x7F || c == 0)
474
        {
475
          *current++ = 0x3F; // '?'
476
        }
477
        else if (c == 0x0A || c == 0x0D)
478
        {
479
          //
480
          //   replace any stray CR or LF with CRLF
481
          //      reset start of line
482
          if (c == ignoreChar && current == startOfLine)
483
            ignoreChar = 0;
484
          else
485
          {
486
            *current++ = 0x0D;
487
            *current++ = 0x0A;
488
            startOfLine = current;
489
            ignoreChar = (c == 0x0A ? 0x0D : 0x0A);
490
          }
491
        }
492
        else
493
        {
494
          //
495
          //    truncate any lines to 1000 characters (including CRLF)
496
          //       as required by RFC.
497
          if (current < startOfLine + 998)
498
          {
499
            *current++ = (char) c;
500
          }
501
        }
502
      }
503
504
      *current = 0;
505
      lenOut = current - retval;
506
      return retval;
507
    }
508
509
    /**
510
     *  Callback for message.
511
     */
512
    static const char* messagecb(void** ctx, int* len, void* arg)
513
    {
514
      *ctx = 0;
515
      const char* retval = 0;
516
      SMTPMessage* pThis = (SMTPMessage*) arg;
517
518
      //   rewind message
519
      if (len == NULL)
520
      {
521
        pThis->current = pThis->body;
522
      }
523
      else
524
      {
525
        // we are asked for headers, but we don't have any
526
        if ((pThis->messagecbState)++ == 0)
527
        {
528
          return NULL;
529
        }
530
531
        if (pThis->current)
532
        {
533
          // Use the stored post-conversion length: strnlen_s over the
534
          // pre-conversion length truncates at an embedded NUL and
535
          // undercounts the CRLF-expanded body, silently dropping the
536
          // newest content from the alert email.
537
          *len = static_cast<int>(pThis->current_len);
538
        }
539
540
        retval = pThis->current;
541
        pThis->current = 0;
542
      }
543
544
      return retval;
545
    }
546
547
};
548
#endif
549
550
class LOG4CXX_EXPORT DefaultEvaluator
551
#if LOG4CXX_ABI_VERSION <= 15
552
  : public virtual spi::TriggeringEventEvaluator
553
  , public virtual helpers::Object
554
#else
555
  : public spi::TriggeringEventEvaluator
556
#endif
557
{
558
  public:
559
    DECLARE_LOG4CXX_OBJECT(DefaultEvaluator)
560
0
    BEGIN_LOG4CXX_CAST_MAP()
561
0
    LOG4CXX_CAST_ENTRY(DefaultEvaluator)
562
0
    LOG4CXX_CAST_ENTRY(spi::TriggeringEventEvaluator)
563
0
    END_LOG4CXX_CAST_MAP()
564
565
    DefaultEvaluator();
566
567
    /**
568
    Is this <code>event</code> the e-mail triggering event?
569
    <p>This method returns <code>true</code>, if the event level
570
    has ERROR level or higher. Otherwise it returns
571
    <code>false</code>.
572
    */
573
    bool isTriggeringEvent(const spi::LoggingEventPtr& event) override;
574
  private:
575
    DefaultEvaluator(const DefaultEvaluator&);
576
    DefaultEvaluator& operator=(const DefaultEvaluator&);
577
}; // class DefaultEvaluator
578
579
}
580
}
581
582
IMPLEMENT_LOG4CXX_OBJECT(DefaultEvaluator)
583
IMPLEMENT_LOG4CXX_OBJECT(SMTPAppender)
584
585
struct SMTPAppender::SMTPPriv : public AppenderSkeletonPrivate
586
{
587
  SMTPPriv() :
588
0
    AppenderSkeletonPrivate(),
589
0
    smtpPort(25),
590
0
    bufferSize(512),
591
0
    locationInfo(false),
592
0
    cb(bufferSize),
593
0
    evaluator(new DefaultEvaluator()) {}
594
595
  SMTPPriv(spi::TriggeringEventEvaluatorPtr evaluator) :
596
0
    AppenderSkeletonPrivate(),
597
0
    smtpPort(25),
598
0
    bufferSize(512),
599
0
    locationInfo(false),
600
0
    cb(bufferSize),
601
0
    evaluator(evaluator) {}
602
603
  LogString to;
604
  LogString cc;
605
  LogString bcc;
606
  LogString from;
607
  LogString subject;
608
  LogString smtpHost;
609
  LogString smtpUsername;
610
  LogString smtpPassword;
611
  int smtpPort;
612
  int bufferSize; // 512
613
  bool locationInfo;
614
  helpers::CyclicBuffer cb;
615
  spi::TriggeringEventEvaluatorPtr evaluator;
616
  // Whether AUTH credentials may be sent without STARTTLS (see setOption)
617
  bool allowPlainTextAuth{false};
618
};
619
620
0
#define _priv static_cast<SMTPPriv*>(m_priv.get())
621
622
DefaultEvaluator::DefaultEvaluator()
623
0
{
624
0
}
Unexecuted instantiation: log4cxx::net::DefaultEvaluator::DefaultEvaluator()
Unexecuted instantiation: log4cxx::net::DefaultEvaluator::DefaultEvaluator()
625
626
bool DefaultEvaluator::isTriggeringEvent(const spi::LoggingEventPtr& event)
627
0
{
628
0
  return event->getLevel()->isGreaterOrEqual(Level::getError());
629
0
}
630
631
SMTPAppender::SMTPAppender()
632
0
  : AppenderSkeleton (std::make_unique<SMTPPriv>())
633
0
{
634
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender()
Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender()
635
636
/**
637
Use <code>evaluator</code> passed as parameter as the
638
TriggeringEventEvaluator for this SMTPAppender.  */
639
SMTPAppender::SMTPAppender(spi::TriggeringEventEvaluatorPtr evaluator)
640
0
  : AppenderSkeleton (std::make_unique<SMTPPriv>(evaluator))
641
0
{
642
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>)
643
644
SMTPAppender::~SMTPAppender()
645
0
{
646
0
  _priv->setClosed();
647
0
}
648
649
bool SMTPAppender::requiresLayout() const
650
0
{
651
0
  return true;
652
0
}
653
654
LogString SMTPAppender::getFrom() const
655
0
{
656
0
  return _priv->from;
657
0
}
658
659
void SMTPAppender::setFrom(const LogString& newVal)
660
0
{
661
0
  _priv->from = stripSmtpControl(newVal, LOG4CXX_STR("From"));
662
0
}
663
664
665
LogString SMTPAppender::getSubject() const
666
0
{
667
0
  return _priv->subject;
668
0
}
669
670
void SMTPAppender::setSubject(const LogString& newVal)
671
0
{
672
0
  _priv->subject = stripSmtpControl(newVal, LOG4CXX_STR("Subject"));
673
0
}
674
675
LogString SMTPAppender::getSMTPHost() const
676
0
{
677
0
  return _priv->smtpHost;
678
0
}
679
680
void SMTPAppender::setSMTPHost(const LogString& newVal)
681
0
{
682
0
  _priv->smtpHost = newVal;
683
0
}
684
685
int SMTPAppender::getSMTPPort() const
686
0
{
687
0
  return _priv->smtpPort;
688
0
}
689
690
void SMTPAppender::setSMTPPort(int newVal)
691
0
{
692
0
  _priv->smtpPort = newVal;
693
0
}
694
695
bool SMTPAppender::getLocationInfo() const
696
0
{
697
0
  return _priv->locationInfo;
698
0
}
699
700
void SMTPAppender::setLocationInfo(bool newVal)
701
0
{
702
0
  _priv->locationInfo = newVal;
703
0
}
704
705
LogString SMTPAppender::getSMTPUsername() const
706
0
{
707
0
  return _priv->smtpUsername;
708
0
}
709
710
void SMTPAppender::setSMTPUsername(const LogString& newVal)
711
0
{
712
0
  _priv->smtpUsername = newVal;
713
0
}
714
715
LogString SMTPAppender::getSMTPPassword() const
716
0
{
717
0
  return _priv->smtpPassword;
718
0
}
719
720
void SMTPAppender::setSMTPPassword(const LogString& newVal)
721
0
{
722
0
  _priv->smtpPassword = newVal;
723
0
}
724
725
726
727
728
729
void SMTPAppender::setOption(const LogString& option,
730
  const LogString& value)
731
0
{
732
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize")))
733
0
  {
734
0
    setBufferSize(OptionConverter::toInt(value, 512));
735
0
  }
736
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("EVALUATORCLASS"), LOG4CXX_STR("evaluatorclass")))
737
0
  {
738
0
    setEvaluatorClass(value);
739
0
  }
740
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FROM"), LOG4CXX_STR("from")))
741
0
  {
742
0
    setFrom(value);
743
0
  }
744
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPHOST"), LOG4CXX_STR("smtphost")))
745
0
  {
746
0
    setSMTPHost(value);
747
0
  }
748
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPUSERNAME"), LOG4CXX_STR("smtpusername")))
749
0
  {
750
0
    setSMTPUsername(value);
751
0
  }
752
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPASSWORD"), LOG4CXX_STR("smtppassword")))
753
0
  {
754
0
    setSMTPPassword(value);
755
0
  }
756
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SUBJECT"), LOG4CXX_STR("subject")))
757
0
  {
758
0
    setSubject(value);
759
0
  }
760
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("TO"), LOG4CXX_STR("to")))
761
0
  {
762
0
    setTo(value);
763
0
  }
764
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CC"), LOG4CXX_STR("cc")))
765
0
  {
766
0
    setCc(value);
767
0
  }
768
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BCC"), LOG4CXX_STR("bcc")))
769
0
  {
770
0
    setBcc(value);
771
0
  }
772
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPORT"), LOG4CXX_STR("smtpport")))
773
0
  {
774
0
    setSMTPPort(OptionConverter::toInt(value, 25));
775
0
  }
776
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ALLOWPLAINTEXTAUTHENTICATION"), LOG4CXX_STR("allowplaintextauthentication")))
777
0
  {
778
    // Explicit opt-out from the STARTTLS-before-AUTH requirement;
779
    // only for servers that cannot offer TLS on a trusted network.
780
0
    _priv->allowPlainTextAuth = OptionConverter::toBoolean(value, false);
781
0
  }
782
0
  else
783
0
  {
784
0
    AppenderSkeleton::setOption(option, value);
785
0
  }
786
0
}
787
788
789
bool SMTPAppender::asciiCheck(const LogString& value, const LogString& field)
790
0
{
791
0
  for (unsigned int item : value)
792
0
  {
793
0
    if (0x7F < item)
794
0
    {
795
0
      LogLog::warn(field + LOG4CXX_STR(" contains non-ASCII character"));
796
0
      return false;
797
0
    }
798
0
  }
799
800
0
  return true;
801
0
}
802
803
/**
804
Activate the specified options, such as the smtp host, the
805
recipient, from, etc. */
806
void SMTPAppender::activateOptions( LOG4CXX_ACTIVATE_OPTIONS_FORMAL_PARAMETERS )
807
0
{
808
0
  if (_priv->layout == 0)
809
0
  {
810
0
    _priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("]."));
811
0
  }
812
813
0
  if (_priv->evaluator == 0)
814
0
  {
815
0
    _priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") +
816
0
      _priv->name + LOG4CXX_STR("]."));
817
0
  }
818
819
0
  if (_priv->smtpHost.empty())
820
0
  {
821
0
    _priv->errorHandler->error(LOG4CXX_STR("No smtpHost is set for appender [") +
822
0
      _priv->name + LOG4CXX_STR("]."));
823
0
  }
824
825
0
  if (_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty())
826
0
  {
827
0
    _priv->errorHandler->error(LOG4CXX_STR("No recipient address is set for appender [") +
828
0
      _priv->name + LOG4CXX_STR("]."));
829
0
  }
830
831
0
  asciiCheck(_priv->to, LOG4CXX_STR("to"));
832
0
  asciiCheck(_priv->cc, LOG4CXX_STR("cc"));
833
0
  asciiCheck(_priv->bcc, LOG4CXX_STR("bcc"));
834
0
  asciiCheck(_priv->from, LOG4CXX_STR("from"));
835
836
0
#if !LOG4CXX_HAVE_LIBESMTP
837
0
  _priv->errorHandler->error(LOG4CXX_STR("log4cxx built without SMTP support."));
838
0
#endif
839
0
}
840
841
/**
842
Perform SMTPAppender specific appending actions, mainly adding
843
the event to a cyclic buffer and checking if the event triggers
844
an e-mail to be sent. */
845
void SMTPAppender::append( LOG4CXX_APPEND_FORMAL_PARAMETERS )
846
0
{
847
0
  if (!checkEntryConditions())
848
0
  {
849
0
    return;
850
0
  }
851
852
  // Get a copy of this thread's diagnostic context
853
0
  event->LoadDC();
854
855
0
  _priv->cb.add(event);
856
857
0
  if (_priv->evaluator->isTriggeringEvent(event))
858
0
  {
859
0
    Pool p;
860
0
    sendBuffer(p);
861
0
  }
862
0
}
863
864
/**
865
This method determines if there is a sense in attempting to append.
866
<p>It checks whether there is a set output target and also if
867
there is a set layout. If these checks fail, then the boolean
868
value <code>false</code> is returned. */
869
bool SMTPAppender::checkEntryConditions()
870
0
{
871
#if LOG4CXX_HAVE_LIBESMTP
872
873
  if ((_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty()) || _priv->from.empty() || _priv->smtpHost.empty())
874
  {
875
    _priv->errorHandler->error(LOG4CXX_STR("Message not configured."));
876
    return false;
877
  }
878
879
  if (_priv->evaluator == 0)
880
  {
881
    _priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") +
882
      _priv->name + LOG4CXX_STR("]."));
883
    return false;
884
  }
885
886
887
  if (_priv->layout == 0)
888
  {
889
    _priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("]."));
890
    return false;
891
  }
892
893
  return true;
894
#else
895
0
  return false;
896
0
#endif
897
0
}
898
899
900
901
void SMTPAppender::close()
902
0
{
903
0
  _priv->setClosed();
904
0
}
905
906
LogString SMTPAppender::getTo() const
907
0
{
908
0
  return _priv->to;
909
0
}
910
911
void SMTPAppender::setTo(const LogString& addressStr)
912
0
{
913
0
  _priv->to = stripSmtpControl(addressStr, LOG4CXX_STR("To"));
914
0
}
915
916
LogString SMTPAppender::getCc() const
917
0
{
918
0
  return _priv->cc;
919
0
}
920
921
void SMTPAppender::setCc(const LogString& addressStr)
922
0
{
923
0
  _priv->cc = stripSmtpControl(addressStr, LOG4CXX_STR("Cc"));
924
0
}
925
926
LogString SMTPAppender::getBcc() const
927
0
{
928
0
  return _priv->bcc;
929
0
}
930
931
void SMTPAppender::setBcc(const LogString& addressStr)
932
0
{
933
0
  _priv->bcc = stripSmtpControl(addressStr, LOG4CXX_STR("Bcc"));
934
0
}
935
936
/**
937
Send the contents of the cyclic buffer as an e-mail message.
938
*/
939
void SMTPAppender::sendBuffer(Pool& p)
940
0
{
941
#if LOG4CXX_HAVE_LIBESMTP
942
943
  // This thread owns the mutex for this appender, hence no need to synchronize on 'cb'.
944
  try
945
  {
946
    LogString sbuf;
947
    _priv->layout->appendHeader(sbuf);
948
949
    int len = _priv->cb.length();
950
951
    for (int i = 0; i < len; i++)
952
    {
953
      LoggingEventPtr event = _priv->cb.get();
954
      _priv->layout->format(sbuf, event);
955
    }
956
957
    _priv->layout->appendFooter(sbuf);
958
959
    SMTPSession session(_priv->smtpHost, _priv->smtpPort, _priv->smtpUsername, _priv->smtpPassword, _priv->allowPlainTextAuth, p);
960
961
    SMTPMessage message(session, _priv->from, _priv->to, _priv->cc,
962
      _priv->bcc, _priv->subject, sbuf, p);
963
964
    session.send();
965
966
  }
967
  catch (std::exception& e)
968
  {
969
    _priv->errorHandler->error(LOG4CXX_STR("Error occured while sending e-mail to [") + _priv->smtpHost + LOG4CXX_STR("]."), e, 0);
970
  }
971
972
#endif
973
0
}
974
975
/**
976
Returns value of the <b>EvaluatorClass</b> option.
977
*/
978
LogString SMTPAppender::getEvaluatorClass()
979
0
{
980
0
  return _priv->evaluator == 0 ? LogString() : _priv->evaluator->getClass().getName();
981
0
}
982
983
LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr SMTPAppender::getEvaluator() const
984
0
{
985
0
  return _priv->evaluator;
986
0
}
987
988
void SMTPAppender::setEvaluator(LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr& trigger)
989
0
{
990
0
  _priv->evaluator = trigger;
991
0
}
992
993
/**
994
The <b>BufferSize</b> option takes a positive integer
995
representing the maximum number of logging events to collect in a
996
cyclic buffer. When the <code>BufferSize</code> is reached,
997
oldest events are deleted as new events are added to the
998
buffer. By default the size of the cyclic buffer is 512 events.
999
*/
1000
void SMTPAppender::setBufferSize(int sz)
1001
0
{
1002
0
  if (sz < 1)
1003
0
  {
1004
0
    sz = 1;
1005
0
  }
1006
1007
0
  _priv->bufferSize = sz;
1008
0
  _priv->cb.resize(sz);
1009
0
}
1010
1011
/**
1012
The <b>EvaluatorClass</b> option takes a string value
1013
representing the name of the class implementing the {@link
1014
TriggeringEventEvaluator} interface. A corresponding object will
1015
be instantiated and assigned as the triggering event evaluator
1016
for the SMTPAppender.
1017
*/
1018
void SMTPAppender::setEvaluatorClass(const LogString& value)
1019
0
{
1020
0
  ObjectPtr obj = ObjectPtr(Loader::loadClass(value).newInstance());
1021
0
  _priv->evaluator = LOG4CXX_NS::cast<TriggeringEventEvaluator>(obj);
1022
0
}
1023
1024
int SMTPAppender::getBufferSize() const
1025
0
{
1026
0
  return _priv->bufferSize;
1027
0
}