1 /*
2  * Copyright (c) 2002-2012 Balabit
3  * Copyright (c) 1998-2012 Balázs Scheidler
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 
25 #include "logmatcher.h"
26 #include "messages.h"
27 #include "cfg.h"
28 #include "str-utils.h"
29 #include "scratch-buffers.h"
30 #include "compat/string.h"
31 #include "compat/pcre.h"
32 
33 static gboolean
_shall_set_values_indirectly(NVHandle value_handle)34 _shall_set_values_indirectly(NVHandle value_handle)
35 {
36   return value_handle != LM_V_NONE &&
37          !log_msg_is_handle_macro(value_handle) &&
38          !log_msg_is_handle_match(value_handle);
39 }
40 
41 static void
log_matcher_store_pattern(LogMatcher * self,const gchar * pattern)42 log_matcher_store_pattern(LogMatcher *self, const gchar *pattern)
43 {
44   g_free(self->pattern);
45   self->pattern = g_strdup(pattern);
46 }
47 
48 static void
log_matcher_free_method(LogMatcher * self)49 log_matcher_free_method(LogMatcher *self)
50 {
51   g_free(self->pattern);
52 }
53 
54 static void
log_matcher_init(LogMatcher * self,const LogMatcherOptions * options)55 log_matcher_init(LogMatcher *self, const LogMatcherOptions *options)
56 {
57   self->ref_cnt = 1;
58   self->flags = options->flags;
59   self->free_fn = log_matcher_free_method;
60 }
61 
62 typedef struct _LogMatcherString
63 {
64   LogMatcher super;
65   gint pattern_len;
66 } LogMatcherString;
67 
68 static gboolean
log_matcher_string_compile(LogMatcher * s,const gchar * pattern,GError ** error)69 log_matcher_string_compile(LogMatcher *s, const gchar *pattern, GError **error)
70 {
71   LogMatcherString *self = (LogMatcherString *) s;
72 
73   g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
74   log_matcher_store_pattern(s, pattern);
75 
76   self->pattern_len = strlen(pattern);
77   return TRUE;
78 }
79 
80 static const gchar *
log_matcher_string_match_string(LogMatcherString * self,const gchar * value,gsize value_len)81 log_matcher_string_match_string(LogMatcherString *self, const gchar *value, gsize value_len)
82 {
83   const gchar *result = NULL;
84   gboolean match = FALSE;
85   const gchar *pattern = self->super.pattern;
86 
87   if (self->pattern_len > value_len)
88     return NULL;
89   if (G_LIKELY((self->super.flags & (LMF_SUBSTRING + LMF_PREFIX)) == 0))
90     {
91       if (self->super.flags & LMF_ICASE)
92         match = strncasecmp(value, pattern, value_len) == 0;
93       else
94         match = strncmp(value, pattern, value_len) == 0;
95     }
96   else if (self->super.flags & LMF_PREFIX)
97     {
98       if (self->super.flags & LMF_ICASE)
99         match = strncasecmp(value, pattern, MIN(value_len, self->pattern_len)) == 0;
100       else
101         match = strncmp(value, pattern, MIN(value_len, self->pattern_len)) == 0;
102     }
103   else if (self->super.flags & LMF_SUBSTRING)
104     {
105       if (self->super.flags & LMF_ICASE)
106         {
107           gchar *buf;
108           gchar *res;
109 
110           APPEND_ZERO(buf, value, value_len);
111           res = strcasestr(buf, pattern);
112           if (res)
113             result = value + (res - buf);
114         }
115       else
116         {
117           result = g_strstr_len(value, value_len, pattern);
118         }
119     }
120 
121   if (match && !result)
122     result = value;
123   return result;
124 }
125 
126 static gboolean
log_matcher_string_match(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len)127 log_matcher_string_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
128 {
129   LogMatcherString *self = (LogMatcherString *) s;
130 
131   return log_matcher_string_match_string(self, value, value_len) != NULL;
132 }
133 
134 static gchar *
log_matcher_string_replace(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len,LogTemplate * replacement,gssize * new_length)135 log_matcher_string_replace(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len,
136                            LogTemplate *replacement, gssize *new_length)
137 {
138   LogMatcherString *self = (LogMatcherString *) s;
139   GString *new_value = NULL;
140   gsize current_ofs = 0;
141   gboolean first_round = TRUE;
142 
143   if (value_len < 0)
144     value_len = strlen(value);
145 
146   const gchar *match;
147 
148   do
149     {
150       if (current_ofs == value_len)
151         break;
152 
153       match = log_matcher_string_match_string(self, value + current_ofs, value_len - current_ofs);
154 
155       if (match != NULL)
156         {
157           /* start_ofs & end_ofs are relative to the original string */
158           gsize start_ofs = match - value;
159           gsize end_ofs = start_ofs + self->pattern_len;
160 
161           if (start_ofs == end_ofs && !first_round)
162             {
163               start_ofs++;
164               end_ofs++;
165             }
166 
167           if ((s->flags & LMF_STORE_MATCHES))
168             log_msg_clear_matches(msg);
169 
170           if (!new_value)
171             new_value = g_string_sized_new(value_len);
172 
173           g_string_append_len(new_value, value + current_ofs, start_ofs - current_ofs);
174           log_template_append_format(replacement, msg, &DEFAULT_TEMPLATE_EVAL_OPTIONS, new_value);
175           current_ofs = end_ofs;
176 
177           if ((self->super.flags & LMF_GLOBAL) == 0)
178             {
179               g_string_append_len(new_value, value + current_ofs, value_len - current_ofs);
180               break;
181             }
182         }
183       else
184         {
185           if (new_value)
186             {
187               /* no more matches, append the end of the string */
188               g_string_append_len(new_value, value + current_ofs, value_len - current_ofs);
189             }
190         }
191       first_round = FALSE;
192     }
193   while (match && (self->super.flags & LMF_GLOBAL));
194 
195   if (new_value)
196     {
197       if (new_length)
198         *new_length = new_value->len;
199       return g_string_free(new_value, FALSE);
200     }
201   return NULL;
202 }
203 
204 LogMatcher *
log_matcher_string_new(const LogMatcherOptions * options)205 log_matcher_string_new(const LogMatcherOptions *options)
206 {
207   LogMatcherString *self = g_new0(LogMatcherString, 1);
208 
209   log_matcher_init(&self->super, options);
210   self->super.compile = log_matcher_string_compile;
211   self->super.match = log_matcher_string_match;
212   self->super.replace = log_matcher_string_replace;
213 
214   return &self->super;
215 }
216 
217 typedef struct _LogMatcherGlob
218 {
219   LogMatcher super;
220   GPatternSpec *pattern;
221 } LogMatcherGlob;
222 
223 static gboolean
log_matcher_glob_compile(LogMatcher * s,const gchar * pattern,GError ** error)224 log_matcher_glob_compile(LogMatcher *s, const gchar *pattern, GError **error)
225 {
226   LogMatcherGlob *self = (LogMatcherGlob *)s;
227 
228   g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
229   log_matcher_store_pattern(s, pattern);
230 
231   self->pattern = g_pattern_spec_new(pattern);
232   return TRUE;
233 }
234 
235 /* GPattern only works with utf8 strings, if the input is not utf8, we risk
236  * a crash
237  */
238 static gboolean
log_matcher_glob_match(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len)239 log_matcher_glob_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
240 {
241   LogMatcherGlob *self =  (LogMatcherGlob *) s;
242 
243   if (G_LIKELY((msg->flags & LF_UTF8) || g_utf8_validate(value, value_len, NULL)))
244     {
245       static gboolean warned = FALSE;
246       gchar *buf;
247 
248       if (G_UNLIKELY(!warned && (msg->flags & LF_UTF8) == 0))
249         {
250           msg_warning("Input is valid utf8, but the log message is not tagged as such, this performs worse than enabling validate-utf8 flag on input",
251                       evt_tag_printf("value", "%.*s", (gint) value_len, value));
252           warned = TRUE;
253         }
254       APPEND_ZERO(buf, value, value_len);
255       return g_pattern_match(self->pattern, value_len, buf, NULL);
256     }
257   else
258     {
259       msg_warning("Input is not valid utf8, glob match requires utf8 input, thus it never matches in this case",
260                   evt_tag_printf("value", "%.*s", (gint) value_len, value));
261     }
262   return FALSE;
263 }
264 
265 static void
log_matcher_glob_free(LogMatcher * s)266 log_matcher_glob_free(LogMatcher *s)
267 {
268   LogMatcherGlob *self = (LogMatcherGlob *)s;
269   g_pattern_spec_free(self->pattern);
270   log_matcher_free_method(s);
271 }
272 
273 LogMatcher *
log_matcher_glob_new(const LogMatcherOptions * options)274 log_matcher_glob_new(const LogMatcherOptions *options)
275 {
276   LogMatcherGlob *self = g_new0(LogMatcherGlob, 1);
277 
278   log_matcher_init(&self->super, options);
279   self->super.compile = log_matcher_glob_compile;
280   self->super.match = log_matcher_glob_match;
281   self->super.replace = NULL;
282   self->super.free_fn = log_matcher_glob_free;
283 
284   return &self->super;
285 }
286 
287 /* libpcre support */
288 
289 typedef struct _LogMatcherPcreRe
290 {
291   LogMatcher super;
292   pcre *pattern;
293   pcre_extra *extra;
294   gint match_options;
295   gchar *nv_prefix;
296   gint nv_prefix_len;
297 } LogMatcherPcreRe;
298 
299 static gboolean
_compile_pcre_regexp(LogMatcherPcreRe * self,const gchar * re,GError ** error)300 _compile_pcre_regexp(LogMatcherPcreRe *self, const gchar *re, GError **error)
301 {
302   gint rc;
303   const gchar *errptr;
304   gint erroffset;
305   gint flags = 0;
306 
307   g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
308 
309   if (self->super.flags & LMF_ICASE)
310     flags |= PCRE_CASELESS;
311 
312   if (self->super.flags & LMF_NEWLINE)
313     {
314       if (!PCRE_NEWLINE_ANYCRLF)
315         msg_warning("syslog-ng was compiled against an old PCRE which doesn't support the 'newline' flag");
316       flags |= PCRE_NEWLINE_ANYCRLF;
317     }
318   if (self->super.flags & LMF_UTF8)
319     {
320       gint support;
321       flags |= PCRE_UTF8 | PCRE_NO_UTF8_CHECK;
322       self->match_options |= PCRE_NO_UTF8_CHECK;
323 
324       pcre_config(PCRE_CONFIG_UTF8, &support);
325       if (!support)
326         {
327           g_set_error(error, LOG_TEMPLATE_ERROR, 0, "PCRE library is compiled without UTF8 support and utf8 flag was present");
328           return FALSE;
329         }
330 
331       pcre_config(PCRE_CONFIG_UNICODE_PROPERTIES, &support);
332       if (!support)
333         {
334           g_set_error(error, LOG_TEMPLATE_ERROR, 0,
335                       "PCRE library is compiled without UTF8 properties support and utf8 flag was present");
336           return FALSE;
337         }
338     }
339   if (self->super.flags & LMF_DUPNAMES)
340     {
341       if (!PCRE_DUPNAMES)
342         msg_warning("syslog-ng was compiled against an old PCRE which doesn't support the 'dupnames' flag");
343       flags |= PCRE_DUPNAMES;
344     }
345 
346   /* compile the regexp */
347   self->pattern = pcre_compile2(re, flags, &rc, &errptr, &erroffset, NULL);
348   if (!self->pattern)
349     {
350       g_set_error(error, LOG_TEMPLATE_ERROR, 0, "Failed to compile PCRE expression >>>%s<<< `%s' at character %d",
351                   re, errptr, erroffset);
352       return FALSE;
353     }
354   return TRUE;
355 }
356 
357 static gboolean
_study_pcre_regexp(LogMatcherPcreRe * self,const gchar * re,GError ** error)358 _study_pcre_regexp(LogMatcherPcreRe *self, const gchar *re, GError **error)
359 {
360   const gchar *errptr;
361   gint options = 0;
362 
363   if ((self->super.flags & LMF_DISABLE_JIT) == 0)
364     options |= PCRE_STUDY_JIT_COMPILE;
365 
366   /* optimize regexp */
367   self->extra = pcre_study(self->pattern, options, &errptr);
368   if (errptr != NULL)
369     {
370       g_set_error(error, LOG_TEMPLATE_ERROR, 0, "Failed to optimize regular expression >>>%s<<< `%s'",
371                   re, errptr);
372       return FALSE;
373     }
374   return TRUE;
375 }
376 
377 static gboolean
log_matcher_pcre_re_compile(LogMatcher * s,const gchar * re,GError ** error)378 log_matcher_pcre_re_compile(LogMatcher *s, const gchar *re, GError **error)
379 {
380   LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
381 
382   g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
383   log_matcher_store_pattern(s, re);
384 
385   if (!_compile_pcre_regexp(self, re, error))
386     return FALSE;
387 
388   if (!_study_pcre_regexp(self, re, error))
389     return FALSE;
390 
391   return TRUE;
392 }
393 
394 static void
log_matcher_pcre_re_feed_backrefs(LogMatcher * s,LogMessage * msg,gint value_handle,int * matches,gint match_num,const gchar * value)395 log_matcher_pcre_re_feed_backrefs(LogMatcher *s, LogMessage *msg, gint value_handle, int *matches, gint match_num,
396                                   const gchar *value)
397 {
398   gint i;
399   gboolean indirect = _shall_set_values_indirectly(value_handle);
400 
401   for (i = 0; i < (RE_MAX_MATCHES) && i < match_num; i++)
402     {
403       gint begin_index = matches[2 * i];
404       gint end_index = matches[2 * i + 1];
405 
406       if (begin_index < 0 || end_index < 0)
407         continue;
408 
409       if (indirect)
410         {
411           log_msg_set_match_indirect(msg, i, value_handle, 0, begin_index, end_index - begin_index);
412         }
413       else
414         {
415           log_msg_set_match(msg, i, &value[begin_index], end_index - begin_index);
416         }
417     }
418 }
419 
420 static inline void
log_matcher_pcre_re_feed_value_by_name(LogMatcherPcreRe * s,LogMessage * msg,GString * formatted_name,gchar * tabptr,const gchar * value,gint begin_index,gint end_index)421 log_matcher_pcre_re_feed_value_by_name(LogMatcherPcreRe *s, LogMessage *msg, GString *formatted_name, gchar *tabptr,
422                                        const gchar *value, gint begin_index, gint end_index)
423 {
424   if(s->nv_prefix != NULL)
425     {
426       if (formatted_name->len > 0)
427         g_string_truncate(formatted_name, s->nv_prefix_len);
428       else
429         g_string_assign(formatted_name, s->nv_prefix);
430       g_string_append(formatted_name, tabptr + 2);
431 
432       log_msg_set_value_by_name(msg, formatted_name->str, value + begin_index, end_index - begin_index);
433     }
434   else
435     {
436       log_msg_set_value_by_name(msg, tabptr + 2, value + begin_index, end_index - begin_index);
437     }
438 }
439 
440 static void
log_matcher_pcre_re_feed_named_substrings(LogMatcher * s,LogMessage * msg,int * matches,const gchar * value)441 log_matcher_pcre_re_feed_named_substrings(LogMatcher *s, LogMessage *msg, int *matches, const gchar *value)
442 {
443   gchar *name_table = NULL;
444   gint i = 0;
445   gint namecount = 0;
446   gint name_entry_size = 0;
447   LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
448 
449   pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_NAMECOUNT, &namecount);
450   if (namecount > 0)
451     {
452       gchar *tabptr;
453       /* Before we can access the substrings, we must extract the table for
454          translating names to numbers, and the size of each entry in the table.
455        */
456       pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_NAMETABLE, &name_table);
457       pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size);
458       /* Now we can scan the table and, for each entry, print the number, the name,
459          and the substring itself.
460        */
461       GString *formatted_name = scratch_buffers_alloc();
462       tabptr = name_table;
463       for (i = 0; i < namecount; i++, tabptr += name_entry_size)
464         {
465           int n = (tabptr[0] << 8) | tabptr[1];
466           gint begin_index = matches[2 * n];
467           gint end_index = matches[2 * n + 1];
468 
469           if (begin_index < 0 || end_index < 0)
470             continue;
471 
472           log_matcher_pcre_re_feed_value_by_name(self, msg, formatted_name, tabptr, value, begin_index, end_index);
473         }
474     }
475 }
476 
477 static gboolean
log_matcher_pcre_re_match(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len)478 log_matcher_pcre_re_match(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len)
479 {
480   LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
481   gint *matches;
482   gsize matches_size;
483   gint num_matches;
484   gint rc;
485 
486   if (value_len == -1)
487     value_len = strlen(value);
488 
489   if (pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_CAPTURECOUNT, &num_matches) < 0)
490     g_assert_not_reached();
491   if (num_matches > RE_MAX_MATCHES)
492     num_matches = RE_MAX_MATCHES;
493 
494   matches_size = 3 * (num_matches + 1);
495   matches = g_alloca(matches_size * sizeof(gint));
496 
497   rc = pcre_exec(self->pattern, self->extra,
498                  value, value_len, 0, self->match_options, matches, matches_size);
499   if (rc < 0)
500     {
501       switch (rc)
502         {
503         case PCRE_ERROR_NOMATCH:
504           break;
505 
506         default:
507           /* Handle other special cases */
508           msg_error("Error while matching regexp",
509                     evt_tag_int("error_code", rc));
510           break;
511         }
512       return FALSE;
513     }
514   if (rc == 0)
515     {
516       msg_error("Error while storing matching substrings");
517     }
518   else
519     {
520       if ((s->flags & LMF_STORE_MATCHES))
521         {
522           log_matcher_pcre_re_feed_backrefs(s, msg, value_handle, matches, rc, value);
523           log_matcher_pcre_re_feed_named_substrings(s, msg, matches, value);
524         }
525     }
526   return TRUE;
527 }
528 
529 static gchar *
log_matcher_pcre_re_replace(LogMatcher * s,LogMessage * msg,gint value_handle,const gchar * value,gssize value_len,LogTemplate * replacement,gssize * new_length)530 log_matcher_pcre_re_replace(LogMatcher *s, LogMessage *msg, gint value_handle, const gchar *value, gssize value_len,
531                             LogTemplate *replacement, gssize *new_length)
532 {
533   LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
534   GString *new_value = NULL;
535   gint *matches;
536   gsize matches_size;
537   gint num_matches;
538   gint rc;
539   gint start_offset, last_offset;
540   gint options;
541   gboolean last_match_was_empty;
542 
543   if (pcre_fullinfo(self->pattern, self->extra, PCRE_INFO_CAPTURECOUNT, &num_matches) < 0)
544     g_assert_not_reached();
545   if (num_matches > RE_MAX_MATCHES)
546     num_matches = RE_MAX_MATCHES;
547 
548   matches_size = 3 * (num_matches + 1);
549   matches = g_alloca(matches_size * sizeof(gint));
550 
551   /* we need zero initialized offsets for the last match as the
552    * algorithm tries uses that as the base position */
553 
554   matches[0] = matches[1] = matches[2] = 0;
555 
556   if (value_len == -1)
557     value_len = strlen(value);
558 
559   last_offset = start_offset = 0;
560   last_match_was_empty = FALSE;
561   do
562     {
563       /* loop over the string, replacing one occurrence at a time. */
564 
565       /* NOTE: zero length matches need special care, as we could spin
566        * forever otherwise (since the current position wouldn't be
567        * advanced).
568        *
569        * A zero-length match can be as simple as "a*" which will be
570        * returned unless PCRE_NOTEMPTY is specified.
571        *
572        * By supporting zero-length matches, we basically make it
573        * possible to insert replacement between each incoming
574        * character.
575        *
576        * For example:
577        *     pattern: a*
578        *     replacement: #
579        *     input: message
580        *     result: #m#e#s#s#a#g#e#
581        *
582        * This mimics Perl behaviour.
583        */
584 
585       if (last_match_was_empty)
586         {
587           /* Otherwise, arrange to run another match at the same point
588            * to see if a non-empty match can be found.
589            */
590 
591           options = PCRE_NOTEMPTY | PCRE_ANCHORED;
592         }
593       else
594         {
595           options = 0;
596         }
597 
598       rc = pcre_exec(self->pattern, self->extra,
599                      value, value_len,
600                      start_offset, (self->match_options | options), matches, matches_size);
601       if (rc < 0 && rc != PCRE_ERROR_NOMATCH)
602         {
603           msg_error("Error while matching regexp",
604                     evt_tag_int("error_code", rc));
605           break;
606         }
607       else if (rc < 0)
608         {
609           if ((options & PCRE_NOTEMPTY) == 0)
610             {
611               /* we didn't match, even when we permitted to match the
612                * empty string. Nothing to find here, bail out */
613               break;
614             }
615 
616           /* we didn't match, quite possibly because the empty match
617            * was not permitted. Skip one character in order to avoid
618            * infinite loop over the same zero-length match. */
619 
620           start_offset = start_offset + 1;
621           /* FIXME: handle complex sequences like utf8 and newline characters */
622           last_match_was_empty = FALSE;
623           continue;
624         }
625       else
626         {
627           /* if the output array was too small, truncate the number of
628              captures to RE_MAX_MATCHES */
629 
630           if (rc == 0)
631             rc = matches_size / 3;
632 
633           log_matcher_pcre_re_feed_backrefs(s, msg, value_handle, matches, rc, value);
634           log_matcher_pcre_re_feed_named_substrings(s, msg, matches, value);
635 
636           if (!new_value)
637             new_value = g_string_sized_new(value_len);
638           /* append non-matching portion */
639           g_string_append_len(new_value, &value[last_offset], matches[0] - last_offset);
640           /* replacement */
641           log_template_append_format(replacement, msg, &DEFAULT_TEMPLATE_EVAL_OPTIONS, new_value);
642 
643           last_match_was_empty = (matches[0] == matches[1]);
644           start_offset = last_offset = matches[1];
645         }
646     }
647   while (self->super.flags & LMF_GLOBAL && start_offset < value_len);
648 
649   if (new_value)
650     {
651       /* append the last literal */
652       g_string_append_len(new_value, &value[last_offset], value_len - last_offset);
653       if (new_length)
654         *new_length = new_value->len;
655       return g_string_free(new_value, FALSE);
656     }
657   return NULL;
658 }
659 
660 static void
log_matcher_pcre_re_free(LogMatcher * s)661 log_matcher_pcre_re_free(LogMatcher *s)
662 {
663   LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
664   pcre_free_study(self->extra);
665   pcre_free(self->pattern);
666   log_matcher_free_method(s);
667 }
668 
669 LogMatcher *
log_matcher_pcre_re_new(const LogMatcherOptions * options)670 log_matcher_pcre_re_new(const LogMatcherOptions *options)
671 {
672   LogMatcherPcreRe *self = g_new0(LogMatcherPcreRe, 1);
673   self->nv_prefix = NULL;
674   self->nv_prefix_len = 0;
675 
676   log_matcher_init(&self->super, options);
677   self->super.compile = log_matcher_pcre_re_compile;
678   self->super.match = log_matcher_pcre_re_match;
679   self->super.replace = log_matcher_pcre_re_replace;
680   self->super.free_fn = log_matcher_pcre_re_free;
681 
682   return &self->super;
683 }
684 
685 void
log_matcher_pcre_set_nv_prefix(LogMatcher * s,const gchar * prefix)686 log_matcher_pcre_set_nv_prefix(LogMatcher *s, const gchar *prefix)
687 {
688   LogMatcherPcreRe *self = (LogMatcherPcreRe *) s;
689 
690   g_free(self->nv_prefix);
691   if (prefix)
692     {
693       self->nv_prefix = g_strdup(prefix);
694       self->nv_prefix_len = strlen(prefix);
695     }
696   else
697     {
698       self->nv_prefix = NULL;
699       self->nv_prefix_len = 0;
700     }
701 }
702 
703 typedef LogMatcher *(*LogMatcherConstructFunc)(const LogMatcherOptions *options);
704 
705 struct
706 {
707   const gchar *name;
708   LogMatcherConstructFunc construct;
709 } matcher_types[] =
710 {
711   { "pcre", log_matcher_pcre_re_new },
712   { "string", log_matcher_string_new },
713   { "glob", log_matcher_glob_new },
714   { NULL, NULL },
715 };
716 
717 static LogMatcherConstructFunc
log_matcher_lookup_construct(const gchar * type)718 log_matcher_lookup_construct(const gchar *type)
719 {
720   gint i;
721 
722   for (i = 0; matcher_types[i].name; i++)
723     {
724       if (strcmp(matcher_types[i].name, type) == 0)
725         return matcher_types[i].construct;
726     }
727   return NULL;
728 }
729 
730 LogMatcher *
log_matcher_new(const LogMatcherOptions * options)731 log_matcher_new(const LogMatcherOptions *options)
732 {
733   LogMatcherConstructFunc construct;
734 
735   construct = log_matcher_lookup_construct(options->type);
736   return construct(options);
737 }
738 
739 LogMatcher *
log_matcher_ref(LogMatcher * s)740 log_matcher_ref(LogMatcher *s)
741 {
742   s->ref_cnt++;
743   return s;
744 }
745 
746 void
log_matcher_unref(LogMatcher * s)747 log_matcher_unref(LogMatcher *s)
748 {
749   if (--s->ref_cnt == 0)
750     {
751       if (s->free_fn)
752         s->free_fn(s);
753       g_free(s);
754     }
755 }
756 
757 gboolean
log_matcher_options_set_type(LogMatcherOptions * options,const gchar * type)758 log_matcher_options_set_type(LogMatcherOptions *options, const gchar *type)
759 {
760   LogMatcherConstructFunc construct;
761 
762   if (strcmp(type, "posix") == 0)
763     {
764       msg_warning_once("WARNING: syslog-ng dropped support for POSIX regexp implementations in " VERSION_3_14
765                        " in favour of PCRE, which should be upward compatible. All 'posix' regexps are "
766                        "automatically switched to 'pcre'. Please ensure that your regexps work with PCRE and "
767                        "specify type('pcre') explicitly or increase @version to remove this warning");
768       type = "pcre";
769     }
770 
771   construct = log_matcher_lookup_construct(type);
772   if (!construct)
773     return FALSE;
774 
775   if (options->type)
776     g_free(options->type);
777   options->type = g_strdup(type);
778   return TRUE;
779 }
780 
781 CfgFlagHandler log_matcher_flag_handlers[] =
782 {
783   /* NOTE: underscores are automatically converted to dashes */
784 
785   { "global",          CFH_SET, offsetof(LogMatcherOptions, flags), LMF_GLOBAL        },
786   { "icase",           CFH_SET, offsetof(LogMatcherOptions, flags), LMF_ICASE         },
787   { "ignore-case",     CFH_SET, offsetof(LogMatcherOptions, flags), LMF_ICASE         },
788   { "newline",         CFH_SET, offsetof(LogMatcherOptions, flags), LMF_NEWLINE       },
789   { "unicode",         CFH_SET, offsetof(LogMatcherOptions, flags), LMF_UTF8          },
790   { "utf8",            CFH_SET, offsetof(LogMatcherOptions, flags), LMF_UTF8          },
791   { "store-matches",   CFH_SET, offsetof(LogMatcherOptions, flags), LMF_STORE_MATCHES },
792   { "substring",       CFH_SET, offsetof(LogMatcherOptions, flags), LMF_SUBSTRING     },
793   { "prefix",          CFH_SET, offsetof(LogMatcherOptions, flags), LMF_PREFIX        },
794   { "disable-jit",     CFH_SET, offsetof(LogMatcherOptions, flags), LMF_DISABLE_JIT   },
795   { "dupnames",        CFH_SET, offsetof(LogMatcherOptions, flags), LMF_DUPNAMES      },
796 
797   { NULL },
798 };
799 
800 gboolean
log_matcher_options_process_flag(LogMatcherOptions * self,const gchar * flag)801 log_matcher_options_process_flag(LogMatcherOptions *self, const gchar *flag)
802 {
803   return cfg_process_flag(log_matcher_flag_handlers, self, flag);
804 }
805 
806 void
log_matcher_options_defaults(LogMatcherOptions * options)807 log_matcher_options_defaults(LogMatcherOptions *options)
808 {
809   options->flags = 0;
810   options->type = NULL;
811 }
812 
813 void
log_matcher_options_init(LogMatcherOptions * options)814 log_matcher_options_init(LogMatcherOptions *options)
815 {
816   if (!options->type)
817     {
818       const gchar *default_matcher = "pcre";
819 
820       if (!log_matcher_options_set_type(options, default_matcher))
821         g_assert_not_reached();
822     }
823 }
824 
825 void
log_matcher_options_destroy(LogMatcherOptions * options)826 log_matcher_options_destroy(LogMatcherOptions *options)
827 {
828   if (options->type)
829     g_free(options->type);
830 }
831 
832 GQuark
log_matcher_error_quark(void)833 log_matcher_error_quark(void)
834 {
835   return g_quark_from_static_string("log-matcher-error-quark");
836 }
837