/src/wireshark/wsutil/filesystem.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* filesystem.c |
2 | | * Filesystem utility routines |
3 | | * |
4 | | * Wireshark - Network traffic analyzer |
5 | | * By Gerald Combs <gerald@wireshark.org> |
6 | | * Copyright 1998 Gerald Combs |
7 | | * |
8 | | * SPDX-License-Identifier: GPL-2.0-or-later |
9 | | */ |
10 | | |
11 | | #include "config.h" |
12 | 0 | #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL |
13 | | |
14 | | #include "filesystem.h" |
15 | | |
16 | | #include <stdio.h> |
17 | | #include <stdlib.h> |
18 | | #include <string.h> |
19 | | #include <errno.h> |
20 | | |
21 | | #ifdef _WIN32 |
22 | | #include <windows.h> |
23 | | #include <tchar.h> |
24 | | #include <shlobj.h> |
25 | | #include <wsutil/unicode-utils.h> |
26 | | #else /* _WIN32 */ |
27 | | #ifdef ENABLE_APPLICATION_BUNDLE |
28 | | #include <mach-o/dyld.h> |
29 | | #endif |
30 | | #ifdef __linux__ |
31 | | #include <sys/utsname.h> |
32 | | #endif |
33 | | #ifdef __FreeBSD__ |
34 | | #include <sys/types.h> |
35 | | #include <sys/sysctl.h> |
36 | | #endif |
37 | | #ifdef HAVE_DLGET |
38 | | #include <dlfcn.h> |
39 | | #endif |
40 | | #include <pwd.h> |
41 | | #endif /* _WIN32 */ |
42 | | |
43 | | #include <wsutil/application_flavor.h> |
44 | | #include <wsutil/file_util.h> |
45 | | #include <wsutil/privileges.h> |
46 | | #include <wsutil/report_message.h> |
47 | | #include <wsutil/utf8_entities.h> |
48 | | |
49 | | #include <wiretap/wtap.h> /* for WTAP_ERR_SHORT_WRITE */ |
50 | | |
51 | | #include "path_config.h" |
52 | | |
53 | | #define PROFILES_DIR "profiles" |
54 | 0 | #define PLUGINS_DIR_NAME "plugins" |
55 | 0 | #define EXTCAP_DIR_NAME "extcap" |
56 | | #define PROFILES_INFO_NAME "profile_files.txt" |
57 | | |
58 | 2 | #define _S G_DIR_SEPARATOR_S |
59 | | |
60 | | char *persconffile_dir; |
61 | | char *datafile_dir; |
62 | | char *persdatafile_dir; |
63 | | char *persconfprofile; |
64 | | char *doc_dir; |
65 | | char *current_working_dir; |
66 | | |
67 | | /* Directory from which the executable came. */ |
68 | | static char *progfile_dir; |
69 | | static char *install_prefix; |
70 | | |
71 | | static bool do_store_persconffiles; |
72 | | static GHashTable *profile_files; |
73 | | |
74 | | /* |
75 | | * Given a pathname, return a pointer to the last pathname separator |
76 | | * character in the pathname, or NULL if the pathname contains no |
77 | | * separators. |
78 | | */ |
79 | | char * |
80 | | find_last_pathname_separator(const char *path) |
81 | 2 | { |
82 | 2 | char *separator; |
83 | | |
84 | | #ifdef _WIN32 |
85 | | char c; |
86 | | |
87 | | /* |
88 | | * We have to scan for '\' or '/'. |
89 | | * Get to the end of the string. |
90 | | */ |
91 | | separator = strchr(path, '\0'); /* points to ending '\0' */ |
92 | | while (separator > path) { |
93 | | c = *--separator; |
94 | | if (c == '\\' || c == '/') |
95 | | return separator; /* found it */ |
96 | | } |
97 | | |
98 | | /* |
99 | | * OK, we didn't find any, so no directories - but there might |
100 | | * be a drive letter.... |
101 | | */ |
102 | | return strchr(path, ':'); |
103 | | #else |
104 | 2 | separator = strrchr(path, '/'); |
105 | 2 | return separator; |
106 | 2 | #endif |
107 | 2 | } |
108 | | |
109 | | /* |
110 | | * Given a pathname, return the last component. |
111 | | */ |
112 | | const char * |
113 | | get_basename(const char *path) |
114 | 0 | { |
115 | 0 | const char *filename; |
116 | |
|
117 | 0 | ws_assert(path != NULL); |
118 | 0 | filename = find_last_pathname_separator(path); |
119 | 0 | if (filename == NULL) { |
120 | | /* |
121 | | * There're no directories, drive letters, etc. in the |
122 | | * name; the pathname *is* the file name. |
123 | | */ |
124 | 0 | filename = path; |
125 | 0 | } else { |
126 | | /* |
127 | | * Skip past the pathname or drive letter separator. |
128 | | */ |
129 | 0 | filename++; |
130 | 0 | } |
131 | 0 | return filename; |
132 | 0 | } |
133 | | |
134 | | /* |
135 | | * Given a pathname, return a string containing everything but the |
136 | | * last component. NOTE: this overwrites the pathname handed into |
137 | | * it.... |
138 | | */ |
139 | | char * |
140 | | get_dirname(char *path) |
141 | 0 | { |
142 | 0 | char *separator; |
143 | |
|
144 | 0 | ws_assert(path != NULL); |
145 | 0 | separator = find_last_pathname_separator(path); |
146 | 0 | if (separator == NULL) { |
147 | | /* |
148 | | * There're no directories, drive letters, etc. in the |
149 | | * name; there is no directory path to return. |
150 | | */ |
151 | 0 | return NULL; |
152 | 0 | } |
153 | | |
154 | | /* |
155 | | * Get rid of the last pathname separator and the final file |
156 | | * name following it. |
157 | | */ |
158 | 0 | *separator = '\0'; |
159 | | |
160 | | /* |
161 | | * "path" now contains the pathname of the directory containing |
162 | | * the file/directory to which it referred. |
163 | | */ |
164 | 0 | return path; |
165 | 0 | } |
166 | | |
167 | | /* |
168 | | * Given a pathname, return: |
169 | | * |
170 | | * the errno, if an attempt to "stat()" the file fails; |
171 | | * |
172 | | * EISDIR, if the attempt succeeded and the file turned out |
173 | | * to be a directory; |
174 | | * |
175 | | * 0, if the attempt succeeded and the file turned out not |
176 | | * to be a directory. |
177 | | */ |
178 | | |
179 | | int |
180 | | test_for_directory(const char *path) |
181 | 4 | { |
182 | 4 | ws_statb64 statb; |
183 | | |
184 | 4 | if (ws_stat64(path, &statb) < 0) |
185 | 4 | return errno; |
186 | | |
187 | 0 | if (S_ISDIR(statb.st_mode)) |
188 | 0 | return EISDIR; |
189 | 0 | else |
190 | 0 | return 0; |
191 | 0 | } |
192 | | |
193 | | int |
194 | | test_for_fifo(const char *path) |
195 | 0 | { |
196 | 0 | ws_statb64 statb; |
197 | |
|
198 | 0 | if (ws_stat64(path, &statb) < 0) |
199 | 0 | return errno; |
200 | | |
201 | 0 | if (S_ISFIFO(statb.st_mode)) |
202 | 0 | return ESPIPE; |
203 | 0 | else |
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | | bool |
208 | | test_for_regular_file(const char *path) |
209 | 0 | { |
210 | 0 | ws_statb64 statb; |
211 | |
|
212 | 0 | if (!path) { |
213 | 0 | return false; |
214 | 0 | } |
215 | | |
216 | 0 | if (ws_stat64(path, &statb) != 0) |
217 | 0 | return false; |
218 | | |
219 | 0 | return S_ISREG(statb.st_mode); |
220 | 0 | } |
221 | | |
222 | | #ifdef ENABLE_APPLICATION_BUNDLE |
223 | | /* |
224 | | * Directory of the application bundle in which we're contained, |
225 | | * if we're contained in an application bundle. Otherwise, NULL. |
226 | | * |
227 | | * Note: Table 2-5 "Subdirectories of the Contents directory" of |
228 | | * |
229 | | * https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1 |
230 | | * |
231 | | * says that the "Frameworks" directory |
232 | | * |
233 | | * Contains any private shared libraries and frameworks used by the |
234 | | * executable. The frameworks in this directory are revision-locked |
235 | | * to the application and cannot be superseded by any other, even |
236 | | * newer, versions that may be available to the operating system. In |
237 | | * other words, the frameworks included in this directory take precedence |
238 | | * over any other similarly named frameworks found in other parts of |
239 | | * the operating system. For information on how to add private |
240 | | * frameworks to your application bundle, see Framework Programming Guide. |
241 | | * |
242 | | * so if we were to ship with any frameworks (e.g. Qt) we should |
243 | | * perhaps put them in a Frameworks directory rather than under |
244 | | * Resources. |
245 | | * |
246 | | * It also says that the "PlugIns" directory |
247 | | * |
248 | | * Contains loadable bundles that extend the basic features of your |
249 | | * application. You use this directory to include code modules that |
250 | | * must be loaded into your applicationbs process space in order to |
251 | | * be used. You would not use this directory to store standalone |
252 | | * executables. |
253 | | * |
254 | | * Our plugins are just raw .so/.dylib files; I don't know whether by |
255 | | * "bundles" they mean application bundles (i.e., directory hierarchies) |
256 | | * or just "bundles" in the Mach-O sense (which are an image type that |
257 | | * can be loaded with dlopen() but not linked as libraries; our plugins |
258 | | * are, I think, built as dylibs and can be loaded either way). |
259 | | * |
260 | | * And it says that the "SharedSupport" directory |
261 | | * |
262 | | * Contains additional non-critical resources that do not impact the |
263 | | * ability of the application to run. You might use this directory to |
264 | | * include things like document templates, clip art, and tutorials |
265 | | * that your application expects to be present but that do not affect |
266 | | * the ability of your application to run. |
267 | | * |
268 | | * I don't think I'd put the files that currently go under Resources/share |
269 | | * into that category; they're not, for example, sample Lua scripts that |
270 | | * don't actually get run by Wireshark, they're configuration/data files |
271 | | * for Wireshark whose absence might not prevent Wireshark from running |
272 | | * but that would affect how it behaves when run. |
273 | | */ |
274 | | static char *appbundle_dir; |
275 | | #endif |
276 | | |
277 | | /* |
278 | | * true if we're running from the build directory and we aren't running |
279 | | * with special privileges. |
280 | | */ |
281 | | static bool running_in_build_directory_flag; |
282 | | |
283 | 8 | static char *configuration_environment_variable(const char *suffix) { |
284 | 8 | switch (get_application_flavor()) { |
285 | 8 | case APPLICATION_FLAVOR_WIRESHARK: |
286 | 8 | return g_strdup_printf("WIRESHARK_%s", suffix); |
287 | 0 | case APPLICATION_FLAVOR_STRATOSHARK: |
288 | 0 | return g_strdup_printf("STRATOSHARK_%s", suffix); |
289 | 0 | default: |
290 | 0 | ws_assert_not_reached(); |
291 | 8 | } |
292 | 8 | } |
293 | | |
294 | | #ifndef _WIN32 |
295 | | /* |
296 | | * Get the pathname of the executable using various platform- |
297 | | * dependent mechanisms for various UN*Xes. |
298 | | * |
299 | | * These calls all should return something independent of the argv[0] |
300 | | * passed to the program, so it shouldn't be fooled by an argv[0] |
301 | | * that doesn't match the executable path. |
302 | | * |
303 | | * We don't use dladdr() because: |
304 | | * |
305 | | * not all UN*Xes necessarily have dladdr(); |
306 | | * |
307 | | * those that do have it don't necessarily have dladdr(main) |
308 | | * return information about the executable image; |
309 | | * |
310 | | * those that do have a dladdr() where dladdr(main) returns |
311 | | * information about the executable image don't necessarily |
312 | | * have a mechanism by which the executable image can get |
313 | | * its own path from the kernel (either by a call or by it |
314 | | * being handed to it along with argv[] and the environment), |
315 | | * so they just fall back on getting it from argv[0], which we |
316 | | * already have code to do; |
317 | | * |
318 | | * those that do have such a mechanism don't necessarily use |
319 | | * it in dladdr(), and, instead, just fall back on getting it |
320 | | * from argv[0]; |
321 | | * |
322 | | * so the only places where it's worth bothering to use dladdr() |
323 | | * are platforms where dladdr(main) return information about the |
324 | | * executable image by getting it from the kernel rather than |
325 | | * by looking at argv[0], and where we can't get at that information |
326 | | * ourselves, and we haven't seen any indication that there are any |
327 | | * such platforms. |
328 | | * |
329 | | * In particular, some dynamic linkers supply a dladdr() such that |
330 | | * dladdr(main) just returns something derived from argv[0], so |
331 | | * just using dladdr(main) is the wrong thing to do if there's |
332 | | * another mechanism that can get you a more reliable version of |
333 | | * the executable path. |
334 | | * |
335 | | * So, on platforms where we know of a mechanism to get that path |
336 | | * (where getting that path doesn't involve argv[0], which is not |
337 | | * guaranteed to reflect the path to the binary), this routine |
338 | | * attempsts to use that platform's mechanism. On other platforms, |
339 | | * it just returns NULL. |
340 | | * |
341 | | * This is not guaranteed to return an absolute path; if it doesn't, |
342 | | * our caller must prepend the current directory if it's a path. |
343 | | * |
344 | | * This is not guaranteed to return the "real path"; it might return |
345 | | * something with symbolic links in the path. Our caller must |
346 | | * use realpath() if they want the real thing, but that's also true of |
347 | | * something obtained by looking at argv[0]. |
348 | | */ |
349 | | #define xx_free free /* hack so checkAPIs doesn't complain */ |
350 | | static const char * |
351 | | get_current_executable_path(void) |
352 | 2 | { |
353 | | #if defined(ENABLE_APPLICATION_BUNDLE) |
354 | | static char *executable_path; |
355 | | uint32_t path_buf_size; |
356 | | |
357 | | if (executable_path) { |
358 | | return executable_path; |
359 | | } |
360 | | |
361 | | path_buf_size = PATH_MAX; |
362 | | executable_path = (char *)g_malloc(path_buf_size); |
363 | | if (_NSGetExecutablePath(executable_path, &path_buf_size) == -1) { |
364 | | executable_path = (char *)g_realloc(executable_path, path_buf_size); |
365 | | if (_NSGetExecutablePath(executable_path, &path_buf_size) == -1) |
366 | | return NULL; |
367 | | } |
368 | | /* |
369 | | * Resolve our path so that it's possible to symlink the executables |
370 | | * in our application bundle. |
371 | | */ |
372 | | char *rp_execpath = realpath(executable_path, NULL); |
373 | | if (rp_execpath) { |
374 | | g_free(executable_path); |
375 | | executable_path = g_strdup(rp_execpath); |
376 | | xx_free(rp_execpath); |
377 | | } |
378 | | return executable_path; |
379 | | #elif defined(__linux__) |
380 | | /* |
381 | | * In older versions of GNU libc's dynamic linker, as used on Linux, |
382 | | * dladdr(main) supplies a path based on argv[0], so we use |
383 | | * /proc/self/exe instead; there are Linux distributions with |
384 | | * kernels that support /proc/self/exe and those older versions |
385 | | * of the dynamic linker, and this will get a better answer on |
386 | | * those versions. |
387 | | * |
388 | | * It only works on Linux 2.2 or later, so we just give up on |
389 | | * earlier versions. |
390 | | * |
391 | | * XXX - are there OS versions that support "exe" but not "self"? |
392 | | */ |
393 | 2 | struct utsname name; |
394 | 2 | static char executable_path[PATH_MAX + 1]; |
395 | 2 | ssize_t r; |
396 | | |
397 | 2 | if (uname(&name) == -1) |
398 | 0 | return NULL; |
399 | 2 | if (strncmp(name.release, "1.", 2) == 0) |
400 | 0 | return NULL; /* Linux 1.x */ |
401 | 2 | if (strcmp(name.release, "2.0") == 0 || |
402 | 2 | strncmp(name.release, "2.0.", 4) == 0 || |
403 | 2 | strcmp(name.release, "2.1") == 0 || |
404 | 2 | strncmp(name.release, "2.1.", 4) == 0) |
405 | 0 | return NULL; /* Linux 2.0.x or 2.1.x */ |
406 | 2 | if ((r = readlink("/proc/self/exe", executable_path, PATH_MAX)) == -1) |
407 | 0 | return NULL; |
408 | 2 | executable_path[r] = '\0'; |
409 | 2 | return executable_path; |
410 | | #elif defined(__FreeBSD__) && defined(KERN_PROC_PATHNAME) |
411 | | /* |
412 | | * In older versions of FreeBSD's dynamic linker, dladdr(main) |
413 | | * supplies a path based on argv[0], so we use the KERN_PROC_PATHNAME |
414 | | * sysctl instead; there are, I think, versions of FreeBSD |
415 | | * that support the sysctl that have and those older versions |
416 | | * of the dynamic linker, and this will get a better answer on |
417 | | * those versions. |
418 | | */ |
419 | | int mib[4]; |
420 | | char *executable_path; |
421 | | size_t path_buf_size; |
422 | | |
423 | | mib[0] = CTL_KERN; |
424 | | mib[1] = KERN_PROC; |
425 | | mib[2] = KERN_PROC_PATHNAME; |
426 | | mib[3] = -1; |
427 | | path_buf_size = PATH_MAX; |
428 | | executable_path = (char *)g_malloc(path_buf_size); |
429 | | if (sysctl(mib, 4, executable_path, &path_buf_size, NULL, 0) == -1) { |
430 | | if (errno != ENOMEM) |
431 | | return NULL; |
432 | | executable_path = (char *)g_realloc(executable_path, path_buf_size); |
433 | | if (sysctl(mib, 4, executable_path, &path_buf_size, NULL, 0) == -1) |
434 | | return NULL; |
435 | | } |
436 | | return executable_path; |
437 | | #elif defined(__NetBSD__) |
438 | | /* |
439 | | * In all versions of NetBSD's dynamic linker as of 2013-08-12, |
440 | | * dladdr(main) supplies a path based on argv[0], so we use |
441 | | * /proc/curproc/exe instead. |
442 | | * |
443 | | * XXX - are there OS versions that support "exe" but not "curproc" |
444 | | * or "self"? Are there any that support "self" but not "curproc"? |
445 | | */ |
446 | | static char executable_path[PATH_MAX + 1]; |
447 | | ssize_t r; |
448 | | |
449 | | if ((r = readlink("/proc/curproc/exe", executable_path, PATH_MAX)) == -1) |
450 | | return NULL; |
451 | | executable_path[r] = '\0'; |
452 | | return executable_path; |
453 | | #elif defined(__DragonFly__) |
454 | | /* |
455 | | * In older versions of DragonFly BSD's dynamic linker, dladdr(main) |
456 | | * supplies a path based on argv[0], so we use /proc/curproc/file |
457 | | * instead; it appears to be supported by all versions of DragonFly |
458 | | * BSD. |
459 | | */ |
460 | | static char executable_path[PATH_MAX + 1]; |
461 | | ssize_t r; |
462 | | |
463 | | if ((r = readlink("/proc/curproc/file", executable_path, PATH_MAX)) == -1) |
464 | | return NULL; |
465 | | executable_path[r] = '\0'; |
466 | | return executable_path; |
467 | | #elif defined(HAVE_GETEXECNAME) |
468 | | /* |
469 | | * Solaris, with getexecname(). |
470 | | * It appears that getexecname() dates back to at least Solaris 8, |
471 | | * but /proc/{pid}/path is first documented in the Solaris 10 documentation, |
472 | | * so we use getexecname() if available, rather than /proc/self/path/a.out |
473 | | * (which isn't documented, but appears to be a symlink to the |
474 | | * executable image file). |
475 | | */ |
476 | | return getexecname(); |
477 | | #elif defined(HAVE_DLGET) |
478 | | /* |
479 | | * HP-UX 11, with dlget(); use dlget() and dlgetname(). |
480 | | * See |
481 | | * |
482 | | * https://web.archive.org/web/20081025174755/http://h21007.www2.hp.com/portal/site/dspp/menuitem.863c3e4cbcdc3f3515b49c108973a801?ciid=88086d6e1de021106d6e1de02110275d6e10RCRD#two |
483 | | */ |
484 | | struct load_module_desc desc; |
485 | | |
486 | | if (dlget(-2, &desc, sizeof(desc)) != NULL) |
487 | | return dlgetname(&desc, sizeof(desc), NULL, NULL, NULL); |
488 | | else |
489 | | return NULL; |
490 | | #else |
491 | | /* Fill in your favorite UN*X's code here, if there is something */ |
492 | | return NULL; |
493 | | #endif |
494 | 2 | } |
495 | | #endif /* _WIN32 */ |
496 | | |
497 | | /* Extcap executables are in their own subdirectory. This trims that off and |
498 | | * reduces progfile_dir to the common program file directory. */ |
499 | | static void trim_progfile_dir(void) |
500 | 2 | { |
501 | 2 | char *progfile_last_dir = find_last_pathname_separator(progfile_dir); |
502 | | |
503 | | #ifdef _WIN32 |
504 | | /* |
505 | | * Check the flavor of our extcap subdirectory. |
506 | | * XXX - Do we only need to do this on Windows, or on other platforms too? |
507 | | */ |
508 | | if (progfile_last_dir && strncmp(progfile_last_dir + 1, application_flavor_name_lower(), strlen(application_flavor_name_lower())) == 0) { |
509 | | char* flavor_last_dir = find_last_pathname_separator(progfile_dir); |
510 | | char flavor_sep = *flavor_last_dir; |
511 | | *flavor_last_dir = '\0'; |
512 | | |
513 | | progfile_last_dir = find_last_pathname_separator(progfile_dir); |
514 | | |
515 | | if (!(progfile_last_dir && strncmp(progfile_last_dir + 1, "extcap", sizeof("extcap")) == 0)) { |
516 | | /* |
517 | | * Not an extcap, restore the flavor separator (it might have been |
518 | | * some other "wireshark" directory, especially on case insensitive |
519 | | * filesystems.) |
520 | | */ |
521 | | *flavor_last_dir = flavor_sep; |
522 | | return; |
523 | | } |
524 | | } else |
525 | | #endif |
526 | 2 | if (! (progfile_last_dir && strncmp(progfile_last_dir + 1, "extcap", sizeof("extcap")) == 0)) { |
527 | | /* Check for an unflavored extcap directory. */ |
528 | 2 | return; |
529 | 2 | } |
530 | | |
531 | 0 | *progfile_last_dir = '\0'; |
532 | 0 | char *extcap_progfile_dir = progfile_dir; |
533 | 0 | progfile_dir = g_strdup(extcap_progfile_dir); |
534 | 0 | g_free(extcap_progfile_dir); |
535 | 0 | } |
536 | | |
537 | | #if !defined(_WIN32) || defined(HAVE_MSYSTEM) |
538 | | static char * |
539 | | trim_last_dir_from_path(const char *_path) |
540 | 0 | { |
541 | 0 | char *path = ws_strdup(_path); |
542 | 0 | char *last_dir = find_last_pathname_separator(path); |
543 | 0 | if (last_dir) { |
544 | 0 | *last_dir = '\0'; |
545 | 0 | } |
546 | 0 | return path; |
547 | 0 | } |
548 | | #endif |
549 | | |
550 | | /* |
551 | | * Construct the path name of a non-extcap Wireshark executable file, |
552 | | * given the program name. The executable name doesn't include ".exe"; |
553 | | * append it on Windows, so that callers don't have to worry about that. |
554 | | * |
555 | | * This presumes that all non-extcap executables are in the same directory. |
556 | | * |
557 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
558 | | * caller is done with it. |
559 | | */ |
560 | | char * |
561 | | get_executable_path(const char *program_name) |
562 | 0 | { |
563 | | /* |
564 | | * Fail if we don't know what directory contains the executables. |
565 | | */ |
566 | 0 | if (progfile_dir == NULL) |
567 | 0 | return NULL; |
568 | | |
569 | | #ifdef _WIN32 |
570 | | return ws_strdup_printf("%s\\%s.exe", progfile_dir, program_name); |
571 | | #else |
572 | 0 | return ws_strdup_printf("%s/%s", progfile_dir, program_name); |
573 | 0 | #endif |
574 | 0 | } |
575 | | |
576 | | /* |
577 | | * Get the pathname of the directory from which the executable came, |
578 | | * and save it for future use. Returns NULL on success, and a |
579 | | * g_mallocated string containing an error on failure. |
580 | | */ |
581 | | #ifdef _WIN32 |
582 | | static char * |
583 | | configuration_init_w32(const char* arg0 _U_) |
584 | | { |
585 | | TCHAR prog_pathname_w[_MAX_PATH+2]; |
586 | | char *prog_pathname; |
587 | | DWORD error; |
588 | | TCHAR *msg_w; |
589 | | unsigned char *msg; |
590 | | size_t msglen; |
591 | | |
592 | | /* |
593 | | * Attempt to get the full pathname of the currently running |
594 | | * program. |
595 | | */ |
596 | | if (GetModuleFileName(NULL, prog_pathname_w, G_N_ELEMENTS(prog_pathname_w)) != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
597 | | /* |
598 | | * XXX - Should we use g_utf16_to_utf8()? |
599 | | */ |
600 | | prog_pathname = utf_16to8(prog_pathname_w); |
601 | | /* |
602 | | * We got it; strip off the last component, which would be |
603 | | * the file name of the executable, giving us the pathname |
604 | | * of the directory where the executable resides. |
605 | | */ |
606 | | progfile_dir = g_path_get_dirname(prog_pathname); |
607 | | if (progfile_dir != NULL) { |
608 | | /* We succeeded. */ |
609 | | trim_progfile_dir(); |
610 | | /* Now try to figure out if we're running in a build directory. */ |
611 | | char *wsutil_lib = g_build_filename(progfile_dir, "wsutil.lib", (char *)NULL); |
612 | | if (file_exists(wsutil_lib)) { |
613 | | running_in_build_directory_flag = true; |
614 | | } |
615 | | g_free(wsutil_lib); |
616 | | } else { |
617 | | /* |
618 | | * OK, no. What do we do now? |
619 | | */ |
620 | | return ws_strdup_printf("No \\ in executable pathname \"%s\"", |
621 | | prog_pathname); |
622 | | } |
623 | | } else { |
624 | | /* |
625 | | * Oh, well. Return an indication of the error. |
626 | | */ |
627 | | error = GetLastError(); |
628 | | if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
629 | | NULL, error, 0, (LPTSTR) &msg_w, 0, NULL) == 0) { |
630 | | /* |
631 | | * Gak. We can't format the message. |
632 | | */ |
633 | | return ws_strdup_printf("GetModuleFileName failed: %lu (FormatMessage failed: %lu)", |
634 | | error, GetLastError()); |
635 | | } |
636 | | msg = utf_16to8(msg_w); |
637 | | LocalFree(msg_w); |
638 | | /* |
639 | | * "FormatMessage()" "helpfully" sticks CR/LF at the |
640 | | * end of the message. Get rid of it. |
641 | | */ |
642 | | msglen = strlen(msg); |
643 | | if (msglen >= 2) { |
644 | | msg[msglen - 1] = '\0'; |
645 | | msg[msglen - 2] = '\0'; |
646 | | } |
647 | | return ws_strdup_printf("GetModuleFileName failed: %s (%lu)", |
648 | | msg, error); |
649 | | } |
650 | | |
651 | | #ifdef HAVE_MSYSTEM |
652 | | /* |
653 | | * We already have the program_dir. Find the installation prefix. |
654 | | * This is one level up from the bin_dir. If the program_dir does |
655 | | * not end with "bin" then assume we are running in the build directory |
656 | | * and the "installation prefix" (staging directory) is the same as |
657 | | * the program_dir. |
658 | | */ |
659 | | if (g_str_has_suffix(progfile_dir, _S"bin")) { |
660 | | install_prefix = trim_last_dir_from_path(progfile_dir); |
661 | | } |
662 | | else { |
663 | | install_prefix = g_strdup(progfile_dir); |
664 | | running_in_build_directory_flag = true; |
665 | | } |
666 | | #endif /* HAVE_MSYSTEM */ |
667 | | |
668 | | return NULL; |
669 | | } |
670 | | |
671 | | #else /* !_WIN32 */ |
672 | | |
673 | | static char * |
674 | | configuration_init_posix(const char* arg0) |
675 | 2 | { |
676 | 2 | const char *execname; |
677 | 2 | char *prog_pathname; |
678 | 2 | char *curdir; |
679 | 2 | long path_max; |
680 | 2 | const char *pathstr; |
681 | 2 | const char *path_start, *path_end; |
682 | 2 | size_t path_component_len, path_len; |
683 | 2 | char *retstr; |
684 | 2 | char *path; |
685 | 2 | char *dir_end; |
686 | | |
687 | | /* Hard-coded value used if we cannot obtain the path of the running executable. */ |
688 | 2 | install_prefix = g_strdup(INSTALL_PREFIX); |
689 | | |
690 | | /* |
691 | | * Check whether XXX_RUN_FROM_BUILD_DIRECTORY is set in the |
692 | | * environment; if so, set running_in_build_directory_flag if we |
693 | | * weren't started with special privileges. (If we were started |
694 | | * with special privileges, it's not safe to allow the user to point |
695 | | * us to some other directory; running_in_build_directory_flag, when |
696 | | * set, causes us to look for plugins and the like in the build |
697 | | * directory.) |
698 | | */ |
699 | 2 | char *run_from_envar = configuration_environment_variable("RUN_FROM_BUILD_DIRECTORY"); |
700 | 2 | if (g_getenv(run_from_envar) != NULL && !started_with_special_privs()) { |
701 | 0 | running_in_build_directory_flag = true; |
702 | 0 | } |
703 | 2 | g_free(run_from_envar); |
704 | | |
705 | 2 | execname = get_current_executable_path(); |
706 | 2 | if (execname == NULL) { |
707 | | /* |
708 | | * OK, guess based on argv[0]. |
709 | | */ |
710 | 0 | execname = arg0; |
711 | 0 | } |
712 | | |
713 | | /* |
714 | | * Try to figure out the directory in which the currently running |
715 | | * program resides, given something purporting to be the executable |
716 | | * name (from an OS mechanism or from the argv[0] it was started with). |
717 | | * That might be the absolute path of the program, or a path relative |
718 | | * to the current directory of the process that started it, or |
719 | | * just a name for the program if it was started from the command |
720 | | * line and was searched for in $PATH. It's not guaranteed to be |
721 | | * any of those, however, so there are no guarantees.... |
722 | | */ |
723 | 2 | if (execname[0] == '/') { |
724 | | /* |
725 | | * It's an absolute path. |
726 | | */ |
727 | 2 | prog_pathname = g_strdup(execname); |
728 | 2 | } else if (strchr(execname, '/') != NULL) { |
729 | | /* |
730 | | * It's a relative path, with a directory in it. |
731 | | * Get the current directory, and combine it |
732 | | * with that directory. |
733 | | */ |
734 | 0 | path_max = pathconf(".", _PC_PATH_MAX); |
735 | 0 | if (path_max == -1) { |
736 | | /* |
737 | | * We have no idea how big a buffer to |
738 | | * allocate for the current directory. |
739 | | */ |
740 | 0 | return ws_strdup_printf("pathconf failed: %s\n", |
741 | 0 | g_strerror(errno)); |
742 | 0 | } |
743 | 0 | curdir = (char *)g_malloc(path_max); |
744 | 0 | if (getcwd(curdir, path_max) == NULL) { |
745 | | /* |
746 | | * It failed - give up, and just stick |
747 | | * with DATA_DIR. |
748 | | */ |
749 | 0 | g_free(curdir); |
750 | 0 | return ws_strdup_printf("getcwd failed: %s\n", |
751 | 0 | g_strerror(errno)); |
752 | 0 | } |
753 | 0 | path = ws_strdup_printf("%s/%s", curdir, execname); |
754 | 0 | g_free(curdir); |
755 | 0 | prog_pathname = path; |
756 | 0 | } else { |
757 | | /* |
758 | | * It's just a file name. |
759 | | * Search the path for a file with that name |
760 | | * that's executable. |
761 | | */ |
762 | 0 | prog_pathname = NULL; /* haven't found it yet */ |
763 | 0 | pathstr = g_getenv("PATH"); |
764 | 0 | path_start = pathstr; |
765 | 0 | if (path_start != NULL) { |
766 | 0 | while (*path_start != '\0') { |
767 | 0 | path_end = strchr(path_start, ':'); |
768 | 0 | if (path_end == NULL) |
769 | 0 | path_end = path_start + strlen(path_start); |
770 | 0 | path_component_len = path_end - path_start; |
771 | 0 | path_len = path_component_len + 1 |
772 | 0 | + strlen(execname) + 1; |
773 | 0 | path = (char *)g_malloc(path_len); |
774 | 0 | memcpy(path, path_start, path_component_len); |
775 | 0 | path[path_component_len] = '\0'; |
776 | 0 | (void) g_strlcat(path, "/", path_len); |
777 | 0 | (void) g_strlcat(path, execname, path_len); |
778 | 0 | if (access(path, X_OK) == 0) { |
779 | | /* |
780 | | * Found it! |
781 | | */ |
782 | 0 | prog_pathname = path; |
783 | 0 | break; |
784 | 0 | } |
785 | | |
786 | | /* |
787 | | * That's not it. If there are more |
788 | | * path components to test, try them. |
789 | | */ |
790 | 0 | if (*path_end == ':') |
791 | 0 | path_end++; |
792 | 0 | path_start = path_end; |
793 | 0 | g_free(path); |
794 | 0 | } |
795 | 0 | if (prog_pathname == NULL) { |
796 | | /* |
797 | | * Program not found in path. |
798 | | */ |
799 | 0 | return ws_strdup_printf("\"%s\" not found in \"%s\"", |
800 | 0 | execname, pathstr); |
801 | 0 | } |
802 | 0 | } else { |
803 | | /* |
804 | | * PATH isn't set. |
805 | | * XXX - should we pick a default? |
806 | | */ |
807 | 0 | return g_strdup("PATH isn't set"); |
808 | 0 | } |
809 | 0 | } |
810 | | |
811 | | /* |
812 | | * OK, we have what we think is the pathname |
813 | | * of the program. |
814 | | * |
815 | | * First, find the last "/" in the directory, |
816 | | * as that marks the end of the directory pathname. |
817 | | */ |
818 | 2 | dir_end = strrchr(prog_pathname, '/'); |
819 | 2 | if (dir_end != NULL) { |
820 | | /* |
821 | | * Found it. Strip off the last component, |
822 | | * as that's the path of the program. |
823 | | */ |
824 | 2 | *dir_end = '\0'; |
825 | | |
826 | | /* |
827 | | * Is there a "/run" at the end? |
828 | | */ |
829 | 2 | dir_end = strrchr(prog_pathname, '/'); |
830 | 2 | if (dir_end != NULL) { |
831 | 2 | if (!started_with_special_privs()) { |
832 | | /* |
833 | | * Check for the CMake output directory. As people may name |
834 | | * their directories "run" (really?), also check for the |
835 | | * CMakeCache.txt file before assuming a CMake output dir. |
836 | | */ |
837 | 0 | if (strcmp(dir_end, "/run") == 0) { |
838 | 0 | char *cmake_file; |
839 | 0 | cmake_file = ws_strdup_printf("%.*s/CMakeCache.txt", |
840 | 0 | (int)(dir_end - prog_pathname), |
841 | 0 | prog_pathname); |
842 | 0 | if (file_exists(cmake_file)) |
843 | 0 | running_in_build_directory_flag = true; |
844 | 0 | g_free(cmake_file); |
845 | 0 | } |
846 | | #ifdef ENABLE_APPLICATION_BUNDLE |
847 | | { |
848 | | /* |
849 | | * Scan up the path looking for a component |
850 | | * named "Contents". If we find it, we assume |
851 | | * we're in a bundle, and that the top-level |
852 | | * directory of the bundle is the one containing |
853 | | * "Contents". |
854 | | * |
855 | | * Not all executables are in the Contents/MacOS |
856 | | * directory, so we can't just check for those |
857 | | * in the path and strip them off. |
858 | | * |
859 | | * XXX - should we assume that it's either |
860 | | * Contents/MacOS or Resources/bin? |
861 | | */ |
862 | | char *component_end, *p; |
863 | | |
864 | | component_end = strchr(prog_pathname, '\0'); |
865 | | p = component_end; |
866 | | for (;;) { |
867 | | while (p >= prog_pathname && *p != '/') |
868 | | p--; |
869 | | if (p == prog_pathname) { |
870 | | /* |
871 | | * We're looking at the first component of |
872 | | * the pathname now, so we're definitely |
873 | | * not in a bundle, even if we're in |
874 | | * "/Contents". |
875 | | */ |
876 | | break; |
877 | | } |
878 | | if (strncmp(p, "/Contents", component_end - p) == 0) { |
879 | | /* Found it. */ |
880 | | appbundle_dir = (char *)g_malloc(p - prog_pathname + 1); |
881 | | memcpy(appbundle_dir, prog_pathname, p - prog_pathname); |
882 | | appbundle_dir[p - prog_pathname] = '\0'; |
883 | | break; |
884 | | } |
885 | | component_end = p; |
886 | | p--; |
887 | | } |
888 | | } |
889 | | #endif |
890 | 0 | } |
891 | 2 | } |
892 | | |
893 | | /* |
894 | | * OK, we have the path we want. |
895 | | */ |
896 | 2 | progfile_dir = prog_pathname; |
897 | 2 | trim_progfile_dir(); |
898 | 2 | } else { |
899 | | /* |
900 | | * This "shouldn't happen"; we apparently |
901 | | * have no "/" in the pathname. |
902 | | * Just free up prog_pathname. |
903 | | */ |
904 | 0 | retstr = ws_strdup_printf("No / found in \"%s\"", prog_pathname); |
905 | 0 | g_free(prog_pathname); |
906 | 0 | return retstr; |
907 | 0 | } |
908 | | |
909 | | /* |
910 | | * We already have the program_dir. Find the installation prefix. |
911 | | * This is one level up from the bin_dir. If the program_dir does |
912 | | * not end with "bin" then assume we are running in the build directory |
913 | | * and the "installation prefix" (staging directory) is the same as |
914 | | * the program_dir. |
915 | | */ |
916 | 2 | g_free(install_prefix); |
917 | 2 | if (g_str_has_suffix(progfile_dir, _S"bin")) { |
918 | 0 | install_prefix = trim_last_dir_from_path(progfile_dir); |
919 | 0 | } |
920 | 2 | else { |
921 | 2 | install_prefix = g_strdup(progfile_dir); |
922 | 2 | running_in_build_directory_flag = true; |
923 | 2 | } |
924 | | |
925 | 2 | return NULL; |
926 | 2 | } |
927 | | #endif /* ?_WIN32 */ |
928 | | |
929 | | char * |
930 | | configuration_init(const char* arg0) |
931 | 2 | { |
932 | | #ifdef _WIN32 |
933 | | return configuration_init_w32(arg0); |
934 | | #else |
935 | 2 | return configuration_init_posix(arg0); |
936 | 2 | #endif |
937 | 2 | } |
938 | | |
939 | | /* |
940 | | * Get the directory in which the program resides. |
941 | | */ |
942 | | const char * |
943 | | get_progfile_dir(void) |
944 | 6 | { |
945 | 6 | return progfile_dir; |
946 | 6 | } |
947 | | |
948 | | extern const char * |
949 | | get_current_working_dir(void) |
950 | 0 | { |
951 | 0 | if (current_working_dir != NULL) { |
952 | 0 | return current_working_dir; |
953 | 0 | } |
954 | | |
955 | | /* |
956 | | * It's good to cache this because on Windows Microsoft cautions |
957 | | * against using GetCurrentDirectory except early on, e.g. when |
958 | | * parsing command line options. |
959 | | */ |
960 | 0 | current_working_dir = g_get_current_dir(); |
961 | | /* |
962 | | * The above always returns something, with a fallback, e.g., on macOS |
963 | | * if the program is run from Finder, of G_DIR_SEPARATOR_S. |
964 | | * On Windows when run from a shortcut / taskbar it returns whatever |
965 | | * the "run in" directory is on the shortcut, which is usually the |
966 | | * directory where the program resides, which isn't that useful. |
967 | | * Should we set it to the home directory on macOS or the |
968 | | * "My Documents" folder on Windows in those cases, |
969 | | * as we do in get_persdatafile_dir()? This isn't the default preference |
970 | | * setting so perhaps caveat emptor is ok. |
971 | | */ |
972 | 0 | return current_working_dir; |
973 | 0 | } |
974 | | |
975 | | /* |
976 | | * Get the directory in which the global configuration and data files are |
977 | | * stored. |
978 | | * |
979 | | * On Windows, we use the directory in which the executable for this |
980 | | * process resides. |
981 | | * |
982 | | * On macOS (when executed from an app bundle), use a directory within |
983 | | * that app bundle. |
984 | | * |
985 | | * Otherwise, if the program was executed from the build directory, use the |
986 | | * directory in which the executable for this process resides. In all other |
987 | | * cases, use the DATA_DIR value that was set at compile time. |
988 | | * |
989 | | * XXX - if we ever make libwireshark a real library, used by multiple |
990 | | * applications (more than just TShark and versions of Wireshark with |
991 | | * various UIs), should the configuration files belong to the library |
992 | | * (and be shared by all those applications) or to the applications? |
993 | | * |
994 | | * If they belong to the library, that could be done on UNIX by the |
995 | | * configure script, but it's trickier on Windows, as you can't just |
996 | | * use the pathname of the executable. |
997 | | * |
998 | | * If they belong to the application, that could be done on Windows |
999 | | * by using the pathname of the executable, but we'd have to have it |
1000 | | * passed in as an argument, in some call, on UNIX. |
1001 | | * |
1002 | | * Note that some of those configuration files might be used by code in |
1003 | | * libwireshark, some of them might be used by dissectors (would they |
1004 | | * belong to libwireshark, the application, or a separate library?), |
1005 | | * and some of them might be used by other code (the Wireshark preferences |
1006 | | * file includes resolver preferences that control the behavior of code |
1007 | | * in libwireshark, dissector preferences, and UI preferences, for |
1008 | | * example). |
1009 | | */ |
1010 | | const char * |
1011 | | get_datafile_dir(void) |
1012 | 293 | { |
1013 | 293 | if (datafile_dir != NULL) |
1014 | 291 | return datafile_dir; |
1015 | | |
1016 | 2 | char *data_dir_envar = configuration_environment_variable("DATA_DIR"); |
1017 | 2 | if (g_getenv(data_dir_envar) && !started_with_special_privs()) { |
1018 | | /* |
1019 | | * The user specified a different directory for data files |
1020 | | * and we aren't running with special privileges. |
1021 | | * Let {WIRESHARK,STRATOSHARK}_DATA_DIR take precedence. |
1022 | | * XXX - We might be able to dispense with the priv check |
1023 | | */ |
1024 | 0 | datafile_dir = g_strdup(g_getenv(data_dir_envar)); |
1025 | 0 | } |
1026 | | |
1027 | | #if defined(HAVE_MSYSTEM) |
1028 | | if (running_in_build_directory_flag) { |
1029 | | datafile_dir = g_strdup(install_prefix); |
1030 | | } else { |
1031 | | datafile_dir = g_build_filename(install_prefix, DATA_DIR, application_flavor_name_lower(), (char *)NULL); |
1032 | | } |
1033 | | #elif defined(_WIN32) |
1034 | | /* |
1035 | | * Do we have the pathname of the program? If so, assume we're |
1036 | | * running an installed version of the program. If we fail, |
1037 | | * we don't change "datafile_dir", and thus end up using the |
1038 | | * default. |
1039 | | * |
1040 | | * XXX - does NSIS put the installation directory into |
1041 | | * "\HKEY_LOCAL_MACHINE\SOFTWARE\Wireshark\InstallDir"? |
1042 | | * If so, perhaps we should read that from the registry, |
1043 | | * instead. |
1044 | | */ |
1045 | | if (progfile_dir != NULL) { |
1046 | | /* |
1047 | | * Yes, we do; use that. |
1048 | | */ |
1049 | | datafile_dir = g_strdup(progfile_dir); |
1050 | | } else { |
1051 | | /* |
1052 | | * No, we don't. |
1053 | | * Fall back on the default installation directory. |
1054 | | */ |
1055 | | datafile_dir = g_strdup("C:\\Program Files\\Wireshark\\"); |
1056 | | } |
1057 | | #else |
1058 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1059 | | /* |
1060 | | * If we're running from an app bundle and weren't started |
1061 | | * with special privileges, use the Contents/Resources/share/wireshark |
1062 | | * subdirectory of the app bundle. |
1063 | | * |
1064 | | * (appbundle_dir is not set to a non-null value if we're |
1065 | | * started with special privileges, so we need only check |
1066 | | * it; we don't need to call started_with_special_privs().) |
1067 | | */ |
1068 | | else if (appbundle_dir != NULL) { |
1069 | | datafile_dir = ws_strdup_printf("%s/Contents/Resources/share/%s", |
1070 | | appbundle_dir, application_flavor_name_lower()); |
1071 | | } |
1072 | | #endif |
1073 | 2 | else if (running_in_build_directory_flag && progfile_dir != NULL) { |
1074 | | /* |
1075 | | * We're (probably) being run from the build directory and |
1076 | | * weren't started with special privileges. |
1077 | | * |
1078 | | * (running_in_build_directory_flag is never set to true |
1079 | | * if we're started with special privileges, so we need |
1080 | | * only check it; we don't need to call started_with_special_privs().) |
1081 | | * |
1082 | | * Data files (dtds/, radius/, etc.) are copied to the build |
1083 | | * directory during the build which also contains executables. A special |
1084 | | * exception is macOS (when built with an app bundle). |
1085 | | */ |
1086 | 2 | datafile_dir = g_strdup(progfile_dir); |
1087 | 2 | } else { |
1088 | 0 | if (g_path_is_absolute(DATA_DIR)) { |
1089 | 0 | datafile_dir = g_build_filename(DATA_DIR, application_flavor_name_lower(), (char *)NULL); |
1090 | 0 | } else { |
1091 | 0 | datafile_dir = g_build_filename(install_prefix, DATA_DIR, application_flavor_name_lower(), (char *)NULL); |
1092 | 0 | } |
1093 | 0 | } |
1094 | 2 | #endif |
1095 | 2 | g_free(data_dir_envar); |
1096 | 2 | return datafile_dir; |
1097 | 293 | } |
1098 | | |
1099 | | const char * |
1100 | | get_doc_dir(void) |
1101 | 0 | { |
1102 | 0 | if (doc_dir != NULL) |
1103 | 0 | return doc_dir; |
1104 | | |
1105 | | /* No environment variable for this. */ |
1106 | 0 | if (false) { |
1107 | 0 | ; |
1108 | 0 | } |
1109 | | |
1110 | | #if defined(HAVE_MSYSTEM) |
1111 | | if (running_in_build_directory_flag) { |
1112 | | doc_dir = g_strdup(install_prefix); |
1113 | | } else { |
1114 | | doc_dir = g_build_filename(install_prefix, DOC_DIR, (char *)NULL); |
1115 | | } |
1116 | | #elif defined(_WIN32) |
1117 | | if (progfile_dir != NULL) { |
1118 | | doc_dir = g_strdup(progfile_dir); |
1119 | | } else { |
1120 | | /* Fall back on the default installation directory. */ |
1121 | | doc_dir = g_strdup("C:\\Program Files\\Wireshark\\"); |
1122 | | } |
1123 | | #else |
1124 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1125 | | /* |
1126 | | * If we're running from an app bundle and weren't started |
1127 | | * with special privileges, use the Contents/Resources/share/wireshark |
1128 | | * subdirectory of the app bundle. |
1129 | | * |
1130 | | * (appbundle_dir is not set to a non-null value if we're |
1131 | | * started with special privileges, so we need only check |
1132 | | * it; we don't need to call started_with_special_privs().) |
1133 | | */ |
1134 | | else if (appbundle_dir != NULL) { |
1135 | | doc_dir = g_strdup(get_datafile_dir()); |
1136 | | } |
1137 | | #endif |
1138 | 0 | else if (running_in_build_directory_flag && progfile_dir != NULL) { |
1139 | | /* |
1140 | | * We're (probably) being run from the build directory and |
1141 | | * weren't started with special privileges. |
1142 | | */ |
1143 | 0 | doc_dir = g_strdup(progfile_dir); |
1144 | 0 | } else { |
1145 | 0 | if (g_path_is_absolute(DOC_DIR)) { |
1146 | 0 | doc_dir = g_strdup(DOC_DIR); |
1147 | 0 | } else { |
1148 | 0 | doc_dir = g_build_filename(install_prefix, DOC_DIR, (char *)NULL); |
1149 | 0 | } |
1150 | 0 | } |
1151 | 0 | #endif |
1152 | 0 | return doc_dir; |
1153 | 0 | } |
1154 | | |
1155 | | /* |
1156 | | * Find the directory where the plugins are stored. |
1157 | | * |
1158 | | * On Windows, we use the plugin\{VERSION} subdirectory of the datafile |
1159 | | * directory, where {VERSION} is the version number of this version of |
1160 | | * Wireshark. |
1161 | | * |
1162 | | * On UN*X: |
1163 | | * |
1164 | | * if we appear to be run from the build directory, we use the |
1165 | | * "plugin" subdirectory of the datafile directory; |
1166 | | * |
1167 | | * otherwise, if the WIRESHARK_PLUGIN_DIR environment variable is |
1168 | | * set and we aren't running with special privileges, we use the |
1169 | | * value of that environment variable; |
1170 | | * |
1171 | | * otherwise, if we're running from an app bundle in macOS, we |
1172 | | * use the Contents/PlugIns/wireshark subdirectory of the app bundle; |
1173 | | * |
1174 | | * otherwise, we use the PLUGIN_DIR value supplied by the |
1175 | | * configure script. |
1176 | | */ |
1177 | | static char *plugin_dir; |
1178 | | static char *plugin_dir_with_version; |
1179 | | static char *plugin_pers_dir; |
1180 | | static char *plugin_pers_dir_with_version; |
1181 | | static char *extcap_pers_dir; |
1182 | | |
1183 | | static void |
1184 | | init_plugin_dir(void) |
1185 | 2 | { |
1186 | 2 | char *plugin_dir_envar = configuration_environment_variable("PLUGIN_DIR"); |
1187 | 2 | if (g_getenv(plugin_dir_envar) && !started_with_special_privs()) { |
1188 | | /* |
1189 | | * The user specified a different directory for plugins |
1190 | | * and we aren't running with special privileges. |
1191 | | * Let {WIRESHARK,STRATOSHARK}_PLUGIN_DIR take precedence. |
1192 | | */ |
1193 | 0 | plugin_dir = g_strdup(g_getenv(plugin_dir_envar)); |
1194 | 0 | } |
1195 | | |
1196 | 2 | #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) |
1197 | | #if defined(HAVE_MSYSTEM) |
1198 | | else if (running_in_build_directory_flag) { |
1199 | | plugin_dir = g_build_filename(install_prefix, "plugins", (char *)NULL); |
1200 | | } else { |
1201 | | plugin_dir = g_build_filename(install_prefix, PLUGIN_DIR, (char *)NULL); |
1202 | | } |
1203 | | #elif defined(_WIN32) |
1204 | | else { |
1205 | | /* |
1206 | | * On Windows, plugins are stored under the program file directory |
1207 | | * in both the build and the installation directories. |
1208 | | */ |
1209 | | plugin_dir = g_build_filename(get_progfile_dir(), "plugins", (char *)NULL); |
1210 | | } |
1211 | | #else |
1212 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1213 | | /* |
1214 | | * If we're running from an app bundle and weren't started |
1215 | | * with special privileges, use the Contents/PlugIns/wireshark |
1216 | | * subdirectory of the app bundle. |
1217 | | * |
1218 | | * (appbundle_dir is not set to a non-null value if we're |
1219 | | * started with special privileges, so we need only check |
1220 | | * it; we don't need to call started_with_special_privs().) |
1221 | | */ |
1222 | | else if (appbundle_dir != NULL) { |
1223 | | plugin_dir = g_build_filename(appbundle_dir, "Contents/PlugIns", |
1224 | | application_flavor_name_lower(), (char *)NULL); |
1225 | | } |
1226 | | #endif // ENABLE_APPLICATION_BUNDLE |
1227 | 2 | else if (running_in_build_directory_flag) { |
1228 | | /* |
1229 | | * We're (probably) being run from the build directory and |
1230 | | * weren't started with special privileges, so we'll use |
1231 | | * the "plugins" subdirectory of the directory where the program |
1232 | | * we're running is (that's the build directory). |
1233 | | */ |
1234 | 2 | plugin_dir = g_build_filename(get_progfile_dir(), "plugins", (char *)NULL); |
1235 | 2 | } else { |
1236 | 0 | if (g_path_is_absolute(PLUGIN_DIR)) { |
1237 | 0 | plugin_dir = g_strdup(PLUGIN_DIR); |
1238 | 0 | } else { |
1239 | 0 | plugin_dir = g_build_filename(install_prefix, PLUGIN_DIR, (char *)NULL); |
1240 | 0 | } |
1241 | 0 | } |
1242 | 2 | #endif // HAVE_MSYSTEM / _WIN32 |
1243 | 2 | #endif /* defined(HAVE_PLUGINS) || defined(HAVE_LUA) */ |
1244 | 2 | g_free(plugin_dir_envar); |
1245 | 2 | } |
1246 | | |
1247 | | static void |
1248 | | init_plugin_pers_dir(void) |
1249 | 0 | { |
1250 | 0 | #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) |
1251 | | #ifdef _WIN32 |
1252 | | plugin_pers_dir = get_persconffile_path(PLUGINS_DIR_NAME, false); |
1253 | | #else |
1254 | 0 | plugin_pers_dir = g_build_filename(g_get_home_dir(), ".local/lib", |
1255 | 0 | application_flavor_name_lower(), PLUGINS_DIR_NAME, (char *)NULL); |
1256 | 0 | #endif |
1257 | 0 | #endif /* defined(HAVE_PLUGINS) || defined(HAVE_LUA) */ |
1258 | 0 | } |
1259 | | |
1260 | | /* |
1261 | | * Get the directory in which the plugins are stored. |
1262 | | */ |
1263 | | const char * |
1264 | | get_plugins_dir(void) |
1265 | 0 | { |
1266 | 0 | if (!plugin_dir) |
1267 | 0 | init_plugin_dir(); |
1268 | 0 | return plugin_dir; |
1269 | 0 | } |
1270 | | |
1271 | | const char * |
1272 | | get_plugins_dir_with_version(void) |
1273 | 2 | { |
1274 | 2 | if (!plugin_dir) |
1275 | 2 | init_plugin_dir(); |
1276 | 2 | if (plugin_dir && !plugin_dir_with_version) |
1277 | 2 | plugin_dir_with_version = g_build_filename(plugin_dir, PLUGIN_PATH_ID, (char *)NULL); |
1278 | 2 | return plugin_dir_with_version; |
1279 | 2 | } |
1280 | | |
1281 | | /* Get the personal plugin dir */ |
1282 | | const char * |
1283 | | get_plugins_pers_dir(void) |
1284 | 0 | { |
1285 | 0 | if (!plugin_pers_dir) |
1286 | 0 | init_plugin_pers_dir(); |
1287 | 0 | return plugin_pers_dir; |
1288 | 0 | } |
1289 | | |
1290 | | const char * |
1291 | | get_plugins_pers_dir_with_version(void) |
1292 | 0 | { |
1293 | 0 | if (!plugin_pers_dir) |
1294 | 0 | init_plugin_pers_dir(); |
1295 | 0 | if (plugin_pers_dir && !plugin_pers_dir_with_version) |
1296 | 0 | plugin_pers_dir_with_version = g_build_filename(plugin_pers_dir, PLUGIN_PATH_ID, (char *)NULL); |
1297 | 0 | return plugin_pers_dir_with_version; |
1298 | 0 | } |
1299 | | |
1300 | | /* |
1301 | | * Find the directory where the extcap hooks are stored. |
1302 | | * |
1303 | | * If the WIRESHARK_EXTCAP_DIR environment variable is set and we are not |
1304 | | * running with special privileges, use that. Otherwise: |
1305 | | * |
1306 | | * On Windows, we use the "extcap" subdirectory of the program directory. |
1307 | | * |
1308 | | * On UN*X: |
1309 | | * |
1310 | | * if we appear to be run from the build directory, we use the |
1311 | | * "extcap" subdirectory of the build directory. |
1312 | | * |
1313 | | * otherwise, if we're running from an app bundle in macOS, we |
1314 | | * use the Contents/MacOS/extcap subdirectory of the app bundle; |
1315 | | * |
1316 | | * otherwise, we use the EXTCAP_DIR value supplied by CMake. |
1317 | | */ |
1318 | | static char *extcap_dir; |
1319 | | |
1320 | | static void |
1321 | | init_extcap_dir(void) |
1322 | 0 | { |
1323 | 0 | char *extcap_dir_envar = configuration_environment_variable("EXTCAP_DIR"); |
1324 | 0 | if (g_getenv(extcap_dir_envar) && !started_with_special_privs()) { |
1325 | | /* |
1326 | | * The user specified a different directory for extcap hooks |
1327 | | * and we aren't running with special privileges. |
1328 | | */ |
1329 | 0 | extcap_dir = g_strdup(g_getenv(extcap_dir_envar)); |
1330 | 0 | } |
1331 | | |
1332 | | #if defined(HAVE_MSYSTEM) |
1333 | | else if (running_in_build_directory_flag) { |
1334 | | extcap_dir = g_build_filename(install_prefix, "extcap", (char *)NULL); |
1335 | | } else { |
1336 | | extcap_dir = g_build_filename(install_prefix, EXTCAP_DIR, (char *)NULL); |
1337 | | } |
1338 | | #elif defined(_WIN32) |
1339 | | /* |
1340 | | * On Windows, extcap utilities are stored in "extcap/<program name>" |
1341 | | * in the build directory and in "extcap" in the installation |
1342 | | * directory. |
1343 | | */ |
1344 | | else if (running_in_build_directory_flag) { |
1345 | | extcap_dir = g_build_filename(get_progfile_dir(), EXTCAP_DIR_NAME, |
1346 | | application_flavor_name_lower(), (char *)NULL); |
1347 | | } else { |
1348 | | extcap_dir = g_build_filename(get_progfile_dir(), EXTCAP_DIR_NAME, |
1349 | | (char *)NULL); |
1350 | | } |
1351 | | #else |
1352 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1353 | | else if (appbundle_dir != NULL) { |
1354 | | /* |
1355 | | * If we're running from an app bundle and weren't started |
1356 | | * with special privileges, use the Contents/MacOS/extcap |
1357 | | * subdirectory of the app bundle. |
1358 | | * |
1359 | | * (appbundle_dir is not set to a non-null value if we're |
1360 | | * started with special privileges, so we need only check |
1361 | | * it; we don't need to call started_with_special_privs().) |
1362 | | */ |
1363 | | extcap_dir = g_build_filename(appbundle_dir, "Contents/MacOS/extcap", (char *)NULL); |
1364 | | } |
1365 | | #endif // ENABLE_APPLICATION_BUNDLE |
1366 | 0 | else if (running_in_build_directory_flag) { |
1367 | | /* |
1368 | | * We're (probably) being run from the build directory and |
1369 | | * weren't started with special privileges, so we'll use |
1370 | | * the "extcap hooks" subdirectory of the directory where the program |
1371 | | * we're running is (that's the build directory). |
1372 | | */ |
1373 | 0 | extcap_dir = g_build_filename(get_progfile_dir(), EXTCAP_DIR_NAME, |
1374 | 0 | application_flavor_name_lower(), (char *)NULL); |
1375 | 0 | } |
1376 | 0 | else { |
1377 | 0 | if (g_path_is_absolute(EXTCAP_DIR)) { |
1378 | 0 | extcap_dir = g_strdup(get_application_flavor() == APPLICATION_FLAVOR_WIRESHARK ? EXTCAP_DIR : STRATOSHARK_EXTCAP_DIR); |
1379 | 0 | } else { |
1380 | 0 | extcap_dir = g_build_filename(install_prefix, |
1381 | 0 | get_application_flavor() == APPLICATION_FLAVOR_WIRESHARK ? EXTCAP_DIR : STRATOSHARK_EXTCAP_DIR, (char *)NULL); |
1382 | 0 | } |
1383 | 0 | } |
1384 | 0 | #endif // HAVE_MSYSTEM / _WIN32 |
1385 | 0 | g_free(extcap_dir_envar); |
1386 | 0 | } |
1387 | | |
1388 | | static void |
1389 | | init_extcap_pers_dir(void) |
1390 | 0 | { |
1391 | | #ifdef _WIN32 |
1392 | | extcap_pers_dir = get_persconffile_path(EXTCAP_DIR_NAME, false); |
1393 | | #else |
1394 | 0 | extcap_pers_dir = g_build_filename(g_get_home_dir(), ".local/lib", |
1395 | 0 | application_flavor_name_lower(), EXTCAP_DIR_NAME, (char *)NULL); |
1396 | 0 | #endif |
1397 | 0 | } |
1398 | | |
1399 | | /* |
1400 | | * Get the directory in which the extcap hooks are stored. |
1401 | | * |
1402 | | */ |
1403 | | const char * |
1404 | | get_extcap_dir(void) |
1405 | 0 | { |
1406 | 0 | if (!extcap_dir) |
1407 | 0 | init_extcap_dir(); |
1408 | 0 | return extcap_dir; |
1409 | 0 | } |
1410 | | |
1411 | | /* Get the personal plugin dir */ |
1412 | | const char * |
1413 | | get_extcap_pers_dir(void) |
1414 | 0 | { |
1415 | 0 | if (!extcap_pers_dir) |
1416 | 0 | init_extcap_pers_dir(); |
1417 | 0 | return extcap_pers_dir; |
1418 | 0 | } |
1419 | | |
1420 | | /* |
1421 | | * Get the flag indicating whether we're running from a build |
1422 | | * directory. |
1423 | | */ |
1424 | | bool |
1425 | | running_in_build_directory(void) |
1426 | 0 | { |
1427 | 0 | return running_in_build_directory_flag; |
1428 | 0 | } |
1429 | | |
1430 | | /* |
1431 | | * Get the directory in which files that, at least on UNIX, are |
1432 | | * system files (such as "/etc/ethers") are stored; on Windows, |
1433 | | * there's no "/etc" directory, so we get them from the global |
1434 | | * configuration and data file directory. |
1435 | | */ |
1436 | | const char * |
1437 | | get_systemfile_dir(void) |
1438 | 8 | { |
1439 | | #ifdef _WIN32 |
1440 | | return get_datafile_dir(); |
1441 | | #else |
1442 | 8 | return "/etc"; |
1443 | 8 | #endif |
1444 | 8 | } |
1445 | | |
1446 | | void |
1447 | | set_profile_name(const char *profilename) |
1448 | 0 | { |
1449 | 0 | g_free (persconfprofile); |
1450 | |
|
1451 | 0 | if (profilename && strlen(profilename) > 0 && |
1452 | 0 | strcmp(profilename, DEFAULT_PROFILE) != 0) { |
1453 | 0 | persconfprofile = g_strdup (profilename); |
1454 | 0 | } else { |
1455 | | /* Default Profile */ |
1456 | 0 | persconfprofile = NULL; |
1457 | 0 | } |
1458 | 0 | } |
1459 | | |
1460 | | const char * |
1461 | | get_profile_name(void) |
1462 | 0 | { |
1463 | 0 | if (persconfprofile) { |
1464 | 0 | return persconfprofile; |
1465 | 0 | } else { |
1466 | 0 | return DEFAULT_PROFILE; |
1467 | 0 | } |
1468 | 0 | } |
1469 | | |
1470 | | bool |
1471 | | is_default_profile(void) |
1472 | 0 | { |
1473 | 0 | return (!persconfprofile || strcmp(persconfprofile, DEFAULT_PROFILE) == 0) ? true : false; |
1474 | 0 | } |
1475 | | |
1476 | | bool |
1477 | | has_global_profiles(void) |
1478 | 0 | { |
1479 | 0 | WS_DIR *dir; |
1480 | 0 | WS_DIRENT *file; |
1481 | 0 | char *global_dir = get_global_profiles_dir(); |
1482 | 0 | char *filename; |
1483 | 0 | bool has_global = false; |
1484 | |
|
1485 | 0 | if ((test_for_directory(global_dir) == EISDIR) && |
1486 | 0 | ((dir = ws_dir_open(global_dir, 0, NULL)) != NULL)) |
1487 | 0 | { |
1488 | 0 | while ((file = ws_dir_read_name(dir)) != NULL) { |
1489 | 0 | filename = ws_strdup_printf ("%s%s%s", global_dir, G_DIR_SEPARATOR_S, |
1490 | 0 | ws_dir_get_name(file)); |
1491 | 0 | if (test_for_directory(filename) == EISDIR) { |
1492 | 0 | has_global = true; |
1493 | 0 | g_free (filename); |
1494 | 0 | break; |
1495 | 0 | } |
1496 | 0 | g_free (filename); |
1497 | 0 | } |
1498 | 0 | ws_dir_close(dir); |
1499 | 0 | } |
1500 | 0 | g_free(global_dir); |
1501 | 0 | return has_global; |
1502 | 0 | } |
1503 | | |
1504 | | void |
1505 | | profile_store_persconffiles(bool store) |
1506 | 0 | { |
1507 | 0 | if (store) { |
1508 | 0 | profile_files = g_hash_table_new (g_str_hash, g_str_equal); |
1509 | 0 | } |
1510 | 0 | do_store_persconffiles = store; |
1511 | 0 | } |
1512 | | |
1513 | | void |
1514 | | profile_register_persconffile(const char *filename) |
1515 | 308 | { |
1516 | 308 | if (do_store_persconffiles && !g_hash_table_lookup (profile_files, filename)) { |
1517 | | /* Store filenames so we know which filenames belongs to a configuration profile */ |
1518 | 0 | g_hash_table_insert (profile_files, g_strdup(filename), g_strdup(filename)); |
1519 | 0 | } |
1520 | 308 | } |
1521 | | |
1522 | | /* |
1523 | | * Get the directory in which personal configuration files reside. |
1524 | | * |
1525 | | * On Windows, it's "Wireshark", under %APPDATA% or, if %APPDATA% isn't set, |
1526 | | * it's "%USERPROFILE%\Application Data" (which is what %APPDATA% normally |
1527 | | * is on Windows 2000). |
1528 | | * |
1529 | | * On UNIX-compatible systems, we first look in XDG_CONFIG_HOME/wireshark |
1530 | | * and, if that doesn't exist, ~/.wireshark, for backwards compatibility. |
1531 | | * If neither exists, we use XDG_CONFIG_HOME/wireshark, so that the directory |
1532 | | * is initially created as XDG_CONFIG_HOME/wireshark. We use that regardless |
1533 | | * of whether the user is running under an XDG desktop or not, so that |
1534 | | * if the user's home directory is on a server and shared between |
1535 | | * different desktop environments on different machines, they can all |
1536 | | * share the same configuration file directory. |
1537 | | * |
1538 | | * XXX - what about stuff that shouldn't be shared between machines, |
1539 | | * such as plugins in the form of shared loadable images? |
1540 | | */ |
1541 | | static const char * |
1542 | | get_persconffile_dir_no_profile(void) |
1543 | 340 | { |
1544 | 340 | const char *env; |
1545 | | |
1546 | | /* Return the cached value, if available */ |
1547 | 340 | if (persconffile_dir != NULL) |
1548 | 338 | return persconffile_dir; |
1549 | | |
1550 | | /* |
1551 | | * See if the user has selected an alternate environment. |
1552 | | */ |
1553 | 2 | char *config_dir_envar = configuration_environment_variable("CONFIG_DIR"); |
1554 | 2 | env = g_getenv(config_dir_envar); |
1555 | 2 | g_free(config_dir_envar); |
1556 | | #ifdef _WIN32 |
1557 | | if (env == NULL) { |
1558 | | /* |
1559 | | * The PortableApps launcher sets this environment variable. |
1560 | | * XXX - That's only for the GUI. We don't have launchers/batch |
1561 | | * scripts for the command line tools, and just package the same |
1562 | | * binaries as built for NSIS and WiX, so if the user is running |
1563 | | * tshark from the PortableApps directory, how do we tell? (#20095) |
1564 | | */ |
1565 | | env = g_getenv("WIRESHARK_APPDATA"); |
1566 | | } |
1567 | | #endif |
1568 | 2 | if (env != NULL) { |
1569 | 0 | persconffile_dir = g_strdup(env); |
1570 | 0 | return persconffile_dir; |
1571 | 0 | } |
1572 | | |
1573 | | #ifdef _WIN32 |
1574 | | /* |
1575 | | * Use %APPDATA% or %USERPROFILE%, so that configuration |
1576 | | * files are stored in the user profile, rather than in |
1577 | | * the home directory. The Windows convention is to store |
1578 | | * configuration information in the user profile, and doing |
1579 | | * so means you can use Wireshark even if the home directory |
1580 | | * is an inaccessible network drive. |
1581 | | */ |
1582 | | env = g_getenv("APPDATA"); |
1583 | | if (env != NULL) { |
1584 | | /* |
1585 | | * Concatenate %APPDATA% with "\Wireshark" or "\Stratoshark". |
1586 | | */ |
1587 | | persconffile_dir = g_build_filename(env, application_flavor_name_proper(), NULL); |
1588 | | return persconffile_dir; |
1589 | | } |
1590 | | |
1591 | | /* |
1592 | | * OK, %APPDATA% wasn't set, so use %USERPROFILE%\Application Data. |
1593 | | */ |
1594 | | env = g_getenv("USERPROFILE"); |
1595 | | if (env != NULL) { |
1596 | | persconffile_dir = g_build_filename(env, "Application Data", application_flavor_name_proper(), NULL); |
1597 | | return persconffile_dir; |
1598 | | } |
1599 | | |
1600 | | /* |
1601 | | * Give up and use "C:". |
1602 | | */ |
1603 | | persconffile_dir = g_build_filename("C:", application_flavor_name_proper(), NULL); |
1604 | | return persconffile_dir; |
1605 | | #else |
1606 | 2 | char *xdg_path, *path; |
1607 | 2 | struct passwd *pwd; |
1608 | 2 | const char *homedir; |
1609 | | |
1610 | | /* |
1611 | | * Check if XDG_CONFIG_HOME/wireshark exists and is a directory. |
1612 | | */ |
1613 | 2 | xdg_path = g_build_filename(g_get_user_config_dir(), |
1614 | 2 | application_flavor_name_lower(), NULL); |
1615 | 2 | if (g_file_test(xdg_path, G_FILE_TEST_IS_DIR)) { |
1616 | 0 | persconffile_dir = xdg_path; |
1617 | 0 | return persconffile_dir; |
1618 | 0 | } |
1619 | | |
1620 | | /* |
1621 | | * It doesn't exist, or it does but isn't a directory, so try |
1622 | | * ~/.wireshark. |
1623 | | * |
1624 | | * If $HOME is set, use that for ~. |
1625 | | * |
1626 | | * (Note: before GLib 2.36, g_get_home_dir() didn't look at $HOME, |
1627 | | * but we always want to do so, so we don't use g_get_home_dir().) |
1628 | | */ |
1629 | 2 | homedir = g_getenv("HOME"); |
1630 | 2 | if (homedir == NULL) { |
1631 | | /* |
1632 | | * It's not set. |
1633 | | * |
1634 | | * Get their home directory from the password file. |
1635 | | * If we can't even find a password file entry for them, |
1636 | | * use "/tmp". |
1637 | | */ |
1638 | 0 | pwd = getpwuid(getuid()); |
1639 | 0 | if (pwd != NULL) { |
1640 | 0 | homedir = pwd->pw_dir; |
1641 | 0 | } else { |
1642 | 0 | homedir = "/tmp"; |
1643 | 0 | } |
1644 | 0 | } |
1645 | 2 | char *dotted_app = g_strdup_printf(".%s", application_flavor_name_lower()); |
1646 | 2 | path = g_build_filename(homedir, dotted_app, NULL); |
1647 | 2 | g_free(dotted_app); |
1648 | 2 | if (g_file_test(path, G_FILE_TEST_IS_DIR)) { |
1649 | 0 | g_free(xdg_path); |
1650 | 0 | persconffile_dir = path; |
1651 | 0 | return persconffile_dir; |
1652 | 0 | } |
1653 | | |
1654 | | /* |
1655 | | * Neither are directories that exist; use the XDG path, so we'll |
1656 | | * create that as necessary. |
1657 | | */ |
1658 | 2 | g_free(path); |
1659 | 2 | persconffile_dir = xdg_path; |
1660 | 2 | return persconffile_dir; |
1661 | 2 | #endif |
1662 | 2 | } |
1663 | | |
1664 | | void |
1665 | | set_persconffile_dir(const char *p) |
1666 | 0 | { |
1667 | 0 | g_free(persconffile_dir); |
1668 | 0 | persconffile_dir = g_strdup(p); |
1669 | 0 | } |
1670 | | |
1671 | | char * |
1672 | | get_profiles_dir(void) |
1673 | 0 | { |
1674 | 0 | return ws_strdup_printf ("%s%s%s", get_persconffile_dir_no_profile (), |
1675 | 0 | G_DIR_SEPARATOR_S, PROFILES_DIR); |
1676 | 0 | } |
1677 | | |
1678 | | int |
1679 | | create_profiles_dir(char **pf_dir_path_return) |
1680 | 0 | { |
1681 | 0 | char *pf_dir_path; |
1682 | 0 | ws_statb64 s_buf; |
1683 | | |
1684 | | /* |
1685 | | * Create the "Default" personal configuration files directory, if necessary. |
1686 | | */ |
1687 | 0 | if (create_persconffile_profile (NULL, pf_dir_path_return) == -1) { |
1688 | 0 | return -1; |
1689 | 0 | } |
1690 | | |
1691 | | /* |
1692 | | * Check if profiles directory exists. |
1693 | | * If not then create it. |
1694 | | */ |
1695 | 0 | pf_dir_path = get_profiles_dir (); |
1696 | 0 | if (ws_stat64(pf_dir_path, &s_buf) != 0) { |
1697 | 0 | if (errno != ENOENT) { |
1698 | | /* Some other problem; give up now. */ |
1699 | 0 | *pf_dir_path_return = pf_dir_path; |
1700 | 0 | return -1; |
1701 | 0 | } |
1702 | | |
1703 | | /* |
1704 | | * It doesn't exist; try to create it. |
1705 | | */ |
1706 | 0 | int ret = ws_mkdir(pf_dir_path, 0755); |
1707 | 0 | if (ret == -1) { |
1708 | 0 | *pf_dir_path_return = pf_dir_path; |
1709 | 0 | return ret; |
1710 | 0 | } |
1711 | 0 | } |
1712 | 0 | g_free(pf_dir_path); |
1713 | |
|
1714 | 0 | return 0; |
1715 | 0 | } |
1716 | | |
1717 | | char * |
1718 | | get_global_profiles_dir(void) |
1719 | 0 | { |
1720 | 0 | return ws_strdup_printf ("%s%s%s", get_datafile_dir(), |
1721 | 0 | G_DIR_SEPARATOR_S, PROFILES_DIR); |
1722 | 0 | } |
1723 | | |
1724 | | static char * |
1725 | | get_persconffile_dir(const char *profilename) |
1726 | 340 | { |
1727 | 340 | char *persconffile_profile_dir = NULL, *profile_dir; |
1728 | | |
1729 | 340 | if (profilename && strlen(profilename) > 0 && |
1730 | 340 | strcmp(profilename, DEFAULT_PROFILE) != 0) { |
1731 | 0 | profile_dir = get_profiles_dir(); |
1732 | 0 | persconffile_profile_dir = ws_strdup_printf ("%s%s%s", profile_dir, |
1733 | 0 | G_DIR_SEPARATOR_S, profilename); |
1734 | 0 | g_free(profile_dir); |
1735 | 340 | } else { |
1736 | 340 | persconffile_profile_dir = g_strdup (get_persconffile_dir_no_profile ()); |
1737 | 340 | } |
1738 | | |
1739 | 340 | return persconffile_profile_dir; |
1740 | 340 | } |
1741 | | |
1742 | | char * |
1743 | | get_profile_dir(const char *profilename, bool is_global) |
1744 | 0 | { |
1745 | 0 | char *profile_dir; |
1746 | |
|
1747 | 0 | if (is_global) { |
1748 | 0 | if (profilename && strlen(profilename) > 0 && |
1749 | 0 | strcmp(profilename, DEFAULT_PROFILE) != 0) |
1750 | 0 | { |
1751 | 0 | char *global_path = get_global_profiles_dir(); |
1752 | 0 | profile_dir = g_build_filename(global_path, profilename, NULL); |
1753 | 0 | g_free(global_path); |
1754 | 0 | } else { |
1755 | 0 | profile_dir = g_strdup(get_datafile_dir()); |
1756 | 0 | } |
1757 | 0 | } else { |
1758 | | /* |
1759 | | * If we didn't supply a profile name, i.e. if profilename is |
1760 | | * null, get_persconffile_dir() returns the default profile. |
1761 | | */ |
1762 | 0 | profile_dir = get_persconffile_dir(profilename); |
1763 | 0 | } |
1764 | |
|
1765 | 0 | return profile_dir; |
1766 | 0 | } |
1767 | | |
1768 | | bool |
1769 | | profile_exists(const char *profilename, bool global) |
1770 | 0 | { |
1771 | 0 | char *path = NULL; |
1772 | 0 | bool exists; |
1773 | | |
1774 | | /* |
1775 | | * If we're looking up a global profile, we must have a |
1776 | | * profile name. |
1777 | | */ |
1778 | 0 | if (global && !profilename) |
1779 | 0 | return false; |
1780 | | |
1781 | 0 | path = get_profile_dir(profilename, global); |
1782 | 0 | exists = (test_for_directory(path) == EISDIR) ? true : false; |
1783 | |
|
1784 | 0 | g_free(path); |
1785 | 0 | return exists; |
1786 | 0 | } |
1787 | | |
1788 | | static int |
1789 | | delete_directory (const char *directory, char **pf_dir_path_return) |
1790 | 0 | { |
1791 | 0 | WS_DIR *dir; |
1792 | 0 | WS_DIRENT *file; |
1793 | 0 | char *filename; |
1794 | 0 | int ret = 0; |
1795 | |
|
1796 | 0 | if ((dir = ws_dir_open(directory, 0, NULL)) != NULL) { |
1797 | 0 | while ((file = ws_dir_read_name(dir)) != NULL) { |
1798 | 0 | filename = ws_strdup_printf ("%s%s%s", directory, G_DIR_SEPARATOR_S, |
1799 | 0 | ws_dir_get_name(file)); |
1800 | 0 | if (test_for_directory(filename) != EISDIR) { |
1801 | 0 | ret = ws_remove(filename); |
1802 | | #if 0 |
1803 | | } else { |
1804 | | /* The user has manually created a directory in the profile directory */ |
1805 | | /* I do not want to delete the directory recursively yet */ |
1806 | | ret = delete_directory (filename, pf_dir_path_return); |
1807 | | #endif |
1808 | 0 | } |
1809 | 0 | if (ret != 0) { |
1810 | 0 | *pf_dir_path_return = filename; |
1811 | 0 | break; |
1812 | 0 | } |
1813 | 0 | g_free (filename); |
1814 | 0 | } |
1815 | 0 | ws_dir_close(dir); |
1816 | 0 | } |
1817 | |
|
1818 | 0 | if (ret == 0 && (ret = ws_remove(directory)) != 0) { |
1819 | 0 | *pf_dir_path_return = g_strdup (directory); |
1820 | 0 | } |
1821 | |
|
1822 | 0 | return ret; |
1823 | 0 | } |
1824 | | |
1825 | | /* Copy files from one directory to another. Does not recursively copy directories */ |
1826 | | static int |
1827 | | copy_directory(const char *from_dir, const char *to_dir, char **pf_filename_return) |
1828 | 0 | { |
1829 | 0 | int ret = 0; |
1830 | 0 | char *from_file, *to_file; |
1831 | 0 | const char *filename; |
1832 | 0 | WS_DIR *dir; |
1833 | 0 | WS_DIRENT *file; |
1834 | |
|
1835 | 0 | if ((dir = ws_dir_open(from_dir, 0, NULL)) != NULL) { |
1836 | 0 | while ((file = ws_dir_read_name(dir)) != NULL) { |
1837 | 0 | filename = ws_dir_get_name(file); |
1838 | 0 | from_file = ws_strdup_printf ("%s%s%s", from_dir, G_DIR_SEPARATOR_S, filename); |
1839 | 0 | if (test_for_directory(from_file) != EISDIR) { |
1840 | 0 | to_file = ws_strdup_printf ("%s%s%s", to_dir, G_DIR_SEPARATOR_S, filename); |
1841 | 0 | if (!copy_file_binary_mode(from_file, to_file)) { |
1842 | 0 | *pf_filename_return = g_strdup(filename); |
1843 | 0 | g_free (from_file); |
1844 | 0 | g_free (to_file); |
1845 | 0 | ret = -1; |
1846 | 0 | break; |
1847 | 0 | } |
1848 | 0 | g_free (to_file); |
1849 | | #if 0 |
1850 | | } else { |
1851 | | /* The user has manually created a directory in the profile |
1852 | | * directory. Do not copy the directory recursively (yet?) |
1853 | | */ |
1854 | | #endif |
1855 | 0 | } |
1856 | 0 | g_free (from_file); |
1857 | 0 | } |
1858 | 0 | ws_dir_close(dir); |
1859 | 0 | } |
1860 | |
|
1861 | 0 | return ret; |
1862 | 0 | } |
1863 | | |
1864 | | static int |
1865 | | reset_default_profile(char **pf_dir_path_return) |
1866 | 0 | { |
1867 | 0 | char *profile_dir = get_persconffile_dir(NULL); |
1868 | 0 | char *filename, *del_file; |
1869 | 0 | GList *files, *file; |
1870 | 0 | int ret = 0; |
1871 | |
|
1872 | 0 | files = g_hash_table_get_keys(profile_files); |
1873 | 0 | file = g_list_first(files); |
1874 | 0 | while (file) { |
1875 | 0 | filename = (char *)file->data; |
1876 | 0 | del_file = ws_strdup_printf("%s%s%s", profile_dir, G_DIR_SEPARATOR_S, filename); |
1877 | |
|
1878 | 0 | if (file_exists(del_file)) { |
1879 | 0 | ret = ws_remove(del_file); |
1880 | 0 | if (ret != 0) { |
1881 | 0 | *pf_dir_path_return = profile_dir; |
1882 | 0 | g_free(del_file); |
1883 | 0 | break; |
1884 | 0 | } |
1885 | 0 | } |
1886 | | |
1887 | 0 | g_free(del_file); |
1888 | 0 | file = g_list_next(file); |
1889 | 0 | } |
1890 | 0 | g_list_free(files); |
1891 | |
|
1892 | 0 | g_free(profile_dir); |
1893 | 0 | return ret; |
1894 | 0 | } |
1895 | | |
1896 | | int |
1897 | | delete_persconffile_profile(const char *profilename, char **pf_dir_path_return) |
1898 | 0 | { |
1899 | 0 | if (strcmp(profilename, DEFAULT_PROFILE) == 0) { |
1900 | 0 | return reset_default_profile(pf_dir_path_return); |
1901 | 0 | } |
1902 | | |
1903 | 0 | char *profile_dir = get_persconffile_dir(profilename); |
1904 | 0 | int ret = 0; |
1905 | |
|
1906 | 0 | if (test_for_directory (profile_dir) == EISDIR) { |
1907 | 0 | ret = delete_directory (profile_dir, pf_dir_path_return); |
1908 | 0 | } |
1909 | |
|
1910 | 0 | g_free(profile_dir); |
1911 | 0 | return ret; |
1912 | 0 | } |
1913 | | |
1914 | | int |
1915 | | rename_persconffile_profile(const char *fromname, const char *toname, |
1916 | | char **pf_from_dir_path_return, char **pf_to_dir_path_return) |
1917 | 0 | { |
1918 | 0 | char *from_dir = get_persconffile_dir(fromname); |
1919 | 0 | char *to_dir = get_persconffile_dir(toname); |
1920 | 0 | int ret = 0; |
1921 | |
|
1922 | 0 | ret = ws_rename (from_dir, to_dir); |
1923 | 0 | if (ret != 0) { |
1924 | 0 | *pf_from_dir_path_return = from_dir; |
1925 | 0 | *pf_to_dir_path_return = to_dir; |
1926 | 0 | return ret; |
1927 | 0 | } |
1928 | | |
1929 | 0 | g_free (from_dir); |
1930 | 0 | g_free (to_dir); |
1931 | |
|
1932 | 0 | return 0; |
1933 | 0 | } |
1934 | | |
1935 | | /* |
1936 | | * Create the directory that holds personal configuration files, if |
1937 | | * necessary. If we attempted to create it, and failed, return -1 and |
1938 | | * set "*pf_dir_path_return" to the pathname of the directory we failed |
1939 | | * to create (it's g_mallocated, so our caller should free it); otherwise, |
1940 | | * return 0. |
1941 | | */ |
1942 | | int |
1943 | | create_persconffile_profile(const char *profilename, char **pf_dir_path_return) |
1944 | 0 | { |
1945 | 0 | char *pf_dir_path; |
1946 | | #ifdef _WIN32 |
1947 | | char *pf_dir_path_copy, *pf_dir_parent_path; |
1948 | | size_t pf_dir_parent_path_len; |
1949 | | int save_errno; |
1950 | | #endif |
1951 | 0 | ws_statb64 s_buf; |
1952 | 0 | int ret; |
1953 | |
|
1954 | 0 | if (profilename) { |
1955 | | /* |
1956 | | * Create the personal profiles directory, if necessary. |
1957 | | */ |
1958 | 0 | if (create_profiles_dir(pf_dir_path_return) == -1) { |
1959 | 0 | return -1; |
1960 | 0 | } |
1961 | 0 | } |
1962 | | |
1963 | 0 | pf_dir_path = get_persconffile_dir(profilename); |
1964 | 0 | if (ws_stat64(pf_dir_path, &s_buf) != 0) { |
1965 | 0 | if (errno != ENOENT) { |
1966 | | /* Some other problem; give up now. */ |
1967 | 0 | *pf_dir_path_return = pf_dir_path; |
1968 | 0 | return -1; |
1969 | 0 | } |
1970 | | #ifdef _WIN32 |
1971 | | /* |
1972 | | * Does the parent directory of that directory |
1973 | | * exist? %APPDATA% may not exist even though |
1974 | | * %USERPROFILE% does. |
1975 | | * |
1976 | | * We check for the existence of the directory |
1977 | | * by first checking whether the parent directory |
1978 | | * is just a drive letter and, if it's not, by |
1979 | | * doing a "stat()" on it. If it's a drive letter, |
1980 | | * or if the "stat()" succeeds, we assume it exists. |
1981 | | */ |
1982 | | pf_dir_path_copy = g_strdup(pf_dir_path); |
1983 | | pf_dir_parent_path = get_dirname(pf_dir_path_copy); |
1984 | | pf_dir_parent_path_len = strlen(pf_dir_parent_path); |
1985 | | if (pf_dir_parent_path_len > 0 |
1986 | | && pf_dir_parent_path[pf_dir_parent_path_len - 1] != ':' |
1987 | | && ws_stat64(pf_dir_parent_path, &s_buf) != 0) { |
1988 | | /* |
1989 | | * Not a drive letter and the stat() failed. |
1990 | | */ |
1991 | | if (errno != ENOENT) { |
1992 | | /* Some other problem; give up now. */ |
1993 | | *pf_dir_path_return = pf_dir_path; |
1994 | | save_errno = errno; |
1995 | | g_free(pf_dir_path_copy); |
1996 | | errno = save_errno; |
1997 | | return -1; |
1998 | | } |
1999 | | /* |
2000 | | * No, it doesn't exist - make it first. |
2001 | | */ |
2002 | | ret = ws_mkdir(pf_dir_parent_path, 0755); |
2003 | | if (ret == -1) { |
2004 | | *pf_dir_path_return = pf_dir_parent_path; |
2005 | | save_errno = errno; |
2006 | | g_free(pf_dir_path); |
2007 | | errno = save_errno; |
2008 | | return -1; |
2009 | | } |
2010 | | } |
2011 | | g_free(pf_dir_path_copy); |
2012 | | ret = ws_mkdir(pf_dir_path, 0755); |
2013 | | #else |
2014 | 0 | ret = g_mkdir_with_parents(pf_dir_path, 0755); |
2015 | 0 | #endif |
2016 | 0 | } else { |
2017 | | /* |
2018 | | * Something with that pathname exists; if it's not |
2019 | | * a directory, we'll get an error if we try to put |
2020 | | * something in it, so we don't fail here, we wait |
2021 | | * for that attempt to fail. |
2022 | | */ |
2023 | 0 | ret = 0; |
2024 | 0 | } |
2025 | 0 | if (ret == -1) |
2026 | 0 | *pf_dir_path_return = pf_dir_path; |
2027 | 0 | else |
2028 | 0 | g_free(pf_dir_path); |
2029 | |
|
2030 | 0 | return ret; |
2031 | 0 | } |
2032 | | |
2033 | | const GHashTable * |
2034 | | allowed_profile_filenames(void) |
2035 | 0 | { |
2036 | 0 | return profile_files; |
2037 | 0 | } |
2038 | | |
2039 | | int |
2040 | | create_persconffile_dir(char **pf_dir_path_return) |
2041 | 0 | { |
2042 | 0 | return create_persconffile_profile(persconfprofile, pf_dir_path_return); |
2043 | 0 | } |
2044 | | |
2045 | | int |
2046 | | copy_persconffile_profile(const char *toname, const char *fromname, bool from_global, |
2047 | | char **pf_filename_return, char **pf_to_dir_path_return, char **pf_from_dir_path_return) |
2048 | 0 | { |
2049 | 0 | int ret = 0; |
2050 | 0 | char *from_dir; |
2051 | 0 | char *to_dir = get_persconffile_dir(toname); |
2052 | 0 | char *from_file, *to_file; |
2053 | 0 | const char *filename; |
2054 | 0 | GHashTableIter files; |
2055 | 0 | void * file; |
2056 | |
|
2057 | 0 | from_dir = get_profile_dir(fromname, from_global); |
2058 | |
|
2059 | 0 | if (!profile_files || do_store_persconffiles) { |
2060 | | /* Either the profile_files hashtable does not exist yet |
2061 | | * (this is very early in startup) or we are still adding |
2062 | | * files to it. Just copy all the non-directories. |
2063 | | */ |
2064 | 0 | ret = copy_directory(from_dir, to_dir, pf_filename_return); |
2065 | 0 | } else { |
2066 | |
|
2067 | 0 | g_hash_table_iter_init(&files, profile_files); |
2068 | 0 | while (g_hash_table_iter_next(&files, &file, NULL)) { |
2069 | 0 | filename = (const char *)file; |
2070 | 0 | from_file = ws_strdup_printf ("%s%s%s", from_dir, G_DIR_SEPARATOR_S, filename); |
2071 | 0 | to_file = ws_strdup_printf ("%s%s%s", to_dir, G_DIR_SEPARATOR_S, filename); |
2072 | |
|
2073 | 0 | if (test_for_regular_file(from_file) && !copy_file_binary_mode(from_file, to_file)) { |
2074 | 0 | *pf_filename_return = g_strdup(filename); |
2075 | 0 | g_free (from_file); |
2076 | 0 | g_free (to_file); |
2077 | 0 | ret = -1; |
2078 | 0 | break; |
2079 | 0 | } |
2080 | | |
2081 | 0 | g_free (to_file); |
2082 | 0 | g_free (from_file); |
2083 | 0 | } |
2084 | 0 | } |
2085 | |
|
2086 | 0 | if (ret != 0) { |
2087 | 0 | *pf_to_dir_path_return = to_dir; |
2088 | 0 | *pf_from_dir_path_return = from_dir; |
2089 | 0 | } else { |
2090 | 0 | g_free (to_dir); |
2091 | 0 | g_free (from_dir); |
2092 | 0 | } |
2093 | |
|
2094 | 0 | return ret; |
2095 | 0 | } |
2096 | | |
2097 | | /* |
2098 | | * Get the (default) directory in which personal data is stored. |
2099 | | * |
2100 | | * On Win32, this is the "My Documents" folder in the personal profile. |
2101 | | * On UNIX this is simply the current directory, unless that's "/", |
2102 | | * which it will be, for example, when Wireshark is run from the |
2103 | | * Finder in macOS, in which case we use the user's home directory. |
2104 | | */ |
2105 | | /* XXX - should this and the get_home_dir() be merged? */ |
2106 | | extern const char * |
2107 | | get_persdatafile_dir(void) |
2108 | 4 | { |
2109 | | /* Return the cached value, if available */ |
2110 | 4 | if (persdatafile_dir != NULL) |
2111 | 2 | return persdatafile_dir; |
2112 | | |
2113 | | #ifdef _WIN32 |
2114 | | TCHAR tszPath[MAX_PATH]; |
2115 | | |
2116 | | /* |
2117 | | * Hint: SHGetFolderPath is not available on MSVC 6 - without |
2118 | | * Platform SDK |
2119 | | */ |
2120 | | if (SHGetSpecialFolderPath(NULL, tszPath, CSIDL_PERSONAL, false)) { |
2121 | | persdatafile_dir = g_utf16_to_utf8(tszPath, -1, NULL, NULL, NULL); |
2122 | | return persdatafile_dir; |
2123 | | } else { |
2124 | | return ""; |
2125 | | } |
2126 | | #else |
2127 | | /* |
2128 | | * Get the current directory. |
2129 | | */ |
2130 | 2 | persdatafile_dir = g_get_current_dir(); |
2131 | 2 | if (persdatafile_dir == NULL) { |
2132 | | /* XXX - can this fail? */ |
2133 | | /* |
2134 | | * g_get_home_dir() returns a const gchar *; g_strdup() it |
2135 | | * so that it's something that can be freed. |
2136 | | */ |
2137 | 0 | persdatafile_dir = g_strdup(g_get_home_dir()); |
2138 | 2 | } else if (strcmp(persdatafile_dir, "/") == 0) { |
2139 | 0 | g_free(persdatafile_dir); |
2140 | | /* |
2141 | | * See above. |
2142 | | */ |
2143 | 0 | persdatafile_dir = g_strdup(g_get_home_dir()); |
2144 | 0 | } |
2145 | 2 | return persdatafile_dir; |
2146 | 4 | #endif |
2147 | 4 | } |
2148 | | |
2149 | | void |
2150 | | set_persdatafile_dir(const char *p) |
2151 | 0 | { |
2152 | 0 | g_free(persdatafile_dir); |
2153 | 0 | persdatafile_dir = g_strdup(p); |
2154 | 0 | } |
2155 | | |
2156 | | /* |
2157 | | * Construct the path name of a personal configuration file, given the |
2158 | | * file name. |
2159 | | * |
2160 | | * On Win32, if "for_writing" is false, we check whether the file exists |
2161 | | * and, if not, construct a path name relative to the ".wireshark" |
2162 | | * subdirectory of the user's home directory, and check whether that |
2163 | | * exists; if it does, we return that, so that configuration files |
2164 | | * from earlier versions can be read. |
2165 | | * |
2166 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
2167 | | * caller is done with it. |
2168 | | */ |
2169 | | char * |
2170 | | get_persconffile_path(const char *filename, bool from_profile) |
2171 | 340 | { |
2172 | 340 | char *path, *dir = NULL; |
2173 | | |
2174 | 340 | if (from_profile) { |
2175 | | /* Store filenames so we know which filenames belongs to a configuration profile */ |
2176 | 308 | profile_register_persconffile(filename); |
2177 | | |
2178 | 308 | dir = get_persconffile_dir(persconfprofile); |
2179 | 308 | } else { |
2180 | 32 | dir = get_persconffile_dir(NULL); |
2181 | 32 | } |
2182 | 340 | path = g_build_filename(dir, filename, NULL); |
2183 | | |
2184 | 340 | g_free(dir); |
2185 | 340 | return path; |
2186 | 340 | } |
2187 | | |
2188 | | /* |
2189 | | * Construct the path name of a global configuration file, given the |
2190 | | * file name. |
2191 | | * |
2192 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
2193 | | * caller is done with it. |
2194 | | */ |
2195 | | char * |
2196 | | get_datafile_path(const char *filename) |
2197 | 296 | { |
2198 | 296 | if (running_in_build_directory_flag && !strcmp(filename, "hosts")) { |
2199 | | /* We're running in the build directory and the requested file is a |
2200 | | * generated (or a test) file. Return the file name in the build |
2201 | | * directory (not in the source/data directory). |
2202 | | * (Oh the things we do to keep the source directory pristine...) |
2203 | | */ |
2204 | 4 | return g_build_filename(get_progfile_dir(), filename, (char *)NULL); |
2205 | 292 | } else { |
2206 | 292 | return g_build_filename(get_datafile_dir(), filename, (char *)NULL); |
2207 | 292 | } |
2208 | 296 | } |
2209 | | |
2210 | | /* |
2211 | | * Construct the path name of a global documentation file, given the |
2212 | | * file name. |
2213 | | * |
2214 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
2215 | | * caller is done with it. |
2216 | | */ |
2217 | | char * |
2218 | | get_docfile_path(const char *filename) |
2219 | 0 | { |
2220 | 0 | if (running_in_build_directory_flag) { |
2221 | | /* We're running in the build directory and the requested file is a |
2222 | | * generated (or a test) file. Return the file name in the build |
2223 | | * directory (not in the source/data directory). |
2224 | | * (Oh the things we do to keep the source directory pristine...) |
2225 | | */ |
2226 | 0 | return g_build_filename(get_progfile_dir(), filename, (char *)NULL); |
2227 | 0 | } else { |
2228 | 0 | return g_build_filename(get_doc_dir(), filename, (char *)NULL); |
2229 | 0 | } |
2230 | 0 | } |
2231 | | |
2232 | | /* |
2233 | | * Return an error message for UNIX-style errno indications on open or |
2234 | | * create operations. |
2235 | | */ |
2236 | | const char * |
2237 | | file_open_error_message(int err, bool for_writing) |
2238 | 0 | { |
2239 | 0 | const char *errmsg; |
2240 | 0 | static char errmsg_errno[1024+1]; |
2241 | |
|
2242 | 0 | switch (err) { |
2243 | | |
2244 | 0 | case ENOENT: |
2245 | 0 | if (for_writing) |
2246 | 0 | errmsg = "The path to the file \"%s\" doesn't exist."; |
2247 | 0 | else |
2248 | 0 | errmsg = "The file \"%s\" doesn't exist."; |
2249 | 0 | break; |
2250 | | |
2251 | 0 | case EACCES: |
2252 | 0 | if (for_writing) |
2253 | 0 | errmsg = "You don't have permission to create or write to the file \"%s\"."; |
2254 | 0 | else |
2255 | 0 | errmsg = "You don't have permission to read the file \"%s\"."; |
2256 | 0 | break; |
2257 | | |
2258 | 0 | case EISDIR: |
2259 | 0 | errmsg = "\"%s\" is a directory (folder), not a file."; |
2260 | 0 | break; |
2261 | | |
2262 | 0 | case ENOSPC: |
2263 | 0 | errmsg = "The file \"%s\" could not be created because there is no space left on the file system."; |
2264 | 0 | break; |
2265 | | |
2266 | 0 | #ifdef EDQUOT |
2267 | 0 | case EDQUOT: |
2268 | 0 | errmsg = "The file \"%s\" could not be created because you are too close to, or over, your disk quota."; |
2269 | 0 | break; |
2270 | 0 | #endif |
2271 | | |
2272 | 0 | case EINVAL: |
2273 | 0 | errmsg = "The file \"%s\" could not be created because an invalid filename was specified."; |
2274 | 0 | break; |
2275 | | |
2276 | 0 | #ifdef ENAMETOOLONG |
2277 | 0 | case ENAMETOOLONG: |
2278 | | /* XXX Make sure we truncate on a character boundary. */ |
2279 | 0 | errmsg = "The file name \"%.80s" UTF8_HORIZONTAL_ELLIPSIS "\" is too long."; |
2280 | 0 | break; |
2281 | 0 | #endif |
2282 | | |
2283 | 0 | case ENOMEM: |
2284 | | /* |
2285 | | * The problem probably has nothing to do with how much RAM the |
2286 | | * user has on their machine, so don't confuse them by saying |
2287 | | * "memory". The problem is probably either virtual address |
2288 | | * space or swap space. |
2289 | | */ |
2290 | | #if GLIB_SIZEOF_VOID_P == 4 |
2291 | | /* |
2292 | | * ILP32; we probably ran out of virtual address space. |
2293 | | */ |
2294 | | #define ENOMEM_REASON "it can't be handled by a 32-bit application" |
2295 | | #else |
2296 | | /* |
2297 | | * LP64 or LLP64; we probably ran out of swap space. |
2298 | | */ |
2299 | | #if defined(_WIN32) |
2300 | | /* |
2301 | | * You need to make the pagefile bigger. |
2302 | | */ |
2303 | | #define ENOMEM_REASON "the pagefile is too small" |
2304 | | #elif defined(ENABLE_APPLICATION_BUNDLE) |
2305 | | /* |
2306 | | * dynamic_pager couldn't, or wouldn't, create more swap files. |
2307 | | */ |
2308 | | #define ENOMEM_REASON "your system ran out of swap file space" |
2309 | | #else |
2310 | | /* |
2311 | | * Either you have a fixed swap partition or a fixed swap file, |
2312 | | * and it needs to be made bigger. |
2313 | | * |
2314 | | * This is UN*X, but it's not macOS, so we assume the user is |
2315 | | * *somewhat* nerdy. |
2316 | | */ |
2317 | 0 | #define ENOMEM_REASON "your system is out of swap space" |
2318 | 0 | #endif |
2319 | 0 | #endif /* GLIB_SIZEOF_VOID_P == 4 */ |
2320 | 0 | if (for_writing) |
2321 | 0 | errmsg = "The file \"%s\" could not be created because " ENOMEM_REASON "."; |
2322 | 0 | else |
2323 | 0 | errmsg = "The file \"%s\" could not be opened because " ENOMEM_REASON "."; |
2324 | 0 | break; |
2325 | | |
2326 | 0 | default: |
2327 | 0 | snprintf(errmsg_errno, sizeof(errmsg_errno), |
2328 | 0 | "The file \"%%s\" could not be %s: %s.", |
2329 | 0 | for_writing ? "created" : "opened", |
2330 | 0 | g_strerror(err)); |
2331 | 0 | errmsg = errmsg_errno; |
2332 | 0 | break; |
2333 | 0 | } |
2334 | 0 | return errmsg; |
2335 | 0 | } |
2336 | | |
2337 | | /* |
2338 | | * Return an error message for UNIX-style errno indications on write |
2339 | | * operations. |
2340 | | */ |
2341 | | const char * |
2342 | | file_write_error_message(int err) |
2343 | 0 | { |
2344 | 0 | const char *errmsg; |
2345 | 0 | static char errmsg_errno[1024+1]; |
2346 | |
|
2347 | 0 | switch (err) { |
2348 | | |
2349 | 0 | case ENOSPC: |
2350 | 0 | errmsg = "The file \"%s\" could not be saved because there is no space left on the file system."; |
2351 | 0 | break; |
2352 | | |
2353 | 0 | #ifdef EDQUOT |
2354 | 0 | case EDQUOT: |
2355 | 0 | errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota."; |
2356 | 0 | break; |
2357 | 0 | #endif |
2358 | | |
2359 | 0 | default: |
2360 | 0 | snprintf(errmsg_errno, sizeof(errmsg_errno), |
2361 | 0 | "An error occurred while writing to the file \"%%s\": %s.", |
2362 | 0 | g_strerror(err)); |
2363 | 0 | errmsg = errmsg_errno; |
2364 | 0 | break; |
2365 | 0 | } |
2366 | 0 | return errmsg; |
2367 | 0 | } |
2368 | | |
2369 | | |
2370 | | bool |
2371 | | file_exists(const char *fname) |
2372 | 548 | { |
2373 | 548 | ws_statb64 file_stat; |
2374 | | |
2375 | 548 | if (!fname) { |
2376 | 0 | return false; |
2377 | 0 | } |
2378 | | |
2379 | 548 | if (ws_stat64(fname, &file_stat) != 0 && errno == ENOENT) { |
2380 | 548 | return false; |
2381 | 548 | } else { |
2382 | 0 | return true; |
2383 | 0 | } |
2384 | 548 | } |
2385 | | |
2386 | | bool config_file_exists_with_entries(const char *fname, char comment_char) |
2387 | 0 | { |
2388 | 0 | bool start_of_line = true; |
2389 | 0 | bool has_entries = false; |
2390 | 0 | FILE *file; |
2391 | 0 | int c; |
2392 | |
|
2393 | 0 | if (!fname) { |
2394 | 0 | return false; |
2395 | 0 | } |
2396 | | |
2397 | 0 | if ((file = ws_fopen(fname, "r")) == NULL) { |
2398 | 0 | return false; |
2399 | 0 | } |
2400 | | |
2401 | 0 | do { |
2402 | 0 | c = ws_getc_unlocked(file); |
2403 | 0 | if (start_of_line && c != comment_char && !g_ascii_isspace(c) && g_ascii_isprint(c)) { |
2404 | 0 | has_entries = true; |
2405 | 0 | break; |
2406 | 0 | } |
2407 | 0 | if (c == '\n' || !g_ascii_isspace(c)) { |
2408 | 0 | start_of_line = (c == '\n'); |
2409 | 0 | } |
2410 | 0 | } while (c != EOF); |
2411 | | |
2412 | 0 | fclose(file); |
2413 | 0 | return has_entries; |
2414 | 0 | } |
2415 | | |
2416 | | /* |
2417 | | * Check that the from file is not the same as to file |
2418 | | * We do it here so we catch all cases ... |
2419 | | * Unfortunately, the file requester gives us an absolute file |
2420 | | * name and the read file name may be relative (if supplied on |
2421 | | * the command line), so we can't just compare paths. From Joerg Mayer. |
2422 | | */ |
2423 | | bool |
2424 | | files_identical(const char *fname1, const char *fname2) |
2425 | 2 | { |
2426 | | /* Two different implementations, because st_ino isn't filled in with |
2427 | | * a meaningful value on Windows. Use the Windows API and FILE_ID_INFO |
2428 | | * instead. |
2429 | | */ |
2430 | | #ifdef _WIN32 |
2431 | | |
2432 | | FILE_ID_INFO filestat1, filestat2; |
2433 | | |
2434 | | /* |
2435 | | * Compare VolumeSerialNumber and FileId. |
2436 | | */ |
2437 | | |
2438 | | HANDLE h1 = CreateFile(utf_8to16(fname1), 0, |
2439 | | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
2440 | | NULL, OPEN_EXISTING, 0, NULL); |
2441 | | |
2442 | | if (h1 == INVALID_HANDLE_VALUE) { |
2443 | | return false; |
2444 | | } |
2445 | | |
2446 | | if (!GetFileInformationByHandleEx(h1, FileIdInfo, &filestat1, sizeof(FILE_ID_INFO))) { |
2447 | | CloseHandle(h1); |
2448 | | return false; |
2449 | | } |
2450 | | CloseHandle(h1); |
2451 | | |
2452 | | HANDLE h2 = CreateFile(utf_8to16(fname2), 0, |
2453 | | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
2454 | | NULL, OPEN_EXISTING, 0, NULL); |
2455 | | |
2456 | | if (h2 == INVALID_HANDLE_VALUE) { |
2457 | | return false; |
2458 | | } |
2459 | | |
2460 | | if (!GetFileInformationByHandleEx(h2, FileIdInfo, &filestat2, sizeof(FILE_ID_INFO))) { |
2461 | | CloseHandle(h2); |
2462 | | return false; |
2463 | | } |
2464 | | CloseHandle(h2); |
2465 | | |
2466 | | return ((memcmp(&filestat1.FileId, &filestat2.FileId, sizeof(FILE_ID_128)) == 0) && |
2467 | | filestat1.VolumeSerialNumber == filestat2.VolumeSerialNumber); |
2468 | | #else |
2469 | 2 | ws_statb64 filestat1, filestat2; |
2470 | | |
2471 | | /* |
2472 | | * Compare st_dev and st_ino. |
2473 | | */ |
2474 | 2 | if (ws_stat64(fname1, &filestat1) == -1) |
2475 | 2 | return false; /* can't get info about the first file */ |
2476 | 0 | if (ws_stat64(fname2, &filestat2) == -1) |
2477 | 0 | return false; /* can't get info about the second file */ |
2478 | 0 | return (filestat1.st_dev == filestat2.st_dev && |
2479 | 0 | filestat1.st_ino == filestat2.st_ino); |
2480 | 0 | #endif |
2481 | 0 | } |
2482 | | |
2483 | | bool |
2484 | | file_needs_reopen(int fd, const char* filename) |
2485 | 0 | { |
2486 | | #ifdef _WIN32 |
2487 | | /* Windows handles st_dev in a way unsuitable here: |
2488 | | * * _fstat() simply casts the file descriptor (ws_fileno(fp)) to unsigned |
2489 | | * and assigns this value to st_dev and st_rdev |
2490 | | * * _wstat() converts drive letter (eg. C) to number (A=0, B=1, C=2, ...) |
2491 | | * and assigns such number to st_dev and st_rdev |
2492 | | * |
2493 | | * The st_ino parameter is simply zero as there is no specific assignment |
2494 | | * to it in the Universal CRT source code. |
2495 | | * |
2496 | | * Thus instead of using fstat(), use Windows specific API. |
2497 | | */ |
2498 | | |
2499 | | HANDLE open_handle = (HANDLE)_get_osfhandle(fd); |
2500 | | HANDLE current_handle = CreateFile(utf_8to16(filename), FILE_READ_ATTRIBUTES, |
2501 | | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, |
2502 | | NULL, OPEN_EXISTING, 0, NULL); |
2503 | | BY_HANDLE_FILE_INFORMATION open_info, current_info; |
2504 | | |
2505 | | if (current_handle == INVALID_HANDLE_VALUE) { |
2506 | | return true; |
2507 | | } |
2508 | | |
2509 | | #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) |
2510 | | FILE_ID_INFO open_id, current_id; |
2511 | | if (GetFileInformationByHandleEx(open_handle, FileIdInfo, &open_id, sizeof(open_id)) && |
2512 | | GetFileInformationByHandleEx(current_handle, FileIdInfo, ¤t_id, sizeof(current_id))) { |
2513 | | /* 128-bit identifier is available, use it */ |
2514 | | CloseHandle(current_handle); |
2515 | | return open_id.VolumeSerialNumber != current_id.VolumeSerialNumber || |
2516 | | memcmp(&open_id.FileId, ¤t_id.FileId, sizeof(open_id.FileId)) != 0; |
2517 | | } |
2518 | | #endif /* _WIN32_WINNT >= _WIN32_WINNT_WIN8 */ |
2519 | | if (GetFileInformationByHandle(open_handle, &open_info) && |
2520 | | GetFileInformationByHandle(current_handle, ¤t_info)) { |
2521 | | /* Fallback to 64-bit identifier */ |
2522 | | CloseHandle(current_handle); |
2523 | | uint64_t open_size = (((uint64_t)open_info.nFileSizeHigh) << 32) | open_info.nFileSizeLow; |
2524 | | uint64_t current_size = (((uint64_t)current_info.nFileSizeHigh) << 32) | current_info.nFileSizeLow; |
2525 | | return open_info.dwVolumeSerialNumber != current_info.dwVolumeSerialNumber || |
2526 | | open_info.nFileIndexHigh != current_info.nFileIndexHigh || |
2527 | | open_info.nFileIndexLow != current_info.nFileIndexLow || |
2528 | | open_size > current_size; |
2529 | | } |
2530 | | CloseHandle(current_handle); |
2531 | | return true; |
2532 | | #else |
2533 | 0 | ws_statb64 open_stat, current_stat; |
2534 | | |
2535 | | /* consider a file deleted when stat fails for either file, |
2536 | | * or when the residing device / inode has changed. */ |
2537 | 0 | if (0 != ws_fstat64(fd, &open_stat)) |
2538 | 0 | return true; |
2539 | 0 | if (0 != ws_stat64(filename, ¤t_stat)) |
2540 | 0 | return true; |
2541 | | |
2542 | 0 | return open_stat.st_dev != current_stat.st_dev || |
2543 | 0 | open_stat.st_ino != current_stat.st_ino || |
2544 | 0 | open_stat.st_size > current_stat.st_size; |
2545 | 0 | #endif |
2546 | 0 | } |
2547 | | |
2548 | | bool |
2549 | | write_file_binary_mode(const char *filename, const void *content, size_t content_len) |
2550 | 0 | { |
2551 | 0 | int fd; |
2552 | 0 | size_t bytes_left; |
2553 | 0 | unsigned int bytes_to_write; |
2554 | 0 | ssize_t bytes_written; |
2555 | 0 | const uint8_t *ptr; |
2556 | 0 | int err; |
2557 | |
|
2558 | 0 | fd = ws_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); |
2559 | 0 | if (fd == -1) { |
2560 | 0 | report_open_failure(filename, errno, true); |
2561 | 0 | return false; |
2562 | 0 | } |
2563 | | |
2564 | | /* |
2565 | | * The third argument to _write() on Windows is an unsigned int, |
2566 | | * so, on Windows, that's the size of the third argument to |
2567 | | * ws_write(). |
2568 | | * |
2569 | | * The third argument to write() on UN*X is a size_t, although |
2570 | | * the return value is an ssize_t, so one probably shouldn't |
2571 | | * write more than the max value of an ssize_t. |
2572 | | * |
2573 | | * In either case, there's no guarantee that a size_t such as |
2574 | | * content_len can be passed to ws_write(), so we write in |
2575 | | * chunks of at most 2^31 bytes. |
2576 | | */ |
2577 | | |
2578 | 0 | ptr = (const uint8_t *)content; |
2579 | 0 | bytes_left = content_len; |
2580 | 0 | while (bytes_left != 0) { |
2581 | 0 | if (bytes_left > 0x40000000) { |
2582 | 0 | bytes_to_write = 0x40000000; |
2583 | 0 | } else { |
2584 | 0 | bytes_to_write = (unsigned int)bytes_left; |
2585 | 0 | } |
2586 | 0 | bytes_written = ws_write(fd, ptr, bytes_to_write); |
2587 | 0 | if (bytes_written <= 0) { |
2588 | 0 | if (bytes_written < 0) { |
2589 | 0 | err = errno; |
2590 | 0 | } else { |
2591 | 0 | err = WTAP_ERR_SHORT_WRITE; |
2592 | 0 | } |
2593 | 0 | report_write_failure(filename, err); |
2594 | 0 | ws_close(fd); |
2595 | 0 | return false; |
2596 | 0 | } |
2597 | 0 | bytes_left -= bytes_written; |
2598 | 0 | ptr += bytes_written; |
2599 | 0 | } |
2600 | | |
2601 | 0 | ws_close(fd); |
2602 | 0 | return true; |
2603 | 0 | } |
2604 | | |
2605 | | /* |
2606 | | * Copy a file in binary mode, for those operating systems that care about |
2607 | | * such things. This should be OK for all files, even text files, as |
2608 | | * we'll copy the raw bytes, and we don't look at the bytes as we copy |
2609 | | * them. |
2610 | | * |
2611 | | * Returns true on success, false on failure. If a failure, it also |
2612 | | * displays a simple dialog window with the error message. |
2613 | | */ |
2614 | | bool |
2615 | | copy_file_binary_mode(const char *from_filename, const char *to_filename) |
2616 | 0 | { |
2617 | 0 | int from_fd, to_fd, err; |
2618 | 0 | ws_file_ssize_t nread, nwritten; |
2619 | 0 | uint8_t *pd = NULL; |
2620 | | |
2621 | | /* Copy the raw bytes of the file. */ |
2622 | 0 | from_fd = ws_open(from_filename, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */); |
2623 | 0 | if (from_fd < 0) { |
2624 | 0 | report_open_failure(from_filename, errno, false); |
2625 | 0 | goto done; |
2626 | 0 | } |
2627 | | |
2628 | | /* Use open() instead of creat() so that we can pass the O_BINARY |
2629 | | flag, which is relevant on Win32; it appears that "creat()" |
2630 | | may open the file in text mode, not binary mode, but we want |
2631 | | to copy the raw bytes of the file, so we need the output file |
2632 | | to be open in binary mode. */ |
2633 | 0 | to_fd = ws_open(to_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); |
2634 | 0 | if (to_fd < 0) { |
2635 | 0 | report_open_failure(to_filename, errno, true); |
2636 | 0 | ws_close(from_fd); |
2637 | 0 | goto done; |
2638 | 0 | } |
2639 | | |
2640 | 0 | #define FS_READ_SIZE 65536 |
2641 | 0 | pd = (uint8_t *)g_malloc(FS_READ_SIZE); |
2642 | 0 | while ((nread = ws_read(from_fd, pd, FS_READ_SIZE)) > 0) { |
2643 | 0 | nwritten = ws_write(to_fd, pd, nread); |
2644 | 0 | if (nwritten < nread) { |
2645 | 0 | if (nwritten < 0) |
2646 | 0 | err = errno; |
2647 | 0 | else |
2648 | 0 | err = WTAP_ERR_SHORT_WRITE; |
2649 | 0 | report_write_failure(to_filename, err); |
2650 | 0 | ws_close(from_fd); |
2651 | 0 | ws_close(to_fd); |
2652 | 0 | goto done; |
2653 | 0 | } |
2654 | 0 | } |
2655 | 0 | if (nread < 0) { |
2656 | 0 | err = errno; |
2657 | 0 | report_read_failure(from_filename, err); |
2658 | 0 | ws_close(from_fd); |
2659 | 0 | ws_close(to_fd); |
2660 | 0 | goto done; |
2661 | 0 | } |
2662 | 0 | ws_close(from_fd); |
2663 | 0 | if (ws_close(to_fd) < 0) { |
2664 | 0 | report_write_failure(to_filename, errno); |
2665 | 0 | goto done; |
2666 | 0 | } |
2667 | | |
2668 | 0 | g_free(pd); |
2669 | 0 | pd = NULL; |
2670 | 0 | return true; |
2671 | | |
2672 | 0 | done: |
2673 | 0 | g_free(pd); |
2674 | 0 | return false; |
2675 | 0 | } |
2676 | | |
2677 | | char * |
2678 | | data_file_url(const char *filename) |
2679 | 0 | { |
2680 | 0 | char *file_path; |
2681 | 0 | char *uri; |
2682 | | |
2683 | | /* Absolute path? */ |
2684 | 0 | if(g_path_is_absolute(filename)) { |
2685 | 0 | file_path = g_strdup(filename); |
2686 | 0 | } else { |
2687 | 0 | file_path = ws_strdup_printf("%s/%s", get_datafile_dir(), filename); |
2688 | 0 | } |
2689 | | |
2690 | | /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */ |
2691 | | |
2692 | | /* convert filename to uri */ |
2693 | 0 | uri = g_filename_to_uri(file_path, NULL, NULL); |
2694 | 0 | g_free(file_path); |
2695 | 0 | return uri; |
2696 | 0 | } |
2697 | | |
2698 | | char * |
2699 | | doc_file_url(const char *filename) |
2700 | 0 | { |
2701 | 0 | char *file_path; |
2702 | 0 | char *uri; |
2703 | | |
2704 | | /* Absolute path? */ |
2705 | 0 | if(g_path_is_absolute(filename)) { |
2706 | 0 | file_path = g_strdup(filename); |
2707 | 0 | } else { |
2708 | 0 | file_path = ws_strdup_printf("%s/%s", get_doc_dir(), filename); |
2709 | 0 | } |
2710 | | |
2711 | | /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */ |
2712 | | |
2713 | | /* convert filename to uri */ |
2714 | 0 | uri = g_filename_to_uri(file_path, NULL, NULL); |
2715 | 0 | g_free(file_path); |
2716 | 0 | return uri; |
2717 | 0 | } |
2718 | | |
2719 | | void |
2720 | | free_progdirs(void) |
2721 | 0 | { |
2722 | 0 | g_free(persconffile_dir); |
2723 | 0 | persconffile_dir = NULL; |
2724 | 0 | g_free(datafile_dir); |
2725 | 0 | datafile_dir = NULL; |
2726 | 0 | g_free(persdatafile_dir); |
2727 | 0 | persdatafile_dir = NULL; |
2728 | 0 | g_free(persconfprofile); |
2729 | 0 | persconfprofile = NULL; |
2730 | 0 | g_free(progfile_dir); |
2731 | 0 | progfile_dir = NULL; |
2732 | 0 | g_free(doc_dir); |
2733 | 0 | doc_dir = NULL; |
2734 | 0 | g_free(install_prefix); |
2735 | 0 | install_prefix = NULL; |
2736 | 0 | g_free(current_working_dir); |
2737 | 0 | current_working_dir = NULL; |
2738 | 0 | #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) |
2739 | 0 | g_free(plugin_dir); |
2740 | 0 | plugin_dir = NULL; |
2741 | 0 | g_free(plugin_dir_with_version); |
2742 | 0 | plugin_dir_with_version = NULL; |
2743 | 0 | g_free(plugin_pers_dir); |
2744 | 0 | plugin_pers_dir = NULL; |
2745 | 0 | g_free(plugin_pers_dir_with_version); |
2746 | 0 | plugin_pers_dir_with_version = NULL; |
2747 | 0 | #endif |
2748 | 0 | g_free(extcap_dir); |
2749 | 0 | extcap_dir = NULL; |
2750 | 0 | g_free(extcap_pers_dir); |
2751 | 0 | extcap_pers_dir = NULL; |
2752 | 0 | } |
2753 | | |
2754 | | /* |
2755 | | * Editor modelines |
2756 | | * |
2757 | | * Local Variables: |
2758 | | * c-basic-offset: 4 |
2759 | | * tab-width: 8 |
2760 | | * indent-tabs-mode: nil |
2761 | | * End: |
2762 | | * |
2763 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
2764 | | * :indentSize=4:tabSize=8:noTabs=true: |
2765 | | */ |