pidgin/win32/winpidgin.c

Tue, 06 Apr 2021 03:35:20 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Tue, 06 Apr 2021 03:35:20 -0500
changeset 40840
b5f355f90ab0
parent 39302
64aabebb476b
child 41086
631e6cba8635
permissions
-rw-r--r--

Port the updates from 2.14.2 and the upcoming 2.14.3 to 3.0.0

* Use the unicode version of the System and UserInfo plugins to fix
non-english languages in the Windows installer.
* Update the installer to use the inetc plugin so that https downloads will
work. This became necessary because Sourceforge now redirects http to
https.
* Standardize everything in pidgin/win32/winpidgin.c to use `wprintf`.

Testing Done:
Compiled and ran on windows 10.

Reviewed at https://reviews.imfreedom.org/r/595/

/*
 *  winpidgin.c
 *
 *  Date: June, 2002
 *  Description: Entry point for win32 pidgin, and various win32 dependant
 *  routines.
 *
 * Pidgin is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 *
 */

/* This file must remain without any immediate dependencies aka don't link
 * directly to Pidgin, libpidgin, GLib, etc. WinPidgin adds a DLL directory
 * at runtime if needed and dynamically loads libpidgin via LoadLibrary().
 */

#include "config.h"

#include <windows.h>
#include <shellapi.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

typedef int (__cdecl* LPFNPIDGINMAIN)(HINSTANCE, int, char**);
typedef BOOL (WINAPI* LPFNSETDLLDIRECTORY)(LPCWSTR);
typedef BOOL (WINAPI* LPFNATTACHCONSOLE)(DWORD);
typedef BOOL (WINAPI* LPFNSETPROCESSDEPPOLICY)(DWORD);

/*
 *  PROTOTYPES
 */
static LPFNPIDGINMAIN pidgin_main = NULL;

static const wchar_t *get_win32_error_message(DWORD err) {
	static wchar_t err_msg[512];

	FormatMessageW(
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, err,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPWSTR) &err_msg, sizeof(err_msg) / sizeof(wchar_t), NULL);

	return err_msg;
}

#define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
#define PIDGIN_WM_PROTOCOL_HANDLE (WM_APP + 14)

static BOOL winpidgin_set_running(BOOL fail_if_running) {
	HANDLE h;

	if ((h = CreateMutexW(NULL, FALSE, L"pidgin_is_running"))) {
		DWORD err = GetLastError();
		if (err == ERROR_ALREADY_EXISTS) {
			if (fail_if_running) {
				HWND msg_win;

				wprintf(L"An instance of Pidgin is already running.\n");

				if((msg_win = FindWindowExW(NULL, NULL, L"WinpidginMsgWinCls", NULL)))
					if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
						return FALSE;

				/* If we get here, the focus request wasn't successful */

				MessageBoxW(NULL,
					L"An instance of Pidgin is already running",
					NULL, MB_OK | MB_TOPMOST);

				return FALSE;
			}
		} else if (err != ERROR_SUCCESS)
			wprintf(L"Error (%u) accessing \"pidgin_is_running\" mutex.\n", (UINT) err);
	}
	return TRUE;
}

#define PROTO_HANDLER_SWITCH L"--protocolhandler="

static void handle_protocol(wchar_t *cmd) {
	char *remote_msg, *utf8msg;
	wchar_t *tmp1, *tmp2;
	int len, wlen;
	SIZE_T len_written;
	HWND msg_win;
	DWORD pid;
	HANDLE process;

	/* The start of the message */
	tmp1 = cmd + wcslen(PROTO_HANDLER_SWITCH);

	/* The end of the message */
	if ((tmp2 = wcschr(tmp1, L' ')))
		wlen = (tmp2 - tmp1);
	else
		wlen = wcslen(tmp1);

	if (wlen == 0) {
		wprintf(L"No protocol message specified.\n");
		return;
	}

	if (!(msg_win = FindWindowExW(NULL, NULL, L"WinpidginMsgWinCls", NULL))) {
		wprintf(L"Unable to find an instance of Pidgin to handle protocol message.\n");
		return;
	}

	len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
			wlen, NULL, 0, NULL, NULL);
	if (len) {
		utf8msg = malloc(len);
		len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
			wlen, utf8msg, len, NULL, NULL);
	}

	if (len == 0) {
		wprintf(L"No protocol message specified.\n");
		return;
	}

	GetWindowThreadProcessId(msg_win, &pid);
	if (!(process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, pid))) {
		DWORD dw = GetLastError();
		const wchar_t *err_msg = get_win32_error_message(dw);
		wprintf(L"Unable to open Pidgin process. (%u) %ls\n",
				(UINT)dw, err_msg);
		return;
	}

	wprintf(L"Trying to handle protocol message:\n'%.*ls'\n", wlen, tmp1);

	/* MEM_COMMIT initializes the memory to zero
	 * so we don't need to worry that our section of utf8msg isn't nul-terminated */
	if ((remote_msg = (char*) VirtualAllocEx(process, NULL, len + 1, MEM_COMMIT, PAGE_READWRITE))) {
		if (WriteProcessMemory(process, remote_msg, utf8msg, len, &len_written)) {
			if (!SendMessageA(msg_win, PIDGIN_WM_PROTOCOL_HANDLE, len_written, (LPARAM) remote_msg))
				printf("Unable to send protocol message to Pidgin instance.\n");
		} else {
			DWORD dw = GetLastError();
			const wchar_t *err_msg = get_win32_error_message(dw);
			wprintf(L"Unable to write to remote memory. (%u) %ls\n",
				       (UINT)dw, err_msg);
		}

		VirtualFreeEx(process, remote_msg, 0, MEM_RELEASE);
	} else {
		DWORD dw = GetLastError();
		const wchar_t *err_msg = get_win32_error_message(dw);
		wprintf(L"Unable to allocate remote memory. (%u) %ls\n",
				(UINT)dw, err_msg);
	}

	CloseHandle(process);
	free(utf8msg);
}


int _stdcall
WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
		char *lpszCmdLine, int nCmdShow) {
	wchar_t errbuf[512];
	wchar_t pidgin_dir[MAX_PATH];
	wchar_t *pidgin_dir_start = NULL;
	wchar_t exe_name[MAX_PATH];
	HMODULE hmod;
	wchar_t *wtmp;
	int pidgin_argc;
	char **pidgin_argv; /* This is in utf-8 */
	int i, j, k;
	BOOL debug = FALSE, help = FALSE, version = FALSE, multiple = FALSE, success;
	LPWSTR *szArglist;
	LPWSTR cmdLine;

	/* If debug or help or version flag used, create console for output */
	for (i = 1; i < __argc; i++) {
		if (strlen(__argv[i]) > 1 && __argv[i][0] == '-') {
			/* check if we're looking at -- or - option */
			if (__argv[i][1] == '-') {
				if (strstr(__argv[i], "--debug") == __argv[i])
					debug = TRUE;
				else if (strstr(__argv[i], "--help") == __argv[i])
					help = TRUE;
				else if (strstr(__argv[i], "--version") == __argv[i])
					version = TRUE;
				else if (strstr(__argv[i], "--multiple") == __argv[i])
					multiple = TRUE;
			} else {
				if (strchr(__argv[i], 'd'))
					debug = TRUE;
				if (strchr(__argv[i], 'h'))
					help = TRUE;
				if (strchr(__argv[i], 'v'))
					version = TRUE;
				if (strchr(__argv[i], 'm'))
					multiple = TRUE;
			}
		}
	}

	/* Permanently enable DEP if the OS supports it */
	if ((hmod = GetModuleHandleW(L"kernel32.dll"))) {
		LPFNSETPROCESSDEPPOLICY MySetProcessDEPPolicy =
			(LPFNSETPROCESSDEPPOLICY)
			GetProcAddress(hmod, "SetProcessDEPPolicy");
		if (MySetProcessDEPPolicy)
			MySetProcessDEPPolicy(1); //PROCESS_DEP_ENABLE
	}

	if (debug || help || version) {
		/* If stdout hasn't been redirected to a file, alloc a console
		 *  (_istty() doesn't work for stuff using the GUI subsystem) */
		if (_fileno(stdout) == -1 || _fileno(stdout) == -2) {
			LPFNATTACHCONSOLE MyAttachConsole = NULL;
			if (hmod)
				MyAttachConsole =
					(LPFNATTACHCONSOLE)
					GetProcAddress(hmod, "AttachConsole");
			if ((MyAttachConsole && MyAttachConsole(ATTACH_PARENT_PROCESS))
					|| AllocConsole()) {
				freopen("CONOUT$", "w", stdout);
				freopen("CONOUT$", "w", stderr);
			}
		}
	}

	cmdLine = GetCommandLineW();

	/* If this is a protocol handler invocation, deal with it accordingly */
	if ((wtmp = wcsstr(cmdLine, PROTO_HANDLER_SWITCH)) != NULL) {
		handle_protocol(wtmp);
		return 0;
	}

	/* Load exception handler if we have it */
	if (GetModuleFileNameW(NULL, pidgin_dir, MAX_PATH) != 0) {

		/* primitive dirname() */
		pidgin_dir_start = wcsrchr(pidgin_dir, L'\\');

		if (pidgin_dir_start) {
			HMODULE hmod;
			pidgin_dir_start[0] = L'\0';

			/* tmp++ will now point to the executable file name */
			wcscpy(exe_name, pidgin_dir_start + 1);

			wcscat(pidgin_dir, L"\\exchndl.dll");
			if ((hmod = LoadLibraryW(pidgin_dir))) {
				typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR);
				LPFNSETLOGFILE MySetLogFile;
				/* exchndl.dll is built without UNICODE */
				char debug_dir[MAX_PATH];
				wprintf(L"Loaded exchndl.dll\n");
				/* Temporarily override exchndl.dll's logfile
				 * to something sane (Pidgin will override it
				 * again when it initializes) */
				MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetLogFile");
				if (MySetLogFile) {
					if (GetTempPathA(sizeof(debug_dir), debug_dir) != 0) {
						strcat(debug_dir, "pidgin.RPT");
						wprintf(L" Setting exchndl.dll LogFile to %s\n",
							debug_dir);
						MySetLogFile(debug_dir);
					}
				}
				/* The function signature for SetDebugInfoDir is the same as SetLogFile,
				 * so we can reuse the variable */
				MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetDebugInfoDir");
				if (MySetLogFile) {
					char *pidgin_dir_ansi = NULL;
					/* Restore pidgin_dir to point to where the executable is */
					pidgin_dir_start[0] = L'\0';
					i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
						-1, NULL, 0, NULL, NULL);
					if (i != 0) {
						pidgin_dir_ansi = malloc(i);
						i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
							-1, pidgin_dir_ansi, i, NULL, NULL);
						if (i == 0) {
							free(pidgin_dir_ansi);
							pidgin_dir_ansi = NULL;
						}
					}
					if (pidgin_dir_ansi != NULL) {
						_snprintf(debug_dir, sizeof(debug_dir),
							"%s\\pidgin-%s-dbgsym",
							pidgin_dir_ansi,  VERSION);
						debug_dir[sizeof(debug_dir) - 1] = '\0';
						wprintf(L" Setting exchndl.dll DebugInfoDir to %s\n",
							debug_dir);
						MySetLogFile(debug_dir);
						free(pidgin_dir_ansi);
					}
				}

			}

			/* Restore pidgin_dir to point to where the executable is */
			pidgin_dir_start[0] = L'\0';
		}

		/* Find parent directory to see if it's bin/ */
		pidgin_dir_start = wcsrchr(pidgin_dir, L'\\');

		/* Add bin/ subdirectory to DLL path if not already in bin/ */
		if (pidgin_dir_start == NULL ||
				wcscmp(pidgin_dir_start + 1, L"bin") != 0) {
			LPFNSETDLLDIRECTORY MySetDllDirectory = NULL;

			hmod = GetModuleHandleW(L"kernel32.dll");

			if (hmod != NULL) {
				MySetDllDirectory = (LPFNSETDLLDIRECTORY) GetProcAddress(hmod, "SetDllDirectoryW");
				if (MySetDllDirectory == NULL) {
					DWORD dw = GetLastError();
					const wchar_t *err_msg = get_win32_error_message(dw);
					wprintf(L"Error loading SetDllDirectory(): (%u) %ls\n", dw, err_msg);
				}
			} else {
				printf("Error getting kernel32.dll handle\n");
			}

			if (MySetDllDirectory) {
				wcscat(pidgin_dir, L"\\bin");
				if (MySetDllDirectory(pidgin_dir)) {
					wprintf(L"Added DLL directory to search path: %ls\n",
							pidgin_dir);
				} else {
					DWORD dw = GetLastError();
					const wchar_t *err_msg = get_win32_error_message(dw);
					wprintf(L"Error calling SetDllDirectory(): (%u) %ls\n", dw, err_msg);
				}
			}
		}
	} else {
		DWORD dw = GetLastError();
		const wchar_t *err_msg = get_win32_error_message(dw);
		_snwprintf(errbuf, sizeof(errbuf) / sizeof(wchar_t),
			L"Error getting module filename.\nError: (%u) %ls",
			(UINT) dw, err_msg);
		wprintf(L"%ls\n", errbuf);
		MessageBoxW(NULL, errbuf, NULL, MB_OK | MB_TOPMOST);
		pidgin_dir[0] = L'\0';
	}

	/* If help, version or multiple flag used, do not check Mutex */
	if (!help && !version)
		if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && !multiple))
			return 0;

	/* Now we are ready for Pidgin .. */
	if ((hmod = LoadLibraryW(LIBPIDGIN_DLL_NAMEW)))
		pidgin_main = (LPFNPIDGINMAIN) GetProcAddress(hmod, "pidgin_main");

	if (!pidgin_main) {
		DWORD dw = GetLastError();
		const wchar_t *err_msg = get_win32_error_message(dw);

		_snwprintf(errbuf, sizeof(errbuf) / sizeof(wchar_t),
				L"Error loading %ls.\nError: (%u) %ls",
				LIBPIDGIN_DLL_NAMEW, (UINT) dw, err_msg);
		wprintf(L"%ls\n", errbuf);
		MessageBoxW(NULL, errbuf, L"Error", MB_OK | MB_TOPMOST);

		return 0;
	}

	/* Convert argv to utf-8*/
	szArglist = CommandLineToArgvW(cmdLine, &j);
	pidgin_argc = j;
	pidgin_argv = malloc(pidgin_argc* sizeof(char*));
	k = 0;
	for (i = 0; i < j; i++) {
		success = FALSE;
		/* Remove the --portable-mode arg from the args passed to pidgin so it doesn't choke */
		if (wcsstr(szArglist[i], L"--portable-mode") == NULL) {
			int len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
				-1, NULL, 0, NULL, NULL);
			if (len != 0) {
				char *arg = malloc(len);
				len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
					-1, arg, len, NULL, NULL);
				if (len != 0) {
					pidgin_argv[k++] = arg;
					success = TRUE;
				}
			}
			if (!success)
				wprintf(L"Error converting argument '%ls' to UTF-8\n",
					szArglist[i]);
		}
		if (!success)
			pidgin_argc--;
	}
	LocalFree(szArglist);


	return pidgin_main(hInstance, pidgin_argc, pidgin_argv);
}

mercurial