LATER... ei_kerberos_kdc_session_key ...
[wireshark-sm.git] / ui / profile.c
blob04025fda97e08899c3c1cc14d879d6f488d81be8
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/filesystem.h>
24 #include "profile.h"
26 #include "ui/simple_dialog.h"
27 #include "ui/recent.h"
29 #include <wsutil/file_util.h>
30 #include <wsutil/ws_assert.h>
32 static GList *current_profiles;
33 static GList *edited_profiles;
35 #define PROF_OPERATION_NEW 1
36 #define PROF_OPERATION_EDIT 2
38 GList * current_profile_list(void) {
39 return g_list_first(current_profiles);
42 GList * edited_profile_list(void) {
43 return g_list_first(edited_profiles);
46 static void load_profile_settings(profile_def *profile);
47 static void save_profile_settings(profile_def *profile);
49 static GList *
50 add_profile_entry(GList *fl, const char *profilename, const char *reference, int status,
51 bool is_global, bool from_global, bool is_import)
53 profile_def *profile;
55 profile = g_new0(profile_def, 1);
56 profile->name = g_strdup(profilename);
57 profile->reference = g_strdup(reference);
58 profile->status = status;
59 profile->is_global = is_global;
60 profile->from_global = from_global;
61 profile->is_import = is_import;
62 return g_list_append(fl, profile);
65 static GList *
66 remove_profile_entry(GList *fl, GList *fl_entry)
68 GList *list;
69 profile_def *profile;
71 profile = (profile_def *) fl_entry->data;
72 g_free(profile->name);
73 g_free(profile->reference);
74 g_free(profile->auto_switch_filter);
75 g_free(profile);
76 list = g_list_remove_link(fl, fl_entry);
77 g_list_free_1(fl_entry);
78 return list;
81 const char *
82 get_profile_parent (const char *profilename)
84 GList *fl_entry = g_list_first(edited_profiles);
85 unsigned no_edited = g_list_length(edited_profiles);
86 profile_def *profile;
87 unsigned i;
89 if (fl_entry) {
90 /* We have edited profiles, find parent */
91 for (i = 0; i < no_edited; i++) {
92 while (fl_entry) {
93 profile = (profile_def *) fl_entry->data;
94 if (strcmp (profile->name, profilename) == 0) {
95 if ((profile->status == PROF_STAT_NEW) ||
96 (profile->reference == NULL)) {
97 /* Copy from a new profile */
98 return NULL;
99 } else {
100 /* Found a parent, use this */
101 profilename = profile->reference;
104 fl_entry = g_list_next(fl_entry);
106 fl_entry = g_list_first(edited_profiles);
110 return profilename;
113 char *apply_profile_changes(void)
115 char *pf_dir_path, *pf_dir_path2, *pf_filename;
116 GList *fl1, *fl2;
117 profile_def *profile1, *profile2;
118 bool found;
119 char *err_msg;
121 /* First validate all profile names */
122 fl1 = edited_profile_list();
123 while (fl1) {
124 profile1 = (profile_def *) fl1->data;
125 g_strstrip(profile1->name);
126 if ((err_msg = profile_name_is_valid(profile1->name)) != NULL) {
127 char *message = ws_strdup_printf("%s\nProfiles unchanged.", err_msg);
128 g_free(err_msg);
129 return message;
131 fl1 = g_list_next(fl1);
134 /* Write recent file for current profile before copying or renaming */
135 write_profile_recent();
137 /* Then do all copy profiles */
138 fl1 = edited_profile_list();
139 while (fl1) {
140 profile1 = (profile_def *) fl1->data;
141 g_strstrip(profile1->name);
142 if (profile1->status == PROF_STAT_COPY) {
143 if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
144 err_msg = ws_strdup_printf("Can't create directory\n\"%s\":\n%s.",
145 pf_dir_path, g_strerror(errno));
147 g_free(pf_dir_path);
148 return err_msg;
150 profile1->status = PROF_STAT_EXISTS;
152 if (profile1->reference) {
153 if (copy_persconffile_profile(profile1->name, profile1->reference, profile1->from_global,
154 &pf_filename, &pf_dir_path, &pf_dir_path2) == -1) {
155 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
156 "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
157 pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
159 g_free(pf_filename);
160 g_free(pf_dir_path);
161 g_free(pf_dir_path2);
165 g_free (profile1->reference);
166 profile1->reference = g_strdup(profile1->name);
168 fl1 = g_list_next(fl1);
172 /* Then create new and rename changed */
173 fl1 = edited_profile_list();
174 while (fl1) {
175 profile1 = (profile_def *) fl1->data;
176 g_strstrip(profile1->name);
177 if (profile1->status == PROF_STAT_NEW) {
178 /* We do not create a directory for the default profile */
179 if (strcmp(profile1->name, DEFAULT_PROFILE)!=0 && ! profile1->is_import) {
180 if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
181 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
182 "Can't create directory\n\"%s\":\n%s.",
183 pf_dir_path, g_strerror(errno));
185 g_free(pf_dir_path);
187 profile1->status = PROF_STAT_EXISTS;
189 g_free (profile1->reference);
190 profile1->reference = g_strdup(profile1->name);
191 /* correctly apply imports as existing profiles */
192 } else if (profile1->is_import) {
193 profile1->status = PROF_STAT_EXISTS;
194 g_free (profile1->reference);
195 profile1->reference = g_strdup(profile1->name);
196 profile1->is_import = false;
198 } else if (profile1->status == PROF_STAT_CHANGED) {
199 if (strcmp(profile1->reference, profile1->name)!=0) {
200 /* Rename old profile directory to new */
201 if (rename_persconffile_profile(profile1->reference, profile1->name,
202 &pf_dir_path, &pf_dir_path2) == -1) {
203 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
204 "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
205 pf_dir_path, pf_dir_path2, g_strerror(errno));
207 g_free(pf_dir_path);
208 g_free(pf_dir_path2);
210 profile1->status = PROF_STAT_EXISTS;
213 fl1 = g_list_next(fl1);
216 /* Last remove deleted */
217 fl1 = current_profile_list();
218 while (fl1) {
219 found = false;
220 profile1 = (profile_def *) fl1->data;
221 fl2 = edited_profile_list();
222 while (fl2) {
223 profile2 = (profile_def *) fl2->data;
224 if (!profile2->is_global) {
225 if (strcmp(profile1->name, profile2->name)==0) {
226 /* Profile exists in both lists */
227 found = true;
228 } else if (strcmp(profile1->name, profile2->reference)==0) {
229 /* Profile has been renamed, update reference to the new name */
230 g_free (profile2->reference);
231 profile2->reference = g_strdup(profile2->name);
232 found = true;
235 fl2 = g_list_next(fl2);
237 if (!found) {
238 /* Exists in existing list and not in edited, this is a deleted profile */
239 if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
240 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
241 "Can't delete profile directory\n\"%s\":\n%s.",
242 pf_dir_path, g_strerror(errno));
244 g_free(pf_dir_path);
247 fl1 = g_list_next(fl1);
250 /* Save our profile settings */
251 for (fl1 = edited_profile_list() ; fl1 ; fl1 = fl1->next) {
252 profile1 = (profile_def *) fl1->data;
253 if (profile1->is_global) {
254 continue;
256 if (profile1->prefs_changed) {
257 save_profile_settings(profile1);
261 copy_profile_list();
262 return NULL;
265 GList *
266 add_to_profile_list(const char *name, const char *expression, int status,
267 bool is_global, bool from_global, bool is_imported)
269 edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
270 is_global, from_global, is_imported);
272 return g_list_last(edited_profiles);
275 void
276 remove_from_profile_list(GList *fl_entry)
278 edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
281 void
282 empty_profile_list(bool edit_list)
284 GList **flpp;
286 if (edit_list) {
287 flpp = &edited_profiles;
289 while(*flpp) {
290 *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
293 ws_assert(g_list_length(*flpp) == 0);
294 if ( ! edited_profiles )
295 edited_profiles = NULL;
298 flpp = &current_profiles;
300 while(*flpp) {
301 *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
304 ws_assert(g_list_length(*flpp) == 0);
305 if ( ! current_profiles )
306 current_profiles = NULL;
309 void
310 copy_profile_list(void)
312 GList *flp_src;
313 profile_def *profile;
315 flp_src = edited_profiles;
317 /* throw away the "old" destination list - a NULL list is ok here */
318 empty_profile_list(false);
320 /* copy the list entries */
321 while(flp_src) {
322 profile = (profile_def *)(flp_src)->data;
324 current_profiles = add_profile_entry(current_profiles, profile->name,
325 profile->reference, profile->status,
326 profile->is_global, profile->from_global, false);
327 if (profile->auto_switch_filter) {
328 profile_def *new_profile = (profile_def *) g_list_last(current_profiles)->data;
329 new_profile->auto_switch_filter = g_strdup(profile->auto_switch_filter);
332 flp_src = g_list_next(flp_src);
336 void
337 init_profile_list(void)
339 WS_DIR *dir; /* scanned directory */
340 WS_DIRENT *file; /* current file */
341 const char *name;
342 GList *local_profiles = NULL;
343 GList *global_profiles = NULL;
344 GList *iter, *item;
345 char *profiles_dir, *filename;
347 empty_profile_list(true);
349 /* Default entry */
350 item = add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, false, false, false);
351 load_profile_settings((profile_def *)item->data);
353 /* Local (user) profiles */
354 profiles_dir = get_profiles_dir();
355 if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
356 while ((file = ws_dir_read_name(dir)) != NULL) {
357 name = ws_dir_get_name(file);
358 filename = ws_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
360 if (test_for_directory(filename) == EISDIR) {
361 local_profiles = g_list_prepend(local_profiles, g_strdup(name));
363 g_free (filename);
365 ws_dir_close (dir);
367 g_free(profiles_dir);
369 local_profiles = g_list_sort(local_profiles, (GCompareFunc)g_ascii_strcasecmp);
370 for (iter = g_list_first(local_profiles); iter; iter = g_list_next(iter)) {
371 name = (char *)iter->data;
372 item = add_to_profile_list(name, name, PROF_STAT_EXISTS, false, false, false);
373 load_profile_settings((profile_def *)item->data);
375 g_list_free_full(local_profiles, g_free);
377 /* Global profiles */
378 profiles_dir = get_global_profiles_dir();
379 if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
380 while ((file = ws_dir_read_name(dir)) != NULL) {
381 name = ws_dir_get_name(file);
382 filename = ws_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
384 if (test_for_directory(filename) == EISDIR) {
385 global_profiles = g_list_prepend(global_profiles, g_strdup(name));
387 g_free (filename);
389 ws_dir_close (dir);
391 g_free(profiles_dir);
393 global_profiles = g_list_sort(global_profiles, (GCompareFunc)g_ascii_strcasecmp);
394 for (iter = g_list_first(global_profiles); iter; iter = g_list_next(iter)) {
395 name = (char *)iter->data;
396 add_to_profile_list(name, name, PROF_STAT_EXISTS, true, true, false);
398 g_list_free_full(global_profiles, g_free);
400 /* Make the current list and the edited list equal */
401 copy_profile_list ();
404 char *
405 profile_name_is_valid(const char *name)
407 char *reason = NULL;
408 char *message;
410 #ifdef _WIN32
411 char *invalid_dir_char = "\\/:*?\"<>|";
412 bool invalid = false;
413 int i;
415 for (i = 0; i < 9; i++) {
416 if (strchr(name, invalid_dir_char[i])) {
417 /* Invalid character in directory */
418 invalid = true;
421 if (name[0] == '.' || name[strlen(name)-1] == '.') {
422 /* Profile name cannot start or end with period */
423 invalid = true;
425 if (invalid) {
426 reason = ws_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
427 " \\ / : * ? \" &lt; &gt; |");
429 #else
430 if (strchr(name, '/')) {
431 /* Invalid character in directory */
432 reason = ws_strdup_printf("contain the '/' character.");
434 #endif
436 if (reason) {
437 message = ws_strdup_printf("A profile name cannot %s", reason);
438 g_free(reason);
439 return message;
442 return NULL;
445 bool delete_current_profile(void) {
446 const char *name = get_profile_name();
447 char *pf_dir_path;
449 if (profile_exists(name, false) && strcmp (name, DEFAULT_PROFILE) != 0) {
450 if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
451 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
452 "Can't delete profile directory\n\"%s\":\n%s.",
453 pf_dir_path, g_strerror(errno));
455 g_free(pf_dir_path);
456 } else {
457 return true;
460 return false;
463 // Use a settings file in case we ever want to include an author, description,
464 // URL, etc.
465 #define PROFILE_SETTINGS_FILENAME "profile_settings"
466 #define AUTO_SWITCH_FILTER_KEY "auto_switch_filter"
468 static char *get_profile_settings_path(const char *profile_name) {
469 char *profile_settings_path;
470 char *profile_dir = get_profile_dir(profile_name, false);
471 profile_settings_path = g_build_filename(profile_dir, PROFILE_SETTINGS_FILENAME, NULL);
472 g_free(profile_dir);
474 return profile_settings_path;
477 /* Set */
478 static prefs_set_pref_e
479 set_profile_setting(char *key, const char *value, void *profile_ptr, bool return_range_errors _U_)
481 profile_def *profile = (profile_def *) profile_ptr;
482 if (strcmp(key, AUTO_SWITCH_FILTER_KEY) == 0) {
483 g_free(profile->auto_switch_filter);
484 profile->auto_switch_filter = g_strdup(value);
487 return PREFS_SET_OK;
490 static void load_profile_settings(profile_def *profile)
492 char *profile_settings_path = get_profile_settings_path(profile->name);
493 FILE *fp;
495 if ((fp = ws_fopen(profile_settings_path, "r")) != NULL) {
496 read_prefs_file(profile_settings_path, fp, set_profile_setting, profile);
497 fclose(fp);
499 g_free(profile_settings_path);
502 void save_profile_settings(profile_def *profile)
504 char *profile_settings_path = get_profile_settings_path(profile->name);
505 FILE *fp;
507 if ((fp = ws_fopen(profile_settings_path, "w")) == NULL) {
508 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
509 "Can't open recent file\n\"%s\": %s.", profile_settings_path,
510 g_strerror(errno));
511 g_free(profile_settings_path);
512 return;
514 g_free(profile_settings_path);
516 fprintf(fp, "# \"%s\" profile settings file for %s " VERSION ". Edit with care.\n",
517 profile->name, get_configuration_namespace());
519 fprintf(fp, "\n# Automatically switch to this profile if this display filter matches.\n");
520 fprintf(fp, AUTO_SWITCH_FILTER_KEY ": %s\n", profile->auto_switch_filter);
522 fclose(fp);