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