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