Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / ui / profile.c
blobc3c53f6fbfa6b6ee74efcb4ffa1f9c15f3be0cc1
1 /* profile.c
2 * Dialog box for profiles editing
3 * Stig Bjorlykke <stig@bjorlykke.org>, 2008
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "config.h"
14 #include <string.h>
15 #include <errno.h>
17 #include <glib.h>
19 #include <epan/prefs.h>
20 #include <epan/prefs-int.h>
22 #include <wsutil/application_flavor.h>
23 #include <wsutil/filesystem.h>
25 #include "profile.h"
27 #include "ui/simple_dialog.h"
28 #include "ui/recent.h"
30 #include <wsutil/file_util.h>
31 #include <wsutil/ws_assert.h>
33 static GList *current_profiles;
34 static GList *edited_profiles;
36 #define PROF_OPERATION_NEW 1
37 #define PROF_OPERATION_EDIT 2
39 GList * current_profile_list(void) {
40 return g_list_first(current_profiles);
43 GList * edited_profile_list(void) {
44 return g_list_first(edited_profiles);
47 static void load_profile_settings(profile_def *profile);
48 static void save_profile_settings(profile_def *profile);
50 static GList *
51 add_profile_entry(GList *fl, const char *profilename, const char *reference, int status,
52 bool is_global, bool from_global, bool is_import)
54 profile_def *profile;
56 profile = g_new0(profile_def, 1);
57 profile->name = g_strdup(profilename);
58 profile->reference = g_strdup(reference);
59 profile->status = status;
60 profile->is_global = is_global;
61 profile->from_global = from_global;
62 profile->is_import = is_import;
63 return g_list_append(fl, profile);
66 static GList *
67 remove_profile_entry(GList *fl, GList *fl_entry)
69 GList *list;
70 profile_def *profile;
72 profile = (profile_def *) fl_entry->data;
73 g_free(profile->name);
74 g_free(profile->reference);
75 g_free(profile->auto_switch_filter);
76 g_free(profile);
77 list = g_list_remove_link(fl, fl_entry);
78 g_list_free_1(fl_entry);
79 return list;
82 const char *
83 get_profile_parent (const char *profilename)
85 GList *fl_entry = g_list_first(edited_profiles);
86 unsigned no_edited = g_list_length(edited_profiles);
87 profile_def *profile;
88 unsigned i;
90 if (fl_entry) {
91 /* We have edited profiles, find parent */
92 for (i = 0; i < no_edited; i++) {
93 while (fl_entry) {
94 profile = (profile_def *) fl_entry->data;
95 if (strcmp (profile->name, profilename) == 0) {
96 if ((profile->status == PROF_STAT_NEW) ||
97 (profile->reference == NULL)) {
98 /* Copy from a new profile */
99 return NULL;
100 } else {
101 /* Found a parent, use this */
102 profilename = profile->reference;
105 fl_entry = g_list_next(fl_entry);
107 fl_entry = g_list_first(edited_profiles);
111 return profilename;
114 char *apply_profile_changes(void)
116 char *pf_dir_path, *pf_dir_path2, *pf_filename;
117 GList *fl1, *fl2;
118 profile_def *profile1, *profile2;
119 bool found;
120 char *err_msg;
122 /* First validate all profile names */
123 fl1 = edited_profile_list();
124 while (fl1) {
125 profile1 = (profile_def *) fl1->data;
126 g_strstrip(profile1->name);
127 if ((err_msg = profile_name_is_valid(profile1->name)) != NULL) {
128 char *message = ws_strdup_printf("%s\nProfiles unchanged.", err_msg);
129 g_free(err_msg);
130 return message;
132 fl1 = g_list_next(fl1);
135 /* Write recent file for current profile before copying or renaming */
136 write_profile_recent();
138 /* Then do all copy profiles */
139 fl1 = edited_profile_list();
140 while (fl1) {
141 profile1 = (profile_def *) fl1->data;
142 g_strstrip(profile1->name);
143 if (profile1->status == PROF_STAT_COPY) {
144 if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
145 err_msg = ws_strdup_printf("Can't create directory\n\"%s\":\n%s.",
146 pf_dir_path, g_strerror(errno));
148 g_free(pf_dir_path);
149 return err_msg;
151 profile1->status = PROF_STAT_EXISTS;
153 if (profile1->reference) {
154 if (copy_persconffile_profile(profile1->name, profile1->reference, profile1->from_global,
155 &pf_filename, &pf_dir_path, &pf_dir_path2) == -1) {
156 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
157 "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
158 pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
160 g_free(pf_filename);
161 g_free(pf_dir_path);
162 g_free(pf_dir_path2);
166 g_free (profile1->reference);
167 profile1->reference = g_strdup(profile1->name);
169 fl1 = g_list_next(fl1);
173 /* Then create new and rename changed */
174 fl1 = edited_profile_list();
175 while (fl1) {
176 profile1 = (profile_def *) fl1->data;
177 g_strstrip(profile1->name);
178 if (profile1->status == PROF_STAT_NEW) {
179 /* We do not create a directory for the default profile */
180 if (strcmp(profile1->name, DEFAULT_PROFILE)!=0 && ! profile1->is_import) {
181 if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
182 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
183 "Can't create directory\n\"%s\":\n%s.",
184 pf_dir_path, g_strerror(errno));
186 g_free(pf_dir_path);
188 profile1->status = PROF_STAT_EXISTS;
190 g_free (profile1->reference);
191 profile1->reference = g_strdup(profile1->name);
192 /* correctly apply imports as existing profiles */
193 } else if (profile1->is_import) {
194 profile1->status = PROF_STAT_EXISTS;
195 g_free (profile1->reference);
196 profile1->reference = g_strdup(profile1->name);
197 profile1->is_import = false;
199 } else if (profile1->status == PROF_STAT_CHANGED) {
200 if (strcmp(profile1->reference, profile1->name)!=0) {
201 /* Rename old profile directory to new */
202 if (rename_persconffile_profile(profile1->reference, profile1->name,
203 &pf_dir_path, &pf_dir_path2) == -1) {
204 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
205 "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
206 pf_dir_path, pf_dir_path2, g_strerror(errno));
208 g_free(pf_dir_path);
209 g_free(pf_dir_path2);
211 profile1->status = PROF_STAT_EXISTS;
214 fl1 = g_list_next(fl1);
217 /* Last remove deleted */
218 fl1 = current_profile_list();
219 while (fl1) {
220 found = false;
221 profile1 = (profile_def *) fl1->data;
222 fl2 = edited_profile_list();
223 while (fl2) {
224 profile2 = (profile_def *) fl2->data;
225 if (!profile2->is_global) {
226 if (strcmp(profile1->name, profile2->name)==0) {
227 /* Profile exists in both lists */
228 found = true;
229 } else if (strcmp(profile1->name, profile2->reference)==0) {
230 /* Profile has been renamed, update reference to the new name */
231 g_free (profile2->reference);
232 profile2->reference = g_strdup(profile2->name);
233 found = true;
236 fl2 = g_list_next(fl2);
238 if (!found) {
239 /* Exists in existing list and not in edited, this is a deleted profile */
240 if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
241 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
242 "Can't delete profile directory\n\"%s\":\n%s.",
243 pf_dir_path, g_strerror(errno));
245 g_free(pf_dir_path);
248 fl1 = g_list_next(fl1);
251 /* Save our profile settings */
252 for (fl1 = edited_profile_list() ; fl1 ; fl1 = fl1->next) {
253 profile1 = (profile_def *) fl1->data;
254 if (profile1->is_global) {
255 continue;
257 if (profile1->prefs_changed) {
258 save_profile_settings(profile1);
262 copy_profile_list();
263 return NULL;
266 GList *
267 add_to_profile_list(const char *name, const char *expression, int status,
268 bool is_global, bool from_global, bool is_imported)
270 edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
271 is_global, from_global, is_imported);
273 return g_list_last(edited_profiles);
276 void
277 remove_from_profile_list(GList *fl_entry)
279 edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
282 void
283 empty_profile_list(bool edit_list)
285 GList **flpp;
287 if (edit_list) {
288 flpp = &edited_profiles;
290 while(*flpp) {
291 *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
294 ws_assert(g_list_length(*flpp) == 0);
295 if ( ! edited_profiles )
296 edited_profiles = NULL;
299 flpp = &current_profiles;
301 while(*flpp) {
302 *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
305 ws_assert(g_list_length(*flpp) == 0);
306 if ( ! current_profiles )
307 current_profiles = NULL;
310 void
311 copy_profile_list(void)
313 GList *flp_src;
314 profile_def *profile;
316 flp_src = edited_profiles;
318 /* throw away the "old" destination list - a NULL list is ok here */
319 empty_profile_list(false);
321 /* copy the list entries */
322 while(flp_src) {
323 profile = (profile_def *)(flp_src)->data;
325 current_profiles = add_profile_entry(current_profiles, profile->name,
326 profile->reference, profile->status,
327 profile->is_global, profile->from_global, false);
328 if (profile->auto_switch_filter) {
329 profile_def *new_profile = (profile_def *) g_list_last(current_profiles)->data;
330 new_profile->auto_switch_filter = g_strdup(profile->auto_switch_filter);
333 flp_src = g_list_next(flp_src);
337 void
338 init_profile_list(void)
340 WS_DIR *dir; /* scanned directory */
341 WS_DIRENT *file; /* current file */
342 const char *name;
343 GList *local_profiles = NULL;
344 GList *global_profiles = NULL;
345 GList *iter, *item;
346 char *profiles_dir, *filename;
348 empty_profile_list(true);
350 /* Default entry */
351 item = add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, false, false, false);
352 load_profile_settings((profile_def *)item->data);
354 /* Local (user) profiles */
355 profiles_dir = get_profiles_dir();
356 if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
357 while ((file = ws_dir_read_name(dir)) != NULL) {
358 name = ws_dir_get_name(file);
359 filename = ws_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
361 if (test_for_directory(filename) == EISDIR) {
362 local_profiles = g_list_prepend(local_profiles, g_strdup(name));
364 g_free (filename);
366 ws_dir_close (dir);
368 g_free(profiles_dir);
370 local_profiles = g_list_sort(local_profiles, (GCompareFunc)g_ascii_strcasecmp);
371 for (iter = g_list_first(local_profiles); iter; iter = g_list_next(iter)) {
372 name = (char *)iter->data;
373 item = add_to_profile_list(name, name, PROF_STAT_EXISTS, false, false, false);
374 load_profile_settings((profile_def *)item->data);
376 g_list_free_full(local_profiles, g_free);
378 /* Global profiles */
379 profiles_dir = get_global_profiles_dir();
380 if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
381 while ((file = ws_dir_read_name(dir)) != NULL) {
382 name = ws_dir_get_name(file);
383 filename = ws_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
385 if (test_for_directory(filename) == EISDIR) {
386 global_profiles = g_list_prepend(global_profiles, g_strdup(name));
388 g_free (filename);
390 ws_dir_close (dir);
392 g_free(profiles_dir);
394 global_profiles = g_list_sort(global_profiles, (GCompareFunc)g_ascii_strcasecmp);
395 for (iter = g_list_first(global_profiles); iter; iter = g_list_next(iter)) {
396 name = (char *)iter->data;
397 add_to_profile_list(name, name, PROF_STAT_EXISTS, true, true, false);
399 g_list_free_full(global_profiles, g_free);
401 /* Make the current list and the edited list equal */
402 copy_profile_list ();
405 char *
406 profile_name_is_valid(const char *name)
408 char *reason = NULL;
409 char *message;
411 #ifdef _WIN32
412 char *invalid_dir_char = "\\/:*?\"<>|";
413 bool invalid = false;
414 int i;
416 for (i = 0; i < 9; i++) {
417 if (strchr(name, invalid_dir_char[i])) {
418 /* Invalid character in directory */
419 invalid = true;
422 if (name[0] == '.' || name[strlen(name)-1] == '.') {
423 /* Profile name cannot start or end with period */
424 invalid = true;
426 if (invalid) {
427 reason = ws_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
428 " \\ / : * ? \" &lt; &gt; |");
430 #else
431 if (strchr(name, '/')) {
432 /* Invalid character in directory */
433 reason = ws_strdup_printf("contain the '/' character.");
435 #endif
437 if (reason) {
438 message = ws_strdup_printf("A profile name cannot %s", reason);
439 g_free(reason);
440 return message;
443 return NULL;
446 bool delete_current_profile(void) {
447 const char *name = get_profile_name();
448 char *pf_dir_path;
450 if (profile_exists(name, false) && strcmp (name, DEFAULT_PROFILE) != 0) {
451 if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
452 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
453 "Can't delete profile directory\n\"%s\":\n%s.",
454 pf_dir_path, g_strerror(errno));
456 g_free(pf_dir_path);
457 } else {
458 return true;
461 return false;
464 // Use a settings file in case we ever want to include an author, description,
465 // URL, etc.
466 #define PROFILE_SETTINGS_FILENAME "profile_settings"
467 #define AUTO_SWITCH_FILTER_KEY "auto_switch_filter"
469 static char *get_profile_settings_path(const char *profile_name) {
470 char *profile_settings_path;
471 char *profile_dir = get_profile_dir(profile_name, false);
472 profile_settings_path = g_build_filename(profile_dir, PROFILE_SETTINGS_FILENAME, NULL);
473 g_free(profile_dir);
475 return profile_settings_path;
478 /* Set */
479 static prefs_set_pref_e
480 set_profile_setting(char *key, const char *value, void *profile_ptr, bool return_range_errors _U_)
482 profile_def *profile = (profile_def *) profile_ptr;
483 if (strcmp(key, AUTO_SWITCH_FILTER_KEY) == 0) {
484 g_free(profile->auto_switch_filter);
485 profile->auto_switch_filter = g_strdup(value);
488 return PREFS_SET_OK;
491 static void load_profile_settings(profile_def *profile)
493 char *profile_settings_path = get_profile_settings_path(profile->name);
494 FILE *fp;
496 if ((fp = ws_fopen(profile_settings_path, "r")) != NULL) {
497 read_prefs_file(profile_settings_path, fp, set_profile_setting, profile);
498 fclose(fp);
500 g_free(profile_settings_path);
503 void save_profile_settings(profile_def *profile)
505 char *profile_settings_path = get_profile_settings_path(profile->name);
506 FILE *fp;
508 if ((fp = ws_fopen(profile_settings_path, "w")) == NULL) {
509 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
510 "Can't open recent file\n\"%s\": %s.", profile_settings_path,
511 g_strerror(errno));
512 g_free(profile_settings_path);
513 return;
515 g_free(profile_settings_path);
517 fprintf(fp, "# \"%s\" profile settings file for %s " VERSION ". Edit with care.\n",
518 profile->name, application_flavor_name_proper());
520 fprintf(fp, "\n# Automatically switch to this profile if this display filter matches.\n");
521 fprintf(fp, AUTO_SWITCH_FILTER_KEY ": %s\n", profile->auto_switch_filter);
523 fclose(fp);