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
19 #include <epan/prefs.h>
20 #include <epan/prefs-int.h>
22 #include <wsutil/application_flavor.h>
23 #include <wsutil/filesystem.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
);
51 add_profile_entry(GList
*fl
, const char *profilename
, const char *reference
, int status
,
52 bool is_global
, bool from_global
, bool is_import
)
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
);
67 remove_profile_entry(GList
*fl
, GList
*fl_entry
)
72 profile
= (profile_def
*) fl_entry
->data
;
73 g_free(profile
->name
);
74 g_free(profile
->reference
);
75 g_free(profile
->auto_switch_filter
);
77 list
= g_list_remove_link(fl
, fl_entry
);
78 g_list_free_1(fl_entry
);
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
);
91 /* We have edited profiles, find parent */
92 for (i
= 0; i
< no_edited
; i
++) {
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 */
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
);
114 char *apply_profile_changes(void)
116 char *pf_dir_path
, *pf_dir_path2
, *pf_filename
;
118 profile_def
*profile1
, *profile2
;
122 /* First validate all profile names */
123 fl1
= edited_profile_list();
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
);
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();
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
));
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
));
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();
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
));
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
));
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();
221 profile1
= (profile_def
*) fl1
->data
;
222 fl2
= edited_profile_list();
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 */
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
);
236 fl2
= g_list_next(fl2
);
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
));
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
) {
257 if (profile1
->prefs_changed
) {
258 save_profile_settings(profile1
);
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
);
277 remove_from_profile_list(GList
*fl_entry
)
279 edited_profiles
= remove_profile_entry(edited_profiles
, fl_entry
);
283 empty_profile_list(bool edit_list
)
288 flpp
= &edited_profiles
;
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
= ¤t_profiles
;
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
;
311 copy_profile_list(void)
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 */
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
);
338 init_profile_list(void)
340 WS_DIR
*dir
; /* scanned directory */
341 WS_DIRENT
*file
; /* current file */
343 GList
*local_profiles
= NULL
;
344 GList
*global_profiles
= NULL
;
346 char *profiles_dir
, *filename
;
348 empty_profile_list(true);
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
));
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
));
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 ();
406 profile_name_is_valid(const char *name
)
412 char *invalid_dir_char
= "\\/:*?\"<>|";
413 bool invalid
= false;
416 for (i
= 0; i
< 9; i
++) {
417 if (strchr(name
, invalid_dir_char
[i
])) {
418 /* Invalid character in directory */
422 if (name
[0] == '.' || name
[strlen(name
)-1] == '.') {
423 /* Profile name cannot start or end with period */
427 reason
= ws_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
428 " \\ / : * ? \" < > |");
431 if (strchr(name
, '/')) {
432 /* Invalid character in directory */
433 reason
= ws_strdup_printf("contain the '/' character.");
438 message
= ws_strdup_printf("A profile name cannot %s", reason
);
446 bool delete_current_profile(void) {
447 const char *name
= get_profile_name();
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
));
464 // Use a settings file in case we ever want to include an author, description,
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
);
475 return profile_settings_path
;
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
);
491 static void load_profile_settings(profile_def
*profile
)
493 char *profile_settings_path
= get_profile_settings_path(profile
->name
);
496 if ((fp
= ws_fopen(profile_settings_path
, "r")) != NULL
) {
497 read_prefs_file(profile_settings_path
, fp
, set_profile_setting
, profile
);
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
);
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
,
512 g_free(profile_settings_path
);
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
);