1 /* filter_files.c
2  * Code for reading and writing the filters file.
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include <config.h>
12 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
13 
14 #include <stdio.h>
15 #include <string.h>
16 #include <errno.h>
17 
18 #include <glib.h>
19 
20 #include <wsutil/file_util.h>
21 #include <wsutil/filesystem.h>
22 #include <wsutil/report_message.h>
23 #include <wsutil/wslog.h>
24 #include <wsutil/ws_assert.h>
25 
26 #include "ui/filter_files.h"
27 
28 /*
29  * List of capture filters - saved.
30  */
31 static GList *capture_filters = NULL;
32 
33 /*
34  * List of display filters - saved.
35  */
36 static GList *display_filters = NULL;
37 
38 /*
39  * Read in a list of filters.
40  *
41  * On error, report the error via the UI.
42  */
43 
44 #define INIT_BUF_SIZE  128
45 
46 static GList *
add_filter_entry(GList * fl,const char * filt_name,const char * filt_expr)47 add_filter_entry(GList *fl, const char *filt_name, const char *filt_expr)
48 {
49     filter_def *filt;
50 
51     filt         = g_new(filter_def, 1);
52     filt->name   = g_strdup(filt_name);
53     filt->strval = g_strdup(filt_expr);
54     return g_list_prepend(fl, filt);
55 }
56 
57 static void
free_filter_entry(gpointer data)58 free_filter_entry(gpointer data)
59 {
60     filter_def *filt = (filter_def*)data;
61     g_free(filt->name);
62     g_free(filt->strval);
63     g_free(filt);
64 }
65 
free_filter_lists(void)66 void free_filter_lists(void)
67 {
68     if (capture_filters) {
69         g_list_free_full(capture_filters, free_filter_entry);
70         capture_filters = NULL;
71     }
72     if (display_filters) {
73         g_list_free_full(display_filters, free_filter_entry);
74         display_filters = NULL;
75     }
76 }
77 
78 static GList *
remove_filter_entry(GList * fl,GList * fl_entry)79 remove_filter_entry(GList *fl, GList *fl_entry)
80 {
81     filter_def *filt;
82 
83     filt = (filter_def *) fl_entry->data;
84     g_free(filt->name);
85     g_free(filt->strval);
86     g_free(filt);
87     return g_list_remove_link(fl, fl_entry);
88 }
89 
90 static int
skip_whitespace(FILE * ff)91 skip_whitespace(FILE *ff)
92 {
93     int c;
94 
95     while ((c = getc(ff)) != EOF && c != '\n' && g_ascii_isspace(c))
96         ;
97     return c;
98 }
99 
100 static int
getc_crlf(FILE * ff)101 getc_crlf(FILE *ff)
102 {
103     int c;
104 
105     c = getc(ff);
106     if (c == '\r') {
107         /* Treat CR-LF at the end of a line like LF, so that if we're reading
108          * a Windows-format file on UN*X, we handle it the same way we'd handle
109          * a UN*X-format file. */
110         c = getc(ff);
111         if (c != EOF && c != '\n') {
112             /* Put back the character after the CR, and process the CR normally. */
113             ungetc(c, ff);
114             c = '\r';
115         }
116     }
117     return c;
118 }
119 
120 void
read_filter_list(filter_list_type_t list_type)121 read_filter_list(filter_list_type_t list_type)
122 {
123     const char *ff_name, *ff_description;
124     char       *ff_path;
125     FILE       *ff;
126     GList      **flpp;
127     int         c;
128     char       *filt_name, *filt_expr;
129     int         filt_name_len, filt_expr_len;
130     int         filt_name_index, filt_expr_index;
131     int         line = 1;
132 
133     switch (list_type) {
134 
135         case CFILTER_LIST:
136             ff_name = CFILTER_FILE_NAME;
137             ff_description = "capture";
138             flpp = &capture_filters;
139             break;
140 
141         case DFILTER_LIST:
142             ff_name = DFILTER_FILE_NAME;
143             ff_description = "display";
144             flpp = &display_filters;
145             break;
146 
147         default:
148             ws_assert_not_reached();
149             return;
150     }
151 
152     /* try to open personal "cfilters"/"dfilters" file */
153     ff_path = get_persconffile_path(ff_name, TRUE);
154     if ((ff = ws_fopen(ff_path, "r")) == NULL) {
155         /*
156          * Did that fail because the file didn't exist?
157          */
158         if (errno != ENOENT) {
159             /*
160              * No.  Just give up.
161              */
162             report_warning("Could not open your %s filter file\n\"%s\": %s.",
163                     ff_description, ff_path, g_strerror(errno));
164             g_free(ff_path);
165             return;
166         }
167 
168         /*
169          * Yes.  See if there's an "old style" personal "filters" file; if so, read it.
170          * This means that a user will start out with their capture and
171          * display filter lists being identical; each list may contain
172          * filters that don't belong in that list.  The user can edit
173          * the filter lists, and delete the ones that don't belong in
174          * a particular list.
175          */
176         g_free(ff_path);
177         ff_path = get_persconffile_path(FILTER_FILE_NAME, FALSE);
178         if ((ff = ws_fopen(ff_path, "r")) == NULL) {
179             /*
180              * Did that fail because the file didn't exist?
181              */
182             if (errno != ENOENT) {
183                 /*
184                  * No.  Just give up.
185                  */
186                 report_warning("Could not open your %s filter file\n\"%s\": %s.",
187                         ff_description, ff_path, g_strerror(errno));
188                 g_free(ff_path);
189                 return;
190             }
191 
192             /*
193              * Try to open the global "cfilters/dfilters" file.
194              */
195             g_free(ff_path);
196             ff_path = get_datafile_path(ff_name);
197             if ((ff = ws_fopen(ff_path, "r")) == NULL) {
198                 /*
199                  * Well, that didn't work, either.  Just give up.
200                  * Report an error if the file existed but we couldn't open it.
201                  */
202                 if (errno != ENOENT) {
203                     report_warning("Could not open your %s filter file\n\"%s\": %s.",
204                             ff_description, ff_path, g_strerror(errno));
205                 }
206                 g_free(ff_path);
207                 return;
208             }
209         }
210     }
211 
212     /* If we already have a list of filters, discard it. */
213     /* this should never happen - this function is called only once for each list! */
214     while(*flpp) {
215         *flpp = remove_filter_entry(*flpp, g_list_first(*flpp));
216     }
217 
218     /* Allocate the filter name buffer. */
219     filt_name_len = INIT_BUF_SIZE;
220     filt_name = (char *)g_malloc(filt_name_len + 1);
221     filt_expr_len = INIT_BUF_SIZE;
222     filt_expr = (char *)g_malloc(filt_expr_len + 1);
223 
224     for (line = 1; ; line++) {
225         /* Lines in a filter file are of the form
226 
227            "name" expression
228 
229            where "name" is a name, in quotes - backslashes in the name
230            escape the next character, so quotes and backslashes can appear
231            in the name - and "expression" is a filter expression, not in
232            quotes, running to the end of the line. */
233 
234         /* Skip over leading white space, if any. */
235         c = skip_whitespace(ff);
236 
237         if (c == EOF)
238             break;    /* Nothing more to read */
239         if (c == '\n')
240             continue; /* Blank line. */
241 
242         /* "c" is the first non-white-space character.
243            If it's not a quote, it's an error. */
244         if (c != '"') {
245             ws_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,
246                     line);
247             while (c != '\n')
248                 c = getc(ff);   /* skip to the end of the line */
249             continue;
250         }
251 
252         /* Get the name of the filter. */
253         filt_name_index = 0;
254         for (;;) {
255             c = getc_crlf(ff);
256             if (c == EOF || c == '\n')
257                 break;  /* End of line - or end of file */
258             if (c == '"') {
259                 /* Closing quote. */
260                 if (filt_name_index >= filt_name_len) {
261                     /* Filter name buffer isn't long enough; double its length. */
262                     filt_name_len *= 2;
263                     filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
264                 }
265                 filt_name[filt_name_index] = '\0';
266                 break;
267             }
268             if (c == '\\') {
269                 /* Next character is escaped */
270                 c = getc_crlf(ff);
271                 if (c == EOF || c == '\n')
272                     break;        /* End of line - or end of file */
273             }
274             /* Add this character to the filter name string. */
275             if (filt_name_index >= filt_name_len) {
276                 /* Filter name buffer isn't long enough; double its length. */
277                 filt_name_len *= 2;
278                 filt_name = (char *)g_realloc(filt_name, filt_name_len + 1);
279             }
280             filt_name[filt_name_index] = c;
281             filt_name_index++;
282         }
283 
284         if (c == EOF) {
285             if (!ferror(ff)) {
286                 /* EOF, not error; no newline seen before EOF */
287                 ws_warning("'%s' line %d doesn't have a newline.", ff_path,
288                         line);
289             }
290             break;    /* nothing more to read */
291         }
292 
293         if (c != '"') {
294             /* No newline seen before end-of-line */
295             ws_warning("'%s' line %d doesn't have a closing quote.", ff_path,
296                     line);
297             continue;
298         }
299 
300         /* Skip over separating white space, if any. */
301         c = skip_whitespace(ff);
302 
303         if (c == EOF) {
304             if (!ferror(ff)) {
305                 /* EOF, not error; no newline seen before EOF */
306                 ws_warning("'%s' line %d doesn't have a newline.", ff_path,
307                         line);
308             }
309             break;    /* nothing more to read */
310         }
311 
312         if (c == '\n') {
313             /* No filter expression */
314             ws_warning("'%s' line %d doesn't have a filter expression.", ff_path,
315                     line);
316             continue;
317         }
318 
319         /* "c" is the first non-white-space character; it's the first
320            character of the filter expression. */
321         filt_expr_index = 0;
322         for (;;) {
323             /* Add this character to the filter expression string. */
324             if (filt_expr_index >= filt_expr_len) {
325                 /* Filter expressioin buffer isn't long enough; double its length. */
326                 filt_expr_len *= 2;
327                 filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
328             }
329             filt_expr[filt_expr_index] = c;
330             filt_expr_index++;
331 
332             /* Get the next character. */
333             c = getc_crlf(ff);
334             if (c == EOF || c == '\n')
335                 break;
336         }
337 
338         if (c == EOF) {
339             if (!ferror(ff)) {
340                 /* EOF, not error; no newline seen before EOF */
341                 ws_warning("'%s' line %d doesn't have a newline.", ff_path,
342                         line);
343             }
344             break;    /* nothing more to read */
345         }
346 
347         /* We saw the ending newline; terminate the filter expression string */
348         if (filt_expr_index >= filt_expr_len) {
349             /* Filter expressioin buffer isn't long enough; double its length. */
350             filt_expr_len *= 2;
351             filt_expr = (char *)g_realloc(filt_expr, filt_expr_len + 1);
352         }
353         filt_expr[filt_expr_index] = '\0';
354 
355         /* Add the new filter to the list of filters */
356         *flpp = add_filter_entry(*flpp, filt_name, filt_expr);
357     }
358     if (ferror(ff)) {
359         report_warning("Error reading your %s filter file\n\"%s\": %s.",
360                 ff_description, ff_path, g_strerror(errno));
361     }
362     g_free(ff_path);
363     fclose(ff);
364     g_free(filt_name);
365     g_free(filt_expr);
366 }
367 
368 /*
369  * Get a pointer to a list of filters.
370  */
371 static GList **
get_filter_list(filter_list_type_t list_type)372 get_filter_list(filter_list_type_t list_type)
373 {
374     GList **flpp;
375 
376     switch (list_type) {
377 
378         case CFILTER_LIST:
379             flpp = &capture_filters;
380             break;
381 
382         case DFILTER_LIST:
383             flpp = &display_filters;
384             break;
385 
386         default:
387             ws_assert_not_reached();
388             flpp = NULL;
389     }
390     return flpp;
391 }
392 
393 /*
394  * Get a pointer to the first entry in a filter list.
395  */
396 GList *
get_filter_list_first(filter_list_type_t list_type)397 get_filter_list_first(filter_list_type_t list_type)
398 {
399     GList      **flpp;
400 
401     flpp = get_filter_list(list_type);
402     return g_list_first(*flpp);
403 }
404 
405 /*
406  * Add a new filter to the end of a list.
407  * Returns a pointer to the newly-added entry.
408  */
409 GList *
add_to_filter_list(filter_list_type_t list_type,const char * name,const char * expression)410 add_to_filter_list(filter_list_type_t list_type, const char *name,
411     const char *expression)
412 {
413     GList      **flpp;
414 
415     flpp  = get_filter_list(list_type);
416     *flpp = add_filter_entry(*flpp, name, expression);
417 
418     return g_list_last(*flpp);
419 }
420 
421 /*
422  * Remove a filter from a list.
423  */
424 void
remove_from_filter_list(filter_list_type_t list_type,GList * fl_entry)425 remove_from_filter_list(filter_list_type_t list_type, GList *fl_entry)
426 {
427     GList      **flpp;
428 
429     flpp  = get_filter_list(list_type);
430     *flpp = remove_filter_entry(*flpp, fl_entry);
431 }
432 
433 /*
434  * Write out a list of filters.
435  *
436  * On error, report the error via the UI.
437  */
438 void
save_filter_list(filter_list_type_t list_type)439 save_filter_list(filter_list_type_t list_type)
440 {
441     char        *pf_dir_path;
442     const gchar *ff_name, *ff_description;
443     gchar       *ff_path, *ff_path_new;
444     GList       *fl;
445     GList       *flpp;
446     filter_def  *filt;
447     FILE        *ff;
448     guchar      *p, c;
449 
450     switch (list_type) {
451 
452         case CFILTER_LIST:
453             ff_name = CFILTER_FILE_NAME;
454             ff_description = "capture";
455             fl = capture_filters;
456             break;
457 
458         case DFILTER_LIST:
459             ff_name = DFILTER_FILE_NAME;
460             ff_description = "display";
461             fl = display_filters;
462             break;
463 
464         default:
465             ws_assert_not_reached();
466             return;
467     }
468 
469     /* Create the directory that holds personal configuration files,
470        if necessary.  */
471     if (create_persconffile_dir(&pf_dir_path) == -1) {
472         report_failure("Can't create directory\n\"%s\"\nfor filter files: %s.",
473                 pf_dir_path, g_strerror(errno));
474         g_free(pf_dir_path);
475         return;
476     }
477 
478     ff_path = get_persconffile_path(ff_name, TRUE);
479 
480     /* Write to "XXX.new", and rename if that succeeds.
481        That means we don't trash the file if we fail to write it out
482        completely. */
483     ff_path_new = g_strdup_printf("%s.new", ff_path);
484 
485     if ((ff = ws_fopen(ff_path_new, "w")) == NULL) {
486         /* We had an error saving the filter. */
487         report_failure("Error saving your %s filter file\nCouldn't open \"%s\": %s.",
488                 ff_description, ff_path_new, g_strerror(errno));
489         g_free(ff_path_new);
490         g_free(ff_path);
491         return;
492     }
493     flpp = g_list_first(fl);
494     while (flpp) {
495         filt = (filter_def *) flpp->data;
496 
497         /* Write out the filter name as a quoted string; escape any quotes
498            or backslashes. */
499         putc('"', ff);
500         for (p = (guchar *)filt->name; (c = *p) != '\0'; p++) {
501             if (c == '"' || c == '\\')
502                 putc('\\', ff);
503             putc(c, ff);
504         }
505         putc('"', ff);
506 
507         /* Separate the filter name and value with a space. */
508         putc(' ', ff);
509 
510         /* Write out the filter expression and a newline. */
511         fprintf(ff, "%s\n", filt->strval);
512         if (ferror(ff)) {
513             report_failure("Error saving your %s filter file\nWrite to \"%s\" failed: %s.",
514                     ff_description, ff_path_new, g_strerror(errno));
515             fclose(ff);
516             ws_unlink(ff_path_new);
517             g_free(ff_path_new);
518             g_free(ff_path);
519             return;
520         }
521         flpp = flpp->next;
522     }
523     if (fclose(ff) == EOF) {
524         report_failure("Error saving your %s filter file\nWrite to \"%s\" failed: %s.",
525                 ff_description, ff_path_new, g_strerror(errno));
526         ws_unlink(ff_path_new);
527         g_free(ff_path_new);
528         g_free(ff_path);
529         return;
530     }
531 
532 #ifdef _WIN32
533     /* ANSI C doesn't say whether "rename()" removes the target if it
534        exists; the Win32 call to rename files doesn't do so, which I
535        infer is the reason why the MSVC++ "rename()" doesn't do so.
536        We must therefore remove the target file first, on Windows.
537 
538        XXX - ws_rename() should be ws_stdio_rename() on Windows,
539        and ws_stdio_rename() uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING,
540        so it should remove the target if it exists, so this stuff
541        shouldn't be necessary.  Perhaps it dates back to when we were
542        calling rename(), with that being a wrapper around Microsoft's
543        _rename(), which didn't remove the target. */
544     if (ws_remove(ff_path) < 0 && errno != ENOENT) {
545         /* It failed for some reason other than "it's not there"; if
546            it's not there, we don't need to remove it, so we just
547            drive on. */
548         report_failure("Error saving your %s filter file\nCouldn't remove \"%s\": %s.",
549                 ff_description, ff_path, g_strerror(errno));
550         ws_unlink(ff_path_new);
551         g_free(ff_path_new);
552         g_free(ff_path);
553         return;
554     }
555 #endif
556 
557     if (ws_rename(ff_path_new, ff_path) < 0) {
558         report_failure("Error saving your %s filter file\nCouldn't rename \"%s\" to \"%s\": %s.",
559                 ff_description, ff_path_new, ff_path, g_strerror(errno));
560         ws_unlink(ff_path_new);
561         g_free(ff_path_new);
562         g_free(ff_path);
563         return;
564     }
565     g_free(ff_path_new);
566     g_free(ff_path);
567 }
568