Allow filter names and expressions of arbitrary length, and, in the
[obnox/wireshark/wip.git] / filters.c
1 /* filters.c
2  * Code for reading and writing the filters file.
3  *
4  * $Id: filters.c,v 1.5 2001/02/03 06:03:42 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34
35 #ifdef HAVE_SYS_STAT_H
36 #include <sys/stat.h>
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #ifdef HAVE_DIRECT_H
44 #include <direct.h>             /* to declare "mkdir()" on Windows */
45 #endif
46
47 #include <glib.h>
48
49 #include <epan.h>
50
51 #include "filters.h"
52 #include "util.h"
53
54 /*
55  * Old filter file name.
56  */
57 #define FILTER_FILE_NAME        "filters"
58
59 /*
60  * Capture filter file name.
61  */
62 #define CFILTER_FILE_NAME       "cfilters"
63
64 /*
65  * Display filter file name.
66  */
67 #define DFILTER_FILE_NAME       "dfilters"
68
69 /*
70  * List of capture filters.
71  */
72 static GList *capture_filters = NULL;
73
74 /*
75  * List of display filters.
76  */
77 static GList *display_filters = NULL;
78
79 /*
80  * Read in a list of filters.
81  *
82  * On success, "*pref_path_return" is set to NULL.
83  * On error, "*pref_path_return" is set to point to the pathname of
84  * the file we tried to read - it should be freed by our caller -
85  * and "*errno_return" is set to the error.
86  */
87
88 #define INIT_BUF_SIZE   128
89
90 void
91 read_filter_list(filter_list_type_t list, char **pref_path_return,
92     int *errno_return)
93 {
94   char       *ff_path, *ff_dir = PF_DIR, *ff_name;
95   FILE       *ff;
96   GList      **flp;
97   GList      *fl_ent;
98   filter_def *filt;
99   int         c;
100   char       *filt_name, *filt_expr;
101   int         filt_name_len, filt_expr_len;
102   int         filt_name_index, filt_expr_index;
103   int         line = 1;
104
105   *pref_path_return = NULL;     /* assume no error */
106
107   switch (list) {
108
109   case CFILTER_LIST:
110     ff_name = CFILTER_FILE_NAME;
111     flp = &capture_filters;
112     break;
113
114   case DFILTER_LIST:
115     ff_name = DFILTER_FILE_NAME;
116     flp = &display_filters;
117     break;
118
119   default:
120     g_assert_not_reached();
121     return;
122   }
123
124   /* To do: generalize this */
125   ff_path = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(ff_dir) +  
126     strlen(ff_name) + 4);
127   sprintf(ff_path, "%s/%s/%s", get_home_dir(), ff_dir, ff_name);
128
129   if ((ff = fopen(ff_path, "r")) == NULL) {
130     /*
131      * Did that fail because we the file didn't exist?
132      */
133     if (errno != ENOENT) {
134       /*
135        * No.  Just give up.
136        */
137       *pref_path_return = ff_path;
138       *errno_return = errno;
139       return;
140     }
141
142     /*
143      * Yes.  See if there's a "filters" file; if so, read it.
144      * This means that a user will start out with their capture and
145      * display filter lists being identical; each list may contain
146      * filters that don't belong in that list.  The user can edit
147      * the filter lists, and delete the ones that don't belong in
148      * a particular list.
149      */
150     sprintf(ff_path, "%s/%s/%s", get_home_dir(), ff_dir, FILTER_FILE_NAME);
151     if ((ff = fopen(ff_path, "r")) == NULL) {
152       /*
153        * Well, that didn't work, either.  Just give up.
154        * Return an error if the file existed but we couldn't open it.
155        */
156       if (errno != ENOENT) {
157         *pref_path_return = ff_path;
158         *errno_return = errno;
159       }
160       return;
161     }
162   }
163
164   /* If we already have a list of filters, discard it. */
165   if (*flp != NULL) {
166     fl_ent = g_list_first(*flp);
167     while (fl_ent != NULL) {
168       filt = (filter_def *) fl_ent->data;
169       g_free(filt->name);
170       g_free(filt->strval);
171       g_free(filt);
172       fl_ent = fl_ent->next;
173     }
174     g_list_free(*flp);
175     *flp = NULL;
176   }
177
178   /* Allocate the filter name buffer. */
179   filt_name_len = INIT_BUF_SIZE;
180   filt_name = g_malloc(filt_name_len + 1);
181   filt_expr_len = INIT_BUF_SIZE;
182   filt_expr = g_malloc(filt_expr_len + 1);
183
184   for (;;) {
185     /* Lines in a filter file are of the form
186
187         "name" expression
188
189        where "name" is a name, in quotes - backslashes in the name
190        escape the next character, so quotes and backslashes can appear
191        in the name - and "expression" is a filter expression, not in
192        quotes, running to the end of the line. */
193
194     /* Skip over leading white space, if any. */
195     while ((c = getc(ff)) != EOF && isspace(c)) {
196       if (c == '\n') {
197         /* Blank line. */
198         line++; /* next line */
199         continue;
200       }
201     }
202
203     if (c == EOF)
204       break;    /* Nothing more to read */
205
206     /* "c" is the first non-white-space character.
207        If it's not a quote, it's an error. */
208     if (c != '"') {
209       g_warning("'%s' line %d doesn't have a quoted filter name.", ff_path,
210                 line);
211       while (c != '\n')
212         c = getc(ff);   /* skip to the end of the line */
213       line++;   /* next line */
214       continue;
215     }
216
217     /* Get the name of the filter. */
218     filt_name_index = 0;
219     for (;;) {
220       c = getc(ff);
221       if (c == EOF || c == '\n')
222         break;  /* End of line - or end of file */
223       if (c == '"') {
224         /* Closing quote. */
225         if (filt_name_index >= filt_name_len) {
226           /* Filter name buffer isn't long enough; double its length. */
227           filt_name_len *= 2;
228           filt_name = g_realloc(filt_name, filt_name_len + 1);
229         }
230         filt_name[filt_name_index] = '\0';
231         break;
232       }
233       if (c == '\\') {
234         /* Next character is escaped */
235         c = getc(ff);
236         if (c == EOF || c == '\n')
237           break;        /* End of line - or end of file */
238       }
239       /* Add this character to the filter name string. */
240       if (filt_name_index >= filt_name_len) {
241         /* Filter name buffer isn't long enough; double its length. */
242         filt_name_len *= 2;
243         filt_name = g_realloc(filt_name, filt_name_len + 1);
244       }
245       filt_name[filt_name_index] = c;
246       filt_name_index++;
247     }
248
249     if (c == EOF) {
250       if (!ferror(ff)) {
251         /* EOF, not error; no newline seen before EOF */
252         g_warning("'%s' line %d doesn't have a newline.", ff_path,
253                   line);
254       }
255       break;    /* nothing more to read */
256     }
257
258     if (c != '"') {
259       /* No newline seen before end-of-line */
260       g_warning("'%s' line %d doesn't have a closing quote.", ff_path,
261                 line);
262       line++;   /* next line */
263       continue;
264     }
265           
266     /* Skip over separating white space, if any. */
267     while ((c = getc(ff)) != EOF && isspace(c)) {
268       if (c == '\n') {
269         /* No filter expression */
270         g_warning("'%s' line %d doesn't have a filter expression.", ff_path,
271                   line);
272         line++; /* next line */
273         continue;
274       }
275     }
276
277     if (c == EOF) {
278       if (!ferror(ff)) {
279         /* EOF, not error; no newline seen before EOF */
280         g_warning("'%s' line %d doesn't have a newline.", ff_path,
281                   line);
282       }
283       break;    /* nothing more to read */
284     }
285
286     /* "c" is the first non-white-space character; it's the first
287        character of the filter expression. */
288     filt_expr_index = 0;
289     for (;;) {
290       /* Add this character to the filter expression string. */
291       if (filt_expr_index >= filt_expr_len) {
292         /* Filter expressioin buffer isn't long enough; double its length. */
293         filt_expr_len *= 2;
294         filt_expr = g_realloc(filt_expr, filt_expr_len + 1);
295       }
296       filt_expr[filt_expr_index] = c;
297       filt_expr_index++;
298
299       /* Get the next character. */
300       c = getc(ff);
301       if (c == EOF || c == '\n')
302         break;
303     }
304
305     if (c == EOF) {
306       if (!ferror(ff)) {
307         /* EOF, not error; no newline seen before EOF */
308         g_warning("'%s' line %d doesn't have a newline.", ff_path,
309                   line);
310       }
311       break;    /* nothing more to read */
312     }
313
314     /* We saw the ending newline; terminate the filter expression string */
315     if (filt_expr_index >= filt_expr_len) {
316       /* Filter expressioin buffer isn't long enough; double its length. */
317       filt_expr_len *= 2;
318       filt_expr = g_realloc(filt_expr, filt_expr_len + 1);
319     }
320     filt_expr[filt_expr_index] = '\0';
321
322     /* Add the new filter to the list of filters */
323     filt         = (filter_def *) g_malloc(sizeof(filter_def));
324     filt->name   = g_strdup(filt_name);
325     filt->strval = g_strdup(filt_expr);
326     *flp = g_list_append(*flp, filt);
327   }
328   if (ferror(ff)) {
329     *pref_path_return = ff_path;
330     *errno_return = errno;
331   } else
332     g_free(ff_path);
333   fclose(ff);
334   g_free(filt_name);
335   g_free(filt_expr);
336 }
337
338 /*
339  * Get a pointer to a list of filters.
340  */
341 static GList **
342 get_filter_list(filter_list_type_t list)
343 {
344   GList **flp;
345
346   switch (list) {
347
348   case CFILTER_LIST:
349     flp = &capture_filters;
350     break;
351
352   case DFILTER_LIST:
353     flp = &display_filters;
354     break;
355
356   default:
357     g_assert_not_reached();
358     flp = NULL;
359   }
360   return flp;
361 }
362
363 /*
364  * Get a pointer to the first entry in a filter list.
365  */
366 GList *
367 get_filter_list_first(filter_list_type_t list)
368 {
369   GList      **flp;
370   
371   flp = get_filter_list(list);
372   return g_list_first(*flp);
373 }
374
375 /*
376  * Add a new filter to the end of a list.
377  * Returns a pointer to the newly-added entry.
378  */
379 GList *
380 add_to_filter_list(filter_list_type_t list, char *name, char *expression)
381 {
382   GList      **flp;
383   filter_def *filt;
384   
385   flp = get_filter_list(list);
386   filt = (filter_def *) g_malloc(sizeof(filter_def));
387   filt->name = g_strdup(name);
388   filt->strval = g_strdup(expression);
389   *flp = g_list_append(*flp, filt);
390   return g_list_last(*flp);
391 }
392
393 /*
394  * Remove a filter from a list.
395  */
396 void
397 remove_from_filter_list(filter_list_type_t list, GList *fl_entry)
398 {
399   GList      **flp;
400   filter_def *filt;
401   
402   flp = get_filter_list(list);
403   filt = (filter_def *) fl_entry->data;
404   g_free(filt->name);
405   g_free(filt->strval);
406   g_free(filt);
407   *flp = g_list_remove_link(*flp, fl_entry);
408 }
409
410 /*
411  * Write out a list of filters.
412  *
413  * On success, "*pref_path_return" is set to NULL.
414  * On error, "*pref_path_return" is set to point to the pathname of
415  * the file we tried to read - it should be freed by our caller -
416  * and "*errno_return" is set to the error.
417  */
418 void
419 save_filter_list(filter_list_type_t list, char **pref_path_return,
420     int *errno_return)
421 {
422   gchar      *ff_path, *ff_path_new, *ff_dir = PF_DIR, *ff_name;
423   int         path_length;
424   GList      *fl;
425   GList      *flp;
426   filter_def *filt;
427   FILE       *ff;
428   struct stat s_buf;
429   guchar     *p, c;
430   
431   *pref_path_return = NULL;     /* assume no error */
432
433   switch (list) {
434
435   case CFILTER_LIST:
436     ff_name = CFILTER_FILE_NAME;
437     fl = capture_filters;
438     break;
439
440   case DFILTER_LIST:
441     ff_name = DFILTER_FILE_NAME;
442     fl = display_filters;
443     break;
444
445   default:
446     g_assert_not_reached();
447     return;
448   }
449
450   path_length = strlen(get_home_dir()) + strlen(ff_dir) + strlen(ff_name)
451                 + 4 + 4;
452   ff_path = (gchar *) g_malloc(path_length);
453   sprintf(ff_path, "%s/%s", get_home_dir(), ff_dir);
454
455   if (stat(ff_path, &s_buf) != 0)
456 #ifdef WIN32
457     mkdir(ff_path);
458 #else
459     mkdir(ff_path, 0755);
460 #endif
461     
462   sprintf(ff_path, "%s/%s/%s", get_home_dir(), ff_dir, ff_name);
463
464   /* Write to "XXX.new", and rename if that succeeds.
465      That means we don't trash the file if we fail to write it out
466      completely. */
467   ff_path_new = (gchar *) g_malloc(path_length);
468   sprintf(ff_path_new, "%s/%s/%s.new", get_home_dir(), ff_dir, ff_name);
469
470   if ((ff = fopen(ff_path_new, "w")) == NULL) {
471     *pref_path_return = ff_path;
472     *errno_return = errno;
473     g_free(ff_path_new);
474     return;
475   }
476   flp = g_list_first(fl);
477   while (flp) {
478     filt = (filter_def *) flp->data;
479
480     /* Write out the filter name as a quoted string; escape any quotes
481        or backslashes. */
482     putc('"', ff);
483     for (p = (guchar *)filt->name; (c = *p) != '\0'; p++) {
484       if (c == '"' || c == '\\')
485         putc('\\', ff);
486       putc(c, ff);
487     }
488     putc('"', ff);
489
490     /* Separate the filter name and value with a space. */
491     putc(' ', ff);
492
493     /* Write out the filter expression and a newline. */
494     fprintf(ff, "%s\n", filt->strval);
495     if (ferror(ff)) {
496       *pref_path_return = ff_path;
497       *errno_return = errno;
498       fclose(ff);
499       unlink(ff_path_new);
500       g_free(ff_path_new);
501       return;
502     }
503     flp = flp->next;
504   }
505   if (fclose(ff) == EOF) {
506     *pref_path_return = ff_path;
507     *errno_return = errno;
508     unlink(ff_path_new);
509     g_free(ff_path_new);
510     return;
511   }
512
513   /* XXX - does "rename()" exist on Win32?  If so, does it remove the
514      target first?  If so, does that mean it's not atomic? */
515   if (rename(ff_path_new, ff_path) < 0) {
516     *pref_path_return = ff_path;
517     *errno_return = errno;
518     unlink(ff_path_new);
519     g_free(ff_path);
520     g_free(ff_path_new);
521     return;
522   }
523   g_free(ff_path_new);
524   g_free(ff_path);
525 }