src/platform_windows.cpp
author Thomas
Fri, 30 Aug 2019 16:49:55 +0200
branchsequoia_windows
changeset 4035 e87da0dd6136
parent 3903 abaab1748cc1
child 4036 9ecb9fbb5f6b
permissions -rw-r--r--
Rename variables
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 // Windows platform specification
     5 
     6 #define WIN32_LEAN_AND_MEAN
     7 #ifndef UNICODE
     8 #define UNICODE
     9 #endif
    10 #define _WIN32_WINNT 0x0600
    11 
    12 #include <windows.h>
    13 #define _CRT_RAND_S
    14 #include <stdlib.h>
    15 #include <assert.h>
    16 #include <string.h>
    17 #include <string>
    18 #include <stdexcept>
    19 #include "platform_windows.h"
    20 #include <fcntl.h>
    21 #include <tchar.h>
    22 #include <sys\stat.h>
    23 
    24 #ifndef WC_ERR_INVALID_CHARS
    25 #define WC_ERR_INVALID_CHARS      0x00000080  // error for invalid chars
    26 #endif
    27 
    28 #define PER_MACHINE_DIRECTORY "%ALLUSERSPROFILE%\\pEp"
    29 #define PER_USER_DIRECTORY "%LOCALAPPDATA%\\pEp"
    30 #define KEYS_DB "keys.db"
    31 #define MANAGEMENT_DB "management.db"
    32 #define SYSTEM_DB "system.db"
    33 #ifndef USER_FOLDER_PATH
    34 #define USER_FOLDER_PATH pEpUserFolderPath().c_str()
    35 #endif
    36 #ifndef SYSTEM_FOLDER_PATH
    37 #define SYSTEM_FOLDER_PATH pEpSystemFolderPath().c_str()
    38 #endif
    39 
    40 using namespace std;
    41 
    42 static string utf8_string(wstring wstr) {
    43     string result;
    44 
    45     if (wstr.length()) {
    46         int size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
    47                 wstr.c_str(), -1, NULL, 0, NULL, NULL);
    48         assert(size);
    49         if (size) {
    50             char *buf = new char[size];
    51             WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wstr.c_str(),
    52                     -1, buf, size, NULL, NULL);
    53             result = buf;
    54             delete[] buf;
    55         } else
    56             throw out_of_range("input wstring is not valid"
    57                     " while converting UTF-16 to UTF-8.");
    58     }
    59 
    60     return result;
    61 }
    62 
    63 static wstring utf16_string(string str) {
    64     wstring result;
    65 
    66     if (str.length()) {
    67         int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
    68                 str.c_str(), -1, NULL, 0);
    69         assert(size);
    70         if (size) {
    71             wchar_t * buf = new wchar_t[size];
    72             MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), -1,
    73                     buf, size);
    74             result = buf;
    75             delete[] buf;
    76         } else
    77             throw out_of_range("input string is not valid"
    78                     " while converting UTF-8 to UTF-16.");
    79     }
    80 
    81     return result;
    82 }
    83 
    84 static bool readRegistryString(
    85         HKEY hKey, LPCTSTR lpSubKey, LPCTSTR lpValueName, LPTSTR lpResult,
    86         DWORD dwSize, LPCTSTR lpDefault
    87     )
    88 {
    89     assert(lpResult);
    90 
    91 	HKEY theKey;
    92 	DWORD type;
    93 	DWORD bytesCopied = dwSize;
    94 	HRESULT result;
    95 
    96 	result = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_READ, &theKey);
    97 	if (result != ERROR_SUCCESS) {
    98 		if (lpDefault) {
    99 			wcsncpy_s(lpResult, dwSize, lpDefault, _TRUNCATE);
   100 			return true;
   101 		}
   102 		else
   103 			return false;
   104 	}
   105 
   106 	result = RegQueryValueEx(theKey, lpValueName, NULL, &type,
   107             (LPBYTE) lpResult, &bytesCopied);
   108     if (result != ERROR_SUCCESS || (type != REG_EXPAND_SZ && type != REG_SZ)) {
   109 		if (lpDefault) {
   110 			wcsncpy_s(lpResult, dwSize, lpDefault, _TRUNCATE);
   111 			RegCloseKey(theKey);
   112 			return true;
   113 		}
   114 		else {
   115 			RegCloseKey(theKey);
   116 			return false;
   117 		}
   118 	}
   119 
   120 	RegCloseKey(theKey);
   121 	return true;
   122 }
   123 
   124 static const DWORD PATH_BUF_SIZE = 32768;
   125 
   126 static inline string managementPath(const char *file_path, const char *file_name)
   127 {
   128     string path;
   129 	static TCHAR tPath[PATH_BUF_SIZE];
   130 
   131     DWORD length = ExpandEnvironmentStringsW(utf16_string(file_path).c_str(),
   132             tPath, PATH_BUF_SIZE);
   133 	assert(length);
   134     if (length == 0)
   135         throw bad_alloc(); // BUG: there are other errors possible beside out of memory
   136 
   137 	CreateDirectory(tPath, NULL);
   138 	DWORD error = GetLastError();
   139 
   140 	path = utf8_string(tPath);
   141 	path += "\\";
   142 	path += file_name;
   143 
   144 	return path;
   145 }
   146 
   147 static inline string pEpSystemFolderPath(void)
   148 {
   149 	static TCHAR tPath[PATH_BUF_SIZE];
   150 	string path = PER_MACHINE_DIRECTORY;
   151 
   152 	// Get SystemFolder Registry value and use if available
   153 	bool result = readRegistryString(HKEY_CURRENT_USER,
   154 		TEXT("SOFTWARE\\pEp"), TEXT("SystemFolder"), tPath,
   155 		PATH_BUF_SIZE, NULL);
   156 
   157 	if (result)
   158 		path = utf8_string(tPath);
   159 
   160 	return path;
   161 }
   162 
   163 static inline string pEpUserFolderPath(void)
   164 {
   165 	static TCHAR tPath[PATH_BUF_SIZE];
   166 	string path = PER_USER_DIRECTORY;
   167 
   168 	// Get UserFolder Registry value and use if available
   169 	bool result = readRegistryString(HKEY_CURRENT_USER,
   170 		TEXT("SOFTWARE\\pEp"), TEXT("UserFolder"), tPath,
   171 		PATH_BUF_SIZE, NULL);
   172 
   173 	if (result)
   174 		path = utf8_string(tPath);
   175 	
   176 	return path;
   177 }
   178 
   179 extern "C" {
   180 
   181 void *dlopen(const char *filename, int flag) {
   182 	static TCHAR path[PATH_BUF_SIZE];
   183 
   184     assert(filename);
   185 	assert(flag == RTLD_LAZY); // only lazy binding is implemented
   186 
   187 	// Look up GnuPG installation in current user scope
   188 	bool result = readRegistryString(HKEY_CURRENT_USER,
   189 		TEXT("SOFTWARE\\GnuPG"), TEXT("Install Directory"), path,
   190 		PATH_BUF_SIZE, NULL);
   191 	// If not found in current user, look up in local machine
   192 	if (!result)
   193 		result = readRegistryString(HKEY_LOCAL_MACHINE,
   194 			TEXT("SOFTWARE\\GnuPG"), TEXT("Install Directory"), path,
   195 			PATH_BUF_SIZE, NULL);
   196 	assert(result);
   197 	if (!result)
   198 		return NULL;
   199 
   200     SetDllDirectory(TEXT(""));
   201     BOOL _result = SetDllDirectory(path);
   202     assert(_result != 0);
   203     if (_result == 0)
   204         return NULL;
   205 
   206 	HMODULE module = LoadLibrary(utf16_string(filename).c_str());
   207 
   208     if (module == NULL) {
   209         SetDllDirectory(NULL);
   210                     
   211 		_tcscat_s(path, TEXT("\\bin"));
   212         
   213         SetDllDirectory(TEXT(""));
   214         _result = SetDllDirectory(path);
   215         assert(_result != 0);
   216         if (_result == 0)
   217             return NULL;
   218 
   219     	module = LoadLibrary(utf16_string(filename).c_str());
   220     }
   221     
   222     SetDllDirectory(NULL);
   223 	if (module == NULL)
   224 		return NULL;
   225 	else
   226 		return (void *) module;
   227 }
   228 
   229 int dlclose(void *handle) {
   230 	if (FreeLibrary((HMODULE) handle))
   231 		return 0;
   232 	else
   233 		return 1;
   234 }
   235 
   236 void *dlsym(void *handle, const char *symbol) {
   237 	return (void *) (intptr_t) GetProcAddress((HMODULE) handle, symbol);
   238 }
   239 
   240 const char *windoze_keys_db(void) {
   241 	static string path;
   242 	if (path.length() == 0) {
   243 		path = managementPath(USER_FOLDER_PATH, KEYS_DB);
   244 	}
   245 	return path.c_str();
   246 }
   247 
   248 const char *windoze_local_db(void) {
   249 	static string path;
   250 	if (path.length() == 0)
   251 		path = managementPath(USER_FOLDER_PATH, MANAGEMENT_DB);
   252     return path.c_str();
   253 }
   254 
   255 const char *windoze_system_db(void) {
   256 	static string path;
   257 	if (path.length() == 0)
   258 		path = managementPath(SYSTEM_FOLDER_PATH, SYSTEM_DB);
   259     return path.c_str();
   260 }
   261 
   262 const char *gpg_conf(void)
   263 {
   264     static string path;
   265     if (path.length() == 0)
   266         path = managementPath("%APPDATA%\\gnupg", "gpg.conf");
   267     return path.c_str();
   268 }
   269 
   270 const char *gpg_agent_conf(void)
   271 {
   272     static string agent_path;
   273     if (agent_path.length() == 0)
   274         agent_path = managementPath("%APPDATA%\\gnupg", "gpg-agent.conf");
   275     return agent_path.c_str();
   276 }
   277 
   278 
   279 long random(void)
   280 {
   281     unsigned int r;
   282     errno_t e;
   283 
   284     assert(sizeof(unsigned int) == sizeof(long)); // this is Windoze
   285 
   286     do {
   287         e = rand_s(&r);
   288     } while (e);
   289 
   290     return (long) (r & ((1U<<31)-1));
   291 }
   292 
   293 char *strndup(const char *s1, size_t n)
   294 {
   295     char *str = (char *) calloc(n + 1, 1);
   296     if (str == NULL)
   297         return NULL;
   298 
   299     strncpy(str, s1, n);
   300     return str;
   301 }
   302 
   303 char *stpcpy(char *dst, const char *src)
   304 {
   305     for (;; ++dst, ++src) {
   306         *dst = *src;
   307         if (*dst == 0)
   308             break;
   309     }
   310     return dst;
   311 }
   312 
   313 size_t strlcpy(char* dst, const	char* src, size_t size) {
   314     size_t retval = strlen(src);
   315     size_t size_to_copy = (retval < size ? retval : size - 1);
   316     
   317     // strlcpy doc says src and dst not allowed to overlap, as
   318     // it's undefined. So this is acceptable:
   319     memcpy((void*)dst, (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
   320     dst[size_to_copy] = '\0';
   321     return retval;
   322 }
   323 size_t strlcat(char* dst, const	char* src, size_t size) {
   324     size_t start_len = strnlen(dst, size);
   325     if (start_len == size)
   326         return size; // no copy, no null termination in size bytes, according to spec
   327     
   328     size_t add_len = strlen(src);
   329     size_t retval = start_len + add_len;
   330     size_t size_to_copy = (retval < size ? add_len : (size - start_len) - 1);
   331     
   332     // strlcat doc says src and dst not allowed to overlap, as
   333     // it's undefined. So this is acceptable:
   334     memcpy((void*)(dst + start_len), (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
   335     dst[start_len + size_to_copy] = '\0';
   336     return retval;
   337 }
   338 char *strnstr(const char *big, const char *little, size_t len) {
   339     if (big == NULL || little == NULL)
   340         return NULL;
   341         
   342     if (*little == '\0')
   343         return (char*)big;
   344         
   345     const char* curr_big = big;
   346     
   347     size_t little_len = strlen(little);
   348     size_t remaining = len;
   349 
   350     const char* retval = NULL;
   351     
   352     for (remaining = len; remaining >= little_len && *curr_big != '\0'; remaining--, curr_big++) {
   353         // find first-char match
   354         if (*curr_big != *little) {
   355             continue;
   356         }
   357         retval = curr_big;
   358 
   359         const char* inner_big = retval + 1;
   360         const char* curr_little = little + 1;
   361         int j;
   362         for (j = 1; j < little_len; j++, inner_big++, curr_little++) {
   363             if (*inner_big != *curr_little) {
   364                 retval = NULL;
   365                 break;
   366             }    
   367         }
   368         if (retval)
   369             break;
   370     }
   371     return (char*)retval;
   372 }
   373 
   374 int mkstemp(char *templ)
   375 {
   376     char *pathname = _mktemp(templ);
   377     if (!pathname)
   378         return -1;
   379     return _open(pathname, _O_RDWR | _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
   380 }
   381 
   382 void uuid_generate_random(pEpUUID out)
   383 {
   384     RPC_STATUS rpc_status = UuidCreate(out);
   385     assert(rpc_status == RPC_S_OK);
   386 }
   387 
   388 int uuid_parse(char *in, pEpUUID uu)
   389 {
   390     unsigned char *_in = (unsigned char *) in;
   391     RPC_STATUS rpc_status = UuidFromStringA(_in, uu);
   392     assert(rpc_status == RPC_S_OK);
   393     if (rpc_status == RPC_S_INVALID_STRING_UUID)
   394         return -1;
   395     return 0;
   396 }
   397 
   398 void uuid_unparse_upper(pEpUUID uu, uuid_string_t out)
   399 {
   400     unsigned char *_out = (unsigned char*)out;
   401     RPC_CSTR str;
   402     RPC_STATUS rpc_status = UuidToStringA(uu, &str);
   403     assert(rpc_status == RPC_S_OK);
   404     if (rpc_status == RPC_S_OK) {
   405         memcpy(out, str, 36);
   406         out[36] = 0;
   407         RpcStringFreeA(&str);
   408     }
   409     else { // if (rpc_status == RPC_S_OUT_OF_MEMORY)
   410         memset(out, 0, 37);
   411     }
   412 }
   413 
   414 void log_output_debug(const char *title,
   415                        const char *entity,
   416                        const char *description,
   417                        const char *comment)
   418 {
   419 	const size_t size = 256;
   420 	char str[size];
   421 	
   422 	snprintf(str, size, "*** %s %s %s %s\n", title, entity, description, comment);
   423 	OutputDebugStringA(str);
   424 }
   425 
   426 time_t timegm(struct tm* tm) {
   427     return _mkgmtime(tm);
   428 }
   429 
   430 } // "C"