FreeRDP
Loading...
Searching...
No Matches
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 NULL", GetKnownPathIdString(WINPR_ASSERTING_INT_CAST(int, id)));
392 return path;
393}
394
395char* GetKnownSubPath(eKnownPathTypes id, const char* path)
396{
397 if (!path)
398 return GetKnownSubPathV(id, "%s", "");
399 return GetKnownSubPathV(id, "%s", path);
400}
401
402char* GetKnownSubPathV(eKnownPathTypes id, const char* path, ...)
403{
404 va_list ap;
405
406 va_start(ap, path);
407 char* str = GetKnownSubPathVA(id, path, ap);
408 va_end(ap);
409 return str;
410}
411
412char* GetKnownSubPathVA(eKnownPathTypes id, const char* path, va_list ap)
413{
414 char* knownPath = GetKnownPath(id);
415 if (!knownPath)
416 return NULL;
417
418 char* subPath = GetCombinedPathVA(knownPath, path, ap);
419 free(knownPath);
420 return subPath;
421}
422
423char* GetEnvironmentPath(char* name)
424{
425 char* env = NULL;
426 DWORD nSize = 0;
427 DWORD nStatus = 0;
428 nSize = GetEnvironmentVariableX(name, NULL, 0);
429
430 if (nSize)
431 {
432 env = (LPSTR)malloc(nSize);
433
434 if (!env)
435 return NULL;
436
437 nStatus = GetEnvironmentVariableX(name, env, nSize);
438
439 if (nStatus != (nSize - 1))
440 {
441 free(env);
442 return NULL;
443 }
444 }
445
446 return env;
447}
448
449char* GetEnvironmentSubPath(char* name, const char* path)
450{
451 if (!path)
452 return GetEnvironmentSubPathV(name, "%s", "");
453 return GetEnvironmentSubPathV(name, "%s", path);
454}
455
456char* GetEnvironmentSubPathV(char* name, const char* path, ...)
457{
458 va_list ap;
459 va_start(ap, path);
460 char* str = GetEnvironmentSubPathVA(name, path, ap);
461 va_end(ap);
462 return str;
463}
464
465char* GetEnvironmentSubPathVA(char* name, WINPR_FORMAT_ARG const char* path, va_list ap)
466{
467 char* env = GetEnvironmentPath(name);
468
469 if (!env)
470 return NULL;
471
472 char* subpath = GetCombinedPathVA(env, path, ap);
473 free(env);
474 return subpath;
475}
476
477char* GetCombinedPath(const char* basePath, const char* subPathFmt)
478{
479 if (!subPathFmt)
480 return GetCombinedPathV(basePath, "%s", "");
481 return GetCombinedPathV(basePath, "%s", subPathFmt);
482}
483
484char* GetCombinedPathV(const char* basePath, const char* subPathFmt, ...)
485{
486 va_list ap;
487
488 va_start(ap, subPathFmt);
489 char* str = GetCombinedPathVA(basePath, subPathFmt, ap);
490 va_end(ap);
491 return str;
492}
493
494char* GetCombinedPathVA(const char* basePath, WINPR_FORMAT_ARG const char* subPathFmt, va_list ap)
495{
496 HRESULT status = 0;
497 char* subPathCpy = NULL;
498 size_t basePathLength = 0;
499 size_t subPathLength = 0;
500
501 if (basePath)
502 basePathLength = strlen(basePath);
503
504 bool haveSubPath = subPathFmt && (*subPathFmt != '\0');
505 if (haveSubPath)
506 {
507 const int rc = winpr_vasprintf(&subPathCpy, &subPathLength, subPathFmt, ap);
508 if (rc < 0)
509 return NULL;
510 if (rc == 0)
511 {
512 free(subPathCpy);
513 subPathCpy = NULL;
514 subPathLength = 0;
515 haveSubPath = false;
516 }
517 }
518
519 const size_t length = basePathLength + subPathLength + 1;
520 char* path = (char*)calloc(1, length + 1);
521
522 if (!path)
523 goto fail;
524
525 if (basePath)
526 CopyMemory(path, basePath, basePathLength);
527
528 if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
529 goto fail;
530
531 if (!haveSubPath)
532 return path;
533
534 if (!subPathCpy)
535 goto fail;
536
537 if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
538 goto fail;
539
540 status = NativePathCchAppendA(path, length + 1, subPathCpy);
541 if (FAILED(status))
542 goto fail;
543
544 free(subPathCpy);
545 return path;
546
547fail:
548 free(path);
549 free(subPathCpy);
550 return NULL;
551}
552
553BOOL PathMakePathA(LPCSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
554{
555#if defined(_UWP)
556 return FALSE;
557#elif defined(_WIN32)
558 return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS);
559#else
560 const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
561 char* dup = NULL;
562 BOOL result = TRUE;
563 /* we only operate on a non-null, absolute path */
564#if defined(__OS2__)
565
566 if (!path)
567 return FALSE;
568
569#else
570
571 if (!path || *path != delim)
572 return FALSE;
573
574#endif
575
576 if (!(dup = _strdup(path)))
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
606BOOL PathMakePathW(LPCWSTR path, WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpAttributes)
607{
608#if defined(_UWP)
609 return FALSE;
610#elif defined(_WIN32)
611 return (SHCreateDirectoryExW(NULL, path, lpAttributes) == ERROR_SUCCESS);
612#else
613 const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE);
614 const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
615 char* dup = NULL;
616 BOOL result = TRUE;
617 /* we only operate on a non-null, absolute path */
618#if defined(__OS2__)
619
620 if (!path)
621 return FALSE;
622
623#else
624
625 if (!path || *path != wdelim)
626 return FALSE;
627
628#endif
629
630 dup = ConvertWCharToUtf8Alloc(path, NULL);
631 if (!dup)
632 return FALSE;
633
634#ifdef __OS2__
635 p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
636
637 while (p)
638#else
639 for (char* p = dup; p;)
640#endif
641 {
642 if ((p = strchr(p + 1, delim)))
643 *p = '\0';
644
645 if (mkdir(dup, 0777) != 0)
646 if (errno != EEXIST)
647 {
648 result = FALSE;
649 break;
650 }
651
652 if (p)
653 *p = delim;
654 }
655
656 free(dup);
657 return (result);
658#endif
659}
660
661#if !defined(_WIN32) || defined(_UWP)
662
663BOOL PathIsRelativeA(LPCSTR pszPath)
664{
665 if (!pszPath)
666 return FALSE;
667
668 return pszPath[0] != '/';
669}
670
671BOOL PathIsRelativeW(LPCWSTR pszPath)
672{
673 LPSTR lpFileNameA = NULL;
674 BOOL ret = FALSE;
675
676 if (!pszPath)
677 goto fail;
678
679 lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
680 if (!lpFileNameA)
681 goto fail;
682 ret = PathIsRelativeA(lpFileNameA);
683fail:
684 free(lpFileNameA);
685 return ret;
686}
687
688BOOL PathFileExistsA(LPCSTR pszPath)
689{
690 struct stat stat_info;
691
692 if (stat(pszPath, &stat_info) != 0)
693 return FALSE;
694
695 return TRUE;
696}
697
698BOOL PathFileExistsW(LPCWSTR pszPath)
699{
700 LPSTR lpFileNameA = NULL;
701 BOOL ret = FALSE;
702
703 if (!pszPath)
704 goto fail;
705 lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
706 if (!lpFileNameA)
707 goto fail;
708
709 ret = winpr_PathFileExists(lpFileNameA);
710fail:
711 free(lpFileNameA);
712 return ret;
713}
714
715BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
716{
717 struct dirent* dp = NULL;
718 int empty = 1;
719 DIR* dir = opendir(pszPath);
720
721 if (dir == NULL) /* Not a directory or doesn't exist */
722 return 1;
723
724 // NOLINTNEXTLINE(concurrency-mt-unsafe)
725 while ((dp = readdir(dir)) != NULL)
726 {
727 if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
728 continue; /* Skip . and .. */
729
730 empty = 0;
731 break;
732 }
733
734 closedir(dir);
735 return empty;
736}
737
738BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
739{
740 LPSTR lpFileNameA = NULL;
741 BOOL ret = FALSE;
742 if (!pszPath)
743 goto fail;
744 lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
745 if (!lpFileNameA)
746 goto fail;
747 ret = PathIsDirectoryEmptyA(lpFileNameA);
748fail:
749 free(lpFileNameA);
750 return ret;
751}
752
753#endif
754
755BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
756{
757 return winpr_MoveFileEx(lpExistingFileName, lpNewFileName, 0);
758}
759
760BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
761{
762#ifndef _WIN32
763 struct stat st;
764 int ret = 0;
765 ret = stat(lpNewFileName, &st);
766
767 if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
768 {
769 if (ret == 0)
770 {
771 SetLastError(ERROR_ALREADY_EXISTS);
772 return FALSE;
773 }
774 }
775 else
776 {
777 if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
778 {
779 SetLastError(ERROR_ACCESS_DENIED);
780 return FALSE;
781 }
782 }
783
784 ret = rename(lpExistingFileName, lpNewFileName);
785
786 if (ret != 0)
787 SetLastError(map_posix_err(errno));
788
789 return ret == 0;
790#else
791 BOOL result = FALSE;
792 LPWSTR lpExistingFileNameW = NULL;
793 LPWSTR lpNewFileNameW = NULL;
794
795 if (!lpExistingFileName || !lpNewFileName)
796 return FALSE;
797
798 lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL);
799 if (!lpExistingFileNameW)
800 goto cleanup;
801 lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL);
802 if (!lpNewFileNameW)
803 goto cleanup;
804
805 result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags);
806
807cleanup:
808 free(lpExistingFileNameW);
809 free(lpNewFileNameW);
810 return result;
811#endif
812}
813
814BOOL winpr_DeleteFile(const char* lpFileName)
815{
816#ifndef _WIN32
817 if (!lpFileName)
818 return FALSE;
819
820 const int status = unlink(lpFileName);
821 return (status != -1) ? TRUE : FALSE;
822#else
823 LPWSTR lpFileNameW = NULL;
824 BOOL result = FALSE;
825
826 if (lpFileName)
827 lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
828
829 if (!lpFileNameW)
830 goto cleanup;
831
832 result = DeleteFileW(lpFileNameW);
833
834cleanup:
835 free(lpFileNameW);
836 return result;
837#endif
838}
839
840BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
841{
842#ifndef _WIN32
843 int ret = rmdir(lpPathName);
844
845 if (ret != 0)
846 SetLastError(map_posix_err(errno));
847 else
848 SetLastError(STATUS_SUCCESS);
849
850 return ret == 0;
851#else
852 LPWSTR lpPathNameW = NULL;
853 BOOL result = FALSE;
854
855 if (lpPathName)
856 lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, NULL);
857
858 if (!lpPathNameW)
859 goto cleanup;
860
861 result = RemoveDirectoryW(lpPathNameW);
862
863cleanup:
864 free(lpPathNameW);
865 return result;
866#endif
867}
868
869BOOL winpr_PathFileExists(const char* pszPath)
870{
871 if (!pszPath)
872 return FALSE;
873#ifndef _WIN32
874 return PathFileExistsA(pszPath);
875#else
876 WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, NULL);
877 BOOL result = FALSE;
878
879 if (!pathW)
880 return FALSE;
881
882 result = PathFileExistsW(pathW);
883 free(pathW);
884
885 return result;
886#endif
887}
888
889BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
890{
891 if (!path)
892 return FALSE;
893#ifndef _WIN32
894 return PathMakePathA(path, lpAttributes);
895#else
896 WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, NULL);
897 BOOL result = FALSE;
898
899 if (!pathW)
900 return FALSE;
901
902 result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS;
903 free(pathW);
904
905 return result;
906#endif
907}