[PATCH 1/6] windows.c: sanitize the backend for path retrieval

Lubomir I. Ivanov neolit123 at gmail.com
Tue Oct 6 03:10:16 PDT 2015


From: "Lubomir I. Ivanov" <neolit123 at gmail.com>

The old backend was less safe because, it didn't:
- support UTF-16 paths!
- use GetUserNameW()

The following changes were made:
- system_default_path_append() can be used to retrieve
a path in the user Roaming path.
- system_default_directory() - this is equal to
system_default_path_append(NULL)
- system_default_filename() also uses the helper
system_default_path_append()

Signed-off-by: Lubomir I. Ivanov <neolit123 at gmail.com>
---
 windows.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 73 insertions(+), 14 deletions(-)

diff --git a/windows.c b/windows.c
index 652a95a..c473f40 100644
--- a/windows.c
+++ b/windows.c
@@ -12,6 +12,7 @@
 #include <assert.h>
 #include <dirent.h>
 #include <zip.h>
+#include <lmcons.h>
 
 const char non_standard_system_divelist_default_font[] = "Calibri";
 const char current_system_divelist_default_font[] = "Segoe UI";
@@ -38,23 +39,81 @@ bool subsurface_ignore_font(const char *font)
 	return false;
 }
 
+/* this function returns the Win32 Roaming path for the current user as UTF-8.
+ * it never returns NULL but fallsback to .\ instead!
+ * the append argument will append a wchar_t string to the end of the path.
+ */
+static const char *system_default_path_append(const wchar_t *append)
+{
+	wchar_t wpath[MAX_PATH] = { 0 };
+	const char *fname = "system_default_path()";
+
+	/* obtain the user path via SHGetFolderPathW.
+	 * this API is deprecated but still supported on modern Win32.
+	 * fallback to .\ if it fails.
+	 */
+	if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, wpath))) {
+		fprintf(stderr, "%s: cannot obtain path!\n", fname);
+		wpath[0] = L'.';
+		wpath[1] = L'\0';
+	}
+
+	wcscat(wpath, L"\\Subsurface");
+	if (append) {
+		wcscat(wpath, L"\\");
+		wcscat(wpath, append);
+	}
+
+	/* attempt to convert the UTF-16 string to UTF-8.
+	 * resize the buffer and fallback to .\Subsurface if it fails.
+	 */
+	const int wsz = wcslen(wpath);
+	const int sz = WideCharToMultiByte(CP_UTF8, 0, wpath, wsz, NULL, 0, NULL, NULL);
+	char *path = (char *)malloc(sz + 1);
+	if (!sz)
+		goto fallback;
+	if (WideCharToMultiByte(CP_UTF8, 0, wpath, wsz, path, sz, NULL, NULL)) {
+		path[sz] = '\0';
+		return path;
+	}
+
+	fallback:
+		fprintf(stderr, "%s: cannot obtain path as UTF-8!\n", fname);
+		const char *local = ".\\Subsurface";
+		const int len = strlen(local) + 1;
+		path = (char *)realloc(path, len);
+		memset(path, 0, len);
+		strcat(path, local);
+		return path;
+}
+
+/* by passing NULL to system_default_path_append() we obtain the pure path.
+ * '\' not included at the end.
+ */
+const char *system_default_directory(void)
+{
+	static const char *path = NULL;
+	if (!path)
+		path = system_default_path_append(NULL);
+	return path;
+}
+
+/* obtain the Roaming path and append "\\<USERNAME>.xml" to it.
+ */
 const char *system_default_filename(void)
 {
-	char datapath[MAX_PATH];
-	const char *user;
-	char *buffer;
-	int len;
-
-	/* I don't think this works on Windows */
-	user = getenv("USERNAME");
-	if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, datapath))) {
-		datapath[0] = '.';
-		datapath[1] = '\0';
+	static wchar_t filename[UNLEN + 5] = { 0 };
+	if (!*filename) {
+		wchar_t username[UNLEN + 1] = { 0 };
+		DWORD username_len = UNLEN + 1;
+		GetUserNameW(username, &username_len);
+		wcscat(filename, username);
+		wcscat(filename, L".xml");
 	}
-	len = strlen(datapath) + strlen(user) + 17;
-	buffer = malloc(len);
-	snprintf(buffer, len, "%s\\Subsurface\\%s.xml", datapath, user);
-	return buffer;
+	static const char *path = NULL;
+	if (!path)
+		path = system_default_path_append(filename);
+	return path;
 }
 
 int enumerate_devices(device_callback_t callback, void *userdata, int dc_type)
-- 
1.7.11.msysgit.0



More information about the subsurface mailing list