wireshark/ui/profile.c

434 lines
13 KiB
C

/* profile.c
* Dialog box for profiles editing
* Stig Bjorlykke <stig@bjorlykke.org>, 2008
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <wsutil/filesystem.h>
#include "profile.h"
#include "ui/simple_dialog.h"
#include "ui/recent.h"
#include <wsutil/file_util.h>
#include <wsutil/ws_assert.h>
static GList *current_profiles = NULL;
static GList *edited_profiles = NULL;
#define PROF_OPERATION_NEW 1
#define PROF_OPERATION_EDIT 2
GList * current_profile_list(void) {
return g_list_first(current_profiles);
}
GList * edited_profile_list(void) {
return g_list_first(edited_profiles);
}
static GList *
add_profile_entry(GList *fl, const char *profilename, const char *reference, int status,
gboolean is_global, gboolean from_global, gboolean is_import)
{
profile_def *profile;
profile = g_new0(profile_def, 1);
profile->name = g_strdup(profilename);
profile->reference = g_strdup(reference);
profile->status = status;
profile->is_global = is_global;
profile->from_global = from_global;
profile->is_import = is_import;
return g_list_append(fl, profile);
}
static GList *
remove_profile_entry(GList *fl, GList *fl_entry)
{
profile_def *profile;
profile = (profile_def *) fl_entry->data;
g_free(profile->name);
g_free(profile->reference);
g_free(profile);
return g_list_remove_link(fl, fl_entry);
}
const gchar *
get_profile_parent (const gchar *profilename)
{
GList *fl_entry = g_list_first(edited_profiles);
guint no_edited = g_list_length(edited_profiles);
profile_def *profile;
guint i;
if (fl_entry) {
/* We have edited profiles, find parent */
for (i = 0; i < no_edited; i++) {
while (fl_entry) {
profile = (profile_def *) fl_entry->data;
if (strcmp (profile->name, profilename) == 0) {
if ((profile->status == PROF_STAT_NEW) ||
(profile->reference == NULL)) {
/* Copy from a new profile */
return NULL;
} else {
/* Found a parent, use this */
profilename = profile->reference;
}
}
fl_entry = g_list_next(fl_entry);
}
fl_entry = g_list_first(edited_profiles);
}
}
return profilename;
}
gchar *apply_profile_changes(void)
{
char *pf_dir_path, *pf_dir_path2, *pf_filename;
GList *fl1, *fl2;
profile_def *profile1, *profile2;
gboolean found;
gchar *err_msg;
/* First validate all profile names */
fl1 = edited_profile_list();
while (fl1) {
profile1 = (profile_def *) fl1->data;
g_strstrip(profile1->name);
if ((err_msg = profile_name_is_valid(profile1->name)) != NULL) {
gchar *message = ws_strdup_printf("%s\nProfiles unchanged.", err_msg);
g_free(err_msg);
return message;
}
fl1 = g_list_next(fl1);
}
/* Write recent file for current profile before copying or renaming */
write_profile_recent();
/* Then do all copy profiles */
fl1 = edited_profile_list();
while (fl1) {
profile1 = (profile_def *) fl1->data;
g_strstrip(profile1->name);
if (profile1->status == PROF_STAT_COPY) {
if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
err_msg = ws_strdup_printf("Can't create directory\n\"%s\":\n%s.",
pf_dir_path, g_strerror(errno));
g_free(pf_dir_path);
return err_msg;
}
profile1->status = PROF_STAT_EXISTS;
if (profile1->reference) {
if (copy_persconffile_profile(profile1->name, profile1->reference, profile1->from_global,
&pf_filename, &pf_dir_path, &pf_dir_path2) == -1) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
g_free(pf_filename);
g_free(pf_dir_path);
g_free(pf_dir_path2);
}
}
g_free (profile1->reference);
profile1->reference = g_strdup(profile1->name);
}
fl1 = g_list_next(fl1);
}
/* Then create new and rename changed */
fl1 = edited_profile_list();
while (fl1) {
profile1 = (profile_def *) fl1->data;
g_strstrip(profile1->name);
if (profile1->status == PROF_STAT_NEW) {
/* We do not create a directory for the default profile */
if (strcmp(profile1->name, DEFAULT_PROFILE)!=0 && ! profile1->is_import) {
if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"Can't create directory\n\"%s\":\n%s.",
pf_dir_path, g_strerror(errno));
g_free(pf_dir_path);
}
profile1->status = PROF_STAT_EXISTS;
g_free (profile1->reference);
profile1->reference = g_strdup(profile1->name);
/* correctly apply imports as existing profiles */
} else if (profile1->is_import) {
profile1->status = PROF_STAT_EXISTS;
g_free (profile1->reference);
profile1->reference = g_strdup(profile1->name);
profile1->is_import = FALSE;
}
} else if (profile1->status == PROF_STAT_CHANGED) {
if (strcmp(profile1->reference, profile1->name)!=0) {
/* Rename old profile directory to new */
if (rename_persconffile_profile(profile1->reference, profile1->name,
&pf_dir_path, &pf_dir_path2) == -1) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
pf_dir_path, pf_dir_path2, g_strerror(errno));
g_free(pf_dir_path);
g_free(pf_dir_path2);
}
profile1->status = PROF_STAT_EXISTS;
}
}
fl1 = g_list_next(fl1);
}
/* Last remove deleted */
fl1 = current_profile_list();
while (fl1) {
found = FALSE;
profile1 = (profile_def *) fl1->data;
fl2 = edited_profile_list();
while (fl2) {
profile2 = (profile_def *) fl2->data;
if (!profile2->is_global) {
if (strcmp(profile1->name, profile2->name)==0) {
/* Profile exists in both lists */
found = TRUE;
} else if (strcmp(profile1->name, profile2->reference)==0) {
/* Profile has been renamed, update reference to the new name */
g_free (profile2->reference);
profile2->reference = g_strdup(profile2->name);
found = TRUE;
}
}
fl2 = g_list_next(fl2);
}
if (!found) {
/* Exists in existing list and not in edited, this is a deleted profile */
if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"Can't delete profile directory\n\"%s\":\n%s.",
pf_dir_path, g_strerror(errno));
g_free(pf_dir_path);
}
}
fl1 = g_list_next(fl1);
}
copy_profile_list();
return NULL;
}
GList *
add_to_profile_list(const char *name, const char *expression, int status,
gboolean is_global, gboolean from_global, gboolean is_imported)
{
edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
is_global, from_global, is_imported);
return g_list_last(edited_profiles);
}
void
remove_from_profile_list(GList *fl_entry)
{
edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
}
void
empty_profile_list(gboolean edit_list)
{
GList **flpp;
if (edit_list) {
flpp = &edited_profiles;
while(*flpp) {
*flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
}
ws_assert(g_list_length(*flpp) == 0);
if ( ! edited_profiles )
edited_profiles = NULL;
}
flpp = &current_profiles;
while(*flpp) {
*flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
}
ws_assert(g_list_length(*flpp) == 0);
if ( ! current_profiles )
current_profiles = NULL;
}
void
copy_profile_list(void)
{
GList *flp_src;
profile_def *profile;
flp_src = edited_profiles;
/* throw away the "old" destination list - a NULL list is ok here */
empty_profile_list(FALSE);
/* copy the list entries */
while(flp_src) {
profile = (profile_def *)(flp_src)->data;
current_profiles = add_profile_entry(current_profiles, profile->name,
profile->reference, profile->status,
profile->is_global, profile->from_global, FALSE);
flp_src = g_list_next(flp_src);
}
}
void
init_profile_list(void)
{
WS_DIR *dir; /* scanned directory */
WS_DIRENT *file; /* current file */
const gchar *name;
GList *local_profiles = NULL;
GList *global_profiles = NULL;
GList *iter;
gchar *profiles_dir, *filename;
empty_profile_list(TRUE);
/* Default entry */
add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, FALSE, FALSE, FALSE);
/* Local (user) profiles */
profiles_dir = get_profiles_dir();
if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
while ((file = ws_dir_read_name(dir)) != NULL) {
name = ws_dir_get_name(file);
filename = ws_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
if (test_for_directory(filename) == EISDIR) {
local_profiles = g_list_prepend(local_profiles, g_strdup(name));
}
g_free (filename);
}
ws_dir_close (dir);
}
g_free(profiles_dir);
local_profiles = g_list_sort(local_profiles, (GCompareFunc)g_ascii_strcasecmp);
for (iter = g_list_first(local_profiles); iter; iter = g_list_next(iter)) {
name = (gchar *)iter->data;
add_to_profile_list(name, name, PROF_STAT_EXISTS, FALSE, FALSE, FALSE);
}
g_list_free_full(local_profiles, g_free);
/* Global profiles */
profiles_dir = get_global_profiles_dir();
if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
while ((file = ws_dir_read_name(dir)) != NULL) {
name = ws_dir_get_name(file);
filename = ws_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
if (test_for_directory(filename) == EISDIR) {
global_profiles = g_list_prepend(global_profiles, g_strdup(name));
}
g_free (filename);
}
ws_dir_close (dir);
}
g_free(profiles_dir);
global_profiles = g_list_sort(global_profiles, (GCompareFunc)g_ascii_strcasecmp);
for (iter = g_list_first(global_profiles); iter; iter = g_list_next(iter)) {
name = (gchar *)iter->data;
add_to_profile_list(name, name, PROF_STAT_EXISTS, TRUE, TRUE, FALSE);
}
g_list_free_full(global_profiles, g_free);
/* Make the current list and the edited list equal */
copy_profile_list ();
}
gchar *
profile_name_is_valid(const gchar *name)
{
gchar *reason = NULL;
gchar *message;
#ifdef _WIN32
char *invalid_dir_char = "\\/:*?\"<>|";
gboolean invalid = FALSE;
int i;
for (i = 0; i < 9; i++) {
if (strchr(name, invalid_dir_char[i])) {
/* Invalid character in directory */
invalid = TRUE;
}
}
if (name[0] == '.' || name[strlen(name)-1] == '.') {
/* Profile name cannot start or end with period */
invalid = TRUE;
}
if (invalid) {
reason = ws_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
" \\ / : * ? \" &lt; &gt; |");
}
#else
if (strchr(name, '/')) {
/* Invalid character in directory */
reason = ws_strdup_printf("contain the '/' character.");
}
#endif
if (reason) {
message = ws_strdup_printf("A profile name cannot %s", reason);
g_free(reason);
return message;
}
return NULL;
}
gboolean delete_current_profile(void) {
const gchar *name = get_profile_name();
char *pf_dir_path;
if (profile_exists(name, FALSE) && strcmp (name, DEFAULT_PROFILE) != 0) {
if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
"Can't delete profile directory\n\"%s\":\n%s.",
pf_dir_path, g_strerror(errno));
g_free(pf_dir_path);
} else {
return TRUE;
}
}
return FALSE;
}