summaryrefslogblamecommitdiffstats
path: root/remote/modules/run-virt/winres/winres.c
blob: 1c5650b8aaa1a4a2751550834343d66ccd50f7fb (plain) (tree)
1
2
3
4
5
6
7
8
9


                                  
                           

                
                    

                     





                           




                   
                    
                    
                     




                                                                                                                 


                              




                             





                                 

                                          
 
                            

                                   
                                                                                                                
                                   









                                                                                                    
                                          








                                        
 


                             



                                        


                                            
 
                                                                                                
 

















                                                

                                         


                              
 






                                                   
 









                                                                                           
 

                                                                                        


























                                                                                             

                                                                                           
                











                                                                                        
                                                                                                              

                                               

                                 






                                                                                                                                                                                                         
               





                        
                                                    





                                                                                                                  





                                                                                                           



















                                                                                                                    







                                                                                                            


                                      





                                                                                                      


                                                                                               
 

                                                    
                           
                                    
                    


                                                                                                      
                                
                                                 
                            


                                                                                                         
                                                   



                                                                                                                         

                                       

                                                                                        



                                                                                                            
                                
                                                                                 
                                    
                                         
                                                                                   
                                
                                                                                                    
                 

                     
                                                                  


                                                                                                       

                 





                                                 


                 


                                                    

































                                                                                                                                  

 

                          



                                            
                                    
                                                                                                         

                                            
                                                                                      





                                                  
                                                                                                                                




















                                                                                                                     
                                          
                                                                                                               




                                                                                          


                                 

                         
















                                                                            

                                            





                                                                                                                          





                      


                                                                                                                                                
                                                              





                                                                                                                    
                                                                     






                                                                                                                                             
                                                                   














                                                                                               
                                                              



                                                                           



























                                                                                       
                                                                  





                                          
                                               




                                                     

                                             

                                              
                                             






























                                                                                                                          



















                                                                                  


                 






















































                                                                                                                   
                             






                                                                  
                           






                                                                                                           
                                                                                                                 

                                      
         
                                                                    
                                             
                               





                                                                                                                 






                                                                                                     
                                                                     
 

















                                                                                                      

 





















                                                                                                                          
                                   




                                         

                      

                                           
                                        
                                                   
                                         
                                                       
                                    


                                                                                                 



                                                                                       






                                                                                                 
                                                       

                                    
                                                                                  





                                                                                                
         
                     

























                                                    

                                  




                                                                                                
                     




                                        
                          




                                                                  
                                               


                                                                                                      
                                                               







                                                                          


                              
































                                                                           

                                              
                                                                                            


















                                                                                                                    
                                                                  
                                                                                 
                                                                                       


                                                                                               

                                                                                                         
















                                                                                                                         

                         
                                                                                                         



                                      
                                   
                     












                                                                                                                             





















                                                                                









                                                                                                            
         


                                                                                              







                                           

                                 







                                                                                                       
                                                       
                          




                                                                                                                                                                         

                          


                                                                                                                                                 




                                                                                                                                                                                    

                              
                                                                                                                                                       


                                                                                                                                                               

                              
                                                                                                                                                    


                                                                                                                                                                                          

                            
                                                                                                                                          


                                                                                                                                                                                 

                          



                                      






















                                                                                                              
                                      


         
#define NTDDI_VERSION  NTDDI_VISTA
#define WINVER 0x0602
#define _WIN32_WINNT 0x0602
#define WIN32_LEAN_AND_MEAN
#define _UNICODE
#define UNICODE
#include <windows.h>
#include <winsock2.h>
#include <winnetwk.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <initguid.h>
#include <stdint.h>
#include <stdarg.h>
#include <wchar.h>
#include <time.h>
#include <shlobj.h>
#include <shlguid.h>
#include <strsafe.h>
#include <tlhelp32.h>

DEFINE_GUID(ID_IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A);
DEFINE_GUID(ID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
DEFINE_GUID(ID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);

#define WM_SOCKDATA (WM_APP+1)

typedef struct {
	const char* path;
	const char* letter;
	const char* shortcut;
	const char* user;
	const char* pass;
	BOOL  success;
} netdrive_t;

static const ssize_t KEYLEN = 16;
#define DRIVEMAX (100)
#define LOGFILELEN (300)
#define SETTINGS_FILE "B:\\OPENSLX.INI"
#define SETTINGS_FILE_W L"B:\\OPENSLX.INI"

static OSVERSIONINFO winVer;
static BOOL bGetShares = FALSE;
static netdrive_t drives[DRIVEMAX];
static wchar_t desktopPath[MAX_PATH+1], tempPath[MAX_PATH+1], programsPath[MAX_PATH+1], windowsPath[MAX_PATH+1];
static wchar_t logFile[LOGFILELEN];
static DWORD _startTime;
#define FS_UNKNOWN (-1)
#define FS_ERROR (0)
#define FS_OK (1)
static int _folderStatus = FS_UNKNOWN; // -1 = Not handled yet, 0 = patching failed, 1 = remapped ok
#define RM_NONE (0)
#define RM_NATIVE (1)
#define RM_NATIVE_FALLBACK (2)
#define RM_VMWARE (3)
static int _remapMode = RM_NONE;
static const char* _remapHomeDrive = NULL;

struct {
	BOOL documents;
	BOOL downloads;
	BOOL desktop;
	BOOL media;
	BOOL other;
} remap;
static BOOL _createMissingRemap = FALSE;

static int setResolution();
static int muteSound();
static int setShutdownText();
static void readShareFile();
static BOOL mountNetworkShares();
static int queryPasswordDaemon();
static BOOL fileExists(wchar_t* szPath);
static BOOL folderExists(wchar_t* szPath);
static void patchUserPaths(wchar_t *letter);
static void remapViaSharedFolder();

static HRESULT createFolderShortcut(wchar_t* sTargetfile, wchar_t* sLinkfile, wchar_t* comment);

static void alog(const char *fmt, ...)
{
	FILE *f = _wfopen(logFile, L"a+");
	if (f == NULL) return;
	time_t raw = time(NULL);
	struct tm *tinf;
	char buffer[80];
	tinf = localtime(&raw);
	strftime(buffer, 80, "%I:%M:%S ", tinf);
	fputs(buffer, f);
	va_list args;
	va_start(args, fmt);
	vfprintf(f, fmt, args);
	va_end(args);
	fputc('\n', f);
	fclose(f);
}

static void wlog(const wchar_t *fmt, ...)
{
	wchar_t wbuffer[1000];
	char    abuffer[1000];
	va_list args;

	FILE *f = _wfopen(logFile, L"a+");
	if (f == NULL) return;
	time_t raw = time(NULL);
	struct tm *tinf;
	tinf = localtime(&raw);
	strftime(abuffer, 1000, "%I:%M:%S ", tinf);
	fputs(abuffer, f);

	va_start(args, fmt);
	StringCchVPrintfW(wbuffer, 1000, fmt, args);
	va_end(args);
	if (WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, abuffer, 1000, NULL, NULL) == 0) {
		snprintf(abuffer, 1000, "Cannot wlog: widechar to utf8 failed.");
	}
	fputs(abuffer, f);
	fputc('\n', f);
	fclose(f);
}

static void CALLBACK resetShutdown(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
	static BOOL bInProc = FALSE;
	if (!bInProc) {
		bInProc = TRUE;
		setShutdownText();
		bInProc = FALSE;
	}
}

static void CALLBACK tmrResolution(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
	static BOOL bInProc = FALSE;
	if (!bInProc) {
		bInProc = TRUE;
		if (setResolution() == 0) {
			KillTimer(hWnd, idEvent);
		}
		bInProc = FALSE;
	}
}

static void CALLBACK setupNetworkDrives(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
	static BOOL bInProc = FALSE;
	static int fails = 0;
	if (bInProc)
		return;
	bInProc = TRUE;
	if (!bGetShares && (_remapMode == RM_NATIVE_FALLBACK || _remapMode == RM_VMWARE)) {
		remapViaSharedFolder();
	} else {
		int ret = queryPasswordDaemon();
		if (ret != 0) {
			if (++fails < 10)
				goto exit_func;
			alog("queryPasswordDaemon returned %d", ret);
		} else {
			if (!mountNetworkShares()) {
				if (GetTickCount() - _startTime < 30000 && ++fails < 15)
					goto exit_func;
			}
		}
		// Finished successfully or failed completely
		if (_folderStatus != FS_OK && (_remapMode == RM_NATIVE_FALLBACK || _remapMode == RM_VMWARE)) {
			remapViaSharedFolder();
		}
	}
	KillTimer(hWnd, idEvent);
	if (_remapMode != RM_NONE) {
		if (_folderStatus == FS_ERROR) {
			MessageBoxA(NULL, "Fehler beim Einbinden des Home-Verzeichnisses. Bitte nichts Wichtiges in der VM speichern, sondern z.B. einen USB-Stick verwenden.", "Warnung", MB_ICONERROR);
		} else if (_folderStatus == FS_UNKNOWN) {
			MessageBoxA(NULL, "Kein Home-Verzeichnis gefunden. Bitte nichts Wichtiges in der VM speichern, sondern z.B. einen USB-Stick verwenden.", "Warnung", MB_ICONERROR);
		}
	}
	return;
exit_func:
	bInProc = FALSE;
}

static void loadPaths()
{
	// Determine a couple of default directories
	if (SHGetFolderPathW(HWND_DESKTOP, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, programsPath) != S_OK) {
		DWORD ret = GetEnvironmentVariableW(L"ProgramFiles", programsPath, MAX_PATH+1);
		if (ret == 0 || ret > MAX_PATH) {
			StringCchPrintfW(programsPath, MAX_PATH+1, L"C:\\Program Files");
		}
	}
	if (SHGetFolderPathW(HWND_DESKTOP, CSIDL_WINDOWS, NULL, SHGFP_TYPE_CURRENT, windowsPath) != S_OK) {
		DWORD ret = GetEnvironmentVariableW(L"windir", windowsPath, MAX_PATH+1);
		if (ret == 0 || ret > MAX_PATH) {
			StringCchPrintfW(windowsPath, MAX_PATH+1, L"C:\\WINDOWS");
		}
	}
	if (SHGetFolderPathW(HWND_DESKTOP, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, desktopPath) != S_OK) {
		desktopPath[0] = 0;
	}
	if (GetTempPathW(MAX_PATH+1, tempPath) == 0) {
		tempPath[0] = 0;
	}
	StringCchPrintfW(logFile, LOGFILELEN, L"%s\\%s", desktopPath, L"openslx.log");
	FILE *tfh = _wfopen(logFile, L"a+");
	if (tfh == NULL) {
		StringCchPrintfW(logFile, LOGFILELEN, L"%s\\%s", tempPath, L"openslx.log");
		tfh = _wfopen(logFile, L"a+");
	}
	if (tfh != NULL) {
		fseek(tfh, 0, SEEK_END);
		long pos = ftell(tfh);
		fclose(tfh);
		if (pos < 3) {
			_wremove(logFile);
		}
	}
	// Read settings from ini file
	remap.documents = GetPrivateProfileIntA("remap", "documents", 1, SETTINGS_FILE) != 0;
	remap.downloads = GetPrivateProfileIntA("remap", "downloads", 1, SETTINGS_FILE) != 0;
	remap.desktop = GetPrivateProfileIntA("remap", "desktop", 0, SETTINGS_FILE) != 0;
	remap.media = GetPrivateProfileIntA("remap", "media", 1, SETTINGS_FILE) != 0;
	remap.other = GetPrivateProfileIntA("remap", "other", 0, SETTINGS_FILE) != 0;
	_createMissingRemap = GetPrivateProfileIntA("openslx", "createMissingRemap", 1, SETTINGS_FILE) != 0;
	_remapMode = GetPrivateProfileIntA("openslx", "remapMode", RM_NATIVE_FALLBACK, SETTINGS_FILE);
	if (_remapMode == RM_NONE) {
		_folderStatus = FS_OK;
	}
	char buffer[10];
	GetPrivateProfileStringA("openslx", "homeDrive", "H:", buffer, sizeof(buffer), SETTINGS_FILE);
	buffer[0] = toupper(buffer[0]);
	buffer[1] = ':';
	buffer[2] = '\0';
	_remapHomeDrive = strdup(buffer);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	winVer.dwOSVersionInfoSize = sizeof(winVer);
	BOOL retVer = GetVersionEx(&winVer);
	CoInitialize(NULL);
	_startTime = GetTickCount();
	loadPaths();
	if (lpCmdLine != NULL && strstr(lpCmdLine, "debug") != NULL) {
		alog("Windows Version %d.%d", (int)winVer.dwMajorVersion, (int)winVer.dwMinorVersion);
	}
	// Mute sound by default
	if (retVer && winVer.dwMajorVersion >= 6)
		muteSound();
	// Disable screen saver as it might give the false impression that the session is securely locked
	SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
	// Disable standby and idle-mode (this is a VM!)
	if (winVer.dwMajorVersion >= 6) { // Vista+
		SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_AWAYMODE_REQUIRED);
	} else { // XP/2003
		SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
	}
	// Any network shares to mount?
	readShareFile();
	if (bGetShares || _remapMode != RM_NONE) {
		UINT_PTR tRet = SetTimer(NULL, 0, 1550, (TIMERPROC)&setupNetworkDrives);
		if (tRet == 0) {
			alog("Could not create timer for mounting network shares: %d", (int)GetLastError());
		}
	}
	// Shutdown button label
	if (retVer && winVer.dwMajorVersion == 6 && winVer.dwMinorVersion == 1) {
		// Only on Windows 7
		// Repeatedly set caption
		UINT_PTR tRet = SetTimer(NULL, 0, 5230, (TIMERPROC)&resetShutdown);
		if (tRet == 0) {
			alog("Could not create timer for shutdown button: %d", (int)GetLastError());
		}
	}
	// Resolution
	if (fileExists(SETTINGS_FILE_W) && setResolution() != 0) {
		UINT_PTR tRet = SetTimer(NULL, 0, 3111, (TIMERPROC)&tmrResolution);
		if (tRet == 0) {
			alog("Could not create timer for resolution setting: %d", (int)GetLastError());
		}
	}
	// Message pump
	MSG Msg;
	while(GetMessage(&Msg, NULL, 0, 0) > 0) {
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return 0;
}

static BOOL fileExists(wchar_t* szPath)
{
	DWORD dwAttrib = GetFileAttributesW(szPath);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0);
}

static BOOL folderExists(wchar_t* szPath)
{
	DWORD dwAttrib = GetFileAttributesW(szPath);
	return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}

static int execute(wchar_t *path, wchar_t *arguments)
{
	STARTUPINFOW si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	si.cb = sizeof(si);
	if (!CreateProcessW(path, arguments, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
		return -1;
	}
	while (MsgWaitForMultipleObjects(1, &pi.hProcess, FALSE, INFINITE, QS_SENDMESSAGE) == WAIT_OBJECT_0+1) {
		MSG Msg;
		while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE)) {
			TranslateMessage(&Msg);
			DispatchMessage(&Msg);
		}
	}
	DWORD exitCode;
	BOOL ret = GetExitCodeProcess(pi.hProcess, &exitCode);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	if (!ret) {
		return -2;
	}
	return (int)exitCode;
}

static int setResolution()
{
	int ret;
	static int width = 0, height = 0;
	if (width == 0 && height == 0) {
		// use config file in floppy
		char data[200] = "";
		GetPrivateProfileStringA("openslx", "resolution", "", data, sizeof(data), SETTINGS_FILE);
		char *x = strchr(data, 'x');
		if (x == NULL) {
			alog("Malformed resolution in " SETTINGS_FILE ": '%s'", data);
			return 0;
		}
		*x++ = '\0';
		width = atoi(data);
		height = atoi(x);
		if (width < 320 || height < 240) {
			alog("Invalid resolution in " SETTINGS_FILE ": '%s' (parsed width=%d, height=%d)", data, width, height);
			return 0;
		}
	}
	// Try vmware tools
	static wchar_t path[MAX_PATH] = L"";
	if (path[0] == 0) {
		StringCchPrintfW(path, MAX_PATH, L"%s\\VMware\\VMware Tools\\VMwareResolutionSet.exe", programsPath);
		if (!fileExists(path)) {
			// Strip (x86) if found and try again
			wchar_t *x86 = wcsstr(path, L" (x86)");
			if (x86 != NULL) {
				while ((*x86 = *(x86 + 6)) != 0) ++x86;
			}
		}
		if (!fileExists(path)) {
			char buffer[300];
			WideCharToMultiByte(CP_UTF8, 0, path, -1, buffer, 300, NULL, NULL);
			alog("vmware tools not found, using winapi to set resolution (path: %s)", buffer);
		}
	}
	if (path[0] != 0 && fileExists(path)) {
		wchar_t cmdline[MAX_PATH];
		StringCchPrintfW(cmdline, MAX_PATH, L"VMwareResolutionSet.exe 0 1 , 0 0 %d %d", width, height);
		int ret = execute(path, cmdline);
		if (ret == -1) {
			alog("VmwareRes: CreateProcess failed (%d)", (int)GetLastError());
		} else if (ret == -2) {
			alog("VmwareRes: GetExitCode failed (%d)", (int)GetLastError());
		}
	}
	// Use WinAPI as fallback
	DEVMODE mode;
	int query = 1337;
	memset(&mode, 0, sizeof(mode));
	mode.dmSize = sizeof(mode);
	// MSDN recommends to fill the struct first by querying....
	query = EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode);
	// Then set our own desired mode
	mode.dmPelsWidth = width;
	mode.dmPelsHeight = height;
	mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
	ret = ChangeDisplaySettings(&mode, CDS_GLOBAL | CDS_UPDATEREGISTRY);
	if (ret != DISP_CHANGE_SUCCESSFUL) {
		ret = ChangeDisplaySettings(&mode, CDS_GLOBAL);
	}
	if (ret != DISP_CHANGE_SUCCESSFUL) {
		ret = ChangeDisplaySettings(&mode, CDS_UPDATEREGISTRY);
	}
	if (ret != DISP_CHANGE_SUCCESSFUL) {
		ret = ChangeDisplaySettings(&mode, 0);
	}
	if (ret != DISP_CHANGE_SUCCESSFUL) {
		static int fails = 0;
		if (++fails == 5) {
			alog("Fehler beim Setzen der Auflösung: %d (soll: 0) / %d ( soll: !0) - Zielaufloesung: %d * %d",
					ret, query, width, height);
		}
		return 1;
	}
	return 0;
}

static int muteSound()
{
	IMMDeviceEnumerator *deviceEnumerator = NULL;
	HRESULT hr = CoCreateInstance(&ID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &ID_IMMDeviceEnumerator, (LPVOID *)&deviceEnumerator);
	if (hr != S_OK) {
		alog("CoCreateInstance failed. Cannot mute.");
		return 1;
	}
	//deviceEnumerator->lpVtbl->AddRef(deviceEnumerator);
	IMMDevice *defaultDevice = NULL;
	hr = deviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(deviceEnumerator, eRender, eConsole, &defaultDevice);
	if (hr != S_OK) {
		alog("GetDefaultAudioEndpoint failed. Cannot mute.");
		return 2;
	}
	//defaultDevice->lpVtbl->AddRef(defaultDevice);
	//deviceEnumerator->lpVtbl->Release(deviceEnumerator);
	IAudioEndpointVolume *endpointVolume = NULL;
	hr = defaultDevice->lpVtbl->Activate(defaultDevice, &ID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
	if (hr != S_OK) {
		alog("IMMDevice::Activate() failed. Cannot mute.");
		return 3;
	}
	//endpointVolume->lpVtbl->AddRef(endpointVolume);
	//defaultDevice->lpVtbl->Release(defaultDevice);
	float targetVolume = 1;
	endpointVolume->lpVtbl->SetMasterVolumeLevelScalar(endpointVolume, targetVolume, NULL);
	endpointVolume->lpVtbl->SetMute(endpointVolume, TRUE, NULL);
	//endpointVolume->lpVtbl->Release(endpointVolume);
	//CoUninitialize();
	return 0;
}

static int setShutdownText()
{
	HWND hMenu = FindWindowA("DV2ControlHost", NULL);
	if (hMenu == NULL) return 1; // TODO: Enum all of them
	HWND hPane = FindWindowExA(hMenu, NULL, "DesktopLogoffPane", NULL);
	if (hMenu == NULL) return 2;
	HWND hButton = FindWindowExA(hPane, NULL, "Button", NULL);
	if (hButton == NULL) return 3;
	if (SendMessageA(hButton, WM_SETTEXT, 0, (LPARAM)"Abmelden") != TRUE) return 4;
	return 0;
}

static char *shost = NULL, *sport = NULL, *suser = NULL, *spass = NULL;
static uint8_t *bkey1 = NULL, *bkey2 = NULL;

static char* xorString(const uint8_t* text, int len, const uint8_t* key);
static int getbin(int x);
static uint8_t* hex2bin(char *szHexString);

static char* getToken(char **ptr, BOOL doDup)
{
	if (*ptr == NULL || **ptr == '\0') return NULL;
	char *dest = *ptr;
	while (**ptr != '\0') {
		if (**ptr == '\n' || **ptr == '\r' || **ptr == '\t') {
			*(*ptr)++ = '\0';
			break;
		}
		(*ptr)++;
	}
	if (doDup) {
		dest = strdup(dest);
	}
	return dest;
}

#define FREENULL(x) do { free((void*)(x)); (x) = NULL; } while (0)

static void readShareFile()
{
	if (bGetShares)
		return;
	memset(drives, 0, sizeof(drives));
	FILE *h = fopen("B:\\shares.dat", "r");
	if (h == NULL) return;
	char creds[300] = "", buffer[500] = "";
	char *skey1 = NULL, *skey2 = NULL;
	if (fgets(creds, sizeof(creds), h) != NULL) {
		char *ptr = creds;
		shost = getToken(&ptr, TRUE);
		sport = getToken(&ptr, TRUE);
		skey1 = getToken(&ptr, FALSE);
		skey2 = getToken(&ptr, FALSE);
		suser = getToken(&ptr, TRUE);
	}
	int idx = 0;
	while (fgets(buffer, sizeof(buffer), h) != NULL && idx < DRIVEMAX) {
		char *ptr = buffer;
		netdrive_t *d = &drives[idx];
		d->path = getToken(&ptr, TRUE);
		d->letter = getToken(&ptr, TRUE);
		d->shortcut = getToken(&ptr, TRUE);
		d->user = getToken(&ptr, TRUE);
		d->pass = getToken(&ptr, TRUE);
		if (d->path == NULL || d->path[0] == '\0')
			goto drive_fail;
		d->success = FALSE;
		idx++;
		continue;
drive_fail:
		FREENULL(d->path);
		FREENULL(d->letter);
		FREENULL(d->shortcut);
		FREENULL(d->user);
		FREENULL(d->pass);
	}
	fclose(h);
	if (idx == 0) // No drives to map
		return;
	if (shost == NULL || sport == NULL || skey1 == NULL || skey2 == NULL || suser == NULL) // Credential stuff missing
		return;
	if (strlen(skey1) != KEYLEN*2 || strlen(skey2) != KEYLEN*2) // Messed up keys
		return;
	if (atoi(sport) < 1000 || atoi(sport) > 65535) // Invalid port
		return;
	bkey1 = hex2bin(skey1);
	bkey2 = hex2bin(skey2);
	if (bkey1 == NULL || bkey2 == NULL)
		return;
	bGetShares = TRUE;
}

static void udpReceived(SOCKET sock);

LRESULT CALLBACK slxWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg != WM_SOCKDATA) {
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
	}
	// Socket event
	int event = LOWORD(lParam);
	int errorCode = HIWORD(lParam);
	if (errorCode == 0 && event == FD_READ) {
		udpReceived((SOCKET)wParam);
	}
	return 0;
}

static int queryPasswordDaemon()
{
	// See if preconditions are met
	if (!bGetShares || spass != NULL)
		return 0;
	static int wsaInit = 1337;
	static SOCKET sock = INVALID_SOCKET;
	static HWND sockWnd = NULL;
	// Init socket stuff
	if (wsaInit == 1337) {
		WSADATA wsa;
		wsaInit = WSAStartup(MAKEWORD(2, 2), &wsa);
	}
	if (wsaInit != 0)
		return 2;
	// Create window for socket events
	if (sockWnd == NULL) {
		sockWnd = CreateWindowA("STATIC", "OpenSLX mystery window", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
		if (sockWnd == NULL)
			return GetLastError();
		SetWindowLong(sockWnd, GWL_WNDPROC, (LONG)&slxWindowProc);
	}
	// Create socket
	if (sock == INVALID_SOCKET) {
		sock = socket(AF_INET, SOCK_DGRAM, 0);
		if (sock == INVALID_SOCKET)
			return 3;
		if (WSAAsyncSelect(sock, sockWnd, WM_SOCKDATA, FD_READ) != 0) {
			alog("WSAAsyncSelect returned %d", (int)WSAGetLastError());
		}
	}
	SOCKADDR_IN remote;
	remote.sin_family = AF_INET;
	remote.sin_port = htons((u_short)atoi(sport));
	remote.sin_addr.s_addr = inet_addr(shost);
	// Send out request for password
	if (sendto(sock, (const char*)bkey1, KEYLEN, 0, (struct sockaddr*)&remote, sizeof(remote)) != KEYLEN)
		return 4;
	if (spass == NULL)
		return -1;
	return 0;
}

static void udpReceived(SOCKET sock)
{
	int ret;
	uint8_t buffer[200];
	ret = recv(sock, (char*)buffer, sizeof(buffer), 0);
	// See if reply is valid
	if (ret < 2) return;
	uint16_t len = (uint16_t)(((uint16_t)buffer[0] << 8) | buffer[1]);
	if (ret - 2 != len) return;
	// Success
	spass = xorString(buffer + 2, len, bkey2);
	closesocket(sock);
	mountNetworkShares();
}

#define BUFLEN (200)

static DWORD mount(LPNETRESOURCEW share, LPWSTR pass, LPWSTR user)
{
	DWORD retval;
	// Now try to mount
	if ((pass && *pass) || (user && *user)) {
		retval = WNetAddConnection2W(share, pass, user, CONNECT_TEMPORARY | CONNECT_CURRENT_MEDIA);
		if (retval == NO_ERROR) {
			return retval;
		}
		if (retval != ERROR_INVALID_PASSWORD && retval != ERROR_LOGON_FAILURE
				&& retval != ERROR_BAD_USERNAME && retval != ERROR_ACCESS_DENIED
				&& retval != ERROR_SESSION_CREDENTIAL_CONFLICT && retval != ERROR_BAD_NET_NAME) {
			return retval;
		}
	}
	static wchar_t nuser[BUFLEN] = L"\0", npass[BUFLEN] = L"\0";
	if (nuser[0] == 0 && npass[0] == 0) {
		BOOL ok = TRUE;
		if (suser != NULL) {
			ok = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)suser, -1, (LPWSTR)nuser, BUFLEN) > 0 && ok;
		}
		if (spass != NULL) {
			ok = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)spass, -1, (LPWSTR)npass, BUFLEN) > 0 && ok;
		}
		if (!ok)
			return ERROR_INVALID_PARAMETER;
	}
	retval = WNetAddConnection2W(share, npass, nuser, CONNECT_TEMPORARY | CONNECT_CURRENT_MEDIA);
	return retval;
}

static void postSuccessfulMount(const netdrive_t *d, wchar_t *letter)
{
	if (d->shortcut != NULL && strlen(d->shortcut) != 0) {
		wchar_t tmp[MAX_PATH], wShortcut[MAX_PATH], wTarget[MAX_PATH];
		MultiByteToWideChar(CP_UTF8, 0, d->shortcut, -1, tmp, MAX_PATH);
		StringCchPrintfW(wShortcut, MAX_PATH, L"%s\\%s.lnk", desktopPath, tmp);
		MultiByteToWideChar(CP_UTF8, 0, d->path, -1, tmp, MAX_PATH);
		StringCchPrintfW(wTarget, MAX_PATH, L"\"%s\"", tmp);
		DeleteFileW(wShortcut);
		createFolderShortcut(wTarget, wShortcut, letter);
		// Fix paths and kill explorer if it's the home directory
		if (_folderStatus != FS_OK && strncmp(d->shortcut, "Home-", 5) == 0) {
			BOOL isVmware = strcmp(d->path, "\\\\vmware-host\\Shared Folders\\home") == 0;
			if (_remapMode == RM_NATIVE_FALLBACK
					|| (isVmware && _remapMode == RM_VMWARE)
					|| (!isVmware && _remapMode == RM_NATIVE)) {
				patchUserPaths(letter);
			}
		}
	}
}

static BOOL mountNetworkShare(const netdrive_t *d)
{
	wchar_t path[BUFLEN] = L"", user[BUFLEN] = L"", pass[BUFLEN] = L"", letter[10] = L"", shortcut[BUFLEN] = L"";
	int ok = -1;
	ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->path, -1, (LPWSTR)path, BUFLEN) > 0;
	if (d->letter != NULL) {
		ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->letter, -1, (LPWSTR)letter, 10) > 0;
	}
	if (d->user != NULL) {
		ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->user, -1, (LPWSTR)user, BUFLEN) > 0;
	}
	if (d->pass != NULL) {
		ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->pass, -1, (LPWSTR)pass, BUFLEN) > 0;
	}
	if (d->shortcut != NULL) {
		ok &= MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)d->shortcut, -1, (LPWSTR)shortcut, BUFLEN) > 0;
	}
	if (!ok || path[0] == 0) { // Convert failed/no path - return true anyways since retrying wouldn't change anything
		alog("mountNetworkShare: utf8 to utf16 failed, or path empty (src: '%s')", d->path);
		return TRUE;
	}
	DWORD retval;
	NETRESOURCEW share = { 0 };
	share.dwType = RESOURCETYPE_DISK;
	share.lpLocalName = letter;
	share.lpRemoteName = path;
	share.lpProvider = NULL;
	letter[1] = ':';
	letter[2] = 0;
	letter[3] = 0;
	if (letter[0] != 0) {
		// Try with specific letter
		// Connect defined share
		retval = mount(&share, pass, user);
		if (retval == NO_ERROR) {
			postSuccessfulMount(d, letter);
			return TRUE;
		}
		if (retval != ERROR_ALREADY_ASSIGNED && retval != ERROR_DEVICE_ALREADY_REMEMBERED
				&& retval != ERROR_CONNECTION_UNAVAIL) {
			alog("mountNetworkShare: with letter failed: %d", (int)retval);
			return FALSE;
		}
	}
	// Try to find free drive letter
	for (letter[0] = 'Z'; letter[0] > 'C'; --letter[0]) {
		retval = mount(&share, pass, user);
		if (retval == ERROR_ALREADY_ASSIGNED || retval == ERROR_DEVICE_ALREADY_REMEMBERED
				|| retval == ERROR_CONNECTION_UNAVAIL)
			continue;
		if (retval == NO_ERROR) {
			postSuccessfulMount(d, letter);
			return TRUE;
		}
		alog("mountNetworkShare: without letter failed: %d", (int)retval);
		if (retval == ERROR_INVALID_PASSWORD || retval == ERROR_LOGON_FAILURE
				|| retval == ERROR_BAD_USERNAME || retval == ERROR_ACCESS_DENIED
				|| retval == ERROR_SESSION_CREDENTIAL_CONFLICT) {
			return TRUE;
		}
		return FALSE;
	}
	return FALSE;
}

static BOOL mountNetworkShares()
{
	if (!bGetShares)
		return TRUE;
	if (spass == NULL)
		return FALSE;
	int failCount = 0;
	for (int i = 0; i < DRIVEMAX; ++i) {
		if (drives[i].path == NULL)
			break;
		if (drives[i].success)
			continue;
		if (mountNetworkShare(&drives[i])) {
			drives[i].success = TRUE;
		} else {
			failCount++;
		}
	}
	if (failCount > 0)
		return FALSE;
	SecureZeroMemory(spass, strlen(spass));
	return TRUE;
}

static void remapViaSharedFolder()
{
	static const char* homeDirA = "\\\\vmware-host\\Shared Folders\\home"; // thiscase!
	static const wchar_t* homeDirW = L"\\\\vmware-host\\shared folders\\home"; // lowercase!
	static BOOL once = FALSE;
	if (once) return;
	once = TRUE;
	netdrive_t d;
	d.path = homeDirA;
	d.letter = _remapHomeDrive;
	d.shortcut = "Home-Verzeichnis";
	d.user = "";
	d.pass = "";
	d.success = FALSE;
	// See if it's already mapped
	wchar_t letter[5] = L"C:\\";
	char buffer[600];
	UNIVERSAL_NAME_INFOW *uni = (UNIVERSAL_NAME_INFOW*)buffer;
	for (letter[0] = 'D'; letter[0] <= 'Z'; ++letter[0]) {
		//wlog(L"Checking %s", letter);
		DWORD len = (DWORD)sizeof(buffer);
		if (NO_ERROR == WNetGetUniversalNameW(letter, UNIVERSAL_NAME_INFO_LEVEL, uni, &len)) {
			_wcslwr(uni->lpUniversalName);
			//wlog(L"Is %s", uni->lpUniversalName);
			if (wcscmp(uni->lpUniversalName, homeDirW) == 0) {
				letter[2] = '\0';
				postSuccessfulMount(&d, letter);
				return;
			}
		}
	}
	// Map vmware shared folder
	mountNetworkShare(&d);
}

static char* xorString(const uint8_t* text, int len, const uint8_t* key)
{
	int i;
	uint8_t *retval = malloc(len + 1);
	uint8_t *ptr = retval;
	for (i = 0; i < len; ++i) {
		ptr[i] = text[i] ^ key[i % KEYLEN];
	}
	ptr[len] = '\0';
	return (char*)retval;
}

static int getbin(int x)
{
	if (x >= '0' && x <= '9')
		return x - '0';
	if (x >= 'A' && x <= 'F')
		return x - 'A' + 10;
	return x - 'a' + 10;
}

static uint8_t* hex2bin(char *szHexString)
{
	int size = strlen(szHexString) / 2, i;
	char *p = szHexString;
	uint8_t *pBinary = malloc(size);

	for(i = 0; i < size; i++, p += 2) {
		pBinary[i] = (uint8_t)((getbin(p[0]) << 4) | getbin(p[1]));
	}
	return pBinary;
}

// Stuff for creating a simple shortcut (.lnk)

static HRESULT createFolderShortcut(wchar_t* targetDir, wchar_t* linkFile, wchar_t* comment)
{
	HRESULT       hRes;                  /* Returned COM result code */
	IShellLink*   pShellLink;            /* IShellLink object pointer */
	IPersistFile* pPersistFile;          /* IPersistFile object pointer */

	hRes = E_INVALIDARG;
	if (
		(targetDir != NULL) && (wcslen(targetDir) > 0) &&
		(linkFile != NULL) && (wcslen(linkFile) > 0)
	) {
		hRes = CoCreateInstance(
					&CLSID_ShellLink,     /* pre-defined CLSID of the IShellLink object */
					NULL,                 /* pointer to parent interface if part of aggregate */
					CLSCTX_INPROC_SERVER, /* caller and called code are in same process */
					&IID_IShellLink,      /* pre-defined interface of the IShellLink object */
					(void**)&pShellLink); /* Returns a pointer to the IShellLink object */
		if (SUCCEEDED(hRes)) {
			wchar_t explorer[MAX_PATH];
			StringCchPrintfW(explorer, MAX_PATH, L"\"%s\\explorer.exe\"", windowsPath);
			// Set the fields in the IShellLink object
			hRes = pShellLink->lpVtbl->SetPath(pShellLink, explorer);
			hRes = pShellLink->lpVtbl->SetArguments(pShellLink, targetDir);
			if (comment != NULL) {
				hRes = pShellLink->lpVtbl->SetDescription(pShellLink, comment);
			}
			StringCchPrintfW(explorer, MAX_PATH, L"%s\\system32\\imageres.dll", windowsPath);
			hRes = pShellLink->lpVtbl->SetIconLocation(pShellLink, explorer, 137);

			/* Use the IPersistFile object to save the shell link */
			hRes = pShellLink->lpVtbl->QueryInterface(
					pShellLink,                /* existing IShellLink object */
					&IID_IPersistFile,         /* pre-defined interface of the IPersistFile object */
					(void**)&pPersistFile);    /* returns a pointer to the IPersistFile object */
			if (SUCCEEDED(hRes)) {
				hRes = pPersistFile->lpVtbl->Save(pPersistFile, linkFile, TRUE);
				pPersistFile->lpVtbl->Release(pPersistFile);
			}
			pShellLink->lpVtbl->Release(pShellLink);
		}

	}
	return (hRes);
}

// Patch user directories

static BOOL patchRegPath(BOOL *patchOk, BOOL *anyMapped, HKEY hKey, wchar_t *letter, wchar_t *value, ...)
{
	wchar_t *folder = NULL;
	wchar_t first[MAX_PATH] = {0};
	wchar_t path[MAX_PATH];
	wchar_t oldvalue[MAX_PATH];
	va_list args;
	LONG ret;
	DWORD type;
	DWORD len;
	// Let's check the path in the registry first
	len = (DWORD)(sizeof(oldvalue) - sizeof(wchar_t));
	ret = RegQueryValueExW(hKey, value, NULL, &type, (BYTE*)oldvalue, &len);
	if (ret == ERROR_SUCCESS && (type == REG_EXPAND_SZ || type == REG_SZ)) {
		len /= 2;
		oldvalue[len] = '\0';
		if (towlower(oldvalue[0]) == towlower(letter[0]) && folderExists(oldvalue)) // Same drive, folder exists, yay
			return TRUE;
	}
	// Old registry value doesn't fit - figure out new value
	va_start(args, value);
	while ((folder = va_arg(args, wchar_t*)) != NULL) {
		StringCchPrintfW(path, MAX_PATH, L"%s\\%s", letter, folder);
		if (folderExists(path))
			break;
		if (*first == 0) {
			wcsncpy(first, path, MAX_PATH);
		}
	}
	va_end(args);
	if (folder != NULL) {
		// Found something existing
		folder = path;
	} else if (!_createMissingRemap) {
		// Nothing found, must not create
		wlog(L"Cannot remap %s to %s: target not found!", value, first);
		return FALSE;
	} else {
		// Nothing found, use first element of list and create it
		folder = first;
		CreateDirectoryW(folder, NULL);
	}
	_wcslwr(folder);
	_wcslwr(oldvalue);
	if (wcscmp(folder, oldvalue) == 0) {
		// Path already in registry, don't update
		return TRUE;
	}
	ret = RegSetValueExW(hKey, value, 0, REG_SZ, (BYTE*)folder, (wcslen(folder) + 1) * sizeof(wchar_t));
	if (ret == ERROR_SUCCESS) {
		*anyMapped = TRUE;
		return TRUE;
	}
	wlog(L"Setting reg key %s to %s failed (return value %ld)", value, folder, (long)ret);
	*patchOk = FALSE;
	return FALSE;
}

static void patchUserPaths(wchar_t *letter)
{
	LONG ret;
	HKEY hKey;
	BOOL patchOk = TRUE;
	BOOL killOk = FALSE;
	BOOL anyMapped = FALSE;
	_folderStatus = FS_ERROR;
	ret = RegOpenKeyExW(HKEY_CURRENT_USER,
			L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
			0, KEY_WOW64_64KEY | KEY_READ | KEY_WRITE, &hKey);
	if (ret != ERROR_SUCCESS) {
		alog("Opening registry for patching of pathes failed with return code %ld", (long)ret);
		return;
	}
	// Ha!
	const BOOL win10 = winVer.dwMajorVersion >= 10;
	if (remap.other) {
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{56784854-C6CB-462B-8169-88E350ACB882}", L"Contacts", L"Profile\\Contacts", L"Kontakte", NULL);
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"Favorites", L"Favorites", L"Profile\\Favorites", L"Favoriten", NULL);
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}", L"Searches", L"Profile\\Searches", NULL);
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}", L"Links", L"Profile\\Links", NULL);
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}", L"Saved Games", L"SavedGames", L"Profile\\SavedGames", NULL);
	}
	if (remap.media) {
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"My Video", L"Videos", L"My Videos", L"Eigene Videos", NULL);
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"My Pictures", L"Pictures", L"My Pictures", L"Eigene Bilder", L"Bilder", NULL);
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"My Music", L"Music", L"My Music", L"Eigene Musik", L"Musik", NULL);
		if (win10) {
			patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{35286a68-3c57-41a1-bbb1-0eae73d76c95}", L"Videos", L"My Videos", L"Eigene Videos", NULL);
			patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{0ddd015d-b06c-45d5-8c4c-f59713854639}", L"Pictures", L"My Pictures", L"Eigene Bilder", L"Bilder", NULL);
			patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{a0c69a99-21c8-4671-8703-7934162fcf1d}", L"Music", L"My Music", L"Eigene Musik", L"Musik", NULL);
		}
	}
	if (remap.downloads) {
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{374DE290-123F-4565-9164-39C4925E467B}", L"Downloads", L"Profile\\Downloads", NULL);
		if (win10) {
			patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{7d83ee9b-2244-4e70-b1f5-5393042af1e4}", L"Downloads", L"Profile\\Downloads", NULL);
		}
	}
	if (remap.documents) {
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"Personal", L"Documents", L"Dokumente", L"My Documents", L"Eigene Dateien", NULL);
		if (win10) {
			patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{f42ee2d3-909f-4907-8871-4c22fc0bf756}", L"Documents", L"Dokumente", L"My Documents", L"Eigene Dateien", NULL);
		}
	}
	if (remap.desktop) {
		patchRegPath(&patchOk, &anyMapped, hKey, letter, L"Desktop", L"Windows Desktop", L"Desktop", L"Arbeitsfl\u00E4che", NULL);
		if (win10) {
			patchRegPath(&patchOk, &anyMapped, hKey, letter, L"{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}", L"Windows Desktop", L"Desktop", L"Arbeitsfl\u00E4che", NULL);
		}
	}
	RegCloseKey(hKey);
	if (!anyMapped) {
		_folderStatus = FS_OK;
		return;
	}
	// Kill explorer
	PROCESSENTRY32W entry;
	entry.dwSize = sizeof(PROCESSENTRY32W);
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (snapshot != INVALID_HANDLE_VALUE && Process32FirstW(snapshot, &entry)) {
		do {
			if (_wcsicmp(entry.szExeFile, L"explorer.exe") == 0) {
				HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
				if (hProcess == NULL) {
					alog("Opening explorer.exe failed");
				} else {
					if (TerminateProcess(hProcess, 23)) {
						killOk = TRUE;
					}
					CloseHandle(hProcess);
				}
			}
		} while (Process32NextW(snapshot, &entry));
	} else {
		alog("Could not get process list");
	}
	CloseHandle(snapshot);
	if (patchOk && killOk) {
		_folderStatus = FS_OK;
	}
}