summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/path/shell.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/path/shell.c821
1 files changed, 821 insertions, 0 deletions
diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c
new file mode 100644
index 0000000..4380a9b
--- /dev/null
+++ b/winpr/libwinpr/path/shell.c
@@ -0,0 +1,821 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Path Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <winpr/crt.h>
+#include <winpr/platform.h>
+#include <winpr/file.h>
+#include <winpr/tchar.h>
+#include <winpr/environment.h>
+
+#include <winpr/path.h>
+#include <winpr/wlog.h>
+
+#include "../log.h"
+#define TAG WINPR_TAG("path.shell")
+
+#if defined(__IOS__)
+#include "shell_ios.h"
+#endif
+
+#if defined(WIN32)
+#include <shlobj.h>
+#else
+#include <errno.h>
+#include <dirent.h>
+#endif
+
+static char* GetPath_XDG_CONFIG_HOME(void);
+static char* GetPath_XDG_RUNTIME_DIR(void);
+
+/**
+ * SHGetKnownFolderPath function:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/
+ */
+
+/**
+ * XDG Base Directory Specification:
+ * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
+ */
+
+char* GetEnvAlloc(LPCSTR lpName)
+{
+ DWORD nSize = 0;
+ DWORD nStatus = 0;
+ char* env = NULL;
+
+ nSize = GetEnvironmentVariableX(lpName, NULL, 0);
+
+ if (nSize > 0)
+ {
+ env = malloc(nSize);
+
+ if (!env)
+ return NULL;
+
+ nStatus = GetEnvironmentVariableX(lpName, env, nSize);
+
+ if (nStatus != (nSize - 1))
+ {
+ free(env);
+ return NULL;
+ }
+ }
+
+ return env;
+}
+
+static char* GetPath_HOME(void)
+{
+ char* path = NULL;
+#ifdef _WIN32
+ path = GetEnvAlloc("UserProfile");
+#elif defined(__IOS__)
+ path = ios_get_home();
+#else
+ path = GetEnvAlloc("HOME");
+#endif
+ return path;
+}
+
+static char* GetPath_TEMP(void)
+{
+ char* path = NULL;
+#ifdef _WIN32
+ path = GetEnvAlloc("TEMP");
+#elif defined(__IOS__)
+ path = ios_get_temp();
+#else
+ path = GetEnvAlloc("TMPDIR");
+
+ if (!path)
+ path = _strdup("/tmp");
+
+#endif
+ return path;
+}
+
+static char* GetPath_XDG_DATA_HOME(void)
+{
+ char* path = NULL;
+#if defined(WIN32) || defined(__IOS__)
+ path = GetPath_XDG_CONFIG_HOME();
+#else
+ size_t size = 0;
+ char* home = NULL;
+ /**
+ * There is a single base directory relative to which user-specific data files should be
+ * written. This directory is defined by the environment variable $XDG_DATA_HOME.
+ *
+ * $XDG_DATA_HOME defines the base directory relative to which user specific data files should
+ * be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
+ * $HOME/.local/share should be used.
+ */
+ path = GetEnvAlloc("XDG_DATA_HOME");
+
+ if (path)
+ return path;
+
+ home = GetPath_HOME();
+
+ if (!home)
+ return NULL;
+
+ size = strlen(home) + strlen("/.local/share") + 1;
+ path = (char*)malloc(size);
+
+ if (!path)
+ {
+ free(home);
+ return NULL;
+ }
+
+ sprintf_s(path, size, "%s%s", home, "/.local/share");
+ free(home);
+#endif
+ return path;
+}
+
+static char* GetPath_XDG_CONFIG_HOME(void)
+{
+ char* path = NULL;
+#if defined(WIN32) && !defined(_UWP)
+ path = calloc(MAX_PATH, sizeof(char));
+
+ if (!path)
+ return NULL;
+
+ if (FAILED(SHGetFolderPathA(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
+ {
+ free(path);
+ return NULL;
+ }
+
+#elif defined(__IOS__)
+ path = ios_get_data();
+#else
+ size_t size = 0;
+ char* home = NULL;
+ /**
+ * There is a single base directory relative to which user-specific configuration files should
+ * be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
+ *
+ * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration
+ * files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to
+ * $HOME/.config should be used.
+ */
+ path = GetEnvAlloc("XDG_CONFIG_HOME");
+
+ if (path)
+ return path;
+
+ home = GetPath_HOME();
+
+ if (!home)
+ home = GetPath_TEMP();
+
+ if (!home)
+ return NULL;
+
+ size = strlen(home) + strlen("/.config") + 1;
+ path = (char*)malloc(size);
+
+ if (!path)
+ {
+ free(home);
+ return NULL;
+ }
+
+ sprintf_s(path, size, "%s%s", home, "/.config");
+ free(home);
+#endif
+ return path;
+}
+
+static char* GetPath_XDG_CACHE_HOME(void)
+{
+ char* path = NULL;
+ char* home = NULL;
+#if defined(WIN32)
+ home = GetPath_XDG_RUNTIME_DIR();
+
+ if (home)
+ {
+ path = GetCombinedPath(home, "cache");
+
+ if (!winpr_PathFileExists(path))
+ if (!CreateDirectoryA(path, NULL))
+ path = NULL;
+ }
+
+ free(home);
+#elif defined(__IOS__)
+ path = ios_get_cache();
+#else
+ size_t size = 0;
+ /**
+ * There is a single base directory relative to which user-specific non-essential (cached) data
+ * should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
+ *
+ * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data
+ * files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to
+ * $HOME/.cache should be used.
+ */
+ path = GetEnvAlloc("XDG_CACHE_HOME");
+
+ if (path)
+ return path;
+
+ home = GetPath_HOME();
+
+ if (!home)
+ return NULL;
+
+ size = strlen(home) + strlen("/.cache") + 1;
+ path = (char*)malloc(size);
+
+ if (!path)
+ {
+ free(home);
+ return NULL;
+ }
+
+ sprintf_s(path, size, "%s%s", home, "/.cache");
+ free(home);
+#endif
+ return path;
+}
+
+char* GetPath_XDG_RUNTIME_DIR(void)
+{
+ char* path = NULL;
+#if defined(WIN32) && !defined(_UWP)
+ path = calloc(MAX_PATH, sizeof(char));
+
+ if (!path)
+ return NULL;
+
+ if (FAILED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
+ {
+ free(path);
+ return NULL;
+ }
+
+#else
+ /**
+ * There is a single base directory relative to which user-specific runtime files and other file
+ * objects should be placed. This directory is defined by the environment variable
+ * $XDG_RUNTIME_DIR.
+ *
+ * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
+ * runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
+ * The directory MUST be owned by the user, and he MUST be the only one having read and write
+ * access to it. Its Unix access mode MUST be 0700.
+ *
+ * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created
+ * when the user first logs in and if the user fully logs out the directory MUST be removed. If
+ * the user logs in more than once he should get pointed to the same directory, and it is
+ * mandatory that the directory continues to exist from his first login to his last logout on
+ * the system, and not removed in between. Files in the directory MUST not survive reboot or a
+ * full logout/login cycle.
+ *
+ * The directory MUST be on a local file system and not shared with any other system. The
+ * directory MUST by fully-featured by the standards of the operating system. More specifically,
+ * on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper
+ * permissions, file locking, sparse files, memory mapping, file change notifications, a
+ * reliable hard link count must be supported, and no restrictions on the file name character
+ * set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To
+ * ensure that your files are not removed, they should have their access time timestamp modified
+ * at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
+ *
+ * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with
+ * similar capabilities and print a warning message. Applications should use this directory for
+ * communication and synchronization purposes and should not place larger files in it, since it
+ * might reside in runtime memory and cannot necessarily be swapped out to disk.
+ */
+ path = GetEnvAlloc("XDG_RUNTIME_DIR");
+#endif
+
+ if (path)
+ return path;
+
+ path = GetPath_TEMP();
+ return path;
+}
+
+char* GetKnownPath(int id)
+{
+ char* path = NULL;
+
+ switch (id)
+ {
+ case KNOWN_PATH_HOME:
+ path = GetPath_HOME();
+ break;
+
+ case KNOWN_PATH_TEMP:
+ path = GetPath_TEMP();
+ break;
+
+ case KNOWN_PATH_XDG_DATA_HOME:
+ path = GetPath_XDG_DATA_HOME();
+ break;
+
+ case KNOWN_PATH_XDG_CONFIG_HOME:
+ path = GetPath_XDG_CONFIG_HOME();
+ break;
+
+ case KNOWN_PATH_XDG_CACHE_HOME:
+ path = GetPath_XDG_CACHE_HOME();
+ break;
+
+ case KNOWN_PATH_XDG_RUNTIME_DIR:
+ path = GetPath_XDG_RUNTIME_DIR();
+ break;
+
+ default:
+ path = NULL;
+ break;
+ }
+
+ if (!path)
+ WLog_WARN(TAG, "Path %s is %p", GetKnownPathIdString(id), path);
+ return path;
+}
+
+char* GetKnownSubPath(int id, const char* path)
+{
+ char* subPath = NULL;
+ char* knownPath = NULL;
+ knownPath = GetKnownPath(id);
+
+ if (!knownPath)
+ return NULL;
+
+ subPath = GetCombinedPath(knownPath, path);
+ free(knownPath);
+ return subPath;
+}
+
+char* GetEnvironmentPath(char* name)
+{
+ char* env = NULL;
+ DWORD nSize = 0;
+ DWORD nStatus = 0;
+ nSize = GetEnvironmentVariableX(name, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ return NULL;
+
+ nStatus = GetEnvironmentVariableX(name, env, nSize);
+
+ if (nStatus != (nSize - 1))
+ {
+ free(env);
+ return NULL;
+ }
+ }
+
+ return env;
+}
+
+char* GetEnvironmentSubPath(char* name, const char* path)
+{
+ char* env = NULL;
+ char* subpath = NULL;
+ env = GetEnvironmentPath(name);
+
+ if (!env)
+ return NULL;
+
+ subpath = GetCombinedPath(env, path);
+ free(env);
+ return subpath;
+}
+
+char* GetCombinedPath(const char* basePath, const char* subPath)
+{
+ size_t length = 0;
+ HRESULT status = 0;
+ char* path = NULL;
+ char* subPathCpy = NULL;
+ size_t basePathLength = 0;
+ size_t subPathLength = 0;
+
+ if (basePath)
+ basePathLength = strlen(basePath);
+
+ if (subPath)
+ subPathLength = strlen(subPath);
+
+ length = basePathLength + subPathLength + 1;
+ path = (char*)calloc(1, length + 1);
+
+ if (!path)
+ goto fail;
+
+ if (basePath)
+ CopyMemory(path, basePath, basePathLength);
+
+ if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
+ goto fail;
+
+ if (!subPath)
+ return path;
+
+ subPathCpy = _strdup(subPath);
+
+ if (!subPathCpy)
+ goto fail;
+
+ if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
+ goto fail;
+
+ status = NativePathCchAppendA(path, length + 1, subPathCpy);
+ if (FAILED(status))
+ goto fail;
+
+ free(subPathCpy);
+ return path;
+
+fail:
+ free(path);
+ free(subPathCpy);
+ return NULL;
+}
+
+BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
+{
+#if defined(_UWP)
+ return FALSE;
+#elif defined(_WIN32)
+ return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS);
+#else
+ const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
+ char* dup = NULL;
+ BOOL result = TRUE;
+ /* we only operate on a non-null, absolute path */
+#if defined(__OS2__)
+
+ if (!path)
+ return FALSE;
+
+#else
+
+ if (!path || *path != delim)
+ return FALSE;
+
+#endif
+
+ if (!(dup = _strdup(path)))
+ return FALSE;
+
+#ifdef __OS2__
+ p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
+
+ while (p)
+#else
+ for (char* p = dup; p;)
+#endif
+ {
+ if ((p = strchr(p + 1, delim)))
+ *p = '\0';
+
+ if (mkdir(dup, 0777) != 0)
+ if (errno != EEXIST)
+ {
+ result = FALSE;
+ break;
+ }
+
+ if (p)
+ *p = delim;
+ }
+
+ free(dup);
+ return (result);
+#endif
+}
+
+BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
+{
+#if defined(_UWP)
+ return FALSE;
+#elif defined(_WIN32)
+ return (SHCreateDirectoryExW(NULL, path, lpAttributes) == ERROR_SUCCESS);
+#else
+ const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE);
+ const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
+ char* dup = NULL;
+ BOOL result = TRUE;
+ /* we only operate on a non-null, absolute path */
+#if defined(__OS2__)
+
+ if (!path)
+ return FALSE;
+
+#else
+
+ if (!path || *path != wdelim)
+ return FALSE;
+
+#endif
+
+ dup = ConvertWCharToUtf8Alloc(path, NULL);
+ if (!dup)
+ return FALSE;
+
+#ifdef __OS2__
+ p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
+
+ while (p)
+#else
+ for (char* p = dup; p;)
+#endif
+ {
+ if ((p = strchr(p + 1, delim)))
+ *p = '\0';
+
+ if (mkdir(dup, 0777) != 0)
+ if (errno != EEXIST)
+ {
+ result = FALSE;
+ break;
+ }
+
+ if (p)
+ *p = delim;
+ }
+
+ free(dup);
+ return (result);
+#endif
+}
+
+#if !defined(_WIN32) || defined(_UWP)
+
+BOOL PathIsRelativeA(LPCSTR pszPath)
+{
+ if (!pszPath)
+ return FALSE;
+
+ return pszPath[0] != '/';
+}
+
+BOOL PathIsRelativeW(LPCWSTR pszPath)
+{
+ LPSTR lpFileNameA = NULL;
+ BOOL ret = FALSE;
+
+ if (!pszPath)
+ goto fail;
+
+ lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
+ if (!lpFileNameA)
+ goto fail;
+ ret = PathIsRelativeA(lpFileNameA);
+fail:
+ free(lpFileNameA);
+ return ret;
+}
+
+BOOL PathFileExistsA(LPCSTR pszPath)
+{
+ struct stat stat_info;
+
+ if (stat(pszPath, &stat_info) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+BOOL PathFileExistsW(LPCWSTR pszPath)
+{
+ LPSTR lpFileNameA = NULL;
+ BOOL ret = FALSE;
+
+ if (!pszPath)
+ goto fail;
+ lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
+ if (!lpFileNameA)
+ goto fail;
+
+ ret = winpr_PathFileExists(lpFileNameA);
+fail:
+ free(lpFileNameA);
+ return ret;
+}
+
+BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
+{
+ struct dirent* dp = NULL;
+ int empty = 1;
+ DIR* dir = opendir(pszPath);
+
+ if (dir == NULL) /* Not a directory or doesn't exist */
+ return 1;
+
+ while ((dp = readdir(dir)) != NULL)
+ {
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+ continue; /* Skip . and .. */
+
+ empty = 0;
+ break;
+ }
+
+ closedir(dir);
+ return empty;
+}
+
+BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
+{
+ LPSTR lpFileNameA = NULL;
+ BOOL ret = FALSE;
+ if (!pszPath)
+ goto fail;
+ lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
+ if (!lpFileNameA)
+ goto fail;
+ ret = PathIsDirectoryEmptyA(lpFileNameA);
+fail:
+ free(lpFileNameA);
+ return ret;
+}
+
+#else
+
+#ifdef _MSC_VER
+#pragma comment(lib, "shlwapi.lib")
+#endif
+
+#endif
+
+BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
+{
+#ifndef _WIN32
+ return MoveFileA(lpExistingFileName, lpNewFileName);
+#else
+ BOOL result = FALSE;
+ LPWSTR lpExistingFileNameW = NULL;
+ LPWSTR lpNewFileNameW = NULL;
+
+ if (!lpExistingFileName || !lpNewFileName)
+ return FALSE;
+
+ lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL);
+ if (!lpExistingFileNameW)
+ goto cleanup;
+ lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL);
+ if (!lpNewFileNameW)
+ goto cleanup;
+
+ result = MoveFileW(lpExistingFileNameW, lpNewFileNameW);
+
+cleanup:
+ free(lpExistingFileNameW);
+ free(lpNewFileNameW);
+ return result;
+#endif
+}
+
+BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
+{
+#ifndef _WIN32
+ return MoveFileExA(lpExistingFileName, lpNewFileName, dwFlags);
+#else
+ BOOL result = FALSE;
+ LPWSTR lpExistingFileNameW = NULL;
+ LPWSTR lpNewFileNameW = NULL;
+
+ if (!lpExistingFileName || !lpNewFileName)
+ return FALSE;
+
+ lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL);
+ if (!lpExistingFileNameW)
+ goto cleanup;
+ lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL);
+ if (!lpNewFileNameW)
+ goto cleanup;
+
+ result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags);
+
+cleanup:
+ free(lpExistingFileNameW);
+ free(lpNewFileNameW);
+ return result;
+#endif
+}
+
+BOOL winpr_DeleteFile(const char* lpFileName)
+{
+#ifndef _WIN32
+ return DeleteFileA(lpFileName);
+#else
+ LPWSTR lpFileNameW = NULL;
+ BOOL result = FALSE;
+
+ if (lpFileName)
+ {
+ lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
+ if (!lpFileNameW)
+ goto cleanup;
+ }
+
+ result = DeleteFileW(lpFileNameW);
+
+cleanup:
+ free(lpFileNameW);
+ return result;
+#endif
+}
+
+BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
+{
+#ifndef _WIN32
+ return RemoveDirectoryA(lpPathName);
+#else
+ LPWSTR lpPathNameW = NULL;
+ BOOL result = FALSE;
+
+ if (lpPathName)
+ {
+ lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, NULL);
+ if (!lpPathNameW)
+ goto cleanup;
+ }
+
+ result = RemoveDirectoryW(lpPathNameW);
+
+cleanup:
+ free(lpPathNameW);
+ return result;
+#endif
+}
+
+BOOL winpr_PathFileExists(const char* pszPath)
+{
+ if (!pszPath)
+ return FALSE;
+#ifndef _WIN32
+ return PathFileExistsA(pszPath);
+#else
+ WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, NULL);
+ BOOL result = FALSE;
+
+ if (!pathW)
+ return FALSE;
+
+ result = PathFileExistsW(pathW);
+ free(pathW);
+
+ return result;
+#endif
+}
+
+BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
+{
+ if (!path)
+ return FALSE;
+#ifndef _WIN32
+ return PathMakePathA(path, lpAttributes);
+#else
+ WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, NULL);
+ BOOL result = FALSE;
+
+ if (!pathW)
+ return FALSE;
+
+ result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS;
+ free(pathW);
+
+ return result;
+#endif
+}