#include "common.h" #include "extern.h" // write data needed by the external process, and restart miranda // returns 1 if any error, 0 if shutdown is imminent int ExternProcess(bool restart) { //HWND hWndMiranda = (HWND)CallService(MS_CLUI_GETHWND, 0, 0); // spawn a process that will: // -- wait for miranda to exit // -- move downloaded plugins from the temp folder to the Plugins folder, possibly backing up old ones // -- restart miranda char msg[1024]; mir_snprintf(msg, 1024, "spawning external process, restart = %s", restart ? "true" : "false"); NLog(msg); TCHAR data_filename[MAX_PATH]; _tcscpy(data_filename, options.data_folder); _tcscat(data_filename, _T("\\ud_data.txt")); // write data to file for external process to use HANDLE hDatFile = CreateFile(data_filename, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); if(hDatFile == INVALID_HANDLE_VALUE) { //MessageBox(0, Translate("Could not create data file for restart."), Translate("Error"), MB_OK | MB_ICONERROR); ShowError(TranslateT("Could not create data file for restart")); return 1; } TCHAR *mir_exe = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)), *plugins_folder = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)); char *db_name = (char *)malloc(MAX_PATH), *db_path = (char *)malloc(MAX_PATH); // Get Miranda exe path and profile CallService(MS_DB_GETPROFILENAME, MAX_PATH, (WPARAM)(char*) db_name); CallService(MS_DB_GETPROFILEPATH, MAX_PATH, (WPARAM)(char*) db_path); strcat(db_path, "\\"); strcat(db_path, db_name); //lstrcpyn(db_path+lstrlen(db_path), db_name, lstrlen(db_name)-3); // Pointer arithmetic //*(db_path + lstrlen(db_name) - 3) = 0; // get plugin folder GetModuleFileName(0, mir_exe, MAX_PATH); _tcscpy(plugins_folder, mir_exe); TCHAR *p = _tcsrchr(plugins_folder, _T('\\')); if(p) *p = 0; _tcscat(plugins_folder, _T("\\Plugins")); unsigned long bytes_written; char *nl = "\r\n"; TCHAR *tnl = _T("\r\n"); char buf[64]; WriteFile(hDatFile, mir_exe, (unsigned)_tcslen(mir_exe) * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, tnl, 2 * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, plugins_folder, (unsigned)_tcslen(plugins_folder) * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, tnl, 2 * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, db_name, (unsigned)strlen(db_name), &bytes_written, FALSE); WriteFile(hDatFile, nl, 2, &bytes_written, FALSE); WriteFile(hDatFile, db_path, (unsigned)strlen(db_path), &bytes_written, FALSE); WriteFile(hDatFile, nl, 2, &bytes_written, FALSE); WriteFile(hDatFile, options.temp_folder, (unsigned)_tcslen(options.temp_folder) * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, tnl, 2 * sizeof(TCHAR), &bytes_written, FALSE); if(options.backup) { WriteFile(hDatFile, options.backup_folder, (unsigned)_tcslen(options.backup_folder) * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, tnl, 2 * sizeof(TCHAR), &bytes_written, FALSE); } else { WriteFile(hDatFile, _T("no backups"),(unsigned) _tcslen(_T("no backups")) * sizeof(TCHAR), &bytes_written, FALSE); WriteFile(hDatFile, tnl, 2 * sizeof(TCHAR), &bytes_written, FALSE); } sprintf(buf, "%d", (unsigned int)GetCurrentProcessId()); //MessageBox(0, buf, "Writing process id", MB_OK); WriteFile(hDatFile, buf, (unsigned)strlen(buf), &bytes_written, FALSE); WriteFile(hDatFile, nl, 2, &bytes_written, FALSE); sprintf(buf, (restart ? "restart" : "no_restart")); WriteFile(hDatFile, buf, (unsigned)strlen(buf), &bytes_written, FALSE); WriteFile(hDatFile, nl, 2, &bytes_written, FALSE); CloseHandle(hDatFile); free(mir_exe); free(plugins_folder); free(db_name); free(db_path); /* if(!CallService(MS_SYSTEM_OKTOEXIT,0,0)) { DeleteFile(data_filename); MessageBox(0, Translate("Miranda's not 'OK TO EXIT'."), Translate("Error"), MB_OK | MB_ICONERROR); return; } */ TCHAR szParams[MAX_PATH], szBuf[MAX_PATH]; // try to fire up external process from new dll (if present), so we can overwrite the old one _tcscpy(szBuf, options.temp_folder); #ifdef PLUGDIR _tcscat(szBuf, _T("\\plugins\\updater.dll")); #else _tcscat(szBuf, _T("\\updater.dll")); #endif HANDLE hFile = CreateFile(szBuf, 0, 0, 0, OPEN_EXISTING, 0, 0); if(hFile == INVALID_HANDLE_VALUE) { // try with short path name...(for win98) TCHAR szShortBuf[MAX_PATH]; GetShortPathName(szBuf, szShortBuf, MAX_PATH); hFile = CreateFile(szShortBuf, 0, 0, 0, OPEN_EXISTING, 0, 0); } if(hFile != INVALID_HANDLE_VALUE) { // new dll exists CloseHandle(hFile); } else { // can't find new updater dll - restart using this dll GetModuleFileName(hInst, szBuf, MAX_PATH); } // p = _tcsrchr(szBuf, _T('\\')); // if (p) *p = 0; // rundll32 hates spaces in the arg, but quotes aren't allowed in earlier versions... // GetShortPath can return paths with spaces (at least on XP with 8.3 filenames disabled)... // so we must 'CreateProcess' with the updater.dll location as the startup directory and pass only updater.dll as the arg mir_sntprintf(szParams, SIZEOF(szParams), _T("RUNDLL32.EXE \"%s\",ExternalUpdate %s"), szBuf, data_filename); PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; si.cb = sizeof(si); if(!CreateProcess(0, szParams, 0, 0, 0, CREATE_NO_WINDOW | DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, 0, NULL, &si, &pi)) { int err = GetLastError(); TCHAR msg[256]; mir_sntprintf(msg, SIZEOF(msg), _T("Error code: %d"), err); MessageBox(0, msg, TranslateT("CreateProcess"), MB_OK | MB_ICONERROR); return 1; } else { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); PostMessage((HWND)CallService(MS_CLUI_GETHWND, 0, 0), WM_COMMAND, ID_ICQ_EXIT, 0); return 0; } return 1; } #ifdef _UD_LOGGING void mWriteFile(HANDLE hFile, char *line) { unsigned long bytes_written; char *nl = "\r\n"; WriteFile(hFile, line, strlen(line), &bytes_written, FALSE); WriteFile(hFile, nl, 2, &bytes_written, FALSE); } #endif // move all files in src_folder to dst_folder - put replaced files in backup folder // if a file in src_folder is a directory, copy it's contents to the same dir in the root folder and // set that dir as the new root (so that dirs in dirs go in the right place) void MoveFiles(HANDLE hLogFile, TCHAR *src_folder, TCHAR *dst_folder, TCHAR *backup_folder, TCHAR *root_folder) { // move files from src_folder to dst_folder if(!src_folder || _tcslen(src_folder) == 0) { MessageBox(0, _T("Your 'temporary files' folder is set to NULL. Install aborted."), _T("Updater Error"), MB_OK | MB_ICONERROR); return; } TCHAR szFilesPath[MAX_PATH], szOldFileName[MAX_PATH], szNewFileName[MAX_PATH], szBackupFileName[MAX_PATH]; bool do_backups = backup_folder ? (_tcscmp(backup_folder, _T("no backups")) != 0) : false; // ensure the destination folder exists if(!CreatePath(dst_folder)) { return; } _tcscpy(szFilesPath, src_folder); _tcscat(szFilesPath, _T("\\*.*")); bool move_file; WIN32_FIND_DATA findData; HANDLE hFileSearch = FindFirstFile(szFilesPath, &findData); if(hFileSearch != INVALID_HANDLE_VALUE) { do { if(findData.cFileName[0] != _T('.')) { //_tcslwr(findData.cFileName); _tcscpy(szOldFileName, src_folder); _tcscat(szOldFileName, _T("\\")); _tcscat(szOldFileName, findData.cFileName); if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // use szNewFileName as destination and new root folder _tcscpy(szNewFileName, root_folder); if(_tcslen(findData.cFileName) < _tcslen(ROOT_FILES_FOLDER) || _tcsicmp(findData.cFileName, ROOT_FILES_FOLDER)) { _tcscat(szNewFileName, _T("\\")); _tcscat(szNewFileName, findData.cFileName); } // recurse MoveFiles(hLogFile, szOldFileName, szNewFileName, backup_folder, szNewFileName); } else { _tcscpy(szNewFileName, dst_folder); // exception for langpack files - move to root_folder // exception for dbtool.exe (e.g. translated) - move to root_folder if((_tcsnicmp(findData.cFileName, _T("dbtool.exe"), _tcslen(_T("dbtool.exe"))) == 0) || (_tcsnicmp(findData.cFileName, _T("langpack_"), _tcslen(_T("langpack_"))) == 0)) { _tcscpy(szNewFileName, root_folder); } _tcscat(szNewFileName, _T("\\")); _tcscat(szNewFileName, findData.cFileName); move_file = false; if(do_backups) { _tcscpy(szBackupFileName, backup_folder); _tcscat(szBackupFileName, _T("\\")); _tcscat(szBackupFileName, findData.cFileName); move_file = true; DeleteFile(szBackupFileName); if(!MoveFile(szNewFileName, szBackupFileName)) { // MessageBox(0, szNewFileName, "Could not backup!", MB_OK | MB_ICONWARNING); } } else { move_file = true; if(!DeleteFile(szNewFileName)) { // MessageBox(0, szNewFileName, "Could not delete!", MB_OK | MB_ICONWARNING); } } if(move_file) { if(!MoveFile(szOldFileName, szNewFileName)) { //MessageBox(0, szOldFileName, "Could not move!", MB_OK | MB_ICONWARNING); // try a copy - possibly win98 etc. will not move the updater.dll when it is being used by this process CopyFile(szOldFileName, szNewFileName, TRUE); DeleteFile(szOldFileName); // docs say it is marked for delete and actually removed when the last handle is closed...hmm } } else { DeleteFile(szOldFileName); } } } } while(FindNextFile(hFileSearch, &findData)); FindClose(hFileSearch); } RemoveDirectory(src_folder); } bool ReadTLine(HANDLE hDatFile, TCHAR *line, int bsize, int &offset) { unsigned long bytes_read; BOOL bResult; while((bResult = ReadFile(hDatFile, line + offset, sizeof(TCHAR), &bytes_read, 0)) && offset < bsize && bytes_read == sizeof(TCHAR) && line[offset] && (line[offset] != _T('\n') || (offset > 0 && line[offset - 1] != _T('\r')))) offset++; #ifndef _UNICODE if(offset == 1 && line[1] == 0) { wchar_t wline[MAX_PATH]; wline[0] = *(wchar_t *)line; while((bResult = ReadFile(hDatFile, wline + offset, sizeof(wchar_t), &bytes_read, 0)) && offset < bsize && bytes_read == sizeof(wchar_t) && wline[offset] && (wline[offset] != L'\n' || (offset > 0 && wline[offset - 1] != L'\r'))) offset++; if(offset > 0) wline[offset - 1] = 0; // cut off /r/n WideCharToMultiByte(CP_ACP, 0, wline, -1, line, bsize, 0, 0); } #endif if(offset > 0) line[offset - 1] = 0; // cut off /r/n return true; } void CALLBACK ExternalUpdate(HWND hwnd, HINSTANCE hInstance, LPSTR lpszCmdLine, int nCmdShow) { //MessageBox(0, _T("ExternalUpdate"), _T("Updater"), MB_OK); HANDLE hDatFile = CreateFileA(lpszCmdLine, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); if(hDatFile == INVALID_HANDLE_VALUE) { char msg[1024]; sprintf(msg, "Could not read data file:\n%s", lpszCmdLine); MessageBoxA(0, msg, "Updater Error", MB_OK | MB_ICONERROR); } else { TCHAR *mir_exe = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)), *plugins_folder = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)), *temp_folder = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)), *backup_folder = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)), *root_folder = (TCHAR *)malloc(MAX_PATH * sizeof(TCHAR)); char *db_name = (char *)malloc(MAX_PATH), *db_path = (char *)malloc(MAX_PATH), *pid = (char *)malloc(64); bool restart = true; { int i = 0, offset; unsigned long bytes_read; char ans_line[MAX_PATH]; TCHAR line[MAX_PATH]; BOOL bResult = TRUE; do { offset = 0; if(i == 2 || i == 3 || i == 6 || i == 7) { while((bResult = ReadFile(hDatFile, ans_line + offset, 1, &bytes_read, 0)) && offset < MAX_PATH && bytes_read == 1 && ans_line[offset] && (ans_line[offset] != '\n' || (offset > 0 && ans_line[offset - 1] != '\r'))) offset++; if(offset > 0) ans_line[offset - 1] = 0; // cut off /r/n //MessageBoxA(0, ans_line, "Read ANSI text line", MB_OK); } else { ReadTLine(hDatFile, line, MAX_PATH, offset); } switch(i) { case 0: _tcsncpy(mir_exe, line, MAX_PATH); break; case 1: _tcsncpy(plugins_folder, line, MAX_PATH); break; case 2: strncpy(db_name, ans_line, MAX_PATH); break; case 3: strncpy(db_path, ans_line, MAX_PATH); break; case 4: _tcsncpy(temp_folder, line, MAX_PATH); break; case 5: _tcsncpy(backup_folder, line, MAX_PATH); break; case 6: strncpy(pid, ans_line, 64); break; case 7: restart = (strncmp(ans_line, "restart", strlen("restart")) == 0); offset = 0; // end loop break; default: offset = 0; // end loop } i++; } while(offset > 0); } CloseHandle(hDatFile); // use data file to log to #ifndef _UD_LOGGING DeleteFileA(lpszCmdLine); #else HANDLE hDatFile = CreateFileA(lpszCmdLine, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0); #endif #ifdef _UD_LOGGING mWriteFile(hDatFile, "Inside external process..."); #endif _tcscpy(root_folder, mir_exe); TCHAR *p = _tcsrchr(root_folder, _T('\\')); if(p) *p = 0; // ensure miranda has exited DWORD mpi = (DWORD)atol(pid); bool exited = false; #ifdef _UD_LOGGING char logmsg[1024]; sprintf(logmsg, "Opening process #%d...", mpi); mWriteFile(hDatFile, logmsg); #endif HANDLE hMiranda = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, mpi); if(hMiranda) { int mbFlags, idRetry, idCancel, idContinue; const int MAX_SIZE = 2048; TCHAR message[MAX_SIZE]; int exitStatus; OSVERSIONINFO vi = {0}; vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&vi); _tcsncpy(message, _T("Miranda did not exit - cannot install or restart.\n"), MAX_SIZE); if (vi.dwMajorVersion = 5) { //windows 2000+ mbFlags = 0x00000006L; //MB_CANCELTRYCONTINUE; idRetry = 10; //IDTRYAGAIN idCancel = IDCANCEL; idContinue = 11; //IDCONTINUE _tcsncat(message, _T("Press 'Try again' to check Miranda's status again, press 'Continue' to kill the process or press 'Cancel' to abort."), MAX_SIZE); } else { //windows 98, me mbFlags = MB_ABORTRETRYIGNORE; idRetry = IDRETRY; idCancel = IDCANCEL; idContinue = IDIGNORE; _tcsncat(message, _T("Press 'Retry' to check Miranda's status again, press 'Ignore' to kill the process or press 'Abort' to abort."), MAX_SIZE); } while ((exitStatus = WaitForSingleObjectEx(hMiranda, 20 * 1000, FALSE)) == WAIT_TIMEOUT) { int res = MessageBox(0, message, _T("Updater Error"), mbFlags | MB_ICONERROR); if (res == idContinue) { //if the user chooses Continue then kill the application TerminateProcess(hMiranda, 1); if((exitStatus = WaitForSingleObjectEx(hMiranda, 5 * 1000, FALSE)) == WAIT_TIMEOUT) { //hMiranda = OpenProcess(SYNCHRONIZE, FALSE, mpi); //if(hMiranda) { //CloseHandle(hMiranda); MessageBox(0, _T("It seems Miranda is still running. Aborting update."), _T("Updater Error"), MB_OK | MB_ICONERROR); } else { #ifdef _UD_LOGGING mWriteFile(hDatFile, "Wait for miranda processs to 'Terminate' interrupted - assuming it exited"); #endif } } else { if (res == idRetry) { //if the user selected 'Try again' then wait a bit more. continue; //wait again } } break; //don't update anymore (happens when user choses 'Continue' or 'Cacel' } #ifdef _UD_LOGGING mWriteFile(hDatFile, "Wait for miranda processs interrupted - assuming it exited"); #endif exited = (exitStatus != WAIT_TIMEOUT); CloseHandle(hMiranda); } else { #ifdef _UD_LOGGING mWriteFile(hDatFile, "Could not open miranda processs - assuming it exited"); #endif //MessageBox(0, "Could not open Miranda process", "Update Error", MB_OK | MB_ICONERROR); exited = true; } if(exited) { #ifdef _UD_LOGGING mWriteFile(hDatFile, "Miranda exited - moving files"); MoveFiles(hDatFile, temp_folder, plugins_folder, backup_folder, root_folder); #else MoveFiles(0, temp_folder, plugins_folder, backup_folder, root_folder); RemoveDirectory(temp_folder); #endif // move files // restart miranda if(restart) { #ifdef _UD_LOGGING mWriteFile(hDatFile, "Restarting"); #endif TCHAR szArgs[MAX_PATH], *temp_str; //wsprintf(szArgs, "\"%s\" \"%s\"", db_path, db_name); mir_sntprintf(szArgs, SIZEOF(szArgs), _T("\"%s\" \"%s\""), mir_exe, temp_str = GetTStringACP(db_path)); // includes name, dummy instead of executable? free(temp_str); //wsprintf(szArgs, "\"%s\"", db_name); PROCESS_INFORMATION pi = {0}; STARTUPINFO si = {0}; si.cb = sizeof(si); if(!CreateProcess(mir_exe, szArgs, 0, 0, 0, DETACHED_PROCESS|NORMAL_PRIORITY_CLASS, 0, 0, &si, &pi)) { MessageBox(0, _T("Failed to restart Miranda"), _T("Updater Error"), MB_OK | MB_ICONERROR); //MessageBox(0, szArgs, mir_exe, MB_OK); } //ShellExecute(0, 0, mir_exe, szArgs, 0, SW_NORMAL); } } #ifdef _UD_LOGGING else mWriteFile(hDatFile, "Miranda did not exit"); #endif free(plugins_folder); free(db_name); free(db_path); free(temp_folder); free(backup_folder); free(root_folder); free(mir_exe); #ifdef _UD_LOGGING CloseHandle(hDatFile); #endif } FreeLibraryAndExitThread(hInstance, TRUE); }