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/filesystem.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
);
50 add_profile_entry(GList
*fl
, const char *profilename
, const char *reference
, int status
,
51 bool is_global
, bool from_global
, bool is_import
)
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
);
66 remove_profile_entry(GList
*fl
, GList
*fl_entry
)
71 profile
= (profile_def
*) fl_entry
->data
;
72 g_free(profile
->name
);
73 g_free(profile
->reference
);
74 g_free(profile
->auto_switch_filter
);
76 list
= g_list_remove_link(fl
, fl_entry
);
77 g_list_free_1(fl_entry
);
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
);
90 /* We have edited profiles, find parent */
91 for (i
= 0; i
< no_edited
; i
++) {
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 */
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
);
113 char *apply_profile_changes(void)
115 char *pf_dir_path
, *pf_dir_path2
, *pf_filename
;
117 profile_def
*profile1
, *profile2
;
121 /* First validate all profile names */
122 fl1
= edited_profile_list();
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
);
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();
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
));
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
));
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();
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
));
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
));
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();
220 profile1
= (profile_def
*) fl1
->data
;
221 fl2
= edited_profile_list();
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 */
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
);
235 fl2
= g_list_next(fl2
);
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
));
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
) {
256 if (profile1
->prefs_changed
) {
257 save_profile_settings(profile1
);
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
);
276 remove_from_profile_list(GList
*fl_entry
)
278 edited_profiles
= remove_profile_entry(edited_profiles
, fl_entry
);
282 empty_profile_list(bool edit_list
)
287 flpp
= &edited_profiles
;
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
= ¤t_profiles
;
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
;
310 copy_profile_list(void)
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 */
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
);
337 init_profile_list(void)
339 WS_DIR
*dir
; /* scanned directory */
340 WS_DIRENT
*file
; /* current file */
342 GList
*local_profiles
= NULL
;
343 GList
*global_profiles
= NULL
;
345 char *profiles_dir
, *filename
;
347 empty_profile_list(true);
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
));
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
));
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 ();
405 profile_name_is_valid(const char *name
)
411 char *invalid_dir_char
= "\\/:*?\"<>|";
412 bool invalid
= false;
415 for (i
= 0; i
< 9; i
++) {
416 if (strchr(name
, invalid_dir_char
[i
])) {
417 /* Invalid character in directory */
421 if (name
[0] == '.' || name
[strlen(name
)-1] == '.') {
422 /* Profile name cannot start or end with period */
426 reason
= ws_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
427 " \\ / : * ? \" < > |");
430 if (strchr(name
, '/')) {
431 /* Invalid character in directory */
432 reason
= ws_strdup_printf("contain the '/' character.");
437 message
= ws_strdup_printf("A profile name cannot %s", reason
);
445 bool delete_current_profile(void) {
446 const char *name
= get_profile_name();
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
));
463 // Use a settings file in case we ever want to include an author, description,
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
);
474 return profile_settings_path
;
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
);
490 static void load_profile_settings(profile_def
*profile
)
492 char *profile_settings_path
= get_profile_settings_path(profile
->name
);
495 if ((fp
= ws_fopen(profile_settings_path
, "r")) != NULL
) {
496 read_prefs_file(profile_settings_path
, fp
, set_profile_setting
, profile
);
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
);
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
,
511 g_free(profile_settings_path
);
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
);