packet-smb2: fix lease epoch fields
[metze/wireshark/wip.git] / ui / profile.c
1 /* profile.c
2  * Dialog box for profiles editing
3  * Stig Bjorlykke <stig@bjorlykke.org>, 2008
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29 #include <errno.h>
30
31 #include <glib.h>
32
33 #include <epan/emem.h>
34 #include <wsutil/filesystem.h>
35 #include <epan/prefs.h>
36
37 #include "profile.h"
38
39 #include "ui/recent.h"
40 #include "ui/simple_dialog.h"
41
42 #include <wsutil/file_util.h>
43
44 static GList *current_profiles = NULL;
45 static GList *edited_profiles = NULL;
46
47 #define PROF_OPERATION_NEW  1
48 #define PROF_OPERATION_EDIT 2
49
50 GList * current_profile_list(void) {
51     return g_list_first(current_profiles);
52 }
53
54 GList * edited_profile_list(void) {
55     return g_list_first(edited_profiles);
56 }
57
58 static GList *
59 add_profile_entry(GList *fl, const char *profilename, const char *reference, int status,
60         gboolean is_global, gboolean from_global)
61 {
62     profile_def *profile;
63
64     profile = (profile_def *) g_malloc0(sizeof(profile_def));
65     profile->name = g_strdup(profilename);
66     profile->reference = g_strdup(reference);
67     profile->status = status;
68     profile->is_global = is_global;
69     profile->from_global = from_global;
70     return g_list_append(fl, profile);
71 }
72
73 static GList *
74 remove_profile_entry(GList *fl, GList *fl_entry)
75 {
76     profile_def *profile;
77
78     profile = (profile_def *) fl_entry->data;
79     g_free(profile->name);
80     g_free(profile->reference);
81     g_free(profile);
82     return g_list_remove_link(fl, fl_entry);
83 }
84
85 const gchar *
86 get_profile_parent (const gchar *profilename)
87 {
88     GList *fl_entry = g_list_first(edited_profiles);
89     guint no_edited = g_list_length(edited_profiles);
90     profile_def *profile;
91     guint i;
92
93     if (fl_entry) {
94         /* We have edited profiles, find parent */
95         for (i = 0; i < no_edited; i++) {
96             while (fl_entry) {
97                 profile = (profile_def *) fl_entry->data;
98                 if (strcmp (profile->name, profilename) == 0) {
99                     if ((profile->status == PROF_STAT_NEW) ||
100                             (profile->reference == NULL)) {
101                         /* Copy from a new profile */
102                         return NULL;
103                     } else {
104                         /* Found a parent, use this */
105                         profilename = profile->reference;
106                     }
107                 }
108                 fl_entry = g_list_next(fl_entry);
109             }
110             fl_entry = g_list_first(edited_profiles);
111         }
112     }
113
114     return profilename;
115 }
116
117 const gchar *apply_profile_changes(void) {
118     char        *pf_dir_path, *pf_dir_path2, *pf_filename;
119     GList       *fl1, *fl2;
120     profile_def *profile1, *profile2;
121     gboolean     found;
122     emem_strbuf_t *message = ep_strbuf_new(NULL);
123     const gchar *err_msg;
124
125     /* First validate all profile names */
126     fl1 = edited_profile_list();
127     while (fl1) {
128         profile1 = (profile_def *) fl1->data;
129         g_strstrip(profile1->name);
130         if ((err_msg = profile_name_is_valid(profile1->name)) != NULL) {
131             ep_strbuf_printf(message, "%s", err_msg);
132             return message->str;
133         }
134         fl1 = g_list_next(fl1);
135     }
136
137     /* Then do all copy profiles */
138     fl1 = edited_profile_list();
139     while (fl1) {
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                 ep_strbuf_printf(message,
145                         "Can't create directory\n\"%s\":\n%s.",
146                         pf_dir_path, g_strerror(errno));
147
148                 g_free(pf_dir_path);
149             }
150             profile1->status = PROF_STAT_EXISTS;
151
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));
158
159                     g_free(pf_filename);
160                     g_free(pf_dir_path);
161                     g_free(pf_dir_path2);
162                 }
163             }
164
165             g_free (profile1->reference);
166             profile1->reference = g_strdup(profile1->name);
167         }
168         fl1 = g_list_next(fl1);
169     }
170
171
172     /* Then create new and rename changed */
173     fl1 = edited_profile_list();
174     while (fl1) {
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) {
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));
184
185                     g_free(pf_dir_path);
186                 }
187                 profile1->status = PROF_STAT_EXISTS;
188
189                 g_free (profile1->reference);
190                 profile1->reference = g_strdup(profile1->name);
191             }
192         } else if (profile1->status == PROF_STAT_CHANGED) {
193             if (strcmp(profile1->reference, profile1->name)!=0) {
194                 /* Rename old profile directory to new */
195                 if (rename_persconffile_profile(profile1->reference, profile1->name,
196                             &pf_dir_path, &pf_dir_path2) == -1) {
197                     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
198                             "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
199                             pf_dir_path, pf_dir_path2, g_strerror(errno));
200
201                     g_free(pf_dir_path);
202                     g_free(pf_dir_path2);
203                 }
204                 profile1->status = PROF_STAT_EXISTS;
205                 g_free (profile1->reference);
206                 profile1->reference = g_strdup(profile1->name);
207             }
208         }
209         fl1 = g_list_next(fl1);
210     }
211
212     /* Last remove deleted */
213     fl1 = current_profile_list();
214     while (fl1) {
215         found = FALSE;
216         profile1 = (profile_def *) fl1->data;
217         fl2 = edited_profile_list();
218         while (fl2) {
219             profile2 = (profile_def *) fl2->data;
220             if (!profile2->is_global) {
221                 if (strcmp(profile1->name, profile2->name)==0) {
222                     /* Profile exists in both lists */
223                     found = TRUE;
224                 } else if (strcmp(profile1->name, profile2->reference)==0) {
225                     /* Profile has been renamed */
226                     found = TRUE;
227                 }
228             }
229             fl2 = g_list_next(fl2);
230         }
231         if (!found) {
232             /* Exists in existing list and not in edited, this is a deleted profile */
233             if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
234                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
235                         "Can't delete profile directory\n\"%s\":\n%s.",
236                         pf_dir_path, g_strerror(errno));
237
238                 g_free(pf_dir_path);
239             }
240         }
241         fl1 = g_list_next(fl1);
242     }
243
244     copy_profile_list();
245     return NULL;
246 }
247
248 GList *
249 add_to_profile_list(const char *name, const char *expression, int status,
250         gboolean is_global, gboolean from_global)
251 {
252     edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
253             is_global, from_global);
254
255     return g_list_last(edited_profiles);
256 }
257
258 void
259 remove_from_profile_list(GList *fl_entry)
260 {
261     edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
262 }
263
264 void
265 empty_profile_list(gboolean edit_list)
266 {
267     GList **flpp;
268
269     if (edit_list) {
270         flpp = &edited_profiles;
271
272         while(*flpp) {
273             *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
274         }
275
276         g_assert(g_list_length(*flpp) == 0);
277     }
278
279     flpp = &current_profiles;
280
281     while(*flpp) {
282         *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
283     }
284
285     g_assert(g_list_length(*flpp) == 0);
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);
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   *profiles_dir, *name;
316     gchar         *filename;
317
318     empty_profile_list(TRUE);
319
320     /* Default entry */
321     add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, FALSE, FALSE);
322
323     /* Local (user) profiles */
324     profiles_dir = get_profiles_dir();
325     if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
326         while ((file = ws_dir_read_name(dir)) != NULL) {
327             name = ws_dir_get_name(file);
328             filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
329
330             if (test_for_directory(filename) == EISDIR) {
331                 /*fl_entry =*/ add_to_profile_list(name, name, PROF_STAT_EXISTS, FALSE, FALSE);
332             }
333             g_free (filename);
334         }
335         ws_dir_close (dir);
336     }
337
338     /* Global profiles */
339     profiles_dir = get_global_profiles_dir();
340     if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
341         while ((file = ws_dir_read_name(dir)) != NULL) {
342             name = ws_dir_get_name(file);
343             filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
344
345             if (test_for_directory(filename) == EISDIR) {
346                 /*fl_entry =*/ add_to_profile_list(name, name, PROF_STAT_EXISTS, TRUE, TRUE);
347                 /*profile = (profile_def *) fl_entry->data;*/
348             }
349             g_free (filename);
350         }
351         ws_dir_close (dir);
352     }
353
354     /* Make the current list and the edited list equal */
355     copy_profile_list ();
356 }
357
358 const gchar *
359 profile_name_is_valid(const gchar *name)
360 {
361     gchar *reason = NULL;
362     emem_strbuf_t  *message = ep_strbuf_new(NULL);
363
364 #ifdef _WIN32
365     char *invalid_dir_char = "\\/:*?\"<>|";
366     gboolean invalid = FALSE;
367     int i;
368
369     for (i = 0; i < 9; i++) {
370         if (strchr(name, invalid_dir_char[i])) {
371             /* Invalid character in directory */
372             invalid = TRUE;
373         }
374     }
375     if (name[0] == '.' || name[strlen(name)-1] == '.') {
376         /* Profile name cannot start or end with period */
377         invalid = TRUE;
378     }
379     if (invalid) {
380         reason = g_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
381                 "   \\ / : * ? \" &lt; &gt; |");
382     }
383 #else
384     if (strchr(name, '/')) {
385         /* Invalid character in directory */
386         reason = g_strdup_printf("contain the '/' character.");
387     }
388 #endif
389
390     if (reason) {
391         ep_strbuf_printf(message, "A profile name cannot %s\nProfiles unchanged.", reason);
392         g_free(reason);
393         return message->str;
394     }
395
396     return NULL;
397 }
398
399 gboolean delete_current_profile(void) {
400     const gchar *name = get_profile_name();
401     char        *pf_dir_path;
402
403     if (profile_exists(name, FALSE) && strcmp (name, DEFAULT_PROFILE) != 0) {
404         if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
405             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
406                     "Can't delete profile directory\n\"%s\":\n%s.",
407                     pf_dir_path, g_strerror(errno));
408
409             g_free(pf_dir_path);
410         } else {
411             return TRUE;
412         }
413     }
414     return FALSE;
415 }
416
417 /*
418  * Editor modelines
419  *
420  * Local Variables:
421  * c-basic-offset: 4
422  * tab-width: 8
423  * indent-tabs-mode: nil
424  * End:
425  *
426  * ex: set shiftwidth=4 tabstop=8 expandtab:
427  * :indentSize=4:tabSize=8:noTabs=true:
428  */