src/platform_windows.cpp
author Krista 'DarthMama' Bennett <krista@pep.foundation>
Thu, 04 Jun 2020 11:18:45 +0200
changeset 4729 3df9a2a67597
parent 4563 191cd3581201
child 4901 eff424b70088
permissions -rw-r--r--
forgot test files
     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     TCHAR tPath2[PATH_BUF_SIZE];
   151 
   152     // Get SystemFolder Registry value and use if available
   153     bool result = readRegistryString(HKEY_CURRENT_USER, TEXT("SOFTWARE\\pEp"),
   154             TEXT("SystemFolder"), tPath2, PATH_BUF_SIZE, NULL);
   155 
   156     DWORD length = 0;
   157 
   158     // If no Registry value was found, use default
   159     if (!result) {
   160         length = ExpandEnvironmentStringsW(utf16_string(string(PER_MACHINE_DIRECTORY)).c_str(),
   161             tPath, PATH_BUF_SIZE);
   162     }
   163     else {
   164         length = ExpandEnvironmentStringsW(tPath2, tPath, PATH_BUF_SIZE);
   165     }
   166 
   167     assert(length);
   168     if (length == 0)
   169         throw bad_alloc(); // BUG: there are other errors possible beside out of memory
   170 
   171     path = utf8_string(wstring(tPath, length));
   172     return path.c_str();
   173 }
   174 
   175 const char *_per_user_directory(void)
   176 {
   177     static string path;
   178     if (path.length())
   179         return path.c_str();
   180 
   181     TCHAR tPath[PATH_BUF_SIZE];
   182     TCHAR tPath2[PATH_BUF_SIZE];
   183 
   184     // Get UserFolder Registry value and use if available
   185     bool result = readRegistryString(HKEY_CURRENT_USER, TEXT("SOFTWARE\\pEp"),
   186             TEXT("UserFolder"), tPath2, PATH_BUF_SIZE, NULL);
   187 
   188     DWORD length = 0;
   189 
   190     // If no Registry value was found, use default
   191     if (!result) {
   192         length = ExpandEnvironmentStringsW(utf16_string(string(PER_USER_DIRECTORY)).c_str(),
   193             tPath, PATH_BUF_SIZE);
   194     }
   195     else {
   196         length = ExpandEnvironmentStringsW(tPath2, tPath, PATH_BUF_SIZE);
   197     }
   198 
   199     assert(length);
   200     if (length == 0)
   201         throw bad_alloc(); // BUG: there are other errors possible beside out of memory
   202 
   203     path = utf8_string(wstring(tPath));
   204     return path.c_str();
   205 }
   206 
   207 extern "C" {
   208 
   209 DYNAMIC_API const char *per_user_directory(void)
   210 {
   211     return _per_user_directory();
   212 }
   213 
   214 DYNAMIC_API const char *per_machine_directory(void)
   215 {
   216     return _per_machine_directory();
   217 }
   218 
   219 void *dlopen(const char *filename, int flag) {
   220     static TCHAR path[PATH_BUF_SIZE];
   221 
   222     assert(filename);
   223     assert(flag == RTLD_LAZY); // only lazy binding is implemented
   224 
   225     // Look up GnuPG installation in current user scope
   226     bool result = readRegistryString(HKEY_CURRENT_USER,
   227         TEXT("SOFTWARE\\GnuPG"), TEXT("Install Directory"), path,
   228         PATH_BUF_SIZE, NULL);
   229     // If not found in current user, look up in local machine
   230     if (!result)
   231         result = readRegistryString(HKEY_LOCAL_MACHINE,
   232             TEXT("SOFTWARE\\GnuPG"), TEXT("Install Directory"), path,
   233             PATH_BUF_SIZE, NULL);
   234     assert(result);
   235     if (!result)
   236         return NULL;
   237 
   238     SetDllDirectory(TEXT(""));
   239     BOOL _result = SetDllDirectory(path);
   240     assert(_result != 0);
   241     if (_result == 0)
   242         return NULL;
   243 
   244     HMODULE module = LoadLibrary(utf16_string(filename).c_str());
   245 
   246     if (module == NULL) {
   247         SetDllDirectory(NULL);
   248                     
   249         _tcscat_s(path, TEXT("\\bin"));
   250         
   251         SetDllDirectory(TEXT(""));
   252         _result = SetDllDirectory(path);
   253         assert(_result != 0);
   254         if (_result == 0)
   255             return NULL;
   256 
   257         module = LoadLibrary(utf16_string(filename).c_str());
   258     }
   259     
   260     SetDllDirectory(NULL);
   261     if (module == NULL)
   262         return NULL;
   263     else
   264         return (void *) module;
   265 }
   266 
   267 int dlclose(void *handle) {
   268     if (FreeLibrary((HMODULE) handle))
   269         return 0;
   270     else
   271         return 1;
   272 }
   273 
   274 void *dlsym(void *handle, const char *symbol) {
   275     return (void *) (intptr_t) GetProcAddress((HMODULE) handle, symbol);
   276 }
   277 
   278 const char *windoze_keys_db(void) {
   279     static string path;
   280     if (path.length() == 0) {
   281         path = managementPath(USER_FOLDER_PATH, KEYS_DB);
   282     }
   283     return path.c_str();
   284 }
   285 
   286 const char *windoze_local_db(void) {
   287     static string path;
   288     if (path.length() == 0)
   289         path = managementPath(USER_FOLDER_PATH, LOCAL_DB_FILENAME);
   290     return path.c_str();
   291 }
   292 
   293 const char *windoze_system_db(void) {
   294     static string path;
   295     if (path.length() == 0)
   296         path = managementPath(PER_MACHINE_DIRECTORY, SYSTEM_DB_FILENAME);
   297     return path.c_str();
   298 }
   299 
   300 long random(void)
   301 {
   302     unsigned int r;
   303     errno_t e;
   304 
   305     assert(sizeof(unsigned int) == sizeof(long)); // this is Windoze
   306 
   307     do {
   308         e = rand_s(&r);
   309     } while (e);
   310 
   311     return (long) (r & ((1U<<31)-1));
   312 }
   313 
   314 char *strndup(const char *s1, size_t n)
   315 {
   316     char *str = (char *) calloc(n + 1, 1);
   317     if (str == NULL)
   318         return NULL;
   319 
   320     strncpy(str, s1, n);
   321     return str;
   322 }
   323 
   324 char *stpcpy(char *dst, const char *src)
   325 {
   326     for (;; ++dst, ++src) {
   327         *dst = *src;
   328         if (*dst == 0)
   329             break;
   330     }
   331     return dst;
   332 }
   333 
   334 size_t strlcpy(char* dst, const	char* src, size_t size) {
   335     size_t retval = strlen(src);
   336     size_t size_to_copy = (retval < size ? retval : size - 1);
   337     
   338     // strlcpy doc says src and dst not allowed to overlap, as
   339     // it's undefined. So this is acceptable:
   340     memcpy((void*)dst, (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
   341     dst[size_to_copy] = '\0';
   342     return retval;
   343 }
   344 size_t strlcat(char* dst, const	char* src, size_t size) {
   345     size_t start_len = strnlen(dst, size);
   346     if (start_len == size)
   347         return size; // no copy, no null termination in size bytes, according to spec
   348     
   349     size_t add_len = strlen(src);
   350     size_t retval = start_len + add_len;
   351     size_t size_to_copy = (retval < size ? add_len : (size - start_len) - 1);
   352     
   353     // strlcat doc says src and dst not allowed to overlap, as
   354     // it's undefined. So this is acceptable:
   355     memcpy((void*)(dst + start_len), (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
   356     dst[start_len + size_to_copy] = '\0';
   357     return retval;
   358 }
   359 char *strnstr(const char *big, const char *little, size_t len) {
   360     if (big == NULL || little == NULL)
   361         return NULL;
   362         
   363     if (*little == '\0')
   364         return (char*)big;
   365         
   366     const char* curr_big = big;
   367     
   368     size_t little_len = strlen(little);
   369     size_t remaining = len;
   370 
   371     const char* retval = NULL;
   372     
   373     for (remaining = len; remaining >= little_len && *curr_big != '\0'; remaining--, curr_big++) {
   374         // find first-char match
   375         if (*curr_big != *little) {
   376             continue;
   377         }
   378         retval = curr_big;
   379 
   380         const char* inner_big = retval + 1;
   381         const char* curr_little = little + 1;
   382         size_t j;
   383         for (j = 1; j < little_len; j++, inner_big++, curr_little++) {
   384             if (*inner_big != *curr_little) {
   385                 retval = NULL;
   386                 break;
   387             }    
   388         }
   389         if (retval)
   390             break;
   391     }
   392     return (char*)retval;
   393 }
   394 
   395 int mkstemp(char *templ)
   396 {
   397     char *pathname = _mktemp(templ);
   398     if (!pathname)
   399         return -1;
   400     return _open(pathname, _O_RDWR | _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
   401 }
   402 
   403 DYNAMIC_API time_t timegm(timestamp *timeptr)
   404 {
   405     assert(timeptr);
   406     if (!timeptr)
   407         return -1;
   408 
   409     time_t result = _mkgmtime((struct tm *) timeptr);
   410     if (result == -1)
   411         return -1;
   412 
   413     return (result - timeptr->tm_gmtoff);
   414 }
   415 
   416 void uuid_generate_random(pEpUUID out)
   417 {
   418     RPC_STATUS rpc_status = UuidCreate(out);
   419     assert(rpc_status == RPC_S_OK);
   420 }
   421 
   422 int uuid_parse(char *in, pEpUUID uu)
   423 {
   424     unsigned char *_in = (unsigned char *) in;
   425     RPC_STATUS rpc_status = UuidFromStringA(_in, uu);
   426     assert(rpc_status == RPC_S_OK);
   427     if (rpc_status == RPC_S_INVALID_STRING_UUID)
   428         return -1;
   429     return 0;
   430 }
   431 
   432 void uuid_unparse_upper(pEpUUID uu, uuid_string_t out)
   433 {
   434     unsigned char *_out = (unsigned char*)out;
   435     RPC_CSTR str;
   436     RPC_STATUS rpc_status = UuidToStringA(uu, &str);
   437     assert(rpc_status == RPC_S_OK);
   438     if (rpc_status == RPC_S_OK) {
   439         memcpy(out, str, 36);
   440         out[36] = 0;
   441         RpcStringFreeA(&str);
   442     }
   443     else { // if (rpc_status == RPC_S_OUT_OF_MEMORY)
   444         memset(out, 0, 37);
   445     }
   446 }
   447 
   448 void log_output_debug(const char *title,
   449                        const char *entity,
   450                        const char *description,
   451                        const char *comment)
   452 {
   453     const size_t size = 256;
   454     char str[size];
   455     
   456     snprintf(str, size, "*** %s %s %s %s\n", title, entity, description, comment);
   457     OutputDebugStringA(str);
   458 }
   459 
   460 } // "C"