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