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