~ubuntu-branches/ubuntu/lucid/rsyslog/lucid

« back to all changes in this revision

Viewing changes to template.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Biebl
  • Date: 2007-10-19 17:21:49 UTC
  • Revision ID: james.westby@ubuntu.com-20071019172149-ie6ej2xve33mxiu7
Tags: upstream-1.19.10
ImportĀ upstreamĀ versionĀ 1.19.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
5
 */
 
6
#include "config.h"
 
7
 
 
8
#ifdef __FreeBSD__
 
9
#define BSD
 
10
#endif
 
11
 
 
12
#include "rsyslog.h"
 
13
#include <stdio.h>
 
14
#include <stdlib.h>
 
15
#include <string.h>
 
16
#include <ctype.h>
 
17
#include <assert.h>
 
18
#include "stringbuf.h"
 
19
#include "syslogd-types.h"
 
20
#include "template.h"
 
21
#include "msg.h"
 
22
#include "syslogd.h"
 
23
 
 
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 */
 
27
 
 
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.
 
33
 *
 
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
 
38
 * is returned.
 
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
 
44
 *
 
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).
 
48
 */
 
49
rsRetVal tplToString(struct template *pTpl, msg_t *pMsg, uchar** ppSz)
 
50
{
 
51
        DEFiRet;
 
52
        struct templateEntry *pTpe;
 
53
        rsCStrObj *pCStr;
 
54
        unsigned short bMustBeFreed;
 
55
        uchar *pVal;
 
56
        size_t iLenVal;
 
57
 
 
58
        assert(pTpl != NULL);
 
59
        assert(pMsg != NULL);
 
60
        assert(ppSz != NULL);
 
61
 
 
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.
 
66
         */
 
67
        if((pCStr = rsCStrConstruct()) == NULL) {
 
68
                dbgprintf("memory shortage, tplToString failed\n");
 
69
                ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
 
70
        }
 
71
 
 
72
        pTpe = pTpl->pEntryRoot;
 
73
        while(pTpe != NULL) {
 
74
                if(pTpe->eEntryType == CONSTANT) {
 
75
                        CHKiRet_Hdlr(rsCStrAppendStrWithLen(pCStr, 
 
76
                                                          (uchar *) pTpe->data.constant.pConstant,
 
77
                                                          pTpe->data.constant.iLenConstant)
 
78
                                                         ) {
 
79
                                dbgprintf("error %d during tplToString()\n", iRet);
 
80
                                /* it does not make sense to continue now */
 
81
                                rsCStrDestruct(pCStr);
 
82
                                FINALIZE;
 
83
                        }
 
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
 
91
                         * existing thing).
 
92
                         */
 
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);
 
102
                                if(bMustBeFreed)
 
103
                                        free(pVal);
 
104
                                FINALIZE;
 
105
                        }
 
106
                        if(bMustBeFreed)
 
107
                                free(pVal);
 
108
                }
 
109
                pTpe = pTpe->pNext;
 
110
        }
 
111
 
 
112
        /* we are done with the template, now let's convert the result into a
 
113
         * "real" (usable) string and discard the helper structures.
 
114
         */
 
115
        CHKiRet(rsCStrFinish(pCStr));
 
116
        CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pVal, 0));
 
117
        
 
118
finalize_it:
 
119
        *ppSz = (iRet == RS_RET_OK) ? pVal : NULL;
 
120
 
 
121
        return iRet;
 
122
}
 
123
 
 
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
 
138
 */
 
139
static void doSQLEmergencyEscape(register uchar *p, int escapeMode)
 
140
{
 
141
        while(*p) {
 
142
                if(*p == '\'')
 
143
                        *p = '"';
 
144
                else if((escapeMode == 1) && (*p == '\\'))
 
145
                        *p = '/';
 
146
                ++p;
 
147
        }
 
148
}
 
149
 
 
150
 
 
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.
 
157
 * --
 
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
 
171
 */
 
172
void doSQLEscape(uchar **pp, size_t *pLen, unsigned short *pbMustBeFreed, int escapeMode)
 
173
{
 
174
        uchar *p;
 
175
        int iLen;
 
176
        rsCStrObj *pStrB;
 
177
        uchar *pszGenerated;
 
178
 
 
179
        assert(pp != NULL);
 
180
        assert(*pp != NULL);
 
181
        assert(pLen != NULL);
 
182
        assert(pbMustBeFreed != NULL);
 
183
 
 
184
        /* first check if we need to do anything at all... */
 
185
        if(escapeMode == 0)
 
186
                for(p = *pp ; *p && *p != '\'' ; ++p)
 
187
                        ;
 
188
        else
 
189
                for(p = *pp ; *p && *p != '\'' && *p != '\\' ; ++p)
 
190
                        ;
 
191
        /* when we get out of the loop, we are either at the
 
192
         * string terminator or the first \'. */
 
193
        if(*p == '\0')
 
194
                return; /* nothing to do in this case! */
 
195
 
 
196
        p = *pp;
 
197
        iLen = *pLen;
 
198
        if((pStrB = rsCStrConstruct()) == NULL) {
 
199
                /* oops - no mem ... Do emergency... */
 
200
                doSQLEmergencyEscape(p, escapeMode);
 
201
                return;
 
202
        }
 
203
        
 
204
        while(*p) {
 
205
                if(*p == '\'') {
 
206
                        if(rsCStrAppendChar(pStrB, (escapeMode == 0) ? '\'' : '\\') != RS_RET_OK) {
 
207
                                doSQLEmergencyEscape(*pp, escapeMode);
 
208
                                rsCStrDestruct(pStrB);
 
209
                                return;
 
210
                                }
 
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);
 
216
                                return;
 
217
                                }
 
218
                        iLen++; /* reflect the extra character */
 
219
                }
 
220
                if(rsCStrAppendChar(pStrB, *p) != RS_RET_OK) {
 
221
                        doSQLEmergencyEscape(*pp, escapeMode);
 
222
                        rsCStrDestruct(pStrB);
 
223
                        return;
 
224
                }
 
225
                ++p;
 
226
        }
 
227
        rsCStrFinish(pStrB);
 
228
        if(rsCStrConvSzStrAndDestruct(pStrB, &pszGenerated, 0) != RS_RET_OK) {
 
229
                doSQLEmergencyEscape(*pp, escapeMode);
 
230
                return;
 
231
        }
 
232
 
 
233
        if(*pbMustBeFreed)
 
234
                free(*pp); /* discard previous value */
 
235
 
 
236
        *pp = pszGenerated;
 
237
        *pLen = iLen;
 
238
        *pbMustBeFreed = 1;
 
239
}
 
240
 
 
241
 
 
242
/* Constructs a template entry object. Returns pointer to it
 
243
 * or NULL (if it fails). Pointer to associated template list entry 
 
244
 * must be provided.
 
245
 */
 
246
struct templateEntry* tpeConstruct(struct template *pTpl)
 
247
{
 
248
        struct templateEntry *pTpe;
 
249
 
 
250
        assert(pTpl != NULL);
 
251
 
 
252
        if((pTpe = calloc(1, sizeof(struct templateEntry))) == NULL)
 
253
                return NULL;
 
254
        
 
255
        /* basic initialization is done via calloc() - need to
 
256
         * initialize only values != 0. */
 
257
 
 
258
        if(pTpl->pEntryLast == NULL){
 
259
                /* we are the first element! */
 
260
                pTpl->pEntryRoot = pTpl->pEntryLast  = pTpe;
 
261
        } else {
 
262
                pTpl->pEntryLast->pNext = pTpe;
 
263
                pTpl->pEntryLast  = pTpe;
 
264
        }
 
265
        pTpl->tpenElements++;
 
266
 
 
267
        return(pTpe);
 
268
}
 
269
 
 
270
 
 
271
/* Constructs a template list object. Returns pointer to it
 
272
 * or NULL (if it fails).
 
273
 */
 
274
struct template* tplConstruct(void)
 
275
{
 
276
        struct template *pTpl;
 
277
        if((pTpl = calloc(1, sizeof(struct template))) == NULL)
 
278
                return NULL;
 
279
        
 
280
        /* basic initialisation is done via calloc() - need to
 
281
         * initialize only values != 0. */
 
282
 
 
283
        if(tplLast == NULL)     {
 
284
                /* we are the first element! */
 
285
                tplRoot = tplLast = pTpl;
 
286
        } else {
 
287
                tplLast->pNext = pTpl;
 
288
                tplLast = pTpl;
 
289
        }
 
290
 
 
291
        return(pTpl);
 
292
}
 
293
 
 
294
 
 
295
/* helper to tplAddLine. Parses a constant and generates
 
296
 * the necessary structure.
 
297
 * returns: 0 - ok, 1 - failure
 
298
 */
 
299
static int do_Constant(unsigned char **pp, struct template *pTpl)
 
300
{
 
301
        register unsigned char *p;
 
302
        rsCStrObj *pStrB;
 
303
        struct templateEntry *pTpe;
 
304
        int i;
 
305
 
 
306
        assert(pp != NULL);
 
307
        assert(*pp != NULL);
 
308
        assert(pTpl != NULL);
 
309
 
 
310
        p = *pp;
 
311
 
 
312
        if((pStrB = rsCStrConstruct()) == NULL)
 
313
                 return 1;
 
314
        rsCStrSetAllocIncrement(pStrB, 32);
 
315
        /* process the message and expand escapes
 
316
         * (additional escapes can be added here if needed)
 
317
         */
 
318
        while(*p && *p != '%' && *p != '\"') {
 
319
                if(*p == '\\') {
 
320
                        switch(*++p) {
 
321
                                case '\0':      
 
322
                                        /* the best we can do - it's invalid anyhow... */
 
323
                                        rsCStrAppendChar(pStrB, *p);
 
324
                                        break;
 
325
                                case 'n':
 
326
                                        rsCStrAppendChar(pStrB, '\n');
 
327
                                        ++p;
 
328
                                        break;
 
329
                                case 'r':
 
330
                                        rsCStrAppendChar(pStrB, '\r');
 
331
                                        ++p;
 
332
                                        break;
 
333
                                case '\\':
 
334
                                        rsCStrAppendChar(pStrB, '\\');
 
335
                                        ++p;
 
336
                                        break;
 
337
                                case '%':
 
338
                                        rsCStrAppendChar(pStrB, '%');
 
339
                                        ++p;
 
340
                                        break;
 
341
                                case '0': /* numerical escape sequence */
 
342
                                case '1':
 
343
                                case '2':
 
344
                                case '3':
 
345
                                case '4':
 
346
                                case '5':
 
347
                                case '6':
 
348
                                case '7':
 
349
                                case '8':
 
350
                                case '9':
 
351
                                        i = 0;
 
352
                                        while(*p && isdigit((int)*p)) {
 
353
                                                i = i * 10 + *p++ - '0';
 
354
                                        }
 
355
                                        rsCStrAppendChar(pStrB, i);
 
356
                                        break;
 
357
                                default:
 
358
                                        rsCStrAppendChar(pStrB, *p++);
 
359
                                        break;
 
360
                        }
 
361
                }
 
362
                else
 
363
                        rsCStrAppendChar(pStrB, *p++);
 
364
        }
 
365
 
 
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
 
370
                 */
 
371
                pTpe->eEntryType = UNDEFINED;
 
372
                return 1;
 
373
        }
 
374
        pTpe->eEntryType = CONSTANT;
 
375
        rsCStrFinish(pStrB);
 
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
 
380
         */
 
381
        pTpe->data.constant.iLenConstant = rsCStrLen(pStrB);
 
382
        if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.constant.pConstant, 0) != RS_RET_OK)
 
383
                return 1;
 
384
 
 
385
        *pp = p;
 
386
 
 
387
        return 0;
 
388
}
 
389
 
 
390
 
 
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.
 
394
 */
 
395
static void doOptions(unsigned char **pp, struct templateEntry *pTpe)
 
396
{
 
397
        register unsigned char *p;
 
398
        unsigned char Buf[64];
 
399
        size_t i;
 
400
 
 
401
        assert(pp != NULL);
 
402
        assert(*pp != NULL);
 
403
        assert(pTpe != NULL);
 
404
 
 
405
        p = *pp;
 
406
 
 
407
        while(*p && *p != '%') {
 
408
                /* outer loop - until end of options */
 
409
                i = 0;
 
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);
 
414
                        ++p;
 
415
                }
 
416
                Buf[i] = '\0'; /* terminate */
 
417
                /* check if we need to skip oversize option */
 
418
                while(*p && *p != '%' && *p != ',')
 
419
                        ++p;    /* just skip */
 
420
                if(*p == ',')
 
421
                        ++p; /* eat ',' */
 
422
                /* OK, we got the option, so now lets look what
 
423
                 * it tells us...
 
424
                 */
 
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;
 
447
                 } else {
 
448
                        dbgprintf("Invalid field option '%s' specified - ignored.\n", Buf);
 
449
                 }
 
450
        }
 
451
 
 
452
        *pp = p;
 
453
}
 
454
 
 
455
 
 
456
/* helper to tplAddLine. Parses a parameter and generates
 
457
 * the necessary structure.
 
458
 * returns: 0 - ok, 1 - failure
 
459
 */
 
460
static int do_Parameter(unsigned char **pp, struct template *pTpl)
 
461
{
 
462
        unsigned char *p;
 
463
        rsCStrObj *pStrB;
 
464
        struct templateEntry *pTpe;
 
465
        int iNum;       /* to compute numbers */
 
466
 
 
467
#ifdef FEATURE_REGEXP
 
468
        /* APR: variables for regex */
 
469
        int longitud;
 
470
        unsigned char *regex_char;
 
471
        unsigned char *regex_end;
 
472
#endif
 
473
 
 
474
        assert(pp != NULL);
 
475
        assert(*pp != NULL);
 
476
        assert(pTpl != NULL);
 
477
 
 
478
        p = (unsigned char*) *pp;
 
479
 
 
480
        if((pStrB = rsCStrConstruct()) == NULL)
 
481
                 return 1;
 
482
 
 
483
        if((pTpe = tpeConstruct(pTpl)) == NULL) {
 
484
                /* TODO: add handler */
 
485
                dbgprintf("Could not allocate memory for template parameter!\n");
 
486
                return 1;
 
487
        }
 
488
        pTpe->eEntryType = FIELD;
 
489
 
 
490
        while(*p && *p != '%' && *p != ':') {
 
491
                rsCStrAppendChar(pStrB, *p++);
 
492
        }
 
493
 
 
494
        /* got the name*/
 
495
        rsCStrFinish(pStrB);
 
496
        if(rsCStrConvSzStrAndDestruct(pStrB, &pTpe->data.field.pPropRepl, 0) != RS_RET_OK)
 
497
                return 1;
 
498
 
 
499
        /* Check frompos, if it has an R, then topos should be a regex */
 
500
        if(*p == ':') {
 
501
                ++p; /* eat ':' */
 
502
#ifdef FEATURE_REGEXP
 
503
                if (*p == 'R') {
 
504
                        /* APR: R found! regex alarm ! :) */
 
505
                        ++p;    /* eat ':' */
 
506
 
 
507
                        if (*p != ':') {
 
508
                                /* There is something more than an R , this is invalid ! */
 
509
                                /* Complain on extra characters */
 
510
                                logerrorSz
 
511
                                  ("error: invalid character in frompos after \"R\", property: '%%%s'",
 
512
                                    (char*) *pp);
 
513
                        } else {
 
514
                                pTpe->data.field.has_regex = 1;
 
515
                        }
 
516
                } else {
 
517
                        /* now we fall through the "regular" FromPos code */
 
518
#endif /* #ifdef FEATURE_REGEXP */
 
519
                        if(*p == 'F') {
 
520
                                /* we have a field counter, so indicate it in the template */
 
521
                                ++p; /* eat 'F' */
 
522
                                if (*p == ':') {
 
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 == ',') {
 
527
                                        ++p; /* eat ',' */
 
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.
 
531
                                         */
 
532
                                        pTpe->data.field.has_fields = 1;
 
533
                                        if(!isdigit((int)*p)) {
 
534
                                                /* complain and use default */
 
535
                                                logerrorSz
 
536
                                                  ("error: invalid character in frompos after \"F,\", property: '%%%s' - using 9 (HT) as field delimiter",
 
537
                                                    (char*) *pp);
 
538
                                                pTpe->data.field.field_delim = 9;
 
539
                                        } else {
 
540
                                                iNum = 0;
 
541
                                                while(isdigit((int)*p))
 
542
                                                        iNum = iNum * 10 + *p++ - '0';
 
543
                                                if(iNum < 0 || iNum > 255) {
 
544
                                                        logerrorInt
 
545
                                                          ("error: non-USASCII delimiter character value in template - using 9 (HT) as substitute", iNum);
 
546
                                                        pTpe->data.field.field_delim = 9;
 
547
                                                  } else {
 
548
                                                        pTpe->data.field.field_delim = iNum;
 
549
                                                        }
 
550
                                        }
 
551
                                } else {
 
552
                                        /* invalid character after F, so we need to reject
 
553
                                         * this.
 
554
                                         */
 
555
                                        logerrorSz
 
556
                                          ("error: invalid character in frompos after \"F\", property: '%%%s'",
 
557
                                            (char*) *pp);
 
558
                                }
 
559
                        } else {
 
560
                                /* we now have a simple offset in frompos (the previously "normal" case) */
 
561
                                iNum = 0;
 
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);
 
569
                                        ++p;
 
570
                                }
 
571
                        }
 
572
#ifdef FEATURE_REGEXP
 
573
                }
 
574
#endif /* #ifdef FEATURE_REGEXP */
 
575
        }
 
576
        /* check topos  (holds an regex if FromPos is "R"*/
 
577
        if(*p == ':') {
 
578
                ++p; /* eat ':' */
 
579
 
 
580
#ifdef FEATURE_REGEXP
 
581
                if (pTpe->data.field.has_regex) {
 
582
 
 
583
                        dbgprintf("debug: has regex \n");
 
584
 
 
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;
 
592
                        } else {
 
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) {
 
598
                                        dbgprintf
 
599
                                            ("Could not allocate memory for template parameter!\n");
 
600
                                        pTpe->data.field.has_regex = 0;
 
601
                                        return 1;
 
602
                                        /* TODO: RGer: check if we can recover better... (probably not) */
 
603
                                }
 
604
 
 
605
                                /* Get the regex string for compiling later */
 
606
                                memcpy(regex_char, p, longitud);
 
607
                                regex_char[longitud] = '\0';
 
608
 
 
609
                                dbgprintf("debug: regex detected: '%s'\n", regex_char);
 
610
 
 
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;
 
616
                                }
 
617
 
 
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")*/;
 
621
                                free(regex_char);
 
622
                        }
 
623
                } else if(*p == '$') {
 
624
                        /* shortcut for "end of message */
 
625
                        p++; /* eat '$' */
 
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
 
631
                         */
 
632
                        pTpe->data.field.iToPos = 9999999;
 
633
                } else {
 
634
                        /* fallthrough to "regular" ToPos code */
 
635
#endif /* #ifdef FEATURE_REGEXP */
 
636
 
 
637
                        iNum = 0;
 
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);
 
645
                                ++p;
 
646
                        }
 
647
#ifdef FEATURE_REGEXP
 
648
                }
 
649
#endif /* #ifdef FEATURE_REGEXP */
 
650
        }
 
651
 
 
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;
 
657
        }
 
658
 
 
659
        /* check options */
 
660
        if(*p == ':') {
 
661
                ++p; /* eat ':' */
 
662
                doOptions(&p, pTpe);
 
663
        }
 
664
 
 
665
        if(*p) ++p; /* eat '%' */
 
666
 
 
667
        *pp = p;
 
668
        return 0;
 
669
}
 
670
 
 
671
 
 
672
/* Add a new template line
 
673
 * returns pointer to new object if it succeeds, NULL otherwise.
 
674
 */
 
675
struct template *tplAddLine(char* pName, unsigned char** ppRestOfConfLine)
 
676
{
 
677
        struct template *pTpl;
 
678
        unsigned char *p;
 
679
        int bDone;
 
680
        char optBuf[128]; /* buffer for options - should be more than enough... */
 
681
        size_t i;
 
682
 
 
683
        assert(pName != NULL);
 
684
        assert(ppRestOfConfLine != NULL);
 
685
 
 
686
        if((pTpl = tplConstruct()) == NULL)
 
687
                return NULL;
 
688
        
 
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!");
 
693
                pTpl->iLenName = 0;
 
694
                return NULL;
 
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
 
698
                 */
 
699
        }
 
700
        memcpy(pTpl->pszName, pName, pTpl->iLenName + 1);
 
701
 
 
702
        /* now actually parse the line */
 
703
        p = *ppRestOfConfLine;
 
704
        assert(p != NULL);
 
705
 
 
706
        while(isspace((int)*p))/* skip whitespace */
 
707
                ++p;
 
708
        
 
709
        if(*p != '"') {
 
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.
 
716
                 */
 
717
                *pTpl->pszName = '\0';
 
718
                return NULL;
 
719
        }
 
720
        ++p;
 
721
 
 
722
        /* we finally go to the actual template string - so let's have some fun... */
 
723
        bDone = *p ? 0 : 1;
 
724
        while(!bDone) {
 
725
                switch(*p) {
 
726
                        case '\0':
 
727
                                bDone = 1;
 
728
                                break;
 
729
                        case '%': /* parameter */
 
730
                                ++p; /* eat '%' */
 
731
                                do_Parameter(&p, pTpl);
 
732
                                break;
 
733
                        default: /* constant */
 
734
                                do_Constant(&p, pTpl);
 
735
                                break;
 
736
                }
 
737
                if(*p == '"') {/* end of template string? */
 
738
                        ++p;    /* eat it! */
 
739
                        bDone = 1;
 
740
                }
 
741
        }
 
742
        
 
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.
 
746
         */
 
747
        while(*p) {
 
748
                while(isspace((int)*p))/* skip whitespace */
 
749
                        ++p;
 
750
                
 
751
                if(*p != ',')
 
752
                        break;
 
753
                ++p; /* eat ',' */
 
754
 
 
755
                while(isspace((int)*p))/* skip whitespace */
 
756
                        ++p;
 
757
                
 
758
                /* read option word */
 
759
                i = 0;
 
760
                while(i < sizeof(optBuf) / sizeof(char) - 1
 
761
                      && *p && *p != '=' && *p !=',' && *p != '\n') {
 
762
                        optBuf[i++] = tolower((int)*p);
 
763
                        ++p;
 
764
                }
 
765
                optBuf[i] = '\0';
 
766
 
 
767
                if(*p == '\n')
 
768
                        ++p;
 
769
 
 
770
                /* as of now, the no form is nonsense... but I do include
 
771
                 * it anyhow... ;) rgerhards 2004-11-22
 
772
                 */
 
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;
 
779
                } else {
 
780
                        dbgprintf("Invalid option '%s' ignored.\n", optBuf);
 
781
                }
 
782
        }
 
783
 
 
784
        *ppRestOfConfLine = p;
 
785
        return(pTpl);
 
786
}
 
787
 
 
788
 
 
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
 
792
 * NULL otherwise.
 
793
 * rgerhards 2004-11-17
 
794
 */
 
795
struct template *tplFind(char *pName, int iLenName)
 
796
{
 
797
        struct template *pTpl;
 
798
 
 
799
        assert(pName != NULL);
 
800
 
 
801
        pTpl = tplRoot;
 
802
        while(pTpl != NULL &&
 
803
              !(pTpl->iLenName == iLenName &&
 
804
                !strcmp(pTpl->pszName, pName)
 
805
                ))
 
806
                {
 
807
                        pTpl = pTpl->pNext;
 
808
                }
 
809
        return(pTpl);
 
810
}
 
811
 
 
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
 
818
 */
 
819
void tplDeleteAll(void)
 
820
{
 
821
        struct template *pTpl, *pTplDel;
 
822
        struct templateEntry *pTpe, *pTpeDel;
 
823
 
 
824
        pTpl = tplRoot;
 
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) {
 
829
                        pTpeDel = pTpe;
 
830
                        pTpe = pTpe->pNext;
 
831
                        /*dbgprintf("\tDelete Entry(%x): type %d, ", (unsigned) pTpeDel, pTpeDel->eEntryType);*/
 
832
                        switch(pTpeDel->eEntryType) {
 
833
                        case UNDEFINED:
 
834
                                /*dbgprintf("(UNDEFINED)");*/
 
835
                                break;
 
836
                        case CONSTANT:
 
837
                                /*dbgprintf("(CONSTANT), value: '%s'",
 
838
                                        pTpeDel->data.constant.pConstant);*/
 
839
                                free(pTpeDel->data.constant.pConstant);
 
840
                                break;
 
841
                        case FIELD:
 
842
                                /*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
 
843
                                free(pTpeDel->data.field.pPropRepl);
 
844
                                break;
 
845
                        }
 
846
                        /*dbgprintf("\n");*/
 
847
                        free(pTpeDel);
 
848
                }
 
849
                pTplDel = pTpl;
 
850
                pTpl = pTpl->pNext;
 
851
                if(pTplDel->pszName != NULL)
 
852
                        free(pTplDel->pszName);
 
853
                free(pTplDel);
 
854
        }
 
855
}
 
856
 
 
857
/* Destroy all templates obtained from conf file
 
858
 * preserving hadcoded ones. This is called from init().
 
859
 */
 
860
void tplDeleteNew(void)
 
861
{
 
862
        struct template *pTpl, *pTplDel;
 
863
        struct templateEntry *pTpe, *pTpeDel;
 
864
 
 
865
        if(tplRoot == NULL || tplLastStatic == NULL)
 
866
                return;
 
867
 
 
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) {
 
875
                        pTpeDel = pTpe;
 
876
                        pTpe = pTpe->pNext;
 
877
                        /*dbgprintf("\tDelete Entry(%x): type %d, ", (unsigned) pTpeDel, pTpeDel->eEntryType);*/
 
878
                        switch(pTpeDel->eEntryType) {
 
879
                        case UNDEFINED:
 
880
                                /*dbgprintf("(UNDEFINED)");*/
 
881
                                break;
 
882
                        case CONSTANT:
 
883
                                /*dbgprintf("(CONSTANT), value: '%s'",
 
884
                                        pTpeDel->data.constant.pConstant);*/
 
885
                                free(pTpeDel->data.constant.pConstant);
 
886
                                break;
 
887
                        case FIELD:
 
888
                                /*dbgprintf("(FIELD), value: '%s'", pTpeDel->data.field.pPropRepl);*/
 
889
                                free(pTpeDel->data.field.pPropRepl);
 
890
                                break;
 
891
                        }
 
892
                        /*dbgprintf("\n");*/
 
893
                        free(pTpeDel);
 
894
                }
 
895
                pTplDel = pTpl;
 
896
                pTpl = pTpl->pNext;
 
897
                if(pTplDel->pszName != NULL)
 
898
                        free(pTplDel->pszName);
 
899
                free(pTplDel);
 
900
        }
 
901
}
 
902
 
 
903
/* Store the pointer to the last hardcoded teplate */
 
904
void tplLastStaticInit(struct template *tpl)
 
905
{
 
906
        tplLastStatic = tpl;
 
907
}
 
908
 
 
909
/* Print the template structure. This is more or less a 
 
910
 * debug or test aid, but anyhow I think it's worth it...
 
911
 */
 
912
void tplPrintList(void)
 
913
{
 
914
        struct template *pTpl;
 
915
        struct templateEntry *pTpe;
 
916
 
 
917
        pTpl = tplRoot;
 
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)] ");
 
924
                dbgprintf("\n");
 
925
                pTpe = pTpl->pEntryRoot;
 
926
                while(pTpe != NULL) {
 
927
                        dbgprintf("\tEntry(%x): type %d, ", (unsigned) pTpe, pTpe->eEntryType);
 
928
                        switch(pTpe->eEntryType) {
 
929
                        case UNDEFINED:
 
930
                                dbgprintf("(UNDEFINED)");
 
931
                                break;
 
932
                        case CONSTANT:
 
933
                                dbgprintf("(CONSTANT), value: '%s'",
 
934
                                        pTpe->data.constant.pConstant);
 
935
                                break;
 
936
                        case FIELD:
 
937
                                dbgprintf("(FIELD), value: '%s' ", pTpe->data.field.pPropRepl);
 
938
                                switch(pTpe->data.field.eDateFormat) {
 
939
                                case tplFmtDefault:
 
940
                                        break;
 
941
                                case tplFmtMySQLDate:
 
942
                                        dbgprintf("[Format as MySQL-Date] ");
 
943
                                        break;
 
944
                                case tplFmtRFC3164Date:
 
945
                                        dbgprintf("[Format as RFC3164-Date] ");
 
946
                                        break;
 
947
                                case tplFmtRFC3339Date:
 
948
                                        dbgprintf("[Format as RFC3339-Date] ");
 
949
                                        break;
 
950
                                default:
 
951
                                        dbgprintf("[INVALID eDateFormat %d] ", pTpe->data.field.eDateFormat);
 
952
                                }
 
953
                                switch(pTpe->data.field.eCaseConv) {
 
954
                                case tplCaseConvNo:
 
955
                                        break;
 
956
                                case tplCaseConvLower:
 
957
                                        dbgprintf("[Converted to Lower Case] ");
 
958
                                        break;
 
959
                                case tplCaseConvUpper:
 
960
                                        dbgprintf("[Converted to Upper Case] ");
 
961
                                        break;
 
962
                                }
 
963
                                if(pTpe->data.field.options.bEscapeCC) {
 
964
                                        dbgprintf("[escape control-characters] ");
 
965
                                }
 
966
                                if(pTpe->data.field.options.bDropCC) {
 
967
                                        dbgprintf("[drop control-characters] ");
 
968
                                }
 
969
                                if(pTpe->data.field.options.bSpaceCC) {
 
970
                                        dbgprintf("[replace control-characters with space] ");
 
971
                                }
 
972
                                if(pTpe->data.field.options.bDropLastLF) {
 
973
                                        dbgprintf("[drop last LF in msg] ");
 
974
                                }
 
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);
 
983
                                }
 
984
                                break;
 
985
                        }
 
986
                        dbgprintf("\n");
 
987
                        pTpe = pTpe->pNext;
 
988
                }
 
989
                pTpl = pTpl->pNext; /* done, go next */
 
990
        }
 
991
}
 
992
 
 
993
int tplGetEntryCount(struct template *pTpl)
 
994
{
 
995
        assert(pTpl != NULL);
 
996
        return(pTpl->tpenElements);
 
997
}
 
998
/*
 
999
 * vi:set ai:
 
1000
 */