QUIC: fix null-ptr dereference in gQUIC version check
[metze/wireshark/wip.git] / ui / profile.c
1 /* profile.c
2  * Dialog box for profiles editing
3  * Stig Bjorlykke <stig@bjorlykke.org>, 2008
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11
12 #include "config.h"
13
14 #include <string.h>
15 #include <errno.h>
16
17 #include <glib.h>
18
19 #include <wsutil/filesystem.h>
20
21 #include "profile.h"
22
23 #include "ui/simple_dialog.h"
24 #include "ui/recent.h"
25
26 #include <wsutil/file_util.h>
27
28 static GList *current_profiles = NULL;
29 static GList *edited_profiles = NULL;
30
31 #define PROF_OPERATION_NEW  1
32 #define PROF_OPERATION_EDIT 2
33
34 GList * current_profile_list(void) {
35     return g_list_first(current_profiles);
36 }
37
38 GList * edited_profile_list(void) {
39     return g_list_first(edited_profiles);
40 }
41
42 static GList *
43 add_profile_entry(GList *fl, const char *profilename, const char *reference, int status,
44         gboolean is_global, gboolean from_global)
45 {
46     profile_def *profile;
47
48     profile = (profile_def *) g_malloc0(sizeof(profile_def));
49     profile->name = g_strdup(profilename);
50     profile->reference = g_strdup(reference);
51     profile->status = status;
52     profile->is_global = is_global;
53     profile->from_global = from_global;
54     return g_list_append(fl, profile);
55 }
56
57 static GList *
58 remove_profile_entry(GList *fl, GList *fl_entry)
59 {
60     profile_def *profile;
61
62     profile = (profile_def *) fl_entry->data;
63     g_free(profile->name);
64     g_free(profile->reference);
65     g_free(profile);
66     return g_list_remove_link(fl, fl_entry);
67 }
68
69 const gchar *
70 get_profile_parent (const gchar *profilename)
71 {
72     GList *fl_entry = g_list_first(edited_profiles);
73     guint no_edited = g_list_length(edited_profiles);
74     profile_def *profile;
75     guint i;
76
77     if (fl_entry) {
78         /* We have edited profiles, find parent */
79         for (i = 0; i < no_edited; i++) {
80             while (fl_entry) {
81                 profile = (profile_def *) fl_entry->data;
82                 if (strcmp (profile->name, profilename) == 0) {
83                     if ((profile->status == PROF_STAT_NEW) ||
84                             (profile->reference == NULL)) {
85                         /* Copy from a new profile */
86                         return NULL;
87                     } else {
88                         /* Found a parent, use this */
89                         profilename = profile->reference;
90                     }
91                 }
92                 fl_entry = g_list_next(fl_entry);
93             }
94             fl_entry = g_list_first(edited_profiles);
95         }
96     }
97
98     return profilename;
99 }
100
101 gchar *apply_profile_changes(void)
102 {
103     char        *pf_dir_path, *pf_dir_path2, *pf_filename;
104     GList       *fl1, *fl2;
105     profile_def *profile1, *profile2;
106     gboolean     found;
107     gchar       *err_msg;
108
109     /* First validate all profile names */
110     fl1 = edited_profile_list();
111     while (fl1) {
112         profile1 = (profile_def *) fl1->data;
113         g_strstrip(profile1->name);
114         if ((err_msg = profile_name_is_valid(profile1->name)) != NULL) {
115             gchar *message = g_strdup_printf("%s\nProfiles unchanged.", err_msg);
116             g_free(err_msg);
117             return message;
118         }
119         fl1 = g_list_next(fl1);
120     }
121
122     /* Write recent file for current profile before copying or renaming */
123     write_profile_recent();
124
125     /* Then do all copy profiles */
126     fl1 = edited_profile_list();
127     while (fl1) {
128         profile1 = (profile_def *) fl1->data;
129         g_strstrip(profile1->name);
130         if (profile1->status == PROF_STAT_COPY) {
131             if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
132                 err_msg = g_strdup_printf("Can't create directory\n\"%s\":\n%s.",
133                         pf_dir_path, g_strerror(errno));
134
135                 g_free(pf_dir_path);
136                 return err_msg;
137             }
138             profile1->status = PROF_STAT_EXISTS;
139
140             if (profile1->reference) {
141                 if (copy_persconffile_profile(profile1->name, profile1->reference, profile1->from_global,
142                             &pf_filename, &pf_dir_path, &pf_dir_path2) == -1) {
143                     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
144                             "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
145                             pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
146
147                     g_free(pf_filename);
148                     g_free(pf_dir_path);
149                     g_free(pf_dir_path2);
150                 }
151             }
152
153             g_free (profile1->reference);
154             profile1->reference = g_strdup(profile1->name);
155         }
156         fl1 = g_list_next(fl1);
157     }
158
159
160     /* Then create new and rename changed */
161     fl1 = edited_profile_list();
162     while (fl1) {
163         profile1 = (profile_def *) fl1->data;
164         g_strstrip(profile1->name);
165         if (profile1->status == PROF_STAT_NEW) {
166             /* We do not create a directory for the default profile */
167             if (strcmp(profile1->name, DEFAULT_PROFILE)!=0) {
168                 if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
169                     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
170                             "Can't create directory\n\"%s\":\n%s.",
171                             pf_dir_path, g_strerror(errno));
172
173                     g_free(pf_dir_path);
174                 }
175                 profile1->status = PROF_STAT_EXISTS;
176
177                 g_free (profile1->reference);
178                 profile1->reference = g_strdup(profile1->name);
179             }
180         } else if (profile1->status == PROF_STAT_CHANGED) {
181             if (strcmp(profile1->reference, profile1->name)!=0) {
182                 /* Rename old profile directory to new */
183                 if (rename_persconffile_profile(profile1->reference, profile1->name,
184                             &pf_dir_path, &pf_dir_path2) == -1) {
185                     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
186                             "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
187                             pf_dir_path, pf_dir_path2, g_strerror(errno));
188
189                     g_free(pf_dir_path);
190                     g_free(pf_dir_path2);
191                 }
192                 profile1->status = PROF_STAT_EXISTS;
193             }
194         }
195         fl1 = g_list_next(fl1);
196     }
197
198     /* Last remove deleted */
199     fl1 = current_profile_list();
200     while (fl1) {
201         found = FALSE;
202         profile1 = (profile_def *) fl1->data;
203         fl2 = edited_profile_list();
204         while (fl2) {
205             profile2 = (profile_def *) fl2->data;
206             if (!profile2->is_global) {
207                 if (strcmp(profile1->name, profile2->name)==0) {
208                     /* Profile exists in both lists */
209                     found = TRUE;
210                 } else if (strcmp(profile1->name, profile2->reference)==0) {
211                     /* Profile has been renamed, update reference to the new name */
212                     g_free (profile2->reference);
213                     profile2->reference = g_strdup(profile2->name);
214                     found = TRUE;
215                 }
216             }
217             fl2 = g_list_next(fl2);
218         }
219         if (!found) {
220             /* Exists in existing list and not in edited, this is a deleted profile */
221             if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
222                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
223                         "Can't delete profile directory\n\"%s\":\n%s.",
224                         pf_dir_path, g_strerror(errno));
225
226                 g_free(pf_dir_path);
227             }
228         }
229         fl1 = g_list_next(fl1);
230     }
231
232     copy_profile_list();
233     return NULL;
234 }
235
236 GList *
237 add_to_profile_list(const char *name, const char *expression, int status,
238         gboolean is_global, gboolean from_global)
239 {
240     edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
241             is_global, from_global);
242
243     return g_list_last(edited_profiles);
244 }
245
246 void
247 remove_from_profile_list(GList *fl_entry)
248 {
249     edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
250 }
251
252 void
253 empty_profile_list(gboolean edit_list)
254 {
255     GList **flpp;
256
257     if (edit_list) {
258         flpp = &edited_profiles;
259
260         while(*flpp) {
261             *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
262         }
263
264         g_assert(g_list_length(*flpp) == 0);
265     }
266
267     flpp = &current_profiles;
268
269     while(*flpp) {
270         *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
271     }
272
273     g_assert(g_list_length(*flpp) == 0);
274 }
275
276 void
277 copy_profile_list(void)
278 {
279     GList      *flp_src;
280     profile_def *profile;
281
282     flp_src = edited_profiles;
283
284     /* throw away the "old" destination list - a NULL list is ok here */
285     empty_profile_list(FALSE);
286
287     /* copy the list entries */
288     while(flp_src) {
289         profile = (profile_def *)(flp_src)->data;
290
291         current_profiles = add_profile_entry(current_profiles, profile->name,
292                 profile->reference, profile->status,
293                 profile->is_global, profile->from_global);
294         flp_src = g_list_next(flp_src);
295     }
296 }
297
298 void
299 init_profile_list(void)
300 {
301     WS_DIR        *dir;             /* scanned directory */
302     WS_DIRENT     *file;            /* current file */
303     const gchar   *name;
304     GList         *local_profiles = NULL;
305     GList         *global_profiles = NULL;
306     GList         *iter;
307     gchar         *profiles_dir, *filename;
308
309     empty_profile_list(TRUE);
310
311     /* Default entry */
312     add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, FALSE, FALSE);
313
314     /* Local (user) profiles */
315     profiles_dir = get_profiles_dir();
316     if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
317         while ((file = ws_dir_read_name(dir)) != NULL) {
318             name = ws_dir_get_name(file);
319             filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
320
321             if (test_for_directory(filename) == EISDIR) {
322                 local_profiles = g_list_prepend(local_profiles, g_strdup(name));
323             }
324             g_free (filename);
325         }
326         ws_dir_close (dir);
327     }
328     g_free(profiles_dir);
329
330     local_profiles = g_list_sort(local_profiles, (GCompareFunc)g_ascii_strcasecmp);
331     for (iter = g_list_first(local_profiles); iter; iter = g_list_next(iter)) {
332         name = (gchar *)iter->data;
333         add_to_profile_list(name, name, PROF_STAT_EXISTS, FALSE, FALSE);
334     }
335     g_list_free_full(local_profiles, g_free);
336
337     /* Global profiles */
338     profiles_dir = get_global_profiles_dir();
339     if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
340         while ((file = ws_dir_read_name(dir)) != NULL) {
341             name = ws_dir_get_name(file);
342             filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
343
344             if (test_for_directory(filename) == EISDIR) {
345                 global_profiles = g_list_prepend(global_profiles, g_strdup(name));
346             }
347             g_free (filename);
348         }
349         ws_dir_close (dir);
350     }
351     g_free(profiles_dir);
352
353     global_profiles = g_list_sort(global_profiles, (GCompareFunc)g_ascii_strcasecmp);
354     for (iter = g_list_first(global_profiles); iter; iter = g_list_next(iter)) {
355         name = (gchar *)iter->data;
356         add_to_profile_list(name, name, PROF_STAT_EXISTS, TRUE, TRUE);
357     }
358     g_list_free_full(global_profiles, g_free);
359
360     /* Make the current list and the edited list equal */
361     copy_profile_list ();
362 }
363
364 gchar *
365 profile_name_is_valid(const gchar *name)
366 {
367     gchar *reason = NULL;
368     gchar *message;
369
370 #ifdef _WIN32
371     char *invalid_dir_char = "\\/:*?\"<>|";
372     gboolean invalid = FALSE;
373     int i;
374
375     for (i = 0; i < 9; i++) {
376         if (strchr(name, invalid_dir_char[i])) {
377             /* Invalid character in directory */
378             invalid = TRUE;
379         }
380     }
381     if (name[0] == '.' || name[strlen(name)-1] == '.') {
382         /* Profile name cannot start or end with period */
383         invalid = TRUE;
384     }
385     if (invalid) {
386         reason = g_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
387                 "   \\ / : * ? \" &lt; &gt; |");
388     }
389 #else
390     if (strchr(name, '/')) {
391         /* Invalid character in directory */
392         reason = g_strdup_printf("contain the '/' character.");
393     }
394 #endif
395
396     if (reason) {
397         message = g_strdup_printf("A profile name cannot %s", reason);
398         g_free(reason);
399         return message;
400     }
401
402     return NULL;
403 }
404
405 gboolean delete_current_profile(void) {
406     const gchar *name = get_profile_name();
407     char        *pf_dir_path;
408
409     if (profile_exists(name, FALSE) && strcmp (name, DEFAULT_PROFILE) != 0) {
410         if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
411             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
412                     "Can't delete profile directory\n\"%s\":\n%s.",
413                     pf_dir_path, g_strerror(errno));
414
415             g_free(pf_dir_path);
416         } else {
417             return TRUE;
418         }
419     }
420     return FALSE;
421 }
422
423 /*
424  * Editor modelines
425  *
426  * Local Variables:
427  * c-basic-offset: 4
428  * tab-width: 8
429  * indent-tabs-mode: nil
430  * End:
431  *
432  * ex: set shiftwidth=4 tabstop=8 expandtab:
433  * :indentSize=4:tabSize=8:noTabs=true:
434  */