1 /*
2     This file is part of darktable,
3     Copyright (C) 2012-2020 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 /* getpwnam_r availability check */
20 #if defined __APPLE__ || defined _POSIX_C_SOURCE >= 1 || defined _XOPEN_SOURCE || defined _BSD_SOURCE        \
21     || defined _SVID_SOURCE || defined _POSIX_SOURCE || defined __DragonFly__ || defined __FreeBSD__         \
22     || defined __NetBSD__ || defined __OpenBSD__
23 #include "config.h"
24 
25 #include <pwd.h>
26 #include <sys/types.h>
27 #define HAVE_GETPWNAM_R 1
28 #endif
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #ifdef __APPLE__
35 #include "osx/osx.h"
36 #endif
37 
38 #include "common/grealpath.h"
39 #include "darktable.h"
40 #include "file_location.h"
41 #include "whereami.h"
42 
dt_loc_init(const char * datadir,const char * moduledir,const char * localedir,const char * configdir,const char * cachedir,const char * tmpdir)43 void dt_loc_init(const char *datadir, const char *moduledir, const char *localedir, const char *configdir, const char *cachedir, const char *tmpdir)
44 {
45   // Assemble pathes
46   char* application_directory = NULL;
47   int dirname_length;
48   // calling wai_getExecutablePath twice as recommended in the docs:
49   // the first call retrieves the length of the path
50   int length = wai_getExecutablePath(NULL, 0, &dirname_length);
51   if (length > 0)
52   {
53     application_directory = (char*)malloc(length + 1);
54     // the second call retrieves the path including the executable
55     wai_getExecutablePath(application_directory, length, &dirname_length);
56     // strip of the executable name from the path to retrieve the path alone
57     application_directory[dirname_length] = '\0';
58   }
59   dt_print(DT_DEBUG_DEV, "application_directory: %s\n", application_directory);
60 
61   // set up absolute pathes based on their relative value
62   dt_loc_init_datadir(application_directory, datadir);
63   dt_loc_init_plugindir(application_directory, moduledir);
64   dt_loc_init_localedir(application_directory, localedir);
65   dt_loc_init_user_config_dir(configdir);
66   dt_loc_init_user_cache_dir(cachedir);
67   dt_loc_init_sharedir(application_directory);
68   dt_loc_init_tmp_dir(tmpdir);
69   free(application_directory);
70 }
71 
dt_loc_get_home_dir(const gchar * user)72 gchar *dt_loc_get_home_dir(const gchar *user)
73 {
74   if(user == NULL || g_strcmp0(user, g_get_user_name()) == 0)
75   {
76     const char *home_dir = g_getenv("HOME");
77     return g_strdup((home_dir != NULL) ? home_dir : g_get_home_dir());
78   }
79 
80 #if defined HAVE_GETPWNAM_R
81   /* if the given username is not the same as the current one, we try
82    * to retrieve the pw dir from the password file entry */
83   struct passwd pwd;
84   struct passwd *result;
85 #ifdef _SC_GETPW_R_SIZE_MAX
86   int bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
87   if(bufsize < 0)
88   {
89     bufsize = 4096;
90   }
91 #else
92   int bufsize = 4096;
93 #endif
94 
95   gchar *buffer = g_malloc0_n(bufsize, sizeof(gchar));
96   if(buffer == NULL)
97   {
98     return NULL;
99   }
100 
101   getpwnam_r(user, &pwd, buffer, bufsize, &result);
102   if(result == NULL)
103   {
104     g_free(buffer);
105     return NULL;
106   }
107 
108   gchar *dir = g_strdup(pwd.pw_dir);
109   g_free(buffer);
110 
111   return dir;
112 #else
113   return NULL;
114 #endif
115 }
116 
dt_loc_init_generic(const char * absolute_value,const char * application_directory,const char * default_value)117 gchar *dt_loc_init_generic(const char *absolute_value, const char *application_directory, const char *default_value)
118 {
119   gchar *result = NULL;
120   gchar *path = NULL;
121 
122   if(absolute_value)
123   {
124     // the only adjustment the absolute path needs is transforming the possible tilde '~' to an absolute path
125     path = dt_util_fix_path(absolute_value);
126   }
127   else
128   {
129     // the default_value could be absolute or relative. we decide upon presence of the application_directory.
130     if(application_directory)
131     {
132       // default_value is relative.
133       // combine basename (application_directory) and relative path (default_value).
134       gchar complete_path[PATH_MAX] = { 0 };
135 #if defined(__APPLE__)
136       char *bundle_path = dt_osx_get_bundle_res_path();
137       if(bundle_path)
138       {
139         /// bundle detected ...
140 
141         // on a mac inside the bundle the executables are in <bundleroot>/Contents/MacOS/
142         // all other directories are a subdirectory of <bundleroot>/Contents/Resources:
143         // <bundleroot>/Contents/Resources/etc
144         // <bundleroot>/Contents/Resources/lib
145         // <bundleroot>/Contents/Resources/share
146         // so the relative path from the binary directory to the other directories differs to the non-bundle version by
147         // ../etc -> ../Resources/etc,
148         // ../lib -> ../Resources/lib,
149         // ../share -> ../Resources/share,
150         // So we have to modify the relative default value
151 
152         // +2: removes the two dots '..'
153         g_snprintf(complete_path, sizeof(complete_path), "%s/../Resources%s", application_directory, default_value + 2);
154       }
155       else
156       {
157         /// outside a bundle. Apply standard linux path rules
158         g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
159       }
160 #else
161       g_snprintf(complete_path, sizeof(complete_path), "%s/%s", application_directory, default_value);
162 #endif
163       path = g_strdup(complete_path);
164     }
165     else
166     {
167       // default_value is absolute
168       path = g_strdup(default_value);
169     }
170   }
171 
172   // create file if it does not exist
173   if(g_file_test(path, G_FILE_TEST_EXISTS) == FALSE) g_mkdir_with_parents(path, 0700);
174 
175   // removes '.', '..', and extra '/' characters.
176   result = g_realpath(path);
177 
178   g_free(path);
179   return result;
180 }
181 
dt_loc_init_user_config_dir(const char * configdir)182 void dt_loc_init_user_config_dir(const char *configdir)
183 {
184   char *default_config_dir = g_build_filename(g_get_user_config_dir(), "darktable", NULL);
185   darktable.configdir = dt_loc_init_generic(configdir, NULL, default_config_dir);
186   dt_check_opendir("darktable.configdir", darktable.configdir);
187   g_free(default_config_dir);
188 }
189 
dt_loc_init_tmp_dir(const char * tmpdir)190 void dt_loc_init_tmp_dir(const char *tmpdir)
191 {
192   darktable.tmpdir = dt_loc_init_generic(tmpdir, NULL, g_get_tmp_dir());
193   dt_check_opendir("darktable.tmpdir", darktable.tmpdir);
194 }
195 
dt_loc_init_user_cache_dir(const char * cachedir)196 void dt_loc_init_user_cache_dir(const char *cachedir)
197 {
198   char *default_cache_dir = g_build_filename(g_get_user_cache_dir(), "darktable", NULL);
199   darktable.cachedir = dt_loc_init_generic(cachedir, NULL, default_cache_dir);
200   dt_check_opendir("darktable.cachedir", darktable.cachedir);
201   g_free(default_cache_dir);
202 }
203 
dt_loc_init_plugindir(const char * application_directory,const char * plugindir)204 void dt_loc_init_plugindir(const char* application_directory, const char *plugindir)
205 {
206   darktable.plugindir = dt_loc_init_generic(plugindir, application_directory, DARKTABLE_LIBDIR);
207   dt_check_opendir("darktable.plugindir", darktable.plugindir);
208 }
209 
dt_check_opendir(const char * context,const char * directory)210 void dt_check_opendir(const char* context, const char* directory)
211 {
212   if (!directory)
213   {
214     fprintf(stderr, "directory for %s has not been set.\n", context);
215     exit(EXIT_FAILURE);
216   }
217 
218 #if _WIN32
219   wchar_t *wdirectory = g_utf8_to_utf16 (directory, -1, NULL, NULL, NULL);
220   DWORD attribs = GetFileAttributesW(wdirectory);
221   g_free(wdirectory);
222   if (attribs != INVALID_FILE_ATTRIBUTES &&
223       (attribs & FILE_ATTRIBUTE_DIRECTORY))
224   {
225     dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
226   }
227   else
228   {
229     fprintf(stderr, "%s: directory '%s' fails to open.'\n", context, directory);
230     exit(EXIT_FAILURE);
231   }
232 #else
233   DIR* dir = opendir(directory);
234   if (dir)
235   {
236     dt_print(DT_DEBUG_DEV, "%s: %s\n", context, directory);
237     closedir(dir);
238   }
239   else
240   {
241     fprintf(stderr, "opendir '%s' fails with: '%s'\n", directory, strerror(errno));
242     exit(EXIT_FAILURE);
243   }
244 #endif
245 }
246 
dt_loc_init_localedir(const char * application_directory,const char * localedir)247 void dt_loc_init_localedir(const char* application_directory, const char *localedir)
248 {
249   darktable.localedir = dt_loc_init_generic(localedir, application_directory, DARKTABLE_LOCALEDIR);
250   dt_check_opendir("darktable.localedir", darktable.localedir);
251 }
252 
dt_loc_init_datadir(const char * application_directory,const char * datadir)253 void dt_loc_init_datadir(const char* application_directory, const char *datadir)
254 {
255   darktable.datadir = dt_loc_init_generic(datadir, application_directory, DARKTABLE_DATADIR);
256   dt_check_opendir("darktable.datadir", darktable.datadir);
257 }
258 
dt_loc_init_sharedir(const char * application_directory)259 void dt_loc_init_sharedir(const char* application_directory)
260 {
261   darktable.sharedir = dt_loc_init_generic(NULL, application_directory, DARKTABLE_SHAREDIR);
262   dt_check_opendir("darktable.sharedir", darktable.sharedir);
263 }
264 
dt_loc_get_kerneldir(char * kerneldir,size_t bufsize)265 void dt_loc_get_kerneldir(char *kerneldir, size_t bufsize)
266 {
267   char datadir[PATH_MAX] = { 0 };
268   dt_loc_get_datadir(datadir, sizeof(datadir));
269   snprintf(kerneldir, bufsize, "%s" G_DIR_SEPARATOR_S "kernels", datadir);
270 }
271 
dt_loc_get_plugindir(char * plugindir,size_t bufsize)272 void dt_loc_get_plugindir(char *plugindir, size_t bufsize)
273 {
274   g_strlcpy(plugindir, darktable.plugindir, bufsize);
275 }
276 
dt_loc_get_localedir(char * localedir,size_t bufsize)277 void dt_loc_get_localedir(char *localedir, size_t bufsize)
278 {
279   g_strlcpy(localedir, darktable.localedir, bufsize);
280 }
281 
dt_loc_get_user_config_dir(char * configdir,size_t bufsize)282 void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
283 {
284   g_strlcpy(configdir, darktable.configdir, bufsize);
285 }
dt_loc_get_user_cache_dir(char * cachedir,size_t bufsize)286 void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
287 {
288   g_strlcpy(cachedir, darktable.cachedir, bufsize);
289 }
dt_loc_get_tmp_dir(char * tmpdir,size_t bufsize)290 void dt_loc_get_tmp_dir(char *tmpdir, size_t bufsize)
291 {
292   g_strlcpy(tmpdir, darktable.tmpdir, bufsize);
293 }
dt_loc_get_datadir(char * datadir,size_t bufsize)294 void dt_loc_get_datadir(char *datadir, size_t bufsize)
295 {
296   g_strlcpy(datadir, darktable.datadir, bufsize);
297 }
dt_loc_get_sharedir(char * sharedir,size_t bufsize)298 void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
299 {
300   g_strlcpy(sharedir, darktable.sharedir, bufsize);
301 }
302 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
303 // vim: shiftwidth=2 expandtab tabstop=2 cindent
304 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
305