1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <sys/types.h>
6 #ifdef _WIN32
7 # include <evil_private.h> /* mkdir */
8 #else
9 # include <pwd.h>
10 #endif
11
12 #include <Eina.h>
13
14 #include "eina_internal.h"
15 #include "eina_private.h"
16
17 static Eina_Hash *vpath_data = NULL;
18
19 #ifdef CRI
20 #undef CRI
21 #endif
22 #define CRI(...) EINA_LOG_DOM_CRIT(_eina_vpath_log_dom, __VA_ARGS__)
23
24 #ifdef ERR
25 #undef ERR
26 #endif
27 #define ERR(...) EINA_LOG_DOM_ERR(_eina_vpath_log_dom, __VA_ARGS__)
28
29 #ifdef DBG
30 #undef DBG
31 #endif
32 #define DBG(...) EINA_LOG_DOM_DBG(_eina_vpath_log_dom, __VA_ARGS__)
33
34 static int _eina_vpath_log_dom = -1;
35
36 static inline void
_eina_vpath_data_add(const char * key,const char * value)37 _eina_vpath_data_add(const char *key, const char *value)
38 {
39 eina_hash_add(vpath_data, key, eina_stringshare_add(value));
40 }
41
42 static inline Eina_Stringshare*
_eina_vpath_data_get(const char * key)43 _eina_vpath_data_get(const char *key)
44 {
45 return eina_hash_find(vpath_data, key);
46 }
47
48
49 static char *
_fallback_runtime_dir(const char * home)50 _fallback_runtime_dir(const char *home)
51 {
52 char buf[PATH_MAX];
53 struct stat st;
54 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
55 uid_t uid = getuid();
56
57 if (setuid(geteuid()) != 0)
58 {
59 fprintf(stderr,
60 "FATAL: Cannot setuid - errno=%i\n",
61 errno);
62 abort();
63 }
64 #endif
65 // fallback - make ~/.run
66 snprintf(buf, sizeof(buf), "%s/.run", home);
67 if (!!mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR))
68 {
69 if (errno == EEXIST)
70 {
71 if (stat(buf, &st) == 0)
72 {
73 // some sanity checks - but not for security
74 if (!(st.st_mode & S_IFDIR))
75 {
76 // fatal - exists but is not a dir
77 fprintf(stderr,
78 "FATAL: run dir '%s' exists but not a dir\n",
79 buf);
80 abort();
81 }
82 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
83 if (st.st_uid != geteuid())
84 {
85 // fatal - run dir doesn't belong to user
86 fprintf(stderr,
87 "FATAL: run dir '%s' not owned by uid %i\n",
88 buf, (int)geteuid());
89 abort();
90 }
91 #endif
92 }
93 else
94 {
95 // fatal - we cant create our run dir in ~/
96 fprintf(stderr,
97 "FATAL: Cannot verify run dir '%s' errno=%i\n",
98 buf, errno);
99 abort();
100 }
101 }
102 else
103 {
104 // fatal - we cant create our run dir in ~/
105 fprintf(stderr,
106 "FATAL: Cannot create run dir '%s' - errno=%i\n",
107 buf, errno);
108 abort();
109 }
110 }
111 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
112 if (setreuid(uid, geteuid()) != 0)
113 {
114 fprintf(stderr,
115 "FATAL: Cannot setreuid - errno=%i\n",
116 errno);
117 abort();
118 }
119 #endif
120
121 return strdup(buf);
122 }
123
124 static char *
_fallback_home_dir()125 _fallback_home_dir()
126 {
127 char buf[PATH_MAX];
128 /* Windows does not have getuid(), but home can't be NULL */
129 #ifdef HAVE_GETEUID
130 uid_t uid = geteuid();
131 struct stat st;
132
133 snprintf(buf, sizeof(buf), "/tmp/%i", (int)uid);
134 if (mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
135 {
136 if (errno != EEXIST)
137 {
138 if (stat("/tmp", &st) == 0)
139 snprintf(buf, sizeof(buf), "/tmp");
140 else
141 snprintf(buf, sizeof(buf), "/");
142 }
143 }
144 if (stat(buf, &st) != 0)
145 {
146 if (stat("/tmp", &st) == 0)
147 snprintf(buf, sizeof(buf), "/tmp");
148 else
149 snprintf(buf, sizeof(buf), "/");
150 }
151 #else
152 snprintf(buf, sizeof(buf), "/");
153 #endif
154 return strdup(buf);
155 }
156
157 static void
_eina_vpath_interface_sys_init(void)158 _eina_vpath_interface_sys_init(void)
159 {
160 const char *home, *tmp;
161
162 // $HOME / ~/ etc.
163 home = eina_environment_home_get();
164 if (!home)
165 {
166 char *home2 = _fallback_home_dir();
167 _eina_vpath_data_add("home", home2);
168 free(home2);
169 }
170 else
171 _eina_vpath_data_add("home", home);
172
173 // tmp dir - system wide
174 tmp = eina_environment_tmp_get();
175 _eina_vpath_data_add("tmp", tmp);
176 }
177
178 Eina_Bool
eina_vpath_init(void)179 eina_vpath_init(void)
180 {
181 vpath_data = eina_hash_string_superfast_new((Eina_Free_Cb)eina_stringshare_del);
182
183 _eina_vpath_interface_sys_init();
184 eina_xdg_env_init();
185
186 _eina_vpath_log_dom = eina_log_domain_register("vpath", "cyan");
187 return EINA_TRUE;
188 }
189
190 Eina_Bool
eina_vpath_shutdown(void)191 eina_vpath_shutdown(void)
192 {
193 eina_hash_free(vpath_data);
194 vpath_data = NULL;
195 eina_log_domain_unregister(_eina_vpath_log_dom);
196 _eina_vpath_log_dom = -1;
197 return EINA_TRUE;
198 }
199
200 #ifdef HAVE_GETPWENT
201 static Eina_Bool
_fetch_user_homedir(char ** str,const char * name,const char * error)202 _fetch_user_homedir(char **str, const char *name, const char *error)
203 {
204 *str = NULL;
205 struct passwd *pwent;
206
207 pwent = getpwnam(name);
208 if (!pwent)
209 {
210 ERR("User %s not found\nThe string was: %s", name, error);
211 return EINA_FALSE;
212 }
213 *str = pwent->pw_dir;
214
215 return EINA_TRUE;
216 }
217 #endif
218
219 static int
_eina_vpath_resolve(const char * path,char * str,size_t size)220 _eina_vpath_resolve(const char *path, char *str, size_t size)
221 {
222 if (path[0] == '~')
223 {
224 char *home = NULL;
225 // ~/ <- home directory
226 if (path[1] == '/')
227 {
228 home = eina_hash_find(vpath_data, "home");
229 path ++;
230 }
231 // ~username/ <- homedir of user "username"
232 else
233 {
234 #ifndef HAVE_GETPWENT
235 ERR("User fetching is disabled on this system\nThe string was: %s", path);
236 return 0;
237 #else
238 const char *p;
239 char *name;
240
241 for (p = path + 1; *p; p++)
242 {
243 if (*p =='/') break;
244 }
245 name = alloca(p - path);
246 strncpy(name, path + 1, p - path - 1);
247 name[p - path - 1] = 0;
248
249 if (!_fetch_user_homedir(&home, name, path))
250 return 0;
251 path = p;
252 #endif
253 }
254 if (home)
255 {
256 return snprintf(str, size, "%s%s", home, path);
257 }
258 }
259 // (:xxx:)/* ... <- meta hash table
260 else if (((path[0] == '(') && (path[1] == ':')) ||
261 ((path[0] == '$') && (path[1] == '{')))
262 {
263 const char *p, *end, *meta;
264 const char *msg_start, *msg_end;
265 char *name;
266 int offset;
267 Eina_Bool found = EINA_FALSE;
268
269 if (path[0] == '(')
270 {
271 end = p = strstr(path + 2, ":)");
272 offset = 2;
273 msg_start = "(:";
274 msg_end = ":)";
275 }
276 else
277 {
278 end = p = strchr(path + 2, '}');
279 offset = 1;
280 msg_start = "${";
281 msg_end = "}";
282 }
283 if (p) found = EINA_TRUE;
284 p += offset;
285
286 if (!found)
287 {
288 ERR("'%s' Needs to have a matching '%s'\nThe string was: %s", msg_start, msg_end, path);
289 return 0;
290 }
291
292 if (*p != '/')
293 {
294 ERR("A / is expected after '%s'\nThe string was: %s", msg_end, path);
295 return 0;
296 }
297
298 if (found)
299 {
300 name = alloca(end - path);
301 strncpy(name, path + 2, end - path - offset);
302 name[end - path - 2] = 0;
303 meta = _eina_vpath_data_get(name);
304 if (meta)
305 {
306 return snprintf(str, size, "%s%s", meta, end + offset);
307 }
308 else
309 {
310 ERR("Meta key '%s' was not registered!\nThe string was: %s", name, path);
311 return 0;
312 }
313 }
314 }
315 //just return the path, since we assume that this is a normal path
316 else
317 {
318 return snprintf(str, size, "%s", path);
319 }
320 str[0] = '\0';
321 return 0;
322 }
323
324 EAPI char *
eina_vpath_resolve(const char * path)325 eina_vpath_resolve(const char* path)
326 {
327 char buf[PATH_MAX];
328 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
329
330 if (_eina_vpath_resolve(path, buf, sizeof(buf)) > 0)
331 return strdup(buf);
332 return NULL;
333 }
334
335 EAPI int
eina_vpath_resolve_snprintf(char * str,size_t size,const char * format,...)336 eina_vpath_resolve_snprintf(char *str, size_t size, const char *format, ...)
337 {
338 va_list args;
339 char *path;
340 int r;
341
342 // XXX: implement parse of path then look up in hash if not just create
343 // object where path and result are the same and return that with
344 // path set and result set to resolved path - return obj handler calls
345 // "do" on object to get the result inside fetched or failed callback.
346 // if it's a url then we need a new classs that overrides the do and
347 // begins a fetch and on finish calls the event cb or when wait is called
348 /* FIXME: not working for WIndows */
349 // /* <- full path
350
351 path = alloca(size + 1);
352
353 va_start(args, format);
354 vsnprintf(path, size, format, args);
355 va_end(args);
356
357 r = _eina_vpath_resolve(path, str, size);
358 if (r > 0) return r;
359
360 ERR("The path has to start with either '~/' or '(:NAME:)/' or be a normal path \nThe string was: %s", path);
361
362 return 0;
363 }
364
365 EAPI void
eina_vpath_interface_app_set(const char * app_domain,Eina_Prefix * app_pfx)366 eina_vpath_interface_app_set(const char *app_domain, Eina_Prefix *app_pfx)
367 {
368 char buf[PATH_MAX];
369
370 EINA_SAFETY_ON_NULL_RETURN(app_domain);
371 EINA_SAFETY_ON_NULL_RETURN(app_pfx);
372
373 _eina_vpath_data_add("app.dir", eina_prefix_get(app_pfx));
374 _eina_vpath_data_add("app.bin", eina_prefix_bin_get(app_pfx));
375 _eina_vpath_data_add("app.lib", eina_prefix_lib_get(app_pfx));
376 _eina_vpath_data_add("app.data", eina_prefix_data_get(app_pfx));
377 _eina_vpath_data_add("app.locale", eina_prefix_locale_get(app_pfx));
378 snprintf(buf, sizeof(buf), "%s/%s",
379 _eina_vpath_data_get("usr.config"), app_domain);
380 _eina_vpath_data_add("app.config", buf);
381 snprintf(buf, sizeof(buf), "%s/%s",
382 _eina_vpath_data_get("usr.cache"), app_domain);
383 _eina_vpath_data_add("app.cache", buf);
384 snprintf(buf, sizeof(buf), "%s/%s",
385 _eina_vpath_data_get("usr.data"), app_domain);
386 _eina_vpath_data_add("app.local", buf);
387 snprintf(buf, sizeof(buf), "%s/%s",
388 _eina_vpath_data_get("usr.tmp"), app_domain);
389 _eina_vpath_data_add("app.tmp", buf);
390 }
391
392 EAPI void
eina_vpath_interface_user_set(Eina_Vpath_Interface_User * user)393 eina_vpath_interface_user_set(Eina_Vpath_Interface_User *user)
394 {
395 Eina_Bool free_run = EINA_FALSE;
396
397 EINA_SAFETY_ON_NULL_RETURN(user);
398
399 if (!user->run)
400 {
401 user->run = _fallback_runtime_dir(_eina_vpath_data_get("home"));
402 free_run = EINA_TRUE;
403 }
404
405 #define ADD(a) _eina_vpath_data_add("usr." #a , user->a)
406 ADD(desktop);
407 ADD(documents);
408 ADD(downloads);
409 ADD(music);
410 ADD(pictures);
411 ADD(pub);
412 ADD(templates);
413 ADD(videos);
414 ADD(data);
415 ADD(config);
416 ADD(cache);
417 ADD(run);
418 ADD(tmp);
419 #undef ADD
420
421 if (free_run)
422 free((char *)user->run);
423 }
424