Coverage Report

Created: 2026-09-21 07:05

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
}
Unexecuted instantiation: smtpappender.cpp:(anonymous namespace)::stripSmtpControl(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*)
Unexecuted instantiation: smtpappender.cpp:(anonymous namespace)::stripSmtpControl(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, wchar_t const*)
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
  : public spi::TriggeringEventEvaluator
552
{
553
  public:
554
    DECLARE_LOG4CXX_OBJECT(DefaultEvaluator)
555
0
    BEGIN_LOG4CXX_CAST_MAP()
556
0
    LOG4CXX_CAST_ENTRY(DefaultEvaluator)
557
0
    LOG4CXX_CAST_ENTRY(spi::TriggeringEventEvaluator)
558
0
    END_LOG4CXX_CAST_MAP()
559
560
    DefaultEvaluator();
561
562
    /**
563
    Is this <code>event</code> the e-mail triggering event?
564
    <p>This method returns <code>true</code>, if the event level
565
    has ERROR level or higher. Otherwise it returns
566
    <code>false</code>.
567
    */
568
    bool isTriggeringEvent(const spi::LoggingEventPtr& event) override;
569
  private:
570
    DefaultEvaluator(const DefaultEvaluator&);
571
    DefaultEvaluator& operator=(const DefaultEvaluator&);
572
}; // class DefaultEvaluator
573
574
}
575
}
576
577
IMPLEMENT_LOG4CXX_OBJECT(DefaultEvaluator)
578
IMPLEMENT_LOG4CXX_OBJECT(SMTPAppender)
579
580
struct SMTPAppender::SMTPPriv : public AppenderSkeletonPrivate
581
{
582
  SMTPPriv() :
583
0
    AppenderSkeletonPrivate(),
584
0
    smtpPort(25),
585
0
    bufferSize(512),
586
0
    locationInfo(false),
587
0
    cb(bufferSize),
588
0
    evaluator(new DefaultEvaluator()) {}
589
590
  SMTPPriv(spi::TriggeringEventEvaluatorPtr evaluator) :
591
0
    AppenderSkeletonPrivate(),
592
0
    smtpPort(25),
593
0
    bufferSize(512),
594
0
    locationInfo(false),
595
0
    cb(bufferSize),
596
0
    evaluator(evaluator) {}
597
598
  LogString to;
599
  LogString cc;
600
  LogString bcc;
601
  LogString from;
602
  LogString subject;
603
  LogString smtpHost;
604
  LogString smtpUsername;
605
  LogString smtpPassword;
606
  int smtpPort;
607
  int bufferSize; // 512
608
  bool locationInfo;
609
  helpers::CyclicBuffer cb;
610
  spi::TriggeringEventEvaluatorPtr evaluator;
611
  // Whether AUTH credentials may be sent without STARTTLS (see setOption)
612
  bool allowPlainTextAuth{false};
613
};
614
615
0
#define _priv static_cast<SMTPPriv*>(m_priv.get())
616
617
DefaultEvaluator::DefaultEvaluator()
618
0
{
619
0
}
Unexecuted instantiation: log4cxx::net::DefaultEvaluator::DefaultEvaluator()
Unexecuted instantiation: log4cxx::net::DefaultEvaluator::DefaultEvaluator()
620
621
bool DefaultEvaluator::isTriggeringEvent(const spi::LoggingEventPtr& event)
622
0
{
623
0
  return event->getLevel()->isGreaterOrEqual(Level::getError());
624
0
}
625
626
SMTPAppender::SMTPAppender()
627
0
  : AppenderSkeleton (std::make_unique<SMTPPriv>())
628
0
{
629
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender()
Unexecuted instantiation: log4cxx::net::SMTPAppender::SMTPAppender()
630
631
/**
632
Use <code>evaluator</code> passed as parameter as the
633
TriggeringEventEvaluator for this SMTPAppender.  */
634
SMTPAppender::SMTPAppender(spi::TriggeringEventEvaluatorPtr evaluator)
635
0
  : AppenderSkeleton (std::make_unique<SMTPPriv>(evaluator))
636
0
{
637
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>)
638
639
SMTPAppender::~SMTPAppender()
640
0
{
641
0
  _priv->setClosed();
642
0
}
643
644
bool SMTPAppender::requiresLayout() const
645
0
{
646
0
  return true;
647
0
}
648
649
LogString SMTPAppender::getFrom() const
650
0
{
651
0
  return _priv->from;
652
0
}
653
654
void SMTPAppender::setFrom(const LogString& newVal)
655
0
{
656
0
  _priv->from = stripSmtpControl(newVal, LOG4CXX_STR("From"));
657
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setFrom(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setFrom(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
658
659
660
LogString SMTPAppender::getSubject() const
661
0
{
662
0
  return _priv->subject;
663
0
}
664
665
void SMTPAppender::setSubject(const LogString& newVal)
666
0
{
667
0
  _priv->subject = stripSmtpControl(newVal, LOG4CXX_STR("Subject"));
668
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSubject(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSubject(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
669
670
LogString SMTPAppender::getSMTPHost() const
671
0
{
672
0
  return _priv->smtpHost;
673
0
}
674
675
void SMTPAppender::setSMTPHost(const LogString& newVal)
676
0
{
677
0
  _priv->smtpHost = newVal;
678
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSMTPHost(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSMTPHost(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
679
680
int SMTPAppender::getSMTPPort() const
681
0
{
682
0
  return _priv->smtpPort;
683
0
}
684
685
void SMTPAppender::setSMTPPort(int newVal)
686
0
{
687
0
  _priv->smtpPort = newVal;
688
0
}
689
690
bool SMTPAppender::getLocationInfo() const
691
0
{
692
0
  return _priv->locationInfo;
693
0
}
694
695
void SMTPAppender::setLocationInfo(bool newVal)
696
0
{
697
0
  _priv->locationInfo = newVal;
698
0
}
699
700
LogString SMTPAppender::getSMTPUsername() const
701
0
{
702
0
  return _priv->smtpUsername;
703
0
}
704
705
void SMTPAppender::setSMTPUsername(const LogString& newVal)
706
0
{
707
0
  _priv->smtpUsername = newVal;
708
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSMTPUsername(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSMTPUsername(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
709
710
LogString SMTPAppender::getSMTPPassword() const
711
0
{
712
0
  return _priv->smtpPassword;
713
0
}
714
715
void SMTPAppender::setSMTPPassword(const LogString& newVal)
716
0
{
717
0
  _priv->smtpPassword = newVal;
718
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSMTPPassword(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setSMTPPassword(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
719
720
721
722
723
724
void SMTPAppender::setOption(const LogString& option,
725
  const LogString& value)
726
0
{
727
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize")))
728
0
  {
729
0
    setBufferSize(OptionConverter::toInt(value, 512));
730
0
  }
731
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("EVALUATORCLASS"), LOG4CXX_STR("evaluatorclass")))
732
0
  {
733
0
    setEvaluatorClass(value);
734
0
  }
735
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FROM"), LOG4CXX_STR("from")))
736
0
  {
737
0
    setFrom(value);
738
0
  }
739
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPHOST"), LOG4CXX_STR("smtphost")))
740
0
  {
741
0
    setSMTPHost(value);
742
0
  }
743
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPUSERNAME"), LOG4CXX_STR("smtpusername")))
744
0
  {
745
0
    setSMTPUsername(value);
746
0
  }
747
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPASSWORD"), LOG4CXX_STR("smtppassword")))
748
0
  {
749
0
    setSMTPPassword(value);
750
0
  }
751
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SUBJECT"), LOG4CXX_STR("subject")))
752
0
  {
753
0
    setSubject(value);
754
0
  }
755
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("TO"), LOG4CXX_STR("to")))
756
0
  {
757
0
    setTo(value);
758
0
  }
759
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CC"), LOG4CXX_STR("cc")))
760
0
  {
761
0
    setCc(value);
762
0
  }
763
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BCC"), LOG4CXX_STR("bcc")))
764
0
  {
765
0
    setBcc(value);
766
0
  }
767
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPORT"), LOG4CXX_STR("smtpport")))
768
0
  {
769
0
    setSMTPPort(OptionConverter::toInt(value, 25));
770
0
  }
771
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ALLOWPLAINTEXTAUTHENTICATION"), LOG4CXX_STR("allowplaintextauthentication")))
772
0
  {
773
    // Explicit opt-out from the STARTTLS-before-AUTH requirement;
774
    // only for servers that cannot offer TLS on a trusted network.
775
0
    _priv->allowPlainTextAuth = OptionConverter::toBoolean(value, false);
776
0
  }
777
0
  else
778
0
  {
779
0
    AppenderSkeleton::setOption(option, value);
780
0
  }
781
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setOption(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setOption(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
782
783
784
bool SMTPAppender::asciiCheck(const LogString& value, const LogString& field)
785
0
{
786
0
  for (unsigned int item : value)
787
0
  {
788
0
    if (0x7F < item)
789
0
    {
790
0
      LogLog::warn(field + LOG4CXX_STR(" contains non-ASCII character"));
791
0
      return false;
792
0
    }
793
0
  }
794
795
0
  return true;
796
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::asciiCheck(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::asciiCheck(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
797
798
/**
799
Activate the specified options, such as the smtp host, the
800
recipient, from, etc. */
801
void SMTPAppender::activateOptions(  )
802
0
{
803
0
  if (_priv->layout == 0)
804
0
  {
805
0
    _priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("]."));
806
0
  }
807
808
0
  if (_priv->evaluator == 0)
809
0
  {
810
0
    _priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") +
811
0
      _priv->name + LOG4CXX_STR("]."));
812
0
  }
813
814
0
  if (_priv->smtpHost.empty())
815
0
  {
816
0
    _priv->errorHandler->error(LOG4CXX_STR("No smtpHost is set for appender [") +
817
0
      _priv->name + LOG4CXX_STR("]."));
818
0
  }
819
820
0
  if (_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty())
821
0
  {
822
0
    _priv->errorHandler->error(LOG4CXX_STR("No recipient address is set for appender [") +
823
0
      _priv->name + LOG4CXX_STR("]."));
824
0
  }
825
826
0
  asciiCheck(_priv->to, LOG4CXX_STR("to"));
827
0
  asciiCheck(_priv->cc, LOG4CXX_STR("cc"));
828
0
  asciiCheck(_priv->bcc, LOG4CXX_STR("bcc"));
829
0
  asciiCheck(_priv->from, LOG4CXX_STR("from"));
830
831
0
#if !LOG4CXX_HAVE_LIBESMTP
832
0
  _priv->errorHandler->error(LOG4CXX_STR("log4cxx built without SMTP support."));
833
0
#endif
834
0
}
835
836
/**
837
Perform SMTPAppender specific appending actions, mainly adding
838
the event to a cyclic buffer and checking if the event triggers
839
an e-mail to be sent. */
840
void SMTPAppender::append( const spi::LoggingEventPtr& event )
841
0
{
842
0
  if (!checkEntryConditions())
843
0
  {
844
0
    return;
845
0
  }
846
847
  // Get a copy of this thread's diagnostic context
848
0
  event->LoadDC();
849
850
0
  _priv->cb.add(event);
851
852
0
  if (_priv->evaluator->isTriggeringEvent(event))
853
0
  {
854
0
    Pool p;
855
0
    sendBuffer(p);
856
0
  }
857
0
}
858
859
/**
860
This method determines if there is a sense in attempting to append.
861
<p>It checks whether there is a set output target and also if
862
there is a set layout. If these checks fail, then the boolean
863
value <code>false</code> is returned. */
864
bool SMTPAppender::checkEntryConditions()
865
0
{
866
#if LOG4CXX_HAVE_LIBESMTP
867
868
  if ((_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty()) || _priv->from.empty() || _priv->smtpHost.empty())
869
  {
870
    _priv->errorHandler->error(LOG4CXX_STR("Message not configured."));
871
    return false;
872
  }
873
874
  if (_priv->evaluator == 0)
875
  {
876
    _priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") +
877
      _priv->name + LOG4CXX_STR("]."));
878
    return false;
879
  }
880
881
882
  if (_priv->layout == 0)
883
  {
884
    _priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("]."));
885
    return false;
886
  }
887
888
  return true;
889
#else
890
0
  return false;
891
0
#endif
892
0
}
893
894
895
896
void SMTPAppender::close()
897
0
{
898
0
  _priv->setClosed();
899
0
}
900
901
LogString SMTPAppender::getTo() const
902
0
{
903
0
  return _priv->to;
904
0
}
905
906
void SMTPAppender::setTo(const LogString& addressStr)
907
0
{
908
0
  _priv->to = stripSmtpControl(addressStr, LOG4CXX_STR("To"));
909
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setTo(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setTo(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
910
911
LogString SMTPAppender::getCc() const
912
0
{
913
0
  return _priv->cc;
914
0
}
915
916
void SMTPAppender::setCc(const LogString& addressStr)
917
0
{
918
0
  _priv->cc = stripSmtpControl(addressStr, LOG4CXX_STR("Cc"));
919
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setCc(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setCc(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
920
921
LogString SMTPAppender::getBcc() const
922
0
{
923
0
  return _priv->bcc;
924
0
}
925
926
void SMTPAppender::setBcc(const LogString& addressStr)
927
0
{
928
0
  _priv->bcc = stripSmtpControl(addressStr, LOG4CXX_STR("Bcc"));
929
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setBcc(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setBcc(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
930
931
/**
932
Send the contents of the cyclic buffer as an e-mail message.
933
*/
934
void SMTPAppender::sendBuffer(Pool& p)
935
0
{
936
#if LOG4CXX_HAVE_LIBESMTP
937
938
  // This thread owns the mutex for this appender, hence no need to synchronize on 'cb'.
939
  try
940
  {
941
    LogString sbuf;
942
    _priv->layout->appendHeader(sbuf);
943
944
    int len = _priv->cb.length();
945
946
    for (int i = 0; i < len; i++)
947
    {
948
      LoggingEventPtr event = _priv->cb.get();
949
      _priv->layout->format(sbuf, event);
950
    }
951
952
    _priv->layout->appendFooter(sbuf);
953
954
    SMTPSession session(_priv->smtpHost, _priv->smtpPort, _priv->smtpUsername, _priv->smtpPassword, _priv->allowPlainTextAuth, p);
955
956
    SMTPMessage message(session, _priv->from, _priv->to, _priv->cc,
957
      _priv->bcc, _priv->subject, sbuf, p);
958
959
    session.send();
960
961
  }
962
  catch (std::exception& e)
963
  {
964
    _priv->errorHandler->error(LOG4CXX_STR("Error occured while sending e-mail to [") + _priv->smtpHost + LOG4CXX_STR("]."), e, 0);
965
  }
966
967
#endif
968
0
}
969
970
/**
971
Returns value of the <b>EvaluatorClass</b> option.
972
*/
973
LogString SMTPAppender::getEvaluatorClass()
974
0
{
975
0
  return _priv->evaluator == 0 ? LogString() : _priv->evaluator->getClass().getName();
976
0
}
977
978
LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr SMTPAppender::getEvaluator() const
979
0
{
980
0
  return _priv->evaluator;
981
0
}
982
983
void SMTPAppender::setEvaluator(LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr& trigger)
984
0
{
985
0
  _priv->evaluator = trigger;
986
0
}
987
988
/**
989
The <b>BufferSize</b> option takes a positive integer
990
representing the maximum number of logging events to collect in a
991
cyclic buffer. When the <code>BufferSize</code> is reached,
992
oldest events are deleted as new events are added to the
993
buffer. By default the size of the cyclic buffer is 512 events.
994
*/
995
void SMTPAppender::setBufferSize(int sz)
996
0
{
997
0
  if (sz < 1)
998
0
  {
999
0
    sz = 1;
1000
0
  }
1001
1002
0
  _priv->bufferSize = sz;
1003
0
  _priv->cb.resize(sz);
1004
0
}
1005
1006
/**
1007
The <b>EvaluatorClass</b> option takes a string value
1008
representing the name of the class implementing the {@link
1009
TriggeringEventEvaluator} interface. A corresponding object will
1010
be instantiated and assigned as the triggering event evaluator
1011
for the SMTPAppender.
1012
*/
1013
void SMTPAppender::setEvaluatorClass(const LogString& value)
1014
0
{
1015
0
  ObjectPtr obj = ObjectPtr(Loader::loadClass(value).newInstance());
1016
0
  _priv->evaluator = LOG4CXX_NS::cast<TriggeringEventEvaluator>(obj);
1017
0
}
Unexecuted instantiation: log4cxx::net::SMTPAppender::setEvaluatorClass(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::net::SMTPAppender::setEvaluatorClass(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
1018
1019
int SMTPAppender::getBufferSize() const
1020
0
{
1021
0
  return _priv->bufferSize;
1022
0
}