/* * winpidgin.c * * Date: June, 2002 * Description: Entry point for win32 pidgin, and various win32 dependent * 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 #include #include #include #include #include #include #include /* We can't include glib.h per the above warning, so we define our own unused * parameter macro. */ #ifdef __GNUC__ # define PIDGIN_WIN32_UNUSED __attribute__((unused)) #else # define PIDGIN_WIN32_UNUSED #endif 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, PIDGIN_WIN32_UNUSED struct HINSTANCE__ *hPrevInstance, PIDGIN_WIN32_UNUSED char *lpszCmdLine, PIDGIN_WIN32_UNUSED 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 = (void *)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 = (void *)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 = (void *)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 = (void *)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 = (void *)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 = (void *)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); }