#include "headers.h" #include "..\Zlib\src\zip.h" TCHAR dbname[MAX_PATH]; HWND progress_dialog; static UINT_PTR timer_id; INT_PTR CALLBACK DlgProcProgress(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_INITDIALOG: { HWND prog = GetDlgItem(hwndDlg, IDC_PROGRESS); TranslateDialogDefault( hwndDlg ); SendMessage(prog, PBM_SETPOS, 0, 0); } break; case WM_COMMAND: if ( HIWORD( wParam ) == BN_CLICKED && LOWORD( wParam ) == IDCANCEL ) { // in the progress dialog, use the user data to indicate that the user has pressed cancel SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)1); return TRUE; } break; } return FALSE; } TCHAR* DoubleSlash(TCHAR *sorce) { TCHAR *ret = (TCHAR*)mir_calloc(MAX_PATH); TCHAR *r, *s; for (s = sorce, r = ret; *s && r - ret < MAX_PATH - 1; s++, r++){ if (*s != _T('\\')) *r = *s; else { *r = _T('\\'); r++; *r = _T('\\'); } } return ret; } bool MakeZip(LPCTSTR tszSource, LPCTSTR tszDest) { bool ret = false; ptrA szSourceName(mir_u2a(dbname)); ptrT tszDestPath(DoubleSlash((TCHAR*)tszDest)); WIN32_FILE_ATTRIBUTE_DATA fad = {0}; SYSTEMTIME st; GetFileAttributesEx(tszSource, GetFileExInfoStandard, &fad); FileTimeToLocalFileTime(&fad.ftLastWriteTime, &fad.ftLastWriteTime); FileTimeToSystemTime(&fad.ftLastWriteTime, &st); HANDLE hSrc = CreateFile( tszSource, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hSrc == INVALID_HANDLE_VALUE) return ret; if (zipFile hZip = zipOpen2_64(tszDestPath, APPEND_STATUS_CREATE, NULL, NULL)) { zip_fileinfo fi = {0}; fi.tmz_date.tm_sec = st.wSecond; fi.tmz_date.tm_min = st.wMinute; fi.tmz_date.tm_hour = st.wHour; fi.tmz_date.tm_mday = st.wDay; fi.tmz_date.tm_mon = st.wMonth-1; fi.tmz_date.tm_year = st.wYear; int res = zipOpenNewFileInZip( hZip, szSourceName, &fi, NULL, 0, NULL, 0, "", Z_DEFLATED, Z_BEST_COMPRESSION ); if (res == ZIP_OK) { DWORD buf_length = 256 * 1024; // 256 KB HWND hProgBar = GetDlgItem(progress_dialog, IDC_PROGRESS); DWORD dwTotalBytes = 0; MSG msg; if (void* buf = mir_alloc( buf_length )) { while (GetWindowLongPtr(progress_dialog, GWLP_USERDATA) != 1) { DWORD dwRead = 0; if (!ReadFile(hSrc, buf, buf_length, &dwRead, NULL)) break; if (dwRead == 0) // EOF { ret = true; break; } res = zipWriteInFileInZip(hZip, buf, dwRead); if (res != ZIP_OK) break; while(PeekMessage(&msg, progress_dialog, 0, 0, PM_REMOVE) != 0) { if (!IsDialogMessage(progress_dialog, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } dwTotalBytes += dwRead; SendMessage(hProgBar, PBM_SETPOS, (WPARAM)(100.0 * double(dwTotalBytes) / double(fad.nFileSizeLow)), 0); } mir_free(buf); } zipCloseFileInZip(hZip); } char szComment[128]; mir_snprintf(szComment, SIZEOF(szComment), "%s\r\n%s %s %d.%d.%d.%d\r\n", Translate("Miranda NG database"), Translate("Created by:"), __PLUGIN_NAME, __MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM); zipClose( hZip, szComment); } CloseHandle(hSrc); return ret; } INT_PTR DBSaveAs(WPARAM wParam, LPARAM lParam) { TCHAR fname_buff[MAX_PATH], tszFilter[200]; OPENFILENAME ofn = {0}; CallService(MS_DB_GETPROFILENAMET,MAX_PATH,(LPARAM)fname_buff); mir_sntprintf(tszFilter, SIZEOF(tszFilter), _T("%s (*.dat)%c*.dat%c%s (*.zip)%c*.zip%c%s (*.*)%c*%c"), TranslateT("Miranda NG databases"), 0, 0, TranslateT("Compressed Miranda NG databases"), 0, 0, TranslateT("All files"), 0, 0); ofn.lStructSize = sizeof(ofn); ofn.lpstrFile = fname_buff; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT; ofn.lpstrFilter = tszFilter; ofn.nFilterIndex = 1; ofn.lpstrDefExt = _T("dat"); if (GetSaveFileName(&ofn)) mir_forkthread(BackupThread, (void*)fname_buff); return 0; } struct backupFile { TCHAR Name[MAX_PATH]; FILETIME CreationTime; }; int Comp(const void *i, const void *j) { backupFile *pi = (backupFile*)i; backupFile *pj = (backupFile*)j; if (pi->CreationTime.dwHighDateTime > pj->CreationTime.dwHighDateTime || (pi->CreationTime.dwHighDateTime == pj->CreationTime.dwHighDateTime && pi->CreationTime.dwLowDateTime > pj->CreationTime.dwLowDateTime)) return -1; else return 1; } int RotateBackups() { TCHAR backupfilename1[MAX_PATH] = {0}, backupfolderTmp[MAX_PATH] = {0}; unsigned int i = 0; WIN32_FIND_DATA FindFileData; TCHAR *backupfolder = Utils_ReplaceVarsT(options.folder); mir_sntprintf(backupfolderTmp, SIZEOF(backupfolderTmp), _T("%s\\%s*"), backupfolder, dbname); HANDLE hFind = FindFirstFile(backupfolderTmp, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) return 0; backupFile *bf = (backupFile*)mir_calloc(sizeof(backupFile)); while(bf) { if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (FindNextFile(hFind, &FindFileData)) continue; else break; } _tcscpy(bf[i].Name, FindFileData.cFileName); bf[i].CreationTime = FindFileData.ftCreationTime; if (FindNextFile(hFind, &FindFileData)) bf = (backupFile*)mir_realloc(bf, (++i + 1) * sizeof(backupFile)); else break; } FindClose(hFind); if (i > 0) qsort(bf, i + 1, sizeof(backupFile), Comp); //Sort the list of found files by date in descending order for(;i >= options.num_backups - 1; i--){ mir_sntprintf(backupfilename1, MAX_PATH, _T("%s\\%s"), backupfolder, bf[i].Name); DeleteFile(backupfilename1); } mir_free(backupfolder); mir_free(bf); return 0; } void BackupThread(void* backup_filename) { Backup((TCHAR*)backup_filename); } int Backup(TCHAR* backup_filename) { TCHAR source_file[MAX_PATH] = { 0 }, dest_file[MAX_PATH] = { 0 }; bool bZip = false; CallService(MS_DB_GETPROFILENAMET, MAX_PATH, (LPARAM)dbname); if (backup_filename == NULL) { TCHAR buffer[MAX_COMPUTERNAME_LENGTH + 1]; DWORD size = sizeof(buffer); bZip = options.use_zip != 0; TCHAR *backupfolder = Utils_ReplaceVarsT(options.folder); // ensure the backup folder exists (either create it or return non-zero signifying error) int err = CreateDirectoryTreeT(backupfolder); if (err != ERROR_ALREADY_EXISTS && err != 0) return 1; SYSTEMTIME st; GetLocalTime(&st); GetComputerName(buffer, &size); mir_sntprintf(dest_file, MAX_PATH, _T("%s\\%s_%02d.%02d.%02d@%02d-%02d-%02d_%s.%s"), backupfolder, dbname, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, buffer, bZip ? _T("zip") : _T("dat")); mir_free(backupfolder); RotateBackups(); } else { lstrcpyn(dest_file, backup_filename, MAX_PATH); if (!_tcscmp(_tcsrchr(backup_filename, _T('.')), _T(".zip"))) bZip = true; } if (!options.disable_popups) ShowPopup(dbname, TranslateT("Backup in progress")); if (!options.disable_progress) progress_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_COPYPROGRESS), 0, DlgProcProgress); SetDlgItemText(progress_dialog, IDC_PROGRESSMESSAGE, TranslateT("Copying database file...")); mir_sntprintf(source_file, MAX_PATH, _T("%s\\%s"), profilePath, dbname); TCHAR *pathtmp = Utils_ReplaceVarsT(source_file); BOOL res = 0; if (bZip) res = MakeZip(pathtmp, dest_file); else res = CopyFile(pathtmp, dest_file, 0); if (res) { if (!bZip) { // Set the backup file to the current time for rotator's correct work FILETIME ft; SYSTEMTIME st; HANDLE hFile = CreateFile(dest_file, FILE_WRITE_ATTRIBUTES, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); SetFileTime(hFile, NULL, NULL, &ft); CloseHandle(hFile); } SendMessage(GetDlgItem(progress_dialog, IDC_PROGRESS), PBM_SETPOS, (WPARAM)(100), 0); UpdateWindow(progress_dialog); db_set_dw(0, "AutoBackups", "LastBackupTimestamp", (DWORD)time(0)); if (!options.disable_popups) { size_t dest_file_len = lstrlen(dest_file); TCHAR *puText; if (dest_file_len > 50) { int i; puText = (TCHAR*)mir_alloc(sizeof(TCHAR) * (dest_file_len + 2)); for(i = (int)dest_file_len - 1; dest_file[i] != _T('\\'); i--); lstrcpyn(puText, dest_file, i + 2); lstrcat(puText, _T("\n")); lstrcat(puText, dest_file + i + 1); } else puText = mir_tstrdup(dest_file); ShowPopup(puText, TranslateT("Database backed up")); mir_free(puText); } } else DeleteFile(dest_file); mir_free(pathtmp); DestroyWindow(progress_dialog); return 0; } VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { time_t t = time(NULL); time_t diff = t - (time_t)db_get_dw(0, "AutoBackups", "LastBackupTimestamp", (DWORD)t); if (diff > (time_t)(options.period * (options.period_type == PT_MINUTES ? 60 : (options.period_type == PT_HOURS ? 60 * 60 : 60 * 60 * 24 )))) mir_forkthread(BackupThread, NULL); } int SetBackupTimer(void) { if (options.backup_types & BT_PERIODIC) { if (timer_id == 0) timer_id = SetTimer(0, timer_id, 1000 * 60, TimerProc); } else if (timer_id != 0) { KillTimer(0, timer_id); timer_id = 0; } return 0; } INT_PTR ABService(WPARAM wParam, LPARAM lParam) { mir_forkthread(BackupThread, (void*)wParam); return 0; }