Coverage Report

Created: 2025-11-24 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/optionconverter.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
18
#include <log4cxx/logstring.h>
19
#include <log4cxx/spi/loggerfactory.h>
20
#include <log4cxx/spi/loggerrepository.h>
21
#include <log4cxx/appenderskeleton.h>
22
#include <log4cxx/helpers/optionconverter.h>
23
#include <algorithm>
24
#include <ctype.h>
25
#include <log4cxx/helpers/stringhelper.h>
26
#include <log4cxx/helpers/exception.h>
27
#include <stdlib.h>
28
#include <log4cxx/helpers/properties.h>
29
#include <log4cxx/helpers/loglog.h>
30
#include <log4cxx/level.h>
31
#include <log4cxx/helpers/object.h>
32
#include <log4cxx/helpers/class.h>
33
#include <log4cxx/helpers/loader.h>
34
#include <log4cxx/helpers/system.h>
35
#include <log4cxx/propertyconfigurator.h>
36
#include <log4cxx/helpers/transcoder.h>
37
#include <log4cxx/file.h>
38
#include <log4cxx/xml/domconfigurator.h>
39
#include <log4cxx/logmanager.h>
40
#if !defined(LOG4CXX)
41
  #define LOG4CXX 1
42
#endif
43
#include <log4cxx/helpers/aprinitializer.h>
44
#include <log4cxx/helpers/filewatchdog.h>
45
#include <log4cxx/helpers/singletonholder.h>
46
47
namespace
48
{
49
using namespace LOG4CXX_NS;
50
51
/// For recursion checking
52
struct LogStringChain
53
{
54
  const LogString& item;
55
  const LogStringChain* parent;
56
};
57
58
/// Is \c item referenced in \c path
59
bool isRecursiveReference(const LogString& newkey, const LogStringChain* path)
60
0
{
61
0
  bool result = false;
62
0
  if (path->item == newkey)
63
0
    result = true;
64
0
  else if (path->parent)
65
0
    result = isRecursiveReference(newkey, path->parent);
66
0
  return result;
67
0
}
68
69
LogString substVarsSafely(const LogString& val, helpers::Properties& props, const LogStringChain* path = 0)
70
5
{
71
5
  LogString sbuf;
72
5
  const logchar delimStartArray[] = { 0x24, 0x7B, 0 }; // '$', '{'
73
5
  const LogString delimStart(delimStartArray);
74
5
  const logchar delimStop = 0x7D; // '}';
75
5
  const size_t DELIM_START_LEN = 2;
76
5
  const size_t DELIM_STOP_LEN = 1;
77
78
5
  size_t i = 0;
79
80
5
  while (true)
81
5
  {
82
5
    size_t j = val.find(delimStart, i);
83
84
5
    if (j == val.npos)
85
5
    {
86
      // no more variables
87
5
      if (i == 0)
88
5
      {
89
        // this is a simple string
90
5
        return val;
91
5
      }
92
0
      else
93
0
      {
94
        // add the tail string which contails no variables and return the result.
95
0
        sbuf.append(val.substr(i, val.length() - i));
96
0
        return sbuf;
97
0
      }
98
5
    }
99
0
    else
100
0
    {
101
0
      sbuf.append(val.substr(i, j - i));
102
0
      size_t k = val.find(delimStop, j);
103
104
0
      if (k == val.npos)
105
0
      {
106
0
        LogString msg(1, (logchar) 0x22 /* '\"' */);
107
0
        msg.append(val);
108
0
        msg.append(LOG4CXX_STR("\" has no closing brace. Opening brace at position "));
109
0
        helpers::Pool p;
110
0
        helpers::StringHelper::toString(j, p, msg);
111
0
        msg.append(1, (logchar) 0x2E /* '.' */);
112
0
        throw helpers::IllegalArgumentException(msg);
113
0
      }
114
0
      else
115
0
      {
116
0
        j += DELIM_START_LEN;
117
0
        LogString key = val.substr(j, k - j);
118
0
        if (path && isRecursiveReference(key, path))
119
0
        {
120
0
          LogString msg(LOG4CXX_STR("The variable ${"));
121
0
          msg.append(key);
122
0
          msg.append(LOG4CXX_STR("} is used recursively"));
123
0
          throw helpers::IllegalArgumentException(msg);
124
0
        }
125
126
        // first try in System properties
127
0
        LogString replacement(helpers::OptionConverter::getSystemProperty(key, LogString()));
128
129
        // then try props parameter
130
0
        if (replacement.empty())
131
0
        {
132
0
          replacement = props.getProperty(key);
133
0
        }
134
135
0
        if (!replacement.empty())
136
0
        {
137
          // Do variable substitution on the replacement string
138
          // such that we can solve "Hello ${x2}" as "Hello p1"
139
          // the where the properties are
140
          // x1=p1
141
          // x2=${x1}
142
0
          LogStringChain current{ key, path };
143
0
          LogString recursiveReplacement = substVarsSafely(replacement, props, &current);
144
0
          sbuf.append(recursiveReplacement);
145
0
        }
146
147
0
        i = k + DELIM_STOP_LEN;
148
0
      }
149
0
    }
150
5
  }
151
5
}
152
153
} // namespace
154
155
namespace LOG4CXX_NS
156
{
157
158
class ConfiguratorWatchdog  : public helpers::FileWatchdog
159
{
160
  spi::ConfiguratorPtr m_config;
161
  public:
162
    ConfiguratorWatchdog(const spi::ConfiguratorPtr& config, const File& filename)
163
0
        : helpers::FileWatchdog(filename)
164
0
        , m_config(config)
165
0
    {
166
0
    }
167
168
    /**
169
    Call PropertyConfigurator#doConfigure(const String& configFileName,
170
    const spi::LoggerRepositoryPtr& hierarchy) with the
171
    <code>filename</code> to reconfigure log4cxx.
172
    */
173
    void doOnChange() override
174
0
    {
175
0
        m_config->doConfigure(file(), LogManager::getLoggerRepository());
176
0
    }
177
178
  static void startWatching(const spi::ConfiguratorPtr& config, const File& filename, long delay)
179
0
  {
180
0
    using WatchdogHolder = helpers::SingletonHolder<ConfiguratorWatchdog>;
181
0
    auto pHolder = helpers::APRInitializer::getOrAddUnique<WatchdogHolder>
182
0
      ( [&config, &filename]() -> helpers::ObjectPtr
183
0
        { return std::make_shared<WatchdogHolder>(config, filename); }
184
0
      );
185
0
    auto& dog = pHolder->value();
186
0
    dog.m_config = config;
187
0
    dog.setFile(filename);
188
0
    dog.setDelay(delay);
189
0
    dog.start();
190
0
  }
191
};
192
193
}
194
195
using namespace LOG4CXX_NS;
196
using namespace LOG4CXX_NS::helpers;
197
using namespace LOG4CXX_NS::spi;
198
199
200
LogString OptionConverter::convertSpecialChars(const LogString& s)
201
1
{
202
1
  logchar c;
203
1
  LogString sbuf;
204
205
1
  LogString::const_iterator i = s.begin();
206
207
24
  while (i != s.end())
208
23
  {
209
23
    c = *i++;
210
211
23
    if (i != s.end() && c == 0x5C /* '\\' */)
212
0
    {
213
0
      c =  *i++;
214
215
0
      switch (c)
216
0
      {
217
0
        case 0x6E: //'n'
218
0
          c = 0x0A;
219
0
          break;
220
221
0
        case 0x72: //'r'
222
0
          c = 0x0D;
223
0
          break;
224
225
0
        case 0x74: //'t'
226
0
          c = 0x09;
227
0
          break;
228
229
0
        case 0x66: //'f'
230
0
          c = 0x0C;
231
0
          break;
232
233
0
        default:
234
0
          break;
235
0
      }
236
0
    }
237
238
23
    sbuf.append(1, c);
239
23
  }
240
241
1
  return sbuf;
242
1
}
243
244
245
bool OptionConverter::toBoolean(const LogString& value, bool dEfault)
246
2
{
247
2
  if (value.length() >= 4)
248
2
  {
249
2
    if (StringHelper::equalsIgnoreCase(value.substr(0, 4),
250
2
        LOG4CXX_STR("TRUE"), LOG4CXX_STR("true")))
251
1
    {
252
1
      return true;
253
1
    }
254
2
  }
255
256
1
  if (dEfault && value.length() >= 5)
257
0
  {
258
0
    if (StringHelper::equalsIgnoreCase(value.substr(0, 5),
259
0
        LOG4CXX_STR("FALSE"), LOG4CXX_STR("false")))
260
0
    {
261
0
      return false;
262
0
    }
263
0
  }
264
265
1
  return dEfault;
266
1
}
267
268
int OptionConverter::toInt(const LogString& value, int dEfault)
269
0
{
270
0
  LogString trimmed(StringHelper::trim(value));
271
272
0
  if (trimmed.empty())
273
0
  {
274
0
    return dEfault;
275
0
  }
276
277
0
  LOG4CXX_ENCODE_CHAR(cvalue, trimmed);
278
279
0
  return (int) atol(cvalue.c_str());
280
0
}
281
282
long OptionConverter::toFileSize(const LogString& s, long dEfault)
283
0
{
284
0
  if (s.empty())
285
0
  {
286
0
    return dEfault;
287
0
  }
288
289
0
  size_t index = s.find_first_of(LOG4CXX_STR("bB"));
290
291
0
  if (index != LogString::npos && index > 0)
292
0
  {
293
0
    long multiplier = 1;
294
0
    index--;
295
296
0
    if (s[index] == 0x6B /* 'k' */ || s[index] == 0x4B /* 'K' */)
297
0
    {
298
0
      multiplier = 1024;
299
0
    }
300
0
    else if (s[index] == 0x6D /* 'm' */ || s[index] == 0x4D /* 'M' */)
301
0
    {
302
0
      multiplier = 1024 * 1024;
303
0
    }
304
0
    else if (s[index] == 0x67 /* 'g'*/ || s[index] == 0x47 /* 'G' */)
305
0
    {
306
0
      multiplier = 1024 * 1024 * 1024;
307
0
    }
308
309
0
    return toInt(s.substr(0, index), 1) * multiplier;
310
0
  }
311
312
0
  return toInt(s, 1);
313
0
}
314
315
LogString OptionConverter::findAndSubst(const LogString& key, Properties& props)
316
8
{
317
8
  LogString value(props.getProperty(key));
318
319
8
  if (value.empty())
320
3
  {
321
3
    return value;
322
3
  }
323
324
5
  try
325
5
  {
326
5
    return substVars(value, props);
327
5
  }
328
5
  catch (IllegalArgumentException& e)
329
5
  {
330
0
    LogLog::error(((LogString) LOG4CXX_STR("Bad option value ["))
331
0
      + value + LOG4CXX_STR("]."), e);
332
0
    return value;
333
0
  }
334
5
}
335
336
LogString OptionConverter::substVars(const LogString& val, Properties& props)
337
5
{
338
5
  return substVarsSafely(val, props);
339
5
}
340
341
LogString OptionConverter::getSystemProperty(const LogString& key, const LogString& def)
342
2
{
343
2
  if (!key.empty())
344
2
  {
345
2
    LogString value(System::getProperty(key));
346
347
2
    if (!value.empty())
348
0
    {
349
0
      return value;
350
0
    }
351
2
  }
352
353
2
  return def;
354
2
}
355
356
LevelPtr OptionConverter::toLevel(const LogString& value,
357
  const LevelPtr& defaultValue)
358
1
{
359
1
  size_t hashIndex = value.find(LOG4CXX_STR("#"));
360
361
1
  if (hashIndex == LogString::npos)
362
1
  {
363
    // no class name specified : use standard Level class
364
1
    if (value.empty())
365
0
    {
366
0
      return defaultValue;
367
0
    }
368
1
    else
369
1
    {
370
1
      return Level::toLevelLS(value, defaultValue);
371
1
    }
372
1
  }
373
374
0
  LogString clazz = value.substr(hashIndex + 1);
375
0
  LogString levelName = value.substr(0, hashIndex);
376
377
  // This is degenerate case but you never know.
378
0
  if (levelName.empty() || clazz.empty())
379
0
  {
380
0
    return Level::toLevelLS(value, defaultValue);
381
0
  }
382
0
  if (LogLog::isDebugEnabled())
383
0
  {
384
0
    LogLog::debug(LOG4CXX_STR("Desired ") + Level::getStaticClass().getName()
385
0
        + LOG4CXX_STR(" sub-class: [") + clazz + LOG4CXX_STR("]"));
386
0
  }
387
388
0
  try
389
0
  {
390
    // Note: the dynamic_cast could fail across DLL boundaries.
391
    // However, without the dynamic_cast a poorly formed XML file
392
    // could attempt to load an invalid class as a filter, causing
393
    // a crash.  If it can't be converted, a std::bad_cast should be
394
    // thrown(and caught by the exception handler below)
395
0
    const Level::LevelClass& levelClass =
396
0
      dynamic_cast<const Level::LevelClass&>(Loader::loadClass(clazz));
397
0
    return levelClass.toLevel(levelName);
398
0
  }
399
0
  catch (Exception& oops)
400
0
  {
401
0
    LogLog::error(LOG4CXX_STR("Could not create ") + Level::getStaticClass().getName() + LOG4CXX_STR(" sub-class"), oops);
402
0
  }
403
0
  catch (const std::bad_cast&)
404
0
  {
405
0
    LogLog::warn(
406
0
      LOG4CXX_STR("class [") + clazz + LOG4CXX_STR("] unable to be converted to "
407
0
      "Level::LevelClass"));
408
0
  }
409
0
  catch (...)
410
0
  {
411
0
    LogLog::warn(
412
0
      LOG4CXX_STR("class [") + clazz + LOG4CXX_STR("], level [") + levelName +
413
0
      LOG4CXX_STR("] conversion) failed."));
414
0
  }
415
416
0
  return defaultValue;
417
0
}
418
419
420
ObjectPtr OptionConverter::instantiateByKey(Properties& props, const LogString& key,
421
  const Class& superClass, const ObjectPtr& defaultValue)
422
2
{
423
  // Get the value of the property in string form
424
2
  LogString className(findAndSubst(key, props));
425
426
2
  if (className.empty())
427
0
  {
428
0
    LogLog::error(
429
0
      ((LogString) LOG4CXX_STR("Could not find value for key ")) + key);
430
0
    return defaultValue;
431
0
  }
432
433
  // Trim className to avoid trailing spaces that cause problems.
434
2
  return OptionConverter::instantiateByClassName(
435
2
      StringHelper::trim(className), superClass, defaultValue);
436
2
}
437
438
ObjectPtr OptionConverter::instantiateByClassName(const LogString& className,
439
  const Class& superClass, const ObjectPtr& defaultValue)
440
2
{
441
2
  if (LogLog::isDebugEnabled())
442
0
  {
443
0
    LogLog::debug(LOG4CXX_STR("Desired ") + superClass.getName()
444
0
      + LOG4CXX_STR(" sub-class: [") + className + LOG4CXX_STR("]"));
445
0
  }
446
2
  try
447
2
  {
448
2
    const Class& classObj = Loader::loadClass(className);
449
2
    ObjectPtr newObject =  ObjectPtr(classObj.newInstance());
450
451
2
    if (!newObject->instanceof(superClass))
452
0
    {
453
0
      LogLog::error(LOG4CXX_STR("Not a ") + superClass.getName() + LOG4CXX_STR(" sub-class"));
454
0
      return defaultValue;
455
0
    }
456
457
2
    return newObject;
458
2
  }
459
2
  catch (Exception& e)
460
2
  {
461
0
    LogLog::error(LOG4CXX_STR("Could not create ") + superClass.getName() + LOG4CXX_STR(" sub-class"), e);
462
0
  }
463
464
0
  return defaultValue;
465
2
}
466
467
void OptionConverter::selectAndConfigure(const File& configFileName,
468
  const LogString& _clazz, spi::LoggerRepositoryPtr hierarchy, int delay)
469
0
{
470
0
  ConfiguratorPtr configurator;
471
0
  LogString clazz = _clazz;
472
473
0
  LogString filename(configFileName.getPath());
474
475
0
#if LOG4CXX_HAS_DOMCONFIGURATOR
476
0
  if (clazz.empty()
477
0
    && filename.length() > 4
478
0
    && StringHelper::equalsIgnoreCase(
479
0
      filename.substr(filename.length() - 4),
480
0
      LOG4CXX_STR(".XML"), LOG4CXX_STR(".xml")))
481
0
  {
482
0
    clazz = LOG4CXX_NS::xml::DOMConfigurator::getStaticClass().getName();
483
0
  }
484
0
#endif
485
486
0
  if (!clazz.empty())
487
0
  {
488
0
    if (LogLog::isDebugEnabled())
489
0
      LogLog::debug(LOG4CXX_STR("Preferred configurator class: ") + clazz);
490
0
    const Class& clazzObj = Loader::loadClass(clazz);
491
0
    ObjectPtr obj = ObjectPtr(clazzObj.newInstance());
492
0
    configurator = LOG4CXX_NS::cast<Configurator>(obj);
493
494
0
    if (configurator == 0)
495
0
    {
496
0
      LogLog::error(LOG4CXX_STR("Could not instantiate configurator [")
497
0
        + clazz + LOG4CXX_STR("]."));
498
0
      return;
499
0
    }
500
0
  }
501
0
  else
502
0
  {
503
0
    configurator = std::make_shared<PropertyConfigurator>();
504
0
  }
505
506
0
  if (0 < delay)
507
0
    ConfiguratorWatchdog::startWatching(configurator, configFileName, delay);
508
0
  else
509
0
    configurator->doConfigure(configFileName, hierarchy);
510
0
}