1
/* This is the template processing code of rsyslog.
2
* Please see syslogd.c for license information.
3
* This code is placed under the GPL.
4
* begun 2004-11-17 rgerhards
18
#include "stringbuf.h"
19
#include "syslogd-types.h"
24
static struct template *tplRoot = NULL; /* the root of the template list */
25
static struct template *tplLast = NULL; /* points to the last element of the template list */
26
static struct template *tplLastStatic = NULL; /* last static element of the template list */
28
/* This functions converts a template into a string. It should
29
* actually be in template.c, but this requires larger re-structuring
30
* of the code (because all the property-access functions are static
31
* to this module). I have placed it next to the iov*() functions, as
32
* it is somewhat similiar in what it does.
34
* The function takes a pointer to a template and a pointer to a msg object.
35
* It the creates a string based on the template definition. A pointer
36
* to that string is returned to the caller. The caller MUST FREE that
37
* pointer when it is no longer needed. If the function fails, NULL
39
* If memory allocation fails in this function, we silently return
40
* NULL. The reason is that we can not do anything against it. And
41
* if we raise an alert, the memory situation might become even
42
* worse. So we prefer to let the caller deal with it.
43
* rgerhards, 2007-07-03
45
* rgerhards, 2007-09-05: I changed the interface to use the standard iRet
46
* "calling sequence". This greatly eases complexity when it comes to handling
47
* errors in called modules (plus, it is much nicer).
49
rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
52
struct templateEntry *pTpe;
54
unsigned short bMustBeFreed;
62
/* loop through the template. We obtain one value
63
* and copy it over to our dynamic string buffer. Then, we
64
* free the obtained value (if requested). We continue this
65
* loop until we got hold of all values.
67
if((pCStr = rsCStrConstruct()) == NULL) {
68
dbgprintf("memory shortage, tplToString failed\n");
69
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
72
pTpe = pTpl->pEntryRoot;
74
if(pTpe->eEntryType == CONSTANT) {
75
CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr,
76
(uchar *) pTpe->data.constant.pConstant,
77
pTpe->data.constant.iLenConstant)
79
dbgprintf("error %d during tplToString()\n", iRet);
80
/* it does not make sense to continue now */
81
rsCStrDestruct(pCStr);
84
} else if(pTpe->eEntryType == FIELD) {
85
pVal = (uchar*) MsgGetProp(pMsg, pTpe, NULL, &bMustBeFreed);
86
iLenVal = strlen((char*) pVal);
87
/* we now need to check if we should use SQL option. In this case,
88
* we must go over the generated string and escape '\'' characters.
89
* rgerhards, 2005-09-22: the option values below look somewhat misplaced,
90
* but they are handled in this way because of legacy (don't break any
93
if(pTpl->optFormatForSQL == 1)
94
doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 1);
95
else if(pTpl->optFormatForSQL == 2)
96
doSQLEscape(&pVal, &iLenVal, &bMustBeFreed, 0);
97
/* value extracted, so lets copy */
98
CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr, (uchar*) pVal, iLenVal)) {
99
dbgprintf("error %d during tplToString()\n", iRet);
100
/* it does not make sense to continue now */
101
rsCStrDestruct(pCStr);
112
/* we are done with the template, now let's convert the result into a
113
* "real" (usable) string and discard the helper structures.
115
CHKiRet(rsCStrFinish(pCStr));
116
CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pVal, 0));
119
*ppSz = (iRet == RS_RET_OK) ? pVal : NULL;
124
/* Helper to doSQLEscape. This is called if doSQLEscape
125
* runs out of memory allocating the escaped string.
126
* Then we are in trouble. We can
127
* NOT simply return the unmodified string because this
128
* may cause SQL injection. But we also can not simply
129
* abort the run, this would be a DoS. I think an appropriate
130
* measure is to remove the dangerous \' characters. We
131
* replace them by \", which will break the message and
132
* signatures eventually present - but this is the
133
* best thing we can do now (or does anybody
134
* have a better idea?). rgerhards 2004-11-23
135
* added support for "escapeMode" (so doSQLEscape for details).
136
* if mode = 1, then backslashes are changed to slashes.
137
* rgerhards 2005-09-22
139
static void doSQLEmergencyEscape(register uchar *p, int escapeMode)
144
else if((escapeMode == 1) && (*p == '\\'))
151
/* SQL-Escape a string. Single quotes are found and
152
* replaced by two of them. A new buffer is allocated
153
* for the provided string and the provided buffer is
154
* freed. The length is updated. Parameter pbMustBeFreed
155
* is set to 1 if a new buffer is allocated. Otherwise,
156
* it is left untouched.
158
* We just discovered a security issue. MySQL is so
159
* "smart" to not only support the standard SQL mechanism
160
* for escaping quotes, but to also provide its own (using
161
* c-type syntax with backslashes). As such, it is actually
162
* possible to do sql injection via rsyslogd. The cure is now
163
* to escape backslashes, too. As we have found on the web, some
164
* other databases seem to be similar "smart" (why do we have standards
165
* at all if they are violated without any need???). Even better, MySQL's
166
* smartness depends on config settings. So we add a new option to this
167
* function that allows the caller to select if they want to standard or
168
* "smart" encoding ;)
169
* new parameter escapeMode is 0 - standard sql, 1 - "smart" engines
170
* 2005-09-22 rgerhards
172
void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode)
181
assert(pLen != NULL);
182
assert(pbMustBeFreed != NULL);
184
/* first check if we need to do anything at all... */
186
for(p = *pp ; *p && *p != '\'' ; ++p)
189
for(p = *pp ; *p && *p != '\'' && *p != '\\' ; ++p)
191
/* when we get out of the loop, we are either at the
192
* string terminator or the first \'. */
194
return; /* nothing to do in this case! */
198
if((pStrB = rsCStrConstruct()) == NULL) {
199
/* oops - no mem ... Do emergency... */
200
doSQLEmergencyEscape(p, escapeMode);
206
if(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\') != RS_RET_OK) {
207
doSQLEmergencyEscape(*pp, escapeMode);
208
rsCStrDestruct(pStrB);
211
iLen++; /* reflect the extra character */
212
} else if((escapeMode == 1) && (*p == '\\')) {
213
if(rsCStrAppendChar(pStrB, '\\') != RS_RET_OK) {
214
doSQLEmergencyEscape(*pp, escapeMode);
215
rsCStrDestruct(pStrB);
218
iLen++; /* reflect the extra character */
220
if(rsCStrAppendChar(pStrB, *p) != RS_RET_OK) {
221
doSQLEmergencyEscape(*pp, escapeMode);
222
rsCStrDestruct(pStrB);
228
if(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0) != RS_RET_OK) {
229
doSQLEmergencyEscape(*pp, escapeMode);
234
free(*pp); /* discard previous value */
242
/* Constructs a template entry object. Returns pointer to it
243
* or NULL (if it fails). Pointer to associated template list entry
246
struct templateEntry* tpeConstruct(struct template *pTpl)
248
struct templateEntry *pTpe;
250
assert(pTpl != NULL);
252
if((pTpe = calloc(1, sizeof(struct templateEntry))) == NULL)
255
/* basic initialization is done via calloc() - need to
256
* initialize only values != 0. */
258
if(pTpl->pEntryLast == NULL){
259
/* we are the first element! */
260
pTpl->pEntryRoot = pTpl->pEntryLast = pTpe;
262
pTpl->pEntryLast->pNext = pTpe;
263
pTpl->pEntryLast = pTpe;
265
pTpl->tpenElements++;
271
/* Constructs a template list object. Returns pointer to it
272
* or NULL (if it fails).
274
struct template* tplConstruct(void)
276
struct template *pTpl;
277
if((pTpl = calloc(1, sizeof(struct template))) == NULL)
280
/* basic initialisation is done via calloc() - need to
281
* initialize only values != 0. */
283
if(tplLast == NULL) {
284
/* we are the first element! */
285
tplRoot = tplLast = pTpl;
287
tplLast->pNext = pTpl;
295
/* helper to tplAddLine. Parses a constant and generates
296
* the necessary structure.
297
* returns: 0 - ok, 1 - failure
299
static int do_Constant(unsigned char **pp, struct template *pTpl)
301
register unsigned char *p;
303
struct templateEntry *pTpe;
308
assert(pTpl != NULL);
312
if((pStrB = rsCStrConstruct()) == NULL)
314
rsCStrSetAllocIncrement(pStrB, 32);
315
/* process the message and expand escapes
316
* (additional escapes can be added here if needed)
318
while(*p && *p != '%' && *p != '\"') {
322
/* the best we can do - it's invalid anyhow... */
323
rsCStrAppendChar(pStrB, *p);
326
rsCStrAppendChar(pStrB, '\n');
330
rsCStrAppendChar(pStrB, '\r');
334
rsCStrAppendChar(pStrB, '\\');
338
rsCStrAppendChar(pStrB, '%');
341
case '0': /* numerical escape sequence */
352
while(*p && isdigit((int)*p)) {
353
i = i * 10 + *p++ - '0';
355
rsCStrAppendChar(pStrB, i);
358
rsCStrAppendChar(pStrB, *p++);
363
rsCStrAppendChar(pStrB, *p++);
366
if((pTpe = tpeConstruct(pTpl)) == NULL) {
367
/* OK, we are out of luck. Let's invalidate the
368
* entry and that's it.
369
* TODO: add panic message once we have a mechanism for this
371
pTpe->eEntryType = UNDEFINED;
374
pTpe->eEntryType = CONSTANT;
376
/* We obtain the length from the counted string object
377
* (before we delete it). Later we might take additional
378
* benefit from the counted string object.
379
* 2005-09-09 rgerhards
381
pTpe->data.constant.iLenConstant = rsCStrLen(pStrB);
382
if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.constant.pConstant, 0) != RS_RET_OK)
391
/* Helper to do_Parameter(). This parses the formatting options
392
* specified in a template variable. It returns the passed-in pointer
393
* updated to the next processed character.
395
static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
397
register unsigned char *p;
398
unsigned char Buf[64];
403
assert(pTpe != NULL);
407
while(*p && *p != '%') {
408
/* outer loop - until end of options */
410
while((i < sizeof(Buf) / sizeof(char)) &&
411
*p && *p != '%' && *p != ',') {
412
/* inner loop - until end of ONE option */
413
Buf[i++] = tolower((int)*p);
416
Buf[i] = '\0'; /* terminate */
417
/* check if we need to skip oversize option */
418
while(*p && *p != '%' && *p != ',')
422
/* OK, we got the option, so now lets look what
425
if(!strcmp((char*)Buf, "date-mysql")) {
426
pTpe->data.field.eDateFormat = tplFmtMySQLDate;
427
} else if(!strcmp((char*)Buf, "date-rfc3164")) {
428
pTpe->data.field.eDateFormat = tplFmtRFC3164Date;
429
} else if(!strcmp((char*)Buf, "date-rfc3339")) {
430
pTpe->data.field.eDateFormat = tplFmtRFC3339Date;
431
} else if(!strcmp((char*)Buf, "lowercase")) {
432
pTpe->data.field.eCaseConv = tplCaseConvLower;
433
} else if(!strcmp((char*)Buf, "uppercase")) {
434
pTpe->data.field.eCaseConv = tplCaseConvUpper;
435
} else if(!strcmp((char*)Buf, "escape-cc")) {
436
pTpe->data.field.options.bEscapeCC = 1;
437
} else if(!strcmp((char*)Buf, "drop-cc")) {
438
pTpe->data.field.options.bDropCC = 1;
439
} else if(!strcmp((char*)Buf, "space-cc")) {
440
pTpe->data.field.options.bSpaceCC = 1;
441
} else if(!strcmp((char*)Buf, "drop-last-lf")) {
442
pTpe->data.field.options.bDropLastLF = 1;
443
} else if(!strcmp((char*)Buf, "secpath-drop")) {
444
pTpe->data.field.options.bSecPathDrop = 1;
445
} else if(!strcmp((char*)Buf, "secpath-replace")) {
446
pTpe->data.field.options.bSecPathReplace = 1;
448
dbgprintf("Invalid field option '%s' specified - ignored.\n", Buf);
456
/* helper to tplAddLine. Parses a parameter and generates
457
* the necessary structure.
458
* returns: 0 - ok, 1 - failure
460
static int do_Parameter(unsigned char **pp, struct template *pTpl)
464
struct templateEntry *pTpe;
465
int iNum; /* to compute numbers */
467
#ifdef FEATURE_REGEXP
468
/* APR: variables for regex */
470
unsigned char *regex_char;
471
unsigned char *regex_end;
476
assert(pTpl != NULL);
478
p = (unsigned char*) *pp;
480
if((pStrB = rsCStrConstruct()) == NULL)
483
if((pTpe = tpeConstruct(pTpl)) == NULL) {
484
/* TODO: add handler */
485
dbgprintf("Could not allocate memory for template parameter!\n");
488
pTpe->eEntryType = FIELD;
490
while(*p && *p != '%' && *p != ':') {
491
rsCStrAppendChar(pStrB, *p++);
496
if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.field.pPropRepl, 0) != RS_RET_OK)
499
/* Check frompos, if it has an R, then topos should be a regex */
502
#ifdef FEATURE_REGEXP
504
/* APR: R found! regex alarm ! :) */
508
/* There is something more than an R , this is invalid ! */
509
/* Complain on extra characters */
511
("error: invalid character in frompos after \"R\", property: '%%%s'",
514
pTpe->data.field.has_regex = 1;
517
/* now we fall through the "regular" FromPos code */
518
#endif /* #ifdef FEATURE_REGEXP */
520
/* we have a field counter, so indicate it in the template */
523
/* no delimiter specified, so use the default (HT) */
524
pTpe->data.field.has_fields = 1;
525
pTpe->data.field.field_delim = 9;
526
} else if (*p == ',') {
528
/* configured delimiter follows, so we need to obtain
529
* it. Important: the following number must be the
530
* **DECIMAL** ASCII value of the delimiter character.
532
pTpe->data.field.has_fields = 1;
533
if(!isdigit((int)*p)) {
534
/* complain and use default */
536
("error: invalid character in frompos after \"F,\", property: '%%%s' - using 9 (HT) as field delimiter",
538
pTpe->data.field.field_delim = 9;
541
while(isdigit((int)*p))
542
iNum = iNum * 10 + *p++ - '0';
543
if(iNum < 0 || iNum > 255) {
545
("error: non-USASCII delimiter character value in template - using 9 (HT) as substitute", iNum);
546
pTpe->data.field.field_delim = 9;
548
pTpe->data.field.field_delim = iNum;
552
/* invalid character after F, so we need to reject
556
("error: invalid character in frompos after \"F\", property: '%%%s'",
560
/* we now have a simple offset in frompos (the previously "normal" case) */
562
while(isdigit((int)*p))
563
iNum = iNum * 10 + *p++ - '0';
564
pTpe->data.field.iFromPos = iNum;
565
/* skip to next known good */
566
while(*p && *p != '%' && *p != ':') {
567
/* TODO: complain on extra characters */
568
dbgprintf("error: extra character in frompos: '%s'\n", p);
572
#ifdef FEATURE_REGEXP
574
#endif /* #ifdef FEATURE_REGEXP */
576
/* check topos (holds an regex if FromPos is "R"*/
580
#ifdef FEATURE_REGEXP
581
if (pTpe->data.field.has_regex) {
583
dbgprintf("debug: has regex \n");
585
/* APR 2005-09 I need the string that represent the regex */
586
/* The regex end is: "--end" */
587
/* TODO : this is hardcoded and cant be escaped, please change */
588
regex_end = (unsigned char*) strstr((char*)p, "--end");
589
if (regex_end == NULL) {
590
dbgprintf("error: can not find regex end in: '%s'\n", p);
591
pTpe->data.field.has_regex = 0;
593
/* We get here ONLY if the regex end was found */
594
longitud = regex_end - p;
595
/* Malloc for the regex string */
596
regex_char = (unsigned char *) malloc(longitud + 1);
597
if (regex_char == NULL) {
599
("Could not allocate memory for template parameter!\n");
600
pTpe->data.field.has_regex = 0;
602
/* TODO: RGer: check if we can recover better... (probably not) */
605
/* Get the regex string for compiling later */
606
memcpy(regex_char, p, longitud);
607
regex_char[longitud] = '\0';
609
dbgprintf("debug: regex detected: '%s'\n", regex_char);
611
/* Now i compile the regex */
612
/* Remember that the re is an attribute of the Template entry */
613
if(regcomp(&(pTpe->data.field.re), (char*) regex_char, 0) != 0) {
614
dbgprintf("error: can not compile regex: '%s'\n", regex_char);
615
pTpe->data.field.has_regex = 2;
618
/* Finally we move the pointer to the end of the regex
619
* so it aint parsed twice or something weird */
620
p = regex_end + 5/*strlen("--end")*/;
623
} else if(*p == '$') {
624
/* shortcut for "end of message */
626
/* in this case, we do a quick, somewhat dirty but totally
627
* legitimate trick: we simply use a topos that is higher than
628
* potentially ever can happen. The code below checks that no copy
629
* will occur after the end of string, so this is perfectly legal.
630
* rgerhards, 2006-10-17
632
pTpe->data.field.iToPos = 9999999;
634
/* fallthrough to "regular" ToPos code */
635
#endif /* #ifdef FEATURE_REGEXP */
638
while(isdigit((int)*p))
639
iNum = iNum * 10 + *p++ - '0';
640
pTpe->data.field.iToPos = iNum;
641
/* skip to next known good */
642
while(*p && *p != '%' && *p != ':') {
643
/* TODO: complain on extra characters */
644
dbgprintf("error: extra character in frompos: '%s'\n", p);
647
#ifdef FEATURE_REGEXP
649
#endif /* #ifdef FEATURE_REGEXP */
652
/* TODO: add more sanity checks. For now, we do the bare minimum */
653
if((pTpe->data.field.has_fields == 0) && (pTpe->data.field.iToPos < pTpe->data.field.iFromPos)) {
654
iNum = pTpe->data.field.iToPos;
655
pTpe->data.field.iToPos = pTpe->data.field.iFromPos;
656
pTpe->data.field.iFromPos = iNum;
665
if(*p) ++p; /* eat '%' */
672
/* Add a new template line
673
* returns pointer to new object if it succeeds, NULL otherwise.
675
struct template *tplAddLine(char* pName, unsigned char** ppRestOfConfLine)
677
struct template *pTpl;
680
char optBuf[128]; /* buffer for options - should be more than enough... */
683
assert(pName != NULL);
684
assert(ppRestOfConfLine != NULL);
686
if((pTpl = tplConstruct()) == NULL)
689
pTpl->iLenName = strlen(pName);
690
pTpl->pszName = (char*) malloc(sizeof(char) * (pTpl->iLenName + 1));
691
if(pTpl->pszName == NULL) {
692
dbgprintf("tplAddLine could not alloc memory for template name!");
695
/* I know - we create a memory leak here - but I deem
696
* it acceptable as it is a) a very small leak b) very
697
* unlikely to happen. rgerhards 2004-11-17
700
memcpy(pTpl->pszName, pName, pTpl->iLenName + 1);
702
/* now actually parse the line */
703
p = *ppRestOfConfLine;
706
while(isspace((int)*p))/* skip whitespace */
710
dbgprintf("Template '%s' invalid, does not start with '\"'!\n", pTpl->pszName);
711
/* we simply make the template defunct in this case by setting
712
* its name to a zero-string. We do not free it, as this would
713
* require additional code and causes only a very small memory
714
* consumption. Memory is freed, however, in normal operation
715
* and most importantly by HUPing syslogd.
717
*pTpl->pszName = '\0';
722
/* we finally go to the actual template string - so let's have some fun... */
729
case '%': /* parameter */
731
do_Parameter(&p, pTpl);
733
default: /* constant */
734
do_Constant(&p, pTpl);
737
if(*p == '"') {/* end of template string? */
743
/* we now have the template - let's look at the options (if any)
744
* we process options until we reach the end of the string or
745
* an error occurs - whichever is first.
748
while(isspace((int)*p))/* skip whitespace */
755
while(isspace((int)*p))/* skip whitespace */
758
/* read option word */
760
while(i < sizeof(optBuf) / sizeof(char) - 1
761
&& *p && *p != '=' && *p !=',' && *p != '\n') {
762
optBuf[i++] = tolower((int)*p);
770
/* as of now, the no form is nonsense... but I do include
771
* it anyhow... ;) rgerhards 2004-11-22
773
if(!strcmp(optBuf, "stdsql")) {
774
pTpl->optFormatForSQL = 2;
775
} else if(!strcmp(optBuf, "sql")) {
776
pTpl->optFormatForSQL = 1;
777
} else if(!strcmp(optBuf, "nosql")) {
778
pTpl->optFormatForSQL = 0;
780
dbgprintf("Invalid option '%s' ignored.\n", optBuf);
784
*ppRestOfConfLine = p;
789
/* Find a template object based on name. Search
790
* currently is case-senstive (should we change?).
791
* returns pointer to template object if found and
793
* rgerhards 2004-11-17
795
struct template *tplFind(char *pName, int iLenName)
797
struct template *pTpl;
799
assert(pName != NULL);
802
while(pTpl != NULL &&
803
!(pTpl->iLenName == iLenName &&
804
!strcmp(pTpl->pszName, pName)
812
/* Destroy the template structure. This is for de-initialization
813
* at program end. Everything is deleted.
814
* rgerhards 2005-02-22
815
* I have commented out dbgprintfs, because they are not needed for
816
* "normal" debugging. Uncomment them, if they are needed.
817
* rgerhards, 2007-07-05
819
void tplDeleteAll(void)
821
struct template *pTpl, *pTplDel;
822
struct templateEntry *pTpe, *pTpeDel;
825
while(pTpl != NULL) {
826
/* dbgprintf("Delete Template: Name='%s'\n ", pTpl->pszName == NULL? "NULL" : pTpl->pszName);*/
827
pTpe = pTpl->pEntryRoot;
828
while(pTpe != NULL) {
831
/*dbgprintf("\tDelete Entry(%x): type %d, ", (unsigned) pTpeDel, pTpeDel->eEntryType);*/
832
switch(pTpeDel->eEntryType) {
834
/*dbgprintf("(UNDEFINED)");*/
837
/*dbgprintf("(CONSTANT), value: '%s'",
838
pTpeDel->data.constant.pConstant);*/
839
free(pTpeDel->data.constant.pConstant);
842
/*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
843
free(pTpeDel->data.field.pPropRepl);
851
if(pTplDel->pszName != NULL)
852
free(pTplDel->pszName);
857
/* Destroy all templates obtained from conf file
858
* preserving hadcoded ones. This is called from init().
860
void tplDeleteNew(void)
862
struct template *pTpl, *pTplDel;
863
struct templateEntry *pTpe, *pTpeDel;
865
if(tplRoot == NULL || tplLastStatic == NULL)
868
pTpl = tplLastStatic->pNext;
869
tplLastStatic->pNext = NULL;
870
tplLast = tplLastStatic;
871
while(pTpl != NULL) {
872
/* dbgprintf("Delete Template: Name='%s'\n ", pTpl->pszName == NULL? "NULL" : pTpl->pszName);*/
873
pTpe = pTpl->pEntryRoot;
874
while(pTpe != NULL) {
877
/*dbgprintf("\tDelete Entry(%x): type %d, ", (unsigned) pTpeDel, pTpeDel->eEntryType);*/
878
switch(pTpeDel->eEntryType) {
880
/*dbgprintf("(UNDEFINED)");*/
883
/*dbgprintf("(CONSTANT), value: '%s'",
884
pTpeDel->data.constant.pConstant);*/
885
free(pTpeDel->data.constant.pConstant);
888
/*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
889
free(pTpeDel->data.field.pPropRepl);
897
if(pTplDel->pszName != NULL)
898
free(pTplDel->pszName);
903
/* Store the pointer to the last hardcoded teplate */
904
void tplLastStaticInit(struct template *tpl)
909
/* Print the template structure. This is more or less a
910
* debug or test aid, but anyhow I think it's worth it...
912
void tplPrintList(void)
914
struct template *pTpl;
915
struct templateEntry *pTpe;
918
while(pTpl != NULL) {
919
dbgprintf("Template: Name='%s' ", pTpl->pszName == NULL? "NULL" : pTpl->pszName);
920
if(pTpl->optFormatForSQL == 1)
921
dbgprintf("[SQL-Format (MySQL)] ");
922
else if(pTpl->optFormatForSQL == 2)
923
dbgprintf("[SQL-Format (standard SQL)] ");
925
pTpe = pTpl->pEntryRoot;
926
while(pTpe != NULL) {
927
dbgprintf("\tEntry(%x): type %d, ", (unsigned) pTpe, pTpe->eEntryType);
928
switch(pTpe->eEntryType) {
930
dbgprintf("(UNDEFINED)");
933
dbgprintf("(CONSTANT), value: '%s'",
934
pTpe->data.constant.pConstant);
937
dbgprintf("(FIELD), value: '%s' ", pTpe->data.field.pPropRepl);
938
switch(pTpe->data.field.eDateFormat) {
941
case tplFmtMySQLDate:
942
dbgprintf("[Format as MySQL-Date] ");
944
case tplFmtRFC3164Date:
945
dbgprintf("[Format as RFC3164-Date] ");
947
case tplFmtRFC3339Date:
948
dbgprintf("[Format as RFC3339-Date] ");
951
dbgprintf("[INVALID eDateFormat %d] ", pTpe->data.field.eDateFormat);
953
switch(pTpe->data.field.eCaseConv) {
956
case tplCaseConvLower:
957
dbgprintf("[Converted to Lower Case] ");
959
case tplCaseConvUpper:
960
dbgprintf("[Converted to Upper Case] ");
963
if(pTpe->data.field.options.bEscapeCC) {
964
dbgprintf("[escape control-characters] ");
966
if(pTpe->data.field.options.bDropCC) {
967
dbgprintf("[drop control-characters] ");
969
if(pTpe->data.field.options.bSpaceCC) {
970
dbgprintf("[replace control-characters with space] ");
972
if(pTpe->data.field.options.bDropLastLF) {
973
dbgprintf("[drop last LF in msg] ");
975
if(pTpe->data.field.has_fields == 1) {
976
dbgprintf("[substring, field #%d only (delemiter %d)] ",
977
pTpe->data.field.iToPos, pTpe->data.field.field_delim);
978
} else if(pTpe->data.field.iFromPos != 0 ||
979
pTpe->data.field.iToPos != 0) {
980
dbgprintf("[substring, from character %d to %d] ",
981
pTpe->data.field.iFromPos,
982
pTpe->data.field.iToPos);
989
pTpl = pTpl->pNext; /* done, go next */
993
int tplGetEntryCount(struct template *pTpl)
995
assert(pTpl != NULL);
996
return(pTpl->tpenElements);