FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
path/shell.c
1
21#include <winpr/config.h>
22#include <winpr/build-config.h>
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28
29#include <winpr/crt.h>
30#include <winpr/platform.h>
31#include <winpr/file.h>
32#include <winpr/tchar.h>
33#include <winpr/environment.h>
34
35#include <winpr/path.h>
36#include <winpr/wlog.h>
37
38#include "../file/file.h"
39
40#include "../log.h"
41#define TAG WINPR_TAG("path.shell")
42
43#if defined(__IOS__)
44#include "shell_ios.h"
45#endif
46
47#if defined(WIN32)
48#include <windows.h>
49#include <shlobj.h>
50#else
51#include <errno.h>
52#include <dirent.h>
53#endif
54
55static char* GetPath_XDG_CONFIG_HOME(void);
56static char* GetPath_XDG_RUNTIME_DIR(void);
57
63#if defined(WIN32) && !defined(_UWP)
64
65static char* win_get_known_folder(REFKNOWNFOLDERID id, BOOL currentUser)
66{
67 WCHAR* wpath = NULL;
68 HANDLE handle = currentUser ? NULL : (HANDLE)-1;
69 if (FAILED(SHGetKnownFolderPath(id, 0, handle, &wpath)))
70 return NULL;
71
72 if (!wpath)
73 return NULL;
74
75 char* path = ConvertWCharToUtf8Alloc(wpath, NULL);
76 CoTaskMemFree(wpath);
77 return path;
78}
79#endif
80
86char* GetEnvAlloc(LPCSTR lpName)
87{
88 DWORD nSize = 0;
89 DWORD nStatus = 0;
90 char* env = NULL;
91
92 nSize = GetEnvironmentVariableX(lpName, NULL, 0);
93
94 if (nSize > 0)
95 {
96 env = malloc(nSize);
97
98 if (!env)
99 return NULL;
100
101 nStatus = GetEnvironmentVariableX(lpName, env, nSize);
102
103 if (nStatus != (nSize - 1))
104 {
105 free(env);
106 return NULL;
107 }
108 }
109
110 return env;
111}
112
113static char* GetPath_HOME(void)
114{
115 char* path = NULL;
116#ifdef _WIN32
117 path = GetEnvAlloc("UserProfile");
118#elif defined(__IOS__)
119 path = ios_get_home();
120#else
121 path = GetEnvAlloc("HOME");
122#endif
123 return path;
124}
125
126static char* GetPath_TEMP(void)
127{
128 char* path = NULL;
129#ifdef _WIN32
130 path = GetEnvAlloc("TEMP");
131#elif defined(__IOS__)
132 path = ios_get_temp();
133#else
134 path = GetEnvAlloc("TMPDIR");
135
136 if (!path)
137 path = _strdup("/tmp");
138
139#endif
140 return path;
141}
142
143static char* GetPath_XDG_DATA_HOME(void)
144{
145 char* path = NULL;
146#if defined(WIN32) || defined(__IOS__)
147 path = GetPath_XDG_CONFIG_HOME();
148#else
149 size_t size = 0;
150 char* home = NULL;
159 path = GetEnvAlloc("XDG_DATA_HOME");
160
161 if (path)
162 return path;
163
164 home = GetPath_HOME();
165
166 if (!home)
167 return NULL;
168
169 size = strlen(home) + strlen("/.local/share") + 1;
170 path = (char*)malloc(size);
171
172 if (!path)
173 {
174 free(home);
175 return NULL;
176 }
177
178 (void)sprintf_s(path, size, "%s%s", home, "/.local/share");
179 free(home);
180#endif
181 return path;
182}
183
184static char* GetPath_XDG_CONFIG_HOME(void)
185{
186 char* path = NULL;
187#if defined(WIN32) && !defined(_UWP)
188
189 path = win_get_known_folder(&FOLDERID_RoamingAppData, TRUE);
190
191#elif defined(__IOS__)
192 path = ios_get_data();
193#else
194 size_t size = 0;
195 char* home = NULL;
204 path = GetEnvAlloc("XDG_CONFIG_HOME");
205
206 if (path)
207 return path;
208
209 home = GetPath_HOME();
210
211 if (!home)
212 home = GetPath_TEMP();
213
214 if (!home)
215 return NULL;
216
217 size = strlen(home) + strlen("/.config") + 1;
218 path = (char*)malloc(size);
219
220 if (!path)
221 {
222 free(home);
223 return NULL;
224 }
225
226 (void)sprintf_s(path, size, "%s%s", home, "/.config");
227 free(home);
228#endif
229 return path;
230}
231
232static char* GetPath_SYSTEM_CONFIG_HOME(void)
233{
234 char* path = NULL;
235#if defined(WIN32) && !defined(_UWP)
236
237 path = win_get_known_folder(&FOLDERID_ProgramData, FALSE);
238
239#elif defined(__IOS__)
240 path = ios_get_data();
241#else
242 path = _strdup(WINPR_INSTALL_SYSCONFDIR);
243#endif
244 return path;
245}
246
247static char* GetPath_XDG_CACHE_HOME(void)
248{
249 char* path = NULL;
250#if defined(WIN32)
251 {
252 char* home = GetPath_XDG_RUNTIME_DIR();
253
254 if (home)
255 {
256 path = GetCombinedPath(home, "cache");
257
258 if (!winpr_PathFileExists(path))
259 if (!winpr_PathMakePath(path, NULL))
260 path = NULL;
261 }
262
263 free(home);
264 }
265#elif defined(__IOS__)
266 path = ios_get_cache();
267#else
268 size_t size = 0;
277 path = GetEnvAlloc("XDG_CACHE_HOME");
278
279 if (path)
280 return path;
281
282 char* home = GetPath_HOME();
283
284 if (!home)
285 return NULL;
286
287 size = strlen(home) + strlen("/.cache") + 1;
288 path = (char*)malloc(size);
289
290 if (!path)
291 {
292 free(home);
293 return NULL;
294 }
295
296 (void)sprintf_s(path, size, "%s%s", home, "/.cache");
297 free(home);
298#endif
299 return path;
300}
301
302char* GetPath_XDG_RUNTIME_DIR(void)
303{
304 char* path = NULL;
305#if defined(WIN32) && !defined(_UWP)
306
307 path = win_get_known_folder(&FOLDERID_LocalAppData, TRUE);
308
309#else
341 path = GetEnvAlloc("XDG_RUNTIME_DIR");
342#endif
343
344 if (path)
345 return path;
346
347 path = GetPath_TEMP();
348 return path;
349}
350
351char* GetKnownPath(eKnownPathTypes id)
352{
353 char* path = NULL;
354
355 switch (id)
356 {
357 case KNOWN_PATH_HOME:
358 path = GetPath_HOME();
359 break;
360
361 case KNOWN_PATH_TEMP:
362 path = GetPath_TEMP();
363 break;
364
365 case KNOWN_PATH_XDG_DATA_HOME:
366 path = GetPath_XDG_DATA_HOME();
367 break;
368
369 case KNOWN_PATH_XDG_CONFIG_HOME:
370 path = GetPath_XDG_CONFIG_HOME();
371 break;
372
373 case KNOWN_PATH_XDG_CACHE_HOME:
374 path = GetPath_XDG_CACHE_HOME();
375 break;
376
377 case KNOWN_PATH_XDG_RUNTIME_DIR:
378 path = GetPath_XDG_RUNTIME_DIR();
379 break;
380
381 case KNOWN_PATH_SYSTEM_CONFIG_HOME:
382 path = GetPath_SYSTEM_CONFIG_HOME();
383 break;
384
385 default:
386 path = NULL;
387 break;
388 }
389
390 if (!path)
391 WLog_WARN(TAG, "Path %s is %p", GetKnownPathIdString(WINPR_ASSERTING_INT_CAST(int, id)),
392 path);
393 return path;
394}
395
396char* GetKnownSubPath(eKnownPathTypes id, const char* path)
397{
398 char* knownPath = GetKnownPath(id);
399 if (!knownPath)
400 return NULL;
401
402 char* subPath = GetCombinedPath(knownPath, path);
403 free(knownPath);
404 return subPath;
405}
406
407char* GetEnvironmentPath(char* name)
408{
409 char* env = NULL;
410 DWORD nSize = 0;
411 DWORD nStatus = 0;
412 nSize = GetEnvironmentVariableX(name, NULL, 0);
413
414 if (nSize)
415 {
416 env = (LPSTR)malloc(nSize);
417
418 if (!env)
419 return NULL;
420
421 nStatus = GetEnvironmentVariableX(name, env, nSize);
422
423 if (nStatus != (nSize - 1))
424 {
425 free(env);
426 return NULL;
427 }
428 }
429
430 return env;
431}
432
433char* GetEnvironmentSubPath(char* name, const char* path)
434{
435 char* env = NULL;
436 char* subpath = NULL;
437 env = GetEnvironmentPath(name);
438
439 if (!env)
440 return NULL;
441
442 subpath = GetCombinedPath(env, path);
443 free(env);
444 return subpath;
445}
446
447char* GetCombinedPath(const char* basePath, const char* subPath)
448{
449 size_t length = 0;
450 HRESULT status = 0;
451 char* path = NULL;
452 char* subPathCpy = NULL;
453 size_t basePathLength = 0;
454 size_t subPathLength = 0;
455
456 if (basePath)
457 basePathLength = strlen(basePath);
458
459 if (subPath)
460 subPathLength = strlen(subPath);
461
462 length = basePathLength + subPathLength + 1;
463 path = (char*)calloc(1, length + 1);
464
465 if (!path)
466 goto fail;
467
468 if (basePath)
469 CopyMemory(path, basePath, basePathLength);
470
471 if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
472 goto fail;
473
474 if (!subPath)
475 return path;
476
477 subPathCpy = _strdup(subPath);
478
479 if (!subPathCpy)
480 goto fail;
481
482 if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
483 goto fail;
484
485 status = NativePathCchAppendA(path, length + 1, subPathCpy);
486 if (FAILED(status))
487 goto fail;
488
489 free(subPathCpy);
490 return path;
491
492fail:
493 free(path);
494 free(subPathCpy);
495 return NULL;
496}
497
498BOOL PathMakePathA(LPCSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
499{
500#if defined(_UWP)
501 return FALSE;
502#elif defined(_WIN32)
503 return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS);
504#else
505 const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
506 char* dup = NULL;
507 BOOL result = TRUE;
508 /* we only operate on a non-null, absolute path */
509#if defined(__OS2__)
510
511 if (!path)
512 return FALSE;
513
514#else
515
516 if (!path || *path != delim)
517 return FALSE;
518
519#endif
520
521 if (!(dup = _strdup(path)))
522 return FALSE;
523
524#ifdef __OS2__
525 p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
526
527 while (p)
528#else
529 for (char* p = dup; p;)
530#endif
531 {
532 if ((p = strchr(p + 1, delim)))
533 *p = '\0';
534
535 if (mkdir(dup, 0777) != 0)
536 if (errno != EEXIST)
537 {
538 result = FALSE;
539 break;
540 }
541
542 if (p)
543 *p = delim;
544 }
545
546 free(dup);
547 return (result);
548#endif
549}
550
551BOOL PathMakePathW(LPCWSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
552{
553#if defined(_UWP)
554 return FALSE;
555#elif defined(_WIN32)
556 return (SHCreateDirectoryExW(NULL, path, lpAttributes) == ERROR_SUCCESS);
557#else
558 const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE);
559 const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
560 char* dup = NULL;
561 BOOL result = TRUE;
562 /* we only operate on a non-null, absolute path */
563#if defined(__OS2__)
564
565 if (!path)
566 return FALSE;
567
568#else
569
570 if (!path || *path != wdelim)
571 return FALSE;
572
573#endif
574
575 dup = ConvertWCharToUtf8Alloc(path, NULL);
576 if (!dup)
577 return FALSE;
578
579#ifdef __OS2__
580 p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
581
582 while (p)
583#else
584 for (char* p = dup; p;)
585#endif
586 {
587 if ((p = strchr(p + 1, delim)))
588 *p = '\0';
589
590 if (mkdir(dup, 0777) != 0)
591 if (errno != EEXIST)
592 {
593 result = FALSE;
594 break;
595 }
596
597 if (p)
598 *p = delim;
599 }
600
601 free(dup);
602 return (result);
603#endif
604}
605
606#if !defined(_WIN32) || defined(_UWP)
607
608BOOL PathIsRelativeA(LPCSTR pszPath)
609{
610 if (!pszPath)
611 return FALSE;
612
613 return pszPath[0] != '/';
614}
615
616BOOL PathIsRelativeW(LPCWSTR pszPath)
617{
618 LPSTR lpFileNameA = NULL;
619 BOOL ret = FALSE;
620
621 if (!pszPath)
622 goto fail;
623
624 lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
625 if (!lpFileNameA)
626 goto fail;
627 ret = PathIsRelativeA(lpFileNameA);
628fail:
629 free(lpFileNameA);
630 return ret;
631}
632
633BOOL PathFileExistsA(LPCSTR pszPath)
634{
635 struct stat stat_info;
636
637 if (stat(pszPath, &stat_info) != 0)
638 return FALSE;
639
640 return TRUE;
641}
642
643BOOL PathFileExistsW(LPCWSTR pszPath)
644{
645 LPSTR lpFileNameA = NULL;
646 BOOL ret = FALSE;
647
648 if (!pszPath)
649 goto fail;
650 lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
651 if (!lpFileNameA)
652 goto fail;
653
654 ret = winpr_PathFileExists(lpFileNameA);
655fail:
656 free(lpFileNameA);
657 return ret;
658}
659
660BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
661{
662 struct dirent* dp = NULL;
663 int empty = 1;
664 DIR* dir = opendir(pszPath);
665
666 if (dir == NULL) /* Not a directory or doesn't exist */
667 return 1;
668
669 // NOLINTNEXTLINE(concurrency-mt-unsafe)
670 while ((dp = readdir(dir)) != NULL)
671 {
672 if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
673 continue; /* Skip . and .. */
674
675 empty = 0;
676 break;
677 }
678
679 closedir(dir);
680 return empty;
681}
682
683BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
684{
685 LPSTR lpFileNameA = NULL;
686 BOOL ret = FALSE;
687 if (!pszPath)
688 goto fail;
689 lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
690 if (!lpFileNameA)
691 goto fail;
692 ret = PathIsDirectoryEmptyA(lpFileNameA);
693fail:
694 free(lpFileNameA);
695 return ret;
696}
697
698#endif
699
700BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
701{
702 return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, 0);
703}
704
705BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
706{
707#ifndef _WIN32
708 struct stat st;
709 int ret = 0;
710 ret = stat(lpNewFileName, &st);
711
712 if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
713 {
714 if (ret == 0)
715 {
716 SetLastError(ERROR_ALREADY_EXISTS);
717 return FALSE;
718 }
719 }
720 else
721 {
722 if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
723 {
724 SetLastError(ERROR_ACCESS_DENIED);
725 return FALSE;
726 }
727 }
728
729 ret = rename(lpExistingFileName, lpNewFileName);
730
731 if (ret != 0)
732 SetLastError(map_posix_err(errno));
733
734 return ret == 0;
735#else
736 BOOL result = FALSE;
737 LPWSTR lpExistingFileNameW = NULL;
738 LPWSTR lpNewFileNameW = NULL;
739
740 if (!lpExistingFileName || !lpNewFileName)
741 return FALSE;
742
743 lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL);
744 if (!lpExistingFileNameW)
745 goto cleanup;
746 lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL);
747 if (!lpNewFileNameW)
748 goto cleanup;
749
750 result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags);
751
752cleanup:
753 free(lpExistingFileNameW);
754 free(lpNewFileNameW);
755 return result;
756#endif
757}
758
759BOOL winpr_DeleteFile(const char* lpFileName)
760{
761#ifndef _WIN32
762 if (!lpFileName)
763 return FALSE;
764
765 const int status = unlink(lpFileName);
766 return (status != -1) ? TRUE : FALSE;
767#else
768 LPWSTR lpFileNameW = NULL;
769 BOOL result = FALSE;
770
771 if (lpFileName)
772 lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
773
774 if (!lpFileNameW)
775 goto cleanup;
776
777 result = DeleteFileW(lpFileNameW);
778
779cleanup:
780 free(lpFileNameW);
781 return result;
782#endif
783}
784
785BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
786{
787#ifndef _WIN32
788 int ret = rmdir(lpPathName);
789
790 if (ret != 0)
791 SetLastError(map_posix_err(errno));
792 else
793 SetLastError(STATUS_SUCCESS);
794
795 return ret == 0;
796#else
797 LPWSTR lpPathNameW = NULL;
798 BOOL result = FALSE;
799
800 if (lpPathName)
801 lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, NULL);
802
803 if (!lpPathNameW)
804 goto cleanup;
805
806 result = RemoveDirectoryW(lpPathNameW);
807
808cleanup:
809 free(lpPathNameW);
810 return result;
811#endif
812}
813
814BOOL winpr_PathFileExists(const char* pszPath)
815{
816 if (!pszPath)
817 return FALSE;
818#ifndef _WIN32
819 return PathFileExistsA(pszPath);
820#else
821 WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, NULL);
822 BOOL result = FALSE;
823
824 if (!pathW)
825 return FALSE;
826
827 result = PathFileExistsW(pathW);
828 free(pathW);
829
830 return result;
831#endif
832}
833
834BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
835{
836 if (!path)
837 return FALSE;
838#ifndef _WIN32
839 return PathMakePathA(path, lpAttributes);
840#else
841 WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, NULL);
842 BOOL result = FALSE;
843
844 if (!pathW)
845 return FALSE;
846
847 result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS;
848 free(pathW);
849
850 return result;
851#endif
852}