src/platform_windows.cpp
author Krista Bennett <krista@pep-project.org>
Fri, 04 May 2018 16:30:21 +0200
branchlocal_cpptest
changeset 2652 43b913f99a27
parent 2571 817d69de911f
child 2807 edd4e99844f1
child 3073 02fe4d2cdc6f
permissions -rw-r--r--
Shelving broken things to break other things
vb@1513
     1
// This file is under GNU General Public License 3.0
vb@1513
     2
// see LICENSE.txt
vb@1513
     3
vb@1513
     4
// Windows platform specification
vb@0
     5
vb@0
     6
#define WIN32_LEAN_AND_MEAN
vb@0
     7
#ifndef UNICODE
vb@0
     8
#define UNICODE
vb@0
     9
#endif
vb@0
    10
#define _WIN32_WINNT 0x0600
vb@0
    11
vb@0
    12
#include <windows.h>
vb@54
    13
#define _CRT_RAND_S
vb@54
    14
#include <stdlib.h>
vb@0
    15
#include <assert.h>
vb@80
    16
#include <string.h>
vb@0
    17
#include <string>
vb@0
    18
#include <stdexcept>
vb@0
    19
#include "platform_windows.h"
vb@130
    20
#include <fcntl.h>
krista@2223
    21
#include <tchar.h>
vb@130
    22
#include <sys\stat.h>
vb@0
    23
vb@0
    24
#ifndef WC_ERR_INVALID_CHARS
vb@0
    25
#define WC_ERR_INVALID_CHARS      0x00000080  // error for invalid chars
vb@0
    26
#endif
vb@0
    27
vb@0
    28
using namespace std;
vb@0
    29
vb@0
    30
static string utf8_string(wstring wstr) {
vb@0
    31
    string result;
vb@0
    32
vb@0
    33
    if (wstr.length()) {
vb@0
    34
        int size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
vb@0
    35
                wstr.c_str(), -1, NULL, 0, NULL, NULL);
vb@0
    36
        assert(size);
vb@0
    37
        if (size) {
vb@0
    38
            char *buf = new char[size];
vb@0
    39
            WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wstr.c_str(),
vb@0
    40
                    -1, buf, size, NULL, NULL);
vb@0
    41
            result = buf;
vb@0
    42
            delete[] buf;
vb@0
    43
        } else
vb@0
    44
            throw out_of_range("input wstring is not valid"
vb@0
    45
                    " while converting UTF-16 to UTF-8.");
vb@0
    46
    }
vb@0
    47
vb@0
    48
    return result;
vb@0
    49
}
vb@0
    50
vb@0
    51
static wstring utf16_string(string str) {
vb@0
    52
    wstring result;
vb@0
    53
vb@0
    54
    if (str.length()) {
vb@0
    55
        int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
vb@0
    56
                str.c_str(), -1, NULL, 0);
vb@0
    57
        assert(size);
vb@0
    58
        if (size) {
vb@0
    59
            wchar_t * buf = new wchar_t[size];
vb@0
    60
            MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.c_str(), -1,
vb@0
    61
                    buf, size);
vb@0
    62
            result = buf;
vb@0
    63
            delete[] buf;
vb@0
    64
        } else
vb@0
    65
            throw out_of_range("input string is not valid"
vb@0
    66
                    " while converting UTF-8 to UTF-16.");
vb@0
    67
    }
vb@0
    68
vb@0
    69
    return result;
vb@0
    70
}
vb@0
    71
vb@0
    72
static bool readRegistryString(
vb@0
    73
        HKEY hKey, LPCTSTR lpSubKey, LPCTSTR lpValueName, LPTSTR lpResult,
vb@0
    74
        DWORD dwSize, LPCTSTR lpDefault
vb@0
    75
    )
vb@0
    76
{
vb@0
    77
    assert(lpResult);
vb@0
    78
vb@0
    79
	HKEY theKey;
vb@0
    80
	DWORD type;
vb@0
    81
	DWORD bytesCopied = dwSize;
vb@0
    82
	HRESULT result;
vb@0
    83
vb@0
    84
	result = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_READ, &theKey);
vb@0
    85
	if (result != ERROR_SUCCESS) {
vb@0
    86
		if (lpDefault) {
vb@0
    87
			wcsncpy_s(lpResult, dwSize, lpDefault, _TRUNCATE);
vb@0
    88
			return true;
vb@0
    89
		}
vb@0
    90
		else
vb@0
    91
			return false;
vb@0
    92
	}
vb@0
    93
vb@0
    94
	result = RegQueryValueEx(theKey, lpValueName, NULL, &type,
vb@0
    95
            (LPBYTE) lpResult, &bytesCopied);
vb@0
    96
    if (result != ERROR_SUCCESS || (type != REG_EXPAND_SZ && type != REG_SZ)) {
vb@0
    97
		if (lpDefault) {
vb@0
    98
			wcsncpy_s(lpResult, dwSize, lpDefault, _TRUNCATE);
vb@0
    99
			RegCloseKey(theKey);
vb@0
   100
			return true;
vb@0
   101
		}
vb@0
   102
		else {
vb@0
   103
			RegCloseKey(theKey);
vb@0
   104
			return false;
vb@0
   105
		}
vb@0
   106
	}
vb@0
   107
vb@0
   108
	RegCloseKey(theKey);
vb@0
   109
	return true;
vb@0
   110
}
vb@0
   111
vb@0
   112
static const DWORD PATH_BUF_SIZE = 32768;
vb@0
   113
vb@0
   114
static inline string managementPath(const char *file_path, const char *file_name)
vb@0
   115
{
vb@0
   116
    string path;
vb@0
   117
	static TCHAR tPath[PATH_BUF_SIZE];
vb@0
   118
vb@0
   119
    DWORD length = ExpandEnvironmentStringsW(utf16_string(file_path).c_str(),
vb@0
   120
            tPath, PATH_BUF_SIZE);
vb@0
   121
	assert(length);
vb@0
   122
    if (length == 0)
vb@8
   123
        throw bad_alloc(); // BUG: there are other errors possible beside out of memory
vb@0
   124
vb@0
   125
	CreateDirectory(tPath, NULL);
vb@0
   126
	DWORD error = GetLastError();
vb@0
   127
vb@0
   128
	path = utf8_string(tPath);
vb@0
   129
	path += "\\";
vb@0
   130
	path += file_name;
vb@0
   131
vb@0
   132
	return path;
vb@0
   133
}
vb@0
   134
vb@0
   135
extern "C" {
vb@0
   136
vb@0
   137
void *dlopen(const char *filename, int flag) {
vb@0
   138
	static TCHAR path[PATH_BUF_SIZE];
vb@0
   139
vb@0
   140
    assert(filename);
vb@0
   141
	assert(flag == RTLD_LAZY); // only lazy binding is implemented
vb@0
   142
Thomas@1796
   143
	// Look up GnuPG installation in current user scope
Thomas@1796
   144
	bool result = readRegistryString(HKEY_CURRENT_USER,
Thomas@2195
   145
		TEXT("SOFTWARE\\GnuPG"), TEXT("Install Directory"), path,
Thomas@1796
   146
		PATH_BUF_SIZE, NULL);
Thomas@1796
   147
	// If not found in current user, look up in local machine
vb@2571
   148
	if (!result)
Thomas@1796
   149
		result = readRegistryString(HKEY_LOCAL_MACHINE,
Thomas@2195
   150
			TEXT("SOFTWARE\\GnuPG"), TEXT("Install Directory"), path,
Thomas@1796
   151
			PATH_BUF_SIZE, NULL);
vb@0
   152
	assert(result);
vb@0
   153
	if (!result)
vb@0
   154
		return NULL;
vb@0
   155
vb@0
   156
    SetDllDirectory(TEXT(""));
vb@0
   157
    BOOL _result = SetDllDirectory(path);
vb@0
   158
    assert(_result != 0);
vb@0
   159
    if (_result == 0)
vb@0
   160
        return NULL;
vb@0
   161
vb@0
   162
	HMODULE module = LoadLibrary(utf16_string(filename).c_str());
krista@2222
   163
krista@2222
   164
    if (module == NULL) {
krista@2222
   165
        SetDllDirectory(NULL);
krista@2223
   166
                    
krista@2223
   167
		_tcscat_s(path, TEXT("\\bin"));
krista@2222
   168
        
krista@2222
   169
        SetDllDirectory(TEXT(""));
krista@2222
   170
        _result = SetDllDirectory(path);
krista@2222
   171
        assert(_result != 0);
krista@2222
   172
        if (_result == 0)
krista@2222
   173
            return NULL;
krista@2222
   174
krista@2222
   175
    	module = LoadLibrary(utf16_string(filename).c_str());
krista@2222
   176
    }
krista@2222
   177
    
vb@0
   178
    SetDllDirectory(NULL);
vb@0
   179
	if (module == NULL)
vb@0
   180
		return NULL;
vb@0
   181
	else
vb@0
   182
		return (void *) module;
vb@0
   183
}
vb@0
   184
vb@0
   185
int dlclose(void *handle) {
vb@0
   186
	if (FreeLibrary((HMODULE) handle))
vb@0
   187
		return 0;
vb@0
   188
	else
vb@0
   189
		return 1;
vb@0
   190
}
vb@0
   191
vb@0
   192
void *dlsym(void *handle, const char *symbol) {
vb@0
   193
	return (void *) (intptr_t) GetProcAddress((HMODULE) handle, symbol);
vb@0
   194
}
vb@0
   195
vb@55
   196
const char *windoze_local_db(void) {
vb@0
   197
	static string path;
vb@0
   198
	if (path.length() == 0)
vb@0
   199
        path = managementPath("%LOCALAPPDATA%\\pEp", "management.db");
vb@0
   200
    return path.c_str();
vb@0
   201
}
vb@0
   202
vb@55
   203
const char *windoze_system_db(void) {
vb@0
   204
	static string path;
vb@0
   205
	if (path.length() == 0)
vb@0
   206
		path = managementPath("%ALLUSERSPROFILE%\\pEp", "system.db");
vb@0
   207
    return path.c_str();
vb@0
   208
}
vb@0
   209
vb@55
   210
const char *gpg_conf(void)
vb@0
   211
{
vb@0
   212
    static string path;
vb@0
   213
    if (path.length() == 0)
vb@0
   214
        path = managementPath("%APPDATA%\\gnupg", "gpg.conf");
vb@0
   215
    return path.c_str();
vb@0
   216
}
vb@0
   217
krista@763
   218
const char *gpg_agent_conf(void)
krista@763
   219
{
krista@763
   220
    static string agent_path;
krista@763
   221
    if (agent_path.length() == 0)
krista@763
   222
        agent_path = managementPath("%APPDATA%\\gnupg", "gpg-agent.conf");
krista@763
   223
    return agent_path.c_str();
krista@763
   224
}
krista@763
   225
krista@763
   226
vb@54
   227
long random(void)
vb@54
   228
{
vb@54
   229
    unsigned int r;
vb@54
   230
    errno_t e;
vb@54
   231
vb@54
   232
    assert(sizeof(unsigned int) == sizeof(long)); // this is Windoze
vb@54
   233
vb@54
   234
    do {
vb@54
   235
        e = rand_s(&r);
vb@54
   236
    } while (e);
vb@54
   237
vb@130
   238
    return (long) (r & ((1U<<31)-1));
vb@54
   239
}
vb@54
   240
vb@82
   241
char *strndup(const char *s1, size_t n)
vb@82
   242
{
vb@130
   243
    char *str = (char *) calloc(n + 1, 1);
vb@82
   244
    if (str == NULL)
vb@82
   245
        return NULL;
vb@82
   246
vb@82
   247
    strncpy(str, s1, n);
vb@82
   248
    return str;
vb@82
   249
}
vb@80
   250
vb@282
   251
char *stpcpy(char *dst, const char *src)
vb@282
   252
{
vb@282
   253
    for (;; ++dst, ++src) {
vb@282
   254
        *dst = *src;
vb@282
   255
        if (*dst == 0)
vb@282
   256
            break;
vb@282
   257
    }
vb@282
   258
    return dst;
vb@282
   259
}
vb@282
   260
krista@920
   261
size_t strlcpy(char* dst, const	char* src, size_t size) {
krista@920
   262
    size_t retval = strlen(src);
krista@920
   263
    size_t size_to_copy = (retval < size ? retval : size - 1);
krista@920
   264
    
krista@920
   265
    // strlcpy doc says src and dst not allowed to overlap, as
krista@920
   266
    // it's undefined. So this is acceptable:
krista@920
   267
    memcpy((void*)dst, (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
krista@920
   268
    dst[size_to_copy] = '\0';
krista@920
   269
    return retval;
krista@920
   270
}
krista@920
   271
size_t strlcat(char* dst, const	char* src, size_t size) {
krista@920
   272
    size_t start_len = strnlen(dst, size);
krista@920
   273
    if (start_len == size)
krista@920
   274
        return size; // no copy, no null termination in size bytes, according to spec
krista@920
   275
    
krista@920
   276
    size_t add_len = strlen(src);
krista@920
   277
    size_t retval = start_len + add_len;
krista@920
   278
    size_t size_to_copy = (retval < size ? add_len : (size - start_len) - 1);
krista@920
   279
    
krista@920
   280
    // strlcat doc says src and dst not allowed to overlap, as
krista@920
   281
    // it's undefined. So this is acceptable:
krista@920
   282
    memcpy((void*)(dst + start_len), (void*)src, size_to_copy); // no defined error return, but strcpy doesn't either
krista@920
   283
    dst[start_len + size_to_copy] = '\0';
krista@920
   284
    return retval;
krista@920
   285
}
krista@920
   286
vb@140
   287
int mkstemp(char *templ)
vb@140
   288
{
vb@140
   289
    char *pathname = _mktemp(templ);
vb@140
   290
    if (errno)
vb@140
   291
        return -1;
vb@140
   292
    return _open(pathname, _O_RDWR | _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE);
vb@140
   293
}
vb@130
   294
markus@1226
   295
void uuid_generate_random(pEpUUID out)
vb@948
   296
{
vb@948
   297
    RPC_STATUS rpc_status = UuidCreate(out);
vb@948
   298
    assert(rpc_status == RPC_S_OK);
vb@948
   299
}
vb@948
   300
markus@1226
   301
int uuid_parse(char *in, pEpUUID uu)
vb@948
   302
{
vb@948
   303
    unsigned char *_in = (unsigned char *) in;
markus@1226
   304
    RPC_STATUS rpc_status = UuidFromStringA(_in, uu);
vb@948
   305
    assert(rpc_status == RPC_S_OK);
vb@948
   306
    if (rpc_status == RPC_S_INVALID_STRING_UUID)
vb@948
   307
        return -1;
vb@948
   308
    return 0;
vb@948
   309
}
vb@948
   310
markus@1226
   311
void uuid_unparse_upper(pEpUUID uu, uuid_string_t out)
vb@948
   312
{
markus@1226
   313
    unsigned char *_out = (unsigned char*)out;
vb@948
   314
    RPC_CSTR str;
markus@1226
   315
    RPC_STATUS rpc_status = UuidToStringA(uu, &str);
vb@948
   316
    assert(rpc_status == RPC_S_OK);
vb@948
   317
    if (rpc_status == RPC_S_OK) {
vb@948
   318
        memcpy(out, str, 36);
vb@948
   319
        out[36] = 0;
markus@1226
   320
        RpcStringFreeA(&str);
vb@948
   321
    }
vb@948
   322
    else { // if (rpc_status == RPC_S_OUT_OF_MEMORY)
vb@948
   323
        memset(out, 0, 37);
vb@948
   324
    }
vb@948
   325
}
vb@948
   326
markus@1258
   327
time_t timegm(struct tm* tm) {
markus@1258
   328
    return _mkgmtime(tm);
markus@1258
   329
}
markus@1258
   330
vb@0
   331
} // "C"