initial support for sub-interfaces; Juniper driver reports logical interfaces on...
[public/netxms.git] / src / libnxlp / parser.cpp
CommitLineData
659bf814 1/*
5039dede
AK
2** NetXMS - Network Management System
3** Log Parsing Library
bc3e2810 4** Copyright (C) 2003-2017 Raden Solutions
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
65d2c384
VK
7** it under the terms of the GNU Lesser General Public License as published by
8** the Free Software Foundation; either version 3 of the License, or
5039dede
AK
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
65d2c384 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: parser.cpp
21**
22**/
23
24#include "libnxlp.h"
5039dede 25#include <expat.h>
5039dede 26
c1482463
VK
27/**
28 * Context state texts
29 */
6646dca4 30static const TCHAR *s_states[] = { _T("MANUAL"), _T("AUTO"), _T("INACTIVE") };
5039dede 31
c1482463 32/**
48126778 33 * XML parser state codes
c1482463 34 */
5039dede
AK
35#define XML_STATE_INIT -1
36#define XML_STATE_END -2
37#define XML_STATE_ERROR -255
38#define XML_STATE_PARSER 0
39#define XML_STATE_RULES 1
40#define XML_STATE_RULE 2
41#define XML_STATE_MATCH 3
42#define XML_STATE_EVENT 4
43#define XML_STATE_FILE 5
44#define XML_STATE_ID 6
45#define XML_STATE_LEVEL 7
46#define XML_STATE_SOURCE 8
47#define XML_STATE_CONTEXT 9
48#define XML_STATE_MACROS 10
49#define XML_STATE_MACRO 11
50#define XML_STATE_DESCRIPTION 12
51
48126778
VK
52/**
53 * XML parser state for creating LogParser object from XML
54 */
565a8f28 55struct XML_PARSER_STATE
5039dede
AK
56{
57 LogParser *parser;
58 int state;
59 String regexp;
60 String event;
721fc691 61 String file;
565a8f28 62 StringList files;
bc3e2810
VK
63 IntegerArray<INT32> encodings;
64 IntegerArray<INT32> preallocFlags;
5039dede
AK
65 String id;
66 String level;
67 String source;
68 String context;
69 String description;
839931d3 70 String ruleName;
5039dede
AK
71 int contextAction;
72 String ruleContext;
73 int numEventParams;
74 String errorText;
75 String macroName;
76 String macro;
6d738067
VK
77 bool invertedRule;
78 bool breakFlag;
659bf814 79 int repeatCount;
80 int repeatInterval;
81 bool resetRepeat;
63e99e56 82
bc3e2810 83 XML_PARSER_STATE() : encodings(4, 4), preallocFlags(4, 4)
48126778
VK
84 {
85 state = -1;
86 parser = NULL;
87 invertedRule = false;
88 breakFlag = false;
89 contextAction = CONTEXT_SET_AUTOMATIC;
90 numEventParams = 0;
659bf814 91 repeatCount = 0;
92 repeatInterval = 0;
93 resetRepeat = true;
48126778 94 }
565a8f28 95};
5039dede 96
66ee0102
VK
97/**
98 * Parser default constructor
99 */
1df332d8 100LogParser::LogParser()
5039dede 101{
1df332d8 102 m_rules = new ObjectArray<LogParserRule>(16, 16, true);
5039dede
AK
103 m_cb = NULL;
104 m_userArg = NULL;
e464b7dc 105 m_name = NULL;
5039dede 106 m_fileName = NULL;
6646dca4 107 m_fileEncoding = LP_FCP_ACP;
bc3e2810 108 m_preallocatedFile = false;
5039dede
AK
109 m_eventNameList = NULL;
110 m_eventResolver = NULL;
111 m_thread = INVALID_THREAD_HANDLE;
112 m_recordsProcessed = 0;
113 m_recordsMatched = 0;
25c5a016 114 m_processAllRules = false;
5039dede
AK
115 m_traceLevel = 0;
116 m_traceCallback = NULL;
e464b7dc 117 _tcscpy(m_status, LPS_INIT);
18b96a67
VK
118#ifdef _WIN32
119 m_marker = NULL;
120#endif
5039dede
AK
121}
122
66ee0102
VK
123/**
124 * Parser copy constructor
125 */
1df332d8 126LogParser::LogParser(LogParser *src)
66ee0102 127{
1df332d8
VK
128 int count = src->m_rules->size();
129 m_rules = new ObjectArray<LogParserRule>(count, 16, true);
bc3e2810 130 for(int i = 0; i < count; i++)
1df332d8 131 m_rules->add(new LogParserRule(src->m_rules->get(i), this));
5039dede 132
66ee0102
VK
133 m_cb = src->m_cb;
134 m_userArg = src->m_userArg;
bc3e2810
VK
135 m_name = _tcsdup_ex(src->m_name);
136 m_fileName = _tcsdup_ex(src->m_fileName);
66ee0102 137 m_fileEncoding = src->m_fileEncoding;
bc3e2810 138 m_preallocatedFile = src->m_preallocatedFile;
5039dede 139
66ee0102
VK
140 if (src->m_eventNameList != NULL)
141 {
142 int count;
143 for(count = 0; src->m_eventNameList[count].text != NULL; count++);
144 m_eventNameList = (count > 0) ? (CODE_TO_TEXT *)nx_memdup(src->m_eventNameList, sizeof(CODE_TO_TEXT) * (count + 1)) : NULL;
145 }
146 else
147 {
148 m_eventNameList = NULL;
149 }
150
151 m_eventResolver = src->m_eventResolver;
152 m_thread = INVALID_THREAD_HANDLE;
153 m_recordsProcessed = 0;
154 m_recordsMatched = 0;
155 m_processAllRules = src->m_processAllRules;
156 m_traceLevel = src->m_traceLevel;
157 m_traceCallback = src->m_traceCallback;
158 _tcscpy(m_status, LPS_INIT);
18b96a67
VK
159#ifdef _WIN32
160 m_marker = _tcsdup_ex(src->m_marker);
161#endif
66ee0102
VK
162}
163
164/**
165 * Destructor
166 */
5039dede
AK
167LogParser::~LogParser()
168{
1df332d8 169 delete m_rules;
e7912b5d
VK
170 free(m_name);
171 free(m_fileName);
99ebf084 172#ifdef _WIN32
e7912b5d 173 free(m_marker);
99ebf084 174#endif
5039dede
AK
175}
176
66ee0102
VK
177/**
178 * Trace
179 */
6646dca4 180void LogParser::trace(int level, const TCHAR *format, ...)
5039dede
AK
181{
182 va_list args;
183
184 if ((m_traceCallback == NULL) || (level > m_traceLevel))
185 return;
186
187 va_start(args, format);
bc3e2810 188 m_traceCallback(level, format, args);
5039dede
AK
189 va_end(args);
190}
191
29bb6817
VK
192/**
193 * Add rule
194 */
6d738067 195bool LogParser::addRule(LogParserRule *rule)
5039dede 196{
bc3e2810
VK
197 bool valid = rule->isValid();
198 if (valid)
5039dede 199 {
1df332d8 200 m_rules->add(rule);
5039dede
AK
201 }
202 else
203 {
204 delete rule;
205 }
bc3e2810 206 return valid;
5039dede
AK
207}
208
29bb6817
VK
209/**
210 * Create and add rule
211 */
659bf814 212bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName, int numParams, int repeatInterval, int repeatCount, bool resetRepeat)
5039dede 213{
839931d3 214 return addRule(new LogParserRule(this, NULL, regexp, eventCode, eventName, numParams, repeatInterval, repeatCount, resetRepeat));
5039dede
AK
215}
216
29bb6817
VK
217/**
218 * Check context
219 */
6646dca4 220const TCHAR *LogParser::checkContext(LogParserRule *rule)
5039dede 221{
6646dca4 222 const TCHAR *state;
659bf814 223
6d738067 224 if (rule->getContext() == NULL)
32ec6391 225 {
6d738067
VK
226 trace(5, _T(" rule has no context"));
227 return s_states[CONTEXT_SET_MANUAL];
32ec6391 228 }
659bf814 229
fb986055 230 state = m_contexts.get(rule->getContext());
5039dede 231 if (state == NULL)
32ec6391 232 {
6d738067 233 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
659bf814 234 return NULL; // Context inactive, don't use this rule
32ec6391 235 }
659bf814 236
6d738067 237 if (!_tcscmp(state, s_states[CONTEXT_CLEAR]))
32ec6391 238 {
6d738067 239 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
32ec6391
VK
240 return NULL;
241 }
242 else
243 {
6d738067 244 trace(5, _T(" context '%s' active (mode=%s)"), rule->getContext(), state);
32ec6391
VK
245 return state;
246 }
5039dede
AK
247}
248
acc04d96
VK
249/**
250 * Match log record
251 */
da623f8b
VK
252bool LogParser::matchLogRecord(bool hasAttributes, const TCHAR *source, UINT32 eventId,
253 UINT32 level, const TCHAR *line, UINT32 objectId)
5039dede 254{
6646dca4 255 const TCHAR *state;
6d738067
VK
256 bool matched = false;
257
258 if (hasAttributes)
856711aa 259 trace(5, _T("Match event: source=\"%s\" id=%u level=%d text=\"%s\""), source, eventId, level, line);
6d738067 260 else
856711aa 261 trace(5, _T("Match line: \"%s\""), line);
5039dede 262
5039dede 263 m_recordsProcessed++;
bc3e2810 264 int i;
1df332d8 265 for(i = 0; i < m_rules->size(); i++)
5039dede 266 {
1df332d8 267 LogParserRule *rule = m_rules->get(i);
bc3e2810
VK
268 trace(6, _T("checking rule %d \"%s\""), i + 1, rule->getDescription());
269 if ((state = checkContext(rule)) != NULL)
5039dede 270 {
7c09239b 271 bool ruleMatched = hasAttributes ?
bc3e2810
VK
272 rule->matchEx(source, eventId, level, line, m_cb, objectId, m_userArg) :
273 rule->match(line, m_cb, objectId, m_userArg);
7c09239b 274 if (ruleMatched)
5039dede 275 {
bc3e2810 276 trace(5, _T("rule %d \"%s\" matched"), i + 1, rule->getDescription());
7c09239b
VK
277 if (!matched)
278 m_recordsMatched++;
659bf814 279
7c09239b 280 // Update context
bc3e2810 281 if (rule->getContextToChange() != NULL)
7c09239b 282 {
bc3e2810
VK
283 m_contexts.set(rule->getContextToChange(), s_states[rule->getContextAction()]);
284 trace(5, _T("rule %d \"%s\": context %s set to %s"), i + 1,
285 rule->getDescription(), rule->getContextToChange(), s_states[rule->getContextAction()]);
7c09239b 286 }
659bf814 287
7c09239b
VK
288 // Set context of this rule to inactive if rule context mode is "automatic reset"
289 if (!_tcscmp(state, s_states[CONTEXT_SET_AUTOMATIC]))
290 {
bc3e2810 291 m_contexts.set(rule->getContext(), s_states[CONTEXT_CLEAR]);
856711aa 292 trace(5, _T("rule %d \"%s\": context %s cleared because it was set to automatic reset mode"),
bc3e2810 293 i + 1, rule->getDescription(), rule->getContext());
7c09239b
VK
294 }
295 matched = true;
bc3e2810 296 if (!m_processAllRules || rule->getBreakFlag())
7c09239b 297 break;
5039dede 298 }
5039dede
AK
299 }
300 }
1df332d8 301 if (i < m_rules->size())
856711aa 302 trace(5, _T("processing stopped at rule %d \"%s\"; result = %s"), i + 1,
1df332d8 303 m_rules->get(i)->getDescription(), matched ? _T("true") : _T("false"));
5039dede 304 else
856711aa 305 trace(5, _T("Processing stopped at end of rules list; result = %s"), matched ? _T("true") : _T("false"));
5039dede
AK
306 return matched;
307}
308
acc04d96
VK
309/**
310 * Match simple log line
311 */
da623f8b 312bool LogParser::matchLine(const TCHAR *line, UINT32 objectId)
6d738067
VK
313{
314 return matchLogRecord(false, NULL, 0, 0, line, objectId);
315}
316
acc04d96
VK
317/**
318 * Match log event (text with additional attributes - source, severity, event id)
319 */
da623f8b 320bool LogParser::matchEvent(const TCHAR *source, UINT32 eventId, UINT32 level, const TCHAR *line, UINT32 objectId)
6d738067
VK
321{
322 return matchLogRecord(true, source, eventId, level, line, objectId);
323}
324
acc04d96
VK
325/**
326 * Set associated file name
327 */
6646dca4 328void LogParser::setFileName(const TCHAR *name)
5039dede 329{
bc3e2810 330 free(m_fileName);
5039dede 331 m_fileName = (name != NULL) ? _tcsdup(name) : NULL;
e464b7dc
VK
332 if (m_name == NULL)
333 m_name = _tcsdup(name); // Set parser name to file name
334}
335
29bb6817
VK
336/**
337 * Set parser name
338 */
6646dca4 339void LogParser::setName(const TCHAR *name)
e464b7dc
VK
340{
341 safe_free(m_name);
342 m_name = _tcsdup((name != NULL) ? name : CHECK_NULL(m_fileName));
5039dede
AK
343}
344
29bb6817
VK
345/**
346 * Add macro
347 */
6646dca4 348void LogParser::addMacro(const TCHAR *name, const TCHAR *value)
5039dede 349{
fb986055 350 m_macros.set(name, value);
5039dede
AK
351}
352
66ee0102
VK
353/**
354 * Get macro
355 */
6646dca4 356const TCHAR *LogParser::getMacro(const TCHAR *name)
5039dede
AK
357{
358 const TCHAR *value;
359
fb986055 360 value = m_macros.get(name);
5039dede
AK
361 return CHECK_NULL_EX(value);
362}
363
66ee0102
VK
364/**
365 * Create parser configuration from XML - callback for element start
366 */
5039dede
AK
367static void StartElement(void *userData, const char *name, const char **attrs)
368{
369 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
370
371 if (!strcmp(name, "parser"))
372 {
373 ps->state = XML_STATE_PARSER;
6d738067
VK
374 ps->parser->setProcessAllFlag(XMLGetAttrBoolean(attrs, "processAll", false));
375 ps->parser->setTraceLevel(XMLGetAttrInt(attrs, "trace", 0));
e464b7dc
VK
376 const char *name = XMLGetAttr(attrs, "name");
377 if (name != NULL)
6646dca4
VK
378 {
379#ifdef UNICODE
380 WCHAR *wname = WideStringFromUTF8String(name);
381 ps->parser->setName(wname);
382 free(wname);
383#else
e464b7dc 384 ps->parser->setName(name);
6646dca4
VK
385#endif
386 }
5039dede
AK
387 }
388 else if (!strcmp(name, "file"))
389 {
390 ps->state = XML_STATE_FILE;
6646dca4
VK
391 const char *encoding = XMLGetAttr(attrs, "encoding");
392 if (encoding != NULL)
393 {
0ea005dd
EJ
394 if ((*encoding == 0))
395 {
bc3e2810 396 ps->encodings.add(LP_FCP_AUTO);
0ea005dd
EJ
397 }
398 if (!stricmp(encoding, "acp"))
6646dca4 399 {
bc3e2810 400 ps->encodings.add(LP_FCP_ACP);
6646dca4
VK
401 }
402 else if (!stricmp(encoding, "utf8") || !stricmp(encoding, "utf-8"))
403 {
bc3e2810 404 ps->encodings.add(LP_FCP_UTF8);
6646dca4
VK
405 }
406 else if (!stricmp(encoding, "ucs2") || !stricmp(encoding, "ucs-2") || !stricmp(encoding, "utf-16"))
407 {
bc3e2810 408 ps->encodings.add(LP_FCP_UCS2);
6646dca4 409 }
0ea005dd
EJ
410 else if (!stricmp(encoding, "ucs2le") || !stricmp(encoding, "ucs-2le") || !stricmp(encoding, "utf-16le"))
411 {
bc3e2810 412 ps->encodings.add(LP_FCP_UCS2_LE);
0ea005dd
EJ
413 }
414 else if (!stricmp(encoding, "ucs2be") || !stricmp(encoding, "ucs-2be") || !stricmp(encoding, "utf-16be"))
415 {
bc3e2810 416 ps->encodings.add(LP_FCP_UCS2_BE);
0ea005dd 417 }
6646dca4
VK
418 else if (!stricmp(encoding, "ucs4") || !stricmp(encoding, "ucs-4") || !stricmp(encoding, "utf-32"))
419 {
bc3e2810 420 ps->encodings.add(LP_FCP_UCS4);
6646dca4 421 }
0ea005dd
EJ
422 else if (!stricmp(encoding, "ucs4le") || !stricmp(encoding, "ucs-4le") || !stricmp(encoding, "utf-32le"))
423 {
bc3e2810 424 ps->encodings.add(LP_FCP_UCS4_LE);
0ea005dd
EJ
425 }
426 else if (!stricmp(encoding, "ucs4be") || !stricmp(encoding, "ucs-4be") || !stricmp(encoding, "utf-32be"))
427 {
bc3e2810 428 ps->encodings.add(LP_FCP_UCS4_BE);
0ea005dd 429 }
6646dca4
VK
430 else
431 {
432 ps->errorText = _T("Invalid file encoding");
433 ps->state = XML_STATE_ERROR;
434 }
435 }
436 else
437 {
bc3e2810 438 ps->encodings.add(LP_FCP_AUTO);
6646dca4 439 }
bc3e2810 440 ps->preallocFlags.add(XMLGetAttrBoolean(attrs, "preallocated", false) ? 1 : 0);
5039dede
AK
441 }
442 else if (!strcmp(name, "macros"))
443 {
444 ps->state = XML_STATE_MACROS;
445 }
446 else if (!strcmp(name, "macro"))
447 {
448 const char *name;
449
450 ps->state = XML_STATE_MACRO;
451 name = XMLGetAttr(attrs, "name");
6646dca4
VK
452#ifdef UNICODE
453 ps->macroName = L"";
4e0e77e6 454 ps->macroName.appendMBString(name, strlen(name), CP_UTF8);
6646dca4 455#else
5039dede 456 ps->macroName = CHECK_NULL_A(name);
6646dca4 457#endif
5039dede
AK
458 ps->macro = NULL;
459 }
460 else if (!strcmp(name, "rules"))
461 {
462 ps->state = XML_STATE_RULES;
463 }
464 else if (!strcmp(name, "rule"))
465 {
466 ps->regexp = NULL;
6d738067 467 ps->invertedRule = false;
5039dede
AK
468 ps->event = NULL;
469 ps->context = NULL;
470 ps->contextAction = CONTEXT_SET_AUTOMATIC;
471 ps->description = NULL;
6d738067
VK
472 ps->id = NULL;
473 ps->source = NULL;
474 ps->level = NULL;
6646dca4 475#ifdef UNICODE
839931d3 476 ps->ruleContext.clear();
6646dca4
VK
477 const char *context = XMLGetAttr(attrs, "context");
478 if (context != NULL)
4e0e77e6 479 ps->ruleContext.appendMBString(context, strlen(context), CP_UTF8);
839931d3
VK
480 ps->ruleName.clear();
481 const char *name = XMLGetAttr(attrs, "name");
482 if (name != NULL)
483 ps->ruleName.appendMBString(name, strlen(name), CP_UTF8);
6646dca4 484#else
5039dede 485 ps->ruleContext = XMLGetAttr(attrs, "context");
839931d3 486 ps->ruleName = XMLGetAttr(attrs, "name");
6646dca4 487#endif
6d738067 488 ps->breakFlag = XMLGetAttrBoolean(attrs, "break", false);
5039dede 489 ps->state = XML_STATE_RULE;
9eb288e6 490 ps->numEventParams = 0;
5039dede
AK
491 }
492 else if (!strcmp(name, "match"))
493 {
494 ps->state = XML_STATE_MATCH;
6d738067 495 ps->invertedRule = XMLGetAttrBoolean(attrs, "invert", false);
659bf814 496 ps->resetRepeat = XMLGetAttrBoolean(attrs, "reset", true);
497 ps->repeatCount = XMLGetAttrInt(attrs, "repeatCount", 0);
498 ps->repeatInterval = XMLGetAttrInt(attrs, "repeatInterval", 0);
5039dede 499 }
d43aee56 500 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
5039dede
AK
501 {
502 ps->state = XML_STATE_ID;
503 }
d43aee56 504 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
5039dede
AK
505 {
506 ps->state = XML_STATE_LEVEL;
507 }
d43aee56 508 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
5039dede
AK
509 {
510 ps->state = XML_STATE_SOURCE;
511 }
512 else if (!strcmp(name, "event"))
513 {
967893bb 514 ps->numEventParams = XMLGetAttrUINT32(attrs, "params", 0);
5039dede
AK
515 ps->state = XML_STATE_EVENT;
516 }
517 else if (!strcmp(name, "context"))
518 {
519 const char *action;
659bf814 520
5039dede 521 ps->state = XML_STATE_CONTEXT;
659bf814 522
5039dede
AK
523 action = XMLGetAttr(attrs, "action");
524 if (action == NULL)
525 action = "set";
659bf814 526
5039dede
AK
527 if (!strcmp(action, "set"))
528 {
529 const char *mode;
530
531 mode = XMLGetAttr(attrs, "reset");
532 if (mode == NULL)
533 mode = "auto";
534
535 if (!strcmp(mode, "auto"))
536 {
537 ps->contextAction = CONTEXT_SET_AUTOMATIC;
659bf814 538 }
5039dede
AK
539 else if (!strcmp(mode, "manual"))
540 {
541 ps->contextAction = CONTEXT_SET_MANUAL;
659bf814 542 }
5039dede
AK
543 else
544 {
545 ps->errorText = _T("Invalid context reset mode");
546 ps->state = XML_STATE_ERROR;
547 }
548 }
549 else if (!strcmp(action, "clear"))
550 {
551 ps->contextAction = CONTEXT_CLEAR;
552 }
553 else
554 {
555 ps->errorText = _T("Invalid context action");
556 ps->state = XML_STATE_ERROR;
557 }
558 }
559 else if (!strcmp(name, "description"))
560 {
561 ps->state = XML_STATE_DESCRIPTION;
562 }
563 else
564 {
565 ps->state = XML_STATE_ERROR;
566 }
567}
568
66ee0102
VK
569/**
570 * Create parser configuration from XML - callback for element end
571 */
5039dede
AK
572static void EndElement(void *userData, const char *name)
573{
574 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
575
0ea005dd
EJ
576 if (ps->state == XML_STATE_ERROR)
577 return;
578
5039dede
AK
579 if (!strcmp(name, "parser"))
580 {
581 ps->state = XML_STATE_END;
582 }
583 else if (!strcmp(name, "file"))
584 {
721fc691
AK
585 ps->files.add(ps->file);
586 ps->file = _T("");
5039dede
AK
587 ps->state = XML_STATE_PARSER;
588 }
589 else if (!strcmp(name, "macros"))
590 {
591 ps->state = XML_STATE_PARSER;
592 }
593 else if (!strcmp(name, "macro"))
594 {
6d738067 595 ps->parser->addMacro(ps->macroName, ps->macro);
5039dede
AK
596 ps->state = XML_STATE_MACROS;
597 }
598 else if (!strcmp(name, "rules"))
599 {
600 ps->state = XML_STATE_PARSER;
601 }
602 else if (!strcmp(name, "rule"))
603 {
da623f8b 604 UINT32 eventCode;
2dd24569 605 const TCHAR *eventName = NULL;
6646dca4 606 TCHAR *eptr;
5039dede
AK
607 LogParserRule *rule;
608
0bbab9d3 609 ps->event.trim();
6646dca4 610 eventCode = _tcstoul(ps->event, &eptr, 0);
5039dede 611 if (*eptr != 0)
2dd24569
VK
612 {
613 eventCode = ps->parser->resolveEventName(ps->event, 0);
614 if (eventCode == 0)
615 {
6646dca4 616 eventName = (const TCHAR *)ps->event;
2dd24569
VK
617 }
618 }
659bf814 619
ce7565e7 620 if (ps->regexp.isEmpty())
d43aee56 621 ps->regexp = _T(".*");
839931d3 622 rule = new LogParserRule(ps->parser, (const TCHAR *)ps->ruleName, (const TCHAR *)ps->regexp, eventCode, eventName, ps->numEventParams, ps->repeatInterval, ps->repeatCount, ps->resetRepeat);
ce7565e7 623 if (!ps->ruleContext.isEmpty())
6d738067 624 rule->setContext(ps->ruleContext);
ce7565e7 625 if (!ps->context.isEmpty())
5039dede 626 {
6d738067
VK
627 rule->setContextToChange(ps->context);
628 rule->setContextAction(ps->contextAction);
5039dede 629 }
6d738067 630
ce7565e7 631 if (!ps->description.isEmpty())
6d738067 632 rule->setDescription(ps->description);
659bf814 633
ce7565e7 634 if (!ps->source.isEmpty())
6d738067
VK
635 rule->setSource(ps->source);
636
ce7565e7 637 if (!ps->level.isEmpty())
6d738067
VK
638 rule->setLevel(_tcstoul(ps->level, NULL, 0));
639
ce7565e7 640 if (!ps->id.isEmpty())
6d738067 641 {
da623f8b 642 UINT32 start, end;
6d738067
VK
643 TCHAR *eptr;
644
6646dca4 645 start = _tcstoul(ps->id, &eptr, 0);
6d738067
VK
646 if (*eptr == 0)
647 {
648 end = start;
649 }
650 else /* TODO: add better error handling */
651 {
652 while(!_istdigit(*eptr))
653 eptr++;
6646dca4 654 end = _tcstoul(eptr, NULL, 0);
6d738067
VK
655 }
656 rule->setIdRange(start, end);
657 }
658
659 rule->setInverted(ps->invertedRule);
660 rule->setBreakFlag(ps->breakFlag);
661
662 ps->parser->addRule(rule);
5039dede
AK
663 ps->state = XML_STATE_RULES;
664 }
665 else if (!strcmp(name, "match"))
666 {
667 ps->state = XML_STATE_RULE;
668 }
d43aee56 669 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
5039dede
AK
670 {
671 ps->state = XML_STATE_RULE;
672 }
d43aee56 673 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
5039dede
AK
674 {
675 ps->state = XML_STATE_RULE;
676 }
d43aee56 677 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
5039dede
AK
678 {
679 ps->state = XML_STATE_RULE;
680 }
681 else if (!strcmp(name, "event"))
682 {
683 ps->state = XML_STATE_RULE;
684 }
685 else if (!strcmp(name, "context"))
686 {
687 ps->state = XML_STATE_RULE;
688 }
689 else if (!strcmp(name, "description"))
690 {
691 ps->state = XML_STATE_RULE;
692 }
693}
694
66ee0102
VK
695/**
696 * Create parser configuration from XML - callback for element data
697 */
5039dede
AK
698static void CharData(void *userData, const XML_Char *s, int len)
699{
700 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
701
702 switch(ps->state)
703 {
704 case XML_STATE_MATCH:
4e0e77e6 705 ps->regexp.appendMBString(s, len, CP_UTF8);
5039dede
AK
706 break;
707 case XML_STATE_ID:
4e0e77e6 708 ps->id.appendMBString(s, len, CP_UTF8);
5039dede
AK
709 break;
710 case XML_STATE_LEVEL:
4e0e77e6 711 ps->level.appendMBString(s, len, CP_UTF8);
5039dede
AK
712 break;
713 case XML_STATE_SOURCE:
4e0e77e6 714 ps->source.appendMBString(s, len, CP_UTF8);
5039dede
AK
715 break;
716 case XML_STATE_EVENT:
4e0e77e6 717 ps->event.appendMBString(s, len, CP_UTF8);
5039dede
AK
718 break;
719 case XML_STATE_FILE:
4e0e77e6 720 ps->file.appendMBString(s, len, CP_UTF8);
5039dede
AK
721 break;
722 case XML_STATE_CONTEXT:
4e0e77e6 723 ps->context.appendMBString(s, len, CP_UTF8);
5039dede
AK
724 break;
725 case XML_STATE_DESCRIPTION:
4e0e77e6 726 ps->description.appendMBString(s, len, CP_UTF8);
5039dede
AK
727 break;
728 case XML_STATE_MACRO:
4e0e77e6 729 ps->macro.appendMBString(s, len, CP_UTF8);
5039dede
AK
730 break;
731 default:
732 break;
733 }
734}
735
66ee0102
VK
736/**
737 * Create parser configuration from XML. Returns array of identical parsers,
738 * one for each <file> tag in source XML. Resulting parsers only differs with file names.
739 */
da623f8b 740ObjectArray<LogParser> *LogParser::createFromXml(const char *xml, int xmlLen, TCHAR *errorText, int errBufSize, bool (*eventResolver)(const TCHAR *, UINT32 *))
5039dede 741{
271c3551 742 ObjectArray<LogParser> *parsers = NULL;
5039dede 743
66ee0102
VK
744 XML_Parser parser = XML_ParserCreate(NULL);
745 XML_PARSER_STATE state;
746 state.parser = new LogParser;
271c3551 747 state.parser->setEventNameResolver(eventResolver);
66ee0102
VK
748 XML_SetUserData(parser, &state);
749 XML_SetElementHandler(parser, StartElement, EndElement);
750 XML_SetCharacterDataHandler(parser, CharData);
2266fe84 751 bool success = (XML_Parse(parser, xml, (xmlLen == -1) ? (int)strlen(xml) : xmlLen, TRUE) != XML_STATUS_ERROR);
66ee0102
VK
752 if (!success && (errorText != NULL))
753 {
754 _sntprintf(errorText, errBufSize, _T("%hs at line %d"),
755 XML_ErrorString(XML_GetErrorCode(parser)),
756 (int)XML_GetCurrentLineNumber(parser));
757 }
758 XML_ParserFree(parser);
759 if (success && (state.state == XML_STATE_ERROR))
760 {
66ee0102 761 if (errorText != NULL)
271c3551 762 nx_strncpy(errorText, state.errorText, errBufSize);
66ee0102
VK
763 }
764 else if (success)
659bf814 765 {
271c3551 766 parsers = new ObjectArray<LogParser>;
a6312bd6 767 if (state.files.size() > 0)
565a8f28 768 {
a6312bd6 769 for(int i = 0; i < state.files.size(); i++)
271c3551
VK
770 {
771 LogParser *p = (i > 0) ? new LogParser(state.parser) : state.parser;
a6312bd6 772 p->setFileName(state.files.get(i));
bc3e2810
VK
773 p->m_fileEncoding = state.encodings.get(i);
774 p->m_preallocatedFile = (state.preallocFlags.get(i) != 0);
271c3551
VK
775 parsers->add(p);
776 }
777 }
778 else
779 {
780 // It is possible to have parser without <file> tag, syslog parser for example
781 parsers->add(state.parser);
565a8f28 782 }
5039dede 783 }
565a8f28
AK
784
785 return parsers;
5039dede
AK
786}
787
66ee0102
VK
788/**
789 * Resolve event name
790 */
da623f8b 791UINT32 LogParser::resolveEventName(const TCHAR *name, UINT32 defVal)
5039dede
AK
792{
793 if (m_eventNameList != NULL)
794 {
795 int i;
796
797 for(i = 0; m_eventNameList[i].text != NULL; i++)
798 if (!_tcsicmp(name, m_eventNameList[i].text))
799 return m_eventNameList[i].code;
800 }
801
802 if (m_eventResolver != NULL)
803 {
da623f8b 804 UINT32 val;
5039dede
AK
805
806 if (m_eventResolver(name, &val))
807 return val;
808 }
809
810 return defVal;
811}
839931d3
VK
812
813/**
814 * Find rule by name
815 */
816const LogParserRule *LogParser::findRuleByName(const TCHAR *name) const
817{
1df332d8 818 for(int i = 0; i < m_rules->size(); i++)
839931d3 819 {
1df332d8 820 LogParserRule *rule = m_rules->get(i);
bc3e2810
VK
821 if (!_tcsicmp(rule->getName(), name))
822 return rule;
839931d3
VK
823 }
824 return NULL;
825}
29b701c3
VK
826
827/**
828 * Restore counters from previous parser copy
829 */
830void LogParser::restoreCounters(const LogParser *parser)
831{
1df332d8 832 for(int i = 0; i < m_rules->size(); i++)
29b701c3 833 {
1df332d8 834 const LogParserRule *rule = parser->findRuleByName(m_rules->get(i)->getName());
29b701c3
VK
835 if (rule != NULL)
836 {
1df332d8 837 m_rules->get(i)->restoreCounters(rule);
29b701c3
VK
838 }
839 }
840}
bc63d017
VK
841
842/**
843 * Get character size in bytes for parser's encoding
844 */
845int LogParser::getCharSize() const
846{
847 switch(m_fileEncoding)
848 {
849 case LP_FCP_UCS4_BE:
850 case LP_FCP_UCS4_LE:
851 case LP_FCP_UCS4:
852 return 4;
853 case LP_FCP_UCS2_BE:
854 case LP_FCP_UCS2_LE:
855 case LP_FCP_UCS2:
856 return 2;
857 default:
858 return 1;
859 }
860}