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