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