From Olivier Jacques: Add #defines to disable XML validation.
[obnox/wireshark/wip.git] / color_filters.c
1 /* color_filters.c
2  * Routines for color filters
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24 /*
25  * Updated 1 Dec 10 jjm
26  */
27
28 /* With MSVC and a libethereal.dll this file needs to import some variables 
29    in a special way. Therefore _NEED_VAR_IMPORT_ is defined. */  
30 #define _NEED_VAR_IMPORT_ 
31  
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <glib.h>
37 #include <ctype.h>
38 #include <string.h>
39
40 #include <epan/filesystem.h>
41
42 #include <epan/packet.h>
43 #include "color.h"
44 #include "color_filters.h"
45 #include "file.h"
46 #include <epan/dfilter/dfilter.h>
47 #include "simple_dialog.h"
48
49 static gboolean read_filters(void);
50 static gboolean read_global_filters(void);
51
52 /* Variables and routines defined in color.h */
53
54 GSList *filter_list = NULL;
55 GSList *removed_filter_list = NULL;
56
57 /* Remove the specified filter from the list of existing color filters,
58  * and add it to the list of removed color filters.
59  * This way, unmarking and marking a packet which matches a now removed
60  * color filter will still be colored correctly as the color filter is
61  * still reachable. */
62 void remove_color_filter(color_filter_t *colorf)
63 {
64         /* Remove colorf from the list of color filters */
65         filter_list = g_slist_remove(filter_list, colorf);
66         /* Add colorf to the list of removed color filters */
67         removed_filter_list = g_slist_prepend(removed_filter_list, colorf);
68 }
69
70 /* delete the specified filter */
71 static void
72 delete_color_filter(color_filter_t *colorf)
73 {
74         if (colorf->filter_name != NULL)
75                 g_free(colorf->filter_name);
76         if (colorf->filter_text != NULL)
77                 g_free(colorf->filter_text);
78         if (colorf->c_colorfilter != NULL)
79                 dfilter_free(colorf->c_colorfilter);
80         g_free(colorf);
81 }
82
83 /* delete the specified filter as an iterator */
84 static void
85 delete_color_filter_it(gpointer filter_arg, gpointer unused _U_)
86 {
87         color_filter_t *colorf = filter_arg;
88         delete_color_filter(colorf);
89 }
90
91 /* delete all the filters */
92 static void
93 delete_all_color_filters (void)
94 {
95         g_slist_foreach(filter_list, delete_color_filter_it, NULL);
96         g_slist_free(filter_list);
97         filter_list = NULL;
98         g_slist_foreach(removed_filter_list, delete_color_filter_it, NULL);
99         g_slist_free(removed_filter_list);
100         removed_filter_list = NULL;
101 }
102
103 /* Initialize the filter structures (reading from file) for general running, including app startup */
104 void
105 colfilter_init(void)
106 {
107         delete_all_color_filters();
108         if (!read_filters())
109                 read_global_filters();
110 }
111
112 /* Create a new filter */
113 color_filter_t *
114 new_color_filter(gchar *name,          /* The name of the filter to create */
115                  gchar *filter_string, /* The string representing the filter */
116                  color_t *bg_color,    /* The background color */
117                  color_t *fg_color)    /* The foreground color */
118 {
119         color_filter_t *colorf;
120
121         colorf = g_malloc(sizeof (color_filter_t));
122         colorf->filter_name = g_strdup(name);
123         colorf->filter_text = g_strdup(filter_string);
124         colorf->bg_color = *bg_color;
125         colorf->fg_color = *fg_color;
126         colorf->c_colorfilter = NULL;
127         colorf->edit_dialog = NULL;
128         colorf->marked = FALSE;
129         filter_list = g_slist_append(filter_list, colorf);
130         return colorf;
131 }
132
133 static void
134 prime_edt(gpointer data, gpointer user_data)
135 {
136         color_filter_t  *colorf = data;
137         epan_dissect_t   *edt = user_data;
138
139         if (colorf->c_colorfilter != NULL)
140                 epan_dissect_prime_dfilter(edt, colorf->c_colorfilter);
141 }
142
143 /* Prime the epan_dissect_t with all the compiler
144  * color filters in 'filter_list'. */
145 void
146 filter_list_prime_edt(epan_dissect_t *edt)
147 {
148         g_slist_foreach(filter_list, prime_edt, edt);
149 }
150
151 /* read filters from the given file */
152
153 /* XXX - Would it make more sense to use GStrings here instead of reallocing
154    our buffers? */
155 static gboolean
156 read_filters_file(FILE *f, gpointer arg)
157 {
158 #define INIT_BUF_SIZE 128
159         gchar  *name = NULL;
160         gchar  *filter_exp = NULL;
161         guint32 name_len = INIT_BUF_SIZE;
162         guint32 filter_exp_len = INIT_BUF_SIZE;
163         guint32 i = 0;
164         gint32  c;
165         guint16 fg_r, fg_g, fg_b, bg_r, bg_g, bg_b;
166         gboolean skip_end_of_line = FALSE;
167
168         name = g_malloc(name_len + 1);
169         filter_exp = g_malloc(filter_exp_len + 1);
170
171         while (1) {
172
173                 if (skip_end_of_line) {
174                         do {
175                                 c = getc(f);
176                         } while (c != EOF && c != '\n');
177                         if (c == EOF)
178                                 break;
179                         skip_end_of_line = FALSE;
180                 }
181
182                 while ((c = getc(f)) != EOF && isspace(c)) {
183                         if (c == '\n') {
184                                 continue;
185                         }
186                 }
187
188                 if (c == EOF)
189                         break;
190
191                 /* skip # comments and invalid lines */
192                 if (c != '@') { 
193                         skip_end_of_line = TRUE;
194                         continue;
195                 }
196
197                 /* we get the @ delimiter.
198                  * Format is:
199                  * @name@filter expression@[background r,g,b][foreground r,g,b]
200                  */
201
202                 /* retrieve name */
203                 i = 0;
204                 while (1) {
205                         c = getc(f);
206                         if (c == EOF || c == '@')
207                                 break;
208                         if (i >= name_len) {
209                                 /* buffer isn't long enough; double its length.*/
210                                 name_len *= 2;
211                                 name = g_realloc(name, name_len + 1);
212                         }
213                         name[i++] = c;            
214                 }
215                 name[i] = '\0';
216
217                 if (c == EOF) {
218                         break;
219                 } else if (i == 0) {
220                         skip_end_of_line = TRUE;
221                         continue;
222                 }
223
224                 /* retrieve filter expression */
225                 i = 0;
226                 while (1) {
227                         c = getc(f);
228                         if (c == EOF || c == '@')
229                                 break;
230                         if (i >= filter_exp_len) {
231                                 /* buffer isn't long enough; double its length.*/
232                                 filter_exp_len *= 2;
233                                 filter_exp = g_realloc(filter_exp, filter_exp_len + 1);
234                         }
235                         filter_exp[i++] = c;
236                 }
237                 filter_exp[i] = '\0';
238
239                 if (c == EOF) {
240                         break;
241                 } else if (i == 0) {
242                         skip_end_of_line = TRUE;
243                         continue;
244                 }
245
246                 /* retrieve background and foreground colors */
247                 if (fscanf(f,"[%hu,%hu,%hu][%hu,%hu,%hu]",
248                         &bg_r, &bg_g, &bg_b, &fg_r, &fg_g, &fg_b) == 6) {
249
250                         /* we got a complete color filter */
251
252                         color_t bg_color, fg_color;
253                         color_filter_t *colorf;
254                         dfilter_t *temp_dfilter;
255
256                         if (!dfilter_compile(filter_exp, &temp_dfilter)) {
257                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
258                                 "Could not compile color filter %s from saved filters.\n%s",
259                                               name, dfilter_error_msg);
260                                 skip_end_of_line = TRUE;
261                                 continue;
262                         }
263
264                         if (!initialize_color(&fg_color, fg_r, fg_g, fg_b)) {
265                                 /* oops */
266                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
267                                     "Could not allocate foreground color "
268                                     "specified in input file for %s.", name);
269                                 dfilter_free(temp_dfilter);
270                                 skip_end_of_line = TRUE;
271                                 continue;
272                         }
273                         if (!initialize_color(&bg_color, bg_r, bg_g, bg_b)) {
274                                 /* oops */
275                                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
276                                     "Could not allocate background color "
277                                     "specified in input file for %s.", name);
278                                 dfilter_free(temp_dfilter);
279                                 skip_end_of_line = TRUE;
280                                 continue;
281                         }
282
283                         colorf = new_color_filter(name, filter_exp, &bg_color,
284                             &fg_color);
285                         colorf->c_colorfilter = temp_dfilter;
286
287                         if (arg != NULL)
288                                 color_add_filter_cb (colorf, arg);
289                 }    /* if sscanf */
290
291                 skip_end_of_line = TRUE;
292         }
293
294         g_free(name);
295         g_free(filter_exp);
296         return TRUE;
297 }
298
299 /* read filters from the user's filter file */
300 static gboolean
301 read_filters(void)
302 {
303         gchar *path;
304         FILE *f;
305         gboolean ret;
306
307         /* decide what file to open (from dfilter code) */
308         path = get_persconffile_path("colorfilters", FALSE);
309         if ((f = fopen(path, "r")) == NULL) {
310                 if (errno != ENOENT) {
311                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
312                             "Could not open filter file\n\"%s\": %s.", path,
313                             strerror(errno));
314                 }
315                 g_free(path);
316                 return FALSE;
317         }
318         g_free(path);
319         path = NULL;
320
321         ret = read_filters_file(f, NULL);
322         fclose(f);
323         return ret;
324 }
325
326 /* read filters from the filter file */
327 static gboolean
328 read_global_filters(void)
329 {
330         gchar *path;
331         FILE *f;
332         gboolean ret;
333
334         /* decide what file to open (from dfilter code) */
335         path = get_datafile_path("colorfilters");
336         if ((f = fopen(path, "r")) == NULL) {
337                 if (errno != ENOENT) {
338                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
339                             "Could not open global filter file\n\"%s\": %s.", path,
340                             strerror(errno));
341                 }
342                 g_free(path);
343                 return FALSE;
344         }
345         g_free(path);
346         path = NULL;
347
348         ret = read_filters_file(f, NULL);
349         fclose(f);
350         return ret;
351 }
352
353 /* read filters from some other filter file (import) */
354 gboolean
355 read_other_filters(gchar *path, gpointer arg)
356 {
357         FILE *f;
358         gboolean ret;
359
360         if ((f = fopen(path, "r")) == NULL) {
361                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
362                     "Could not open\n%s\nfor reading: %s.",
363                     path, strerror(errno));
364                 return FALSE;
365         }
366
367         ret = read_filters_file(f, arg);
368         fclose(f);
369         return ret;
370 }
371
372 struct write_filter_data
373 {
374   FILE * f;
375   gboolean only_marked;
376 };
377
378 /* save a single filter */
379 static void
380 write_filter(gpointer filter_arg, gpointer data_arg)
381 {
382         struct write_filter_data *data = data_arg;
383         color_filter_t *colorf = filter_arg;
384         FILE *f = data->f;
385
386         if (colorf->marked || !data->only_marked) {
387                 fprintf(f,"@%s@%s@[%d,%d,%d][%d,%d,%d]\n",
388                     colorf->filter_name,
389                     colorf->filter_text,
390                     colorf->bg_color.red,
391                     colorf->bg_color.green,
392                     colorf->bg_color.blue,
393                     colorf->fg_color.red,
394                     colorf->fg_color.green,
395                     colorf->fg_color.blue);
396         }
397 }
398
399 /* save filters in a filter file */
400 gboolean
401 write_filters_file(FILE *f, gboolean only_marked)
402 {
403         struct write_filter_data data;
404
405         data.f = f;
406         data.only_marked = only_marked;
407   
408         fprintf(f,"# DO NOT EDIT THIS FILE!  It was created by Ethereal\n");
409         g_slist_foreach(filter_list, write_filter, &data);
410         return TRUE;
411 }
412
413 /* save filters in users filter file */
414 gboolean
415 write_filters(void)
416 {
417         gchar *pf_dir_path;
418         const gchar *path;
419         FILE *f;
420
421         /* Create the directory that holds personal configuration files,
422            if necessary.  */
423         if (create_persconffile_dir(&pf_dir_path) == -1) {
424                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
425                     "Can't create directory\n\"%s\"\nfor color files: %s.",
426                     pf_dir_path, strerror(errno));
427                 g_free(pf_dir_path);
428                 return FALSE;
429         }
430
431         path = get_persconffile_path("colorfilters", TRUE);
432         if ((f = fopen(path, "w+")) == NULL) {
433                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
434                     "Could not open\n%s\nfor writing: %s.",
435                     path, strerror(errno));
436                 return FALSE;
437         }
438         write_filters_file(f, FALSE);
439         fclose(f);
440         return TRUE;
441 }
442
443 /* delete users filter file and reload global filters */
444 gboolean
445 revert_filters(void)
446 {
447         gchar *pf_dir_path;
448         const gchar *path;
449
450         /* Create the directory that holds personal configuration files,
451            if necessary.  */
452         if (create_persconffile_dir(&pf_dir_path) == -1) {
453                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
454                     "Can't create directory\n\"%s\"\nfor color files: %s.",
455                     pf_dir_path, strerror(errno));
456                 g_free(pf_dir_path);
457                 return FALSE;
458         }
459
460         path = get_persconffile_path("colorfilters", TRUE);
461         if (!deletefile(path)) {
462                 g_free((gchar *)path);
463                 return FALSE;
464         }
465
466         g_free((gchar *)path);
467
468         /* Reload the (global) filters - Note: this does not update the dialog. */
469         colfilter_init();
470         return TRUE;
471 }
472
473
474 /* save filters in some other filter file (export) */
475 gboolean
476 write_other_filters(gchar *path, gboolean only_marked)
477 {
478         FILE *f;
479
480         if ((f = fopen(path, "w+")) == NULL) {
481                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
482                     "Could not open\n%s\nfor writing: %s.",
483                     path, strerror(errno));
484                 return FALSE;
485         }
486         write_filters_file(f, only_marked);
487         fclose(f);
488         return TRUE;
489 }