1 /**
2 * WinPR: Windows Portable Runtime
3 * Path Functions
4 *
5 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29
30 #include <winpr/crt.h>
31 #include <winpr/platform.h>
32 #include <winpr/heap.h>
33 #include <winpr/file.h>
34 #include <winpr/tchar.h>
35 #include <winpr/environment.h>
36
37 #include <winpr/path.h>
38
39 #if defined(__IOS__)
40 #include "shell_ios.h"
41 #endif
42
43 #if defined(WIN32)
44 #include <shlobj.h>
45 #else
46 #include <errno.h>
47 #include <dirent.h>
48 #endif
49
50 static char* GetPath_XDG_CONFIG_HOME(void);
51 static char* GetPath_XDG_RUNTIME_DIR(void);
52
53 /**
54 * SHGetKnownFolderPath function:
55 * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/
56 */
57
58 /**
59 * XDG Base Directory Specification:
60 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
61 */
62
GetEnvAlloc(LPCSTR lpName)63 static char* GetEnvAlloc(LPCSTR lpName)
64 {
65 DWORD length;
66 char* env = NULL;
67 length = GetEnvironmentVariableA(lpName, NULL, 0);
68
69 if (length > 0)
70 {
71 env = malloc(length);
72
73 if (!env)
74 return NULL;
75
76 if (GetEnvironmentVariableA(lpName, env, length) != length - 1)
77 {
78 free(env);
79 return NULL;
80 }
81 }
82
83 return env;
84 }
85
GetPath_HOME(void)86 static char* GetPath_HOME(void)
87 {
88 char* path = NULL;
89 #ifdef _WIN32
90 path = GetEnvAlloc("UserProfile");
91 #elif defined(__IOS__)
92 path = ios_get_home();
93 #else
94 path = GetEnvAlloc("HOME");
95 #endif
96 return path;
97 }
98
GetPath_TEMP(void)99 static char* GetPath_TEMP(void)
100 {
101 char* path = NULL;
102 #ifdef _WIN32
103 path = GetEnvAlloc("TEMP");
104 #elif defined(__IOS__)
105 path = ios_get_temp();
106 #else
107 path = GetEnvAlloc("TMPDIR");
108
109 if (!path)
110 path = _strdup("/tmp");
111
112 #endif
113 return path;
114 }
115
GetPath_XDG_DATA_HOME(void)116 static char* GetPath_XDG_DATA_HOME(void)
117 {
118 char* path = NULL;
119 #if defined(WIN32) || defined(__IOS__)
120 path = GetPath_XDG_CONFIG_HOME();
121 #else
122 size_t size;
123 char* home = NULL;
124 /**
125 * There is a single base directory relative to which user-specific data files should be
126 * written. This directory is defined by the environment variable $XDG_DATA_HOME.
127 *
128 * $XDG_DATA_HOME defines the base directory relative to which user specific data files should
129 * be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
130 * $HOME/.local/share should be used.
131 */
132 path = GetEnvAlloc("XDG_DATA_HOME");
133
134 if (path)
135 return path;
136
137 home = GetPath_HOME();
138
139 if (!home)
140 return NULL;
141
142 size = strlen(home) + strlen("/.local/share") + 1;
143 path = (char*)malloc(size);
144
145 if (!path)
146 {
147 free(home);
148 return NULL;
149 }
150
151 sprintf_s(path, size, "%s%s", home, "/.local/share");
152 free(home);
153 #endif
154 return path;
155 }
156
GetPath_XDG_CONFIG_HOME(void)157 static char* GetPath_XDG_CONFIG_HOME(void)
158 {
159 char* path = NULL;
160 #if defined(WIN32) && !defined(_UWP)
161 path = calloc(MAX_PATH, sizeof(char));
162
163 if (!path)
164 return NULL;
165
166 if (FAILED(SHGetFolderPathA(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
167 {
168 free(path);
169 return NULL;
170 }
171
172 #elif defined(__IOS__)
173 path = ios_get_data();
174 #else
175 size_t size;
176 char* home = NULL;
177 /**
178 * There is a single base directory relative to which user-specific configuration files should
179 * be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
180 *
181 * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration
182 * files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to
183 * $HOME/.config should be used.
184 */
185 path = GetEnvAlloc("XDG_CONFIG_HOME");
186
187 if (path)
188 return path;
189
190 home = GetPath_HOME();
191
192 if (!home)
193 home = GetPath_TEMP();
194
195 if (!home)
196 return NULL;
197
198 size = strlen(home) + strlen("/.config") + 1;
199 path = (char*)malloc(size);
200
201 if (!path)
202 {
203 free(home);
204 return NULL;
205 }
206
207 sprintf_s(path, size, "%s%s", home, "/.config");
208 free(home);
209 #endif
210 return path;
211 }
212
GetPath_XDG_CACHE_HOME(void)213 static char* GetPath_XDG_CACHE_HOME(void)
214 {
215 char* path = NULL;
216 char* home = NULL;
217 #if defined(WIN32)
218 home = GetPath_XDG_RUNTIME_DIR();
219
220 if (home)
221 {
222 path = GetCombinedPath(home, "cache");
223
224 if (!winpr_PathFileExists(path))
225 if (!CreateDirectoryA(path, NULL))
226 path = NULL;
227 }
228
229 free(home);
230 #elif defined(__IOS__)
231 path = ios_get_cache();
232 #else
233 size_t size;
234 /**
235 * There is a single base directory relative to which user-specific non-essential (cached) data
236 * should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
237 *
238 * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data
239 * files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to
240 * $HOME/.cache should be used.
241 */
242 path = GetEnvAlloc("XDG_CACHE_HOME");
243
244 if (path)
245 return path;
246
247 home = GetPath_HOME();
248
249 if (!home)
250 return NULL;
251
252 size = strlen(home) + strlen("/.cache") + 1;
253 path = (char*)malloc(size);
254
255 if (!path)
256 {
257 free(home);
258 return NULL;
259 }
260
261 sprintf_s(path, size, "%s%s", home, "/.cache");
262 free(home);
263 #endif
264 return path;
265 }
266
GetPath_XDG_RUNTIME_DIR(void)267 char* GetPath_XDG_RUNTIME_DIR(void)
268 {
269 char* path = NULL;
270 #if defined(WIN32) && !defined(_UWP)
271 path = calloc(MAX_PATH, sizeof(char));
272
273 if (!path)
274 return NULL;
275
276 if (FAILED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
277 {
278 free(path);
279 return NULL;
280 }
281
282 #else
283 /**
284 * There is a single base directory relative to which user-specific runtime files and other file
285 * objects should be placed. This directory is defined by the environment variable
286 * $XDG_RUNTIME_DIR.
287 *
288 * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
289 * runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
290 * The directory MUST be owned by the user, and he MUST be the only one having read and write
291 * access to it. Its Unix access mode MUST be 0700.
292 *
293 * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created
294 * when the user first logs in and if the user fully logs out the directory MUST be removed. If
295 * the user logs in more than once he should get pointed to the same directory, and it is
296 * mandatory that the directory continues to exist from his first login to his last logout on
297 * the system, and not removed in between. Files in the directory MUST not survive reboot or a
298 * full logout/login cycle.
299 *
300 * The directory MUST be on a local file system and not shared with any other system. The
301 * directory MUST by fully-featured by the standards of the operating system. More specifically,
302 * on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper
303 * permissions, file locking, sparse files, memory mapping, file change notifications, a
304 * reliable hard link count must be supported, and no restrictions on the file name character
305 * set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To
306 * ensure that your files are not removed, they should have their access time timestamp modified
307 * at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
308 *
309 * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with
310 * similar capabilities and print a warning message. Applications should use this directory for
311 * communication and synchronization purposes and should not place larger files in it, since it
312 * might reside in runtime memory and cannot necessarily be swapped out to disk.
313 */
314 path = GetEnvAlloc("XDG_RUNTIME_DIR");
315 #endif
316
317 if (path)
318 return path;
319
320 path = GetPath_TEMP();
321 return path;
322 }
323
GetKnownPath(int id)324 char* GetKnownPath(int id)
325 {
326 char* path = NULL;
327
328 switch (id)
329 {
330 case KNOWN_PATH_HOME:
331 path = GetPath_HOME();
332 break;
333
334 case KNOWN_PATH_TEMP:
335 path = GetPath_TEMP();
336 break;
337
338 case KNOWN_PATH_XDG_DATA_HOME:
339 path = GetPath_XDG_DATA_HOME();
340 break;
341
342 case KNOWN_PATH_XDG_CONFIG_HOME:
343 path = GetPath_XDG_CONFIG_HOME();
344 break;
345
346 case KNOWN_PATH_XDG_CACHE_HOME:
347 path = GetPath_XDG_CACHE_HOME();
348 break;
349
350 case KNOWN_PATH_XDG_RUNTIME_DIR:
351 path = GetPath_XDG_RUNTIME_DIR();
352 break;
353
354 default:
355 path = NULL;
356 break;
357 }
358
359 return path;
360 }
361
GetKnownSubPath(int id,const char * path)362 char* GetKnownSubPath(int id, const char* path)
363 {
364 char* subPath;
365 char* knownPath;
366 knownPath = GetKnownPath(id);
367
368 if (!knownPath)
369 return NULL;
370
371 subPath = GetCombinedPath(knownPath, path);
372 free(knownPath);
373 return subPath;
374 }
375
GetEnvironmentPath(char * name)376 char* GetEnvironmentPath(char* name)
377 {
378 char* env = NULL;
379 DWORD nSize;
380 nSize = GetEnvironmentVariableA(name, NULL, 0);
381
382 if (nSize)
383 {
384 env = (LPSTR)malloc(nSize);
385
386 if (!env)
387 return NULL;
388
389 if (GetEnvironmentVariableA(name, env, nSize) != nSize - 1)
390 {
391 free(env);
392 return NULL;
393 }
394 }
395
396 return env;
397 }
398
GetEnvironmentSubPath(char * name,const char * path)399 char* GetEnvironmentSubPath(char* name, const char* path)
400 {
401 char* env;
402 char* subpath;
403 env = GetEnvironmentPath(name);
404
405 if (!env)
406 return NULL;
407
408 subpath = GetCombinedPath(env, path);
409 free(env);
410 return subpath;
411 }
412
GetCombinedPath(const char * basePath,const char * subPath)413 char* GetCombinedPath(const char* basePath, const char* subPath)
414 {
415 int length;
416 HRESULT status;
417 char* path = NULL;
418 char* subPathCpy;
419 int basePathLength = 0;
420 int subPathLength = 0;
421
422 if (basePath)
423 basePathLength = (int)strlen(basePath);
424
425 if (subPath)
426 subPathLength = (int)strlen(subPath);
427
428 length = basePathLength + subPathLength + 1;
429 path = (char*)malloc(length + 1);
430
431 if (!path)
432 return NULL;
433
434 if (basePath)
435 CopyMemory(path, basePath, basePathLength);
436
437 path[basePathLength] = '\0';
438
439 if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
440 {
441 free(path);
442 return NULL;
443 }
444
445 if (!subPath)
446 return path;
447
448 subPathCpy = _strdup(subPath);
449
450 if (!subPathCpy)
451 {
452 free(path);
453 return NULL;
454 }
455
456 if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
457 {
458 free(path);
459 free(subPathCpy);
460 return NULL;
461 }
462
463 status = NativePathCchAppendA(path, length + 1, subPathCpy);
464 free(subPathCpy);
465
466 if (FAILED(status))
467 {
468 free(path);
469 return NULL;
470 }
471 else
472 return path;
473 }
474
PathMakePathA(LPCSTR path,LPSECURITY_ATTRIBUTES lpAttributes)475 BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
476 {
477 #if defined(_UWP)
478 return FALSE;
479 #elif defined(_WIN32)
480 return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS);
481 #else
482 const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
483 char* dup;
484 char* p;
485 BOOL result = TRUE;
486 /* we only operate on a non-null, absolute path */
487 #if defined(__OS2__)
488
489 if (!path)
490 return FALSE;
491
492 #else
493
494 if (!path || *path != delim)
495 return FALSE;
496
497 #endif
498
499 if (!(dup = _strdup(path)))
500 return FALSE;
501
502 #ifdef __OS2__
503 p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
504
505 while (p)
506 #else
507 for (p = dup; p;)
508 #endif
509 {
510 if ((p = strchr(p + 1, delim)))
511 *p = '\0';
512
513 if (mkdir(dup, 0777) != 0)
514 if (errno != EEXIST)
515 {
516 result = FALSE;
517 break;
518 }
519
520 if (p)
521 *p = delim;
522 }
523
524 free(dup);
525 return (result);
526 #endif
527 }
528
529 #if !defined(_WIN32) || defined(_UWP)
530
PathIsRelativeA(LPCSTR pszPath)531 BOOL PathIsRelativeA(LPCSTR pszPath)
532 {
533 if (!pszPath)
534 return FALSE;
535
536 return pszPath[0] != '/';
537 }
538
PathIsRelativeW(LPCWSTR pszPath)539 BOOL PathIsRelativeW(LPCWSTR pszPath)
540 {
541 LPSTR lpFileNameA = NULL;
542 BOOL ret;
543
544 if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1)
545 return FALSE;
546
547 ret = PathIsRelativeA(lpFileNameA);
548 free(lpFileNameA);
549 return ret;
550 }
551
PathFileExistsA(LPCSTR pszPath)552 BOOL PathFileExistsA(LPCSTR pszPath)
553 {
554 struct stat stat_info;
555
556 if (stat(pszPath, &stat_info) != 0)
557 return FALSE;
558
559 return TRUE;
560 }
561
PathFileExistsW(LPCWSTR pszPath)562 BOOL PathFileExistsW(LPCWSTR pszPath)
563 {
564 LPSTR lpFileNameA = NULL;
565 BOOL ret;
566
567 if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1)
568 return FALSE;
569
570 ret = winpr_PathFileExists(lpFileNameA);
571 free(lpFileNameA);
572 return ret;
573 }
574
PathIsDirectoryEmptyA(LPCSTR pszPath)575 BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
576 {
577 struct dirent* dp;
578 int empty = 1;
579 DIR* dir = opendir(pszPath);
580
581 if (dir == NULL) /* Not a directory or doesn't exist */
582 return 1;
583
584 while ((dp = readdir(dir)) != NULL)
585 {
586 if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
587 continue; /* Skip . and .. */
588
589 empty = 0;
590 break;
591 }
592
593 closedir(dir);
594 return empty;
595 }
596
PathIsDirectoryEmptyW(LPCWSTR pszPath)597 BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
598 {
599 LPSTR lpFileNameA = NULL;
600 BOOL ret;
601
602 if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1)
603 return FALSE;
604
605 ret = PathIsDirectoryEmptyA(lpFileNameA);
606 free(lpFileNameA);
607 return ret;
608 }
609
610 #else
611
612 #ifdef _WIN32
613 #pragma comment(lib, "shlwapi.lib")
614 #endif
615
616 #endif
617
winpr_MoveFile(LPCSTR lpExistingFileName,LPCSTR lpNewFileName)618 BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
619 {
620 #ifndef _WIN32
621 return MoveFileA(lpExistingFileName, lpNewFileName);
622 #else
623 BOOL result = FALSE;
624 LPWSTR lpExistingFileNameW = NULL;
625 LPWSTR lpNewFileNameW = NULL;
626
627 if (!lpExistingFileName || !lpNewFileName)
628 return FALSE;
629
630 if (ConvertToUnicode(CP_UTF8, 0, lpExistingFileName, -1, &lpExistingFileNameW, 0) < 1)
631 goto cleanup;
632
633 if (ConvertToUnicode(CP_UTF8, 0, lpNewFileName, -1, &lpNewFileNameW, 0) < 1)
634 goto cleanup;
635
636 result = MoveFileW(lpExistingFileNameW, lpNewFileNameW);
637
638 cleanup:
639 free(lpExistingFileNameW);
640 free(lpNewFileNameW);
641 return result;
642 #endif
643 }
644
winpr_DeleteFile(const char * lpFileName)645 BOOL winpr_DeleteFile(const char* lpFileName)
646 {
647 #ifndef _WIN32
648 return DeleteFileA(lpFileName);
649 #else
650 LPWSTR lpFileNameW = NULL;
651 BOOL result = FALSE;
652
653 if (lpFileName)
654 {
655 if (ConvertToUnicode(CP_UTF8, 0, lpFileName, -1, &lpFileNameW, 0) < 1)
656 goto cleanup;
657 }
658
659 result = DeleteFileW(lpFileNameW);
660
661 cleanup:
662 free(lpFileNameW);
663 return result;
664 #endif
665 }
666
winpr_RemoveDirectory(LPCSTR lpPathName)667 BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
668 {
669 #ifndef _WIN32
670 return RemoveDirectoryA(lpPathName);
671 #else
672 LPWSTR lpPathNameW = NULL;
673 BOOL result = FALSE;
674
675 if (lpPathName)
676 {
677 if (ConvertToUnicode(CP_UTF8, 0, lpPathName, -1, &lpPathNameW, 0) < 1)
678 goto cleanup;
679 }
680
681 result = RemoveDirectoryW(lpPathNameW);
682
683 cleanup:
684 free(lpPathNameW);
685 return result;
686 #endif
687 }
688
winpr_PathFileExists(const char * pszPath)689 BOOL winpr_PathFileExists(const char* pszPath)
690 {
691 #ifndef _WIN32
692 return PathFileExistsA(pszPath);
693 #else
694 WCHAR* pszPathW = NULL;
695 BOOL result = FALSE;
696
697 if (ConvertToUnicode(CP_UTF8, 0, pszPath, -1, &pszPathW, 0) < 1)
698 return FALSE;
699
700 result = PathFileExistsW(pszPathW);
701 free(pszPathW);
702
703 return result;
704 #endif
705 }
706
winpr_PathMakePath(const char * path,LPSECURITY_ATTRIBUTES lpAttributes)707 BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
708 {
709 #ifndef _WIN32
710 return PathMakePathA(path, lpAttributes);
711 #else
712 WCHAR* pathW = NULL;
713 BOOL result = FALSE;
714
715 if (ConvertToUnicode(CP_UTF8, 0, path, -1, &pathW, 0) < 1)
716 return FALSE;
717
718 result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS;
719 free(pathW);
720
721 return result;
722 #endif
723 }
724