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