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 35 GList * current_profile_list(void) { 36 return g_list_first(current_profiles); 37 } 38 39 GList * edited_profile_list(void) { 40 return g_list_first(edited_profiles); 41 } 42 43 static GList * 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 * 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 * 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 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 * 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 255 remove_from_profile_list(GList *fl_entry) 256 { 257 edited_profiles = remove_profile_entry(edited_profiles, fl_entry); 258 } 259 260 void 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 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 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 * 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 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