/* Basic History plugin Copyright (C) 2011-2012 Krzysztof Kral 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 version 2 of the License. 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, see . */ #include "StdAfx.h" #include "Options.h" #include "ExportManager.h" #include "HistoryWindow.h" // Sorry for plain C implementation #define MODULE "BasicHistory" extern HANDLE g_hMainThread; bool bPopupsEnabled; bool DoTask(TaskOptions& to); bool IsValidTask(TaskOptions& to, std::list* top = nullptr, std::wstring* err = nullptr, std::wstring* errDescr = nullptr); std::wstring GetFileName(const std::wstring &baseName, std::wstring contactName, std::map& existingContacts, bool replaceContact); std::wstring GetDirectoryName(const std::wstring &path); std::wstring GetName(const std::wstring &path); bool DeleteDirectory(LPCTSTR lpszDir, bool noRecycleBin = true); void ListDirectory(const std::wstring &basePath, const std::wstring &path, std::list& files); std::wstring ReplaceStr(const std::wstring& str, wchar_t oldCh, wchar_t newCh); time_t GetNextExportTime(TaskOptions& to); void SchedulerThreadFunc(void*); volatile bool finishThread = false; bool initTask = false; HANDLE hThread = nullptr; HANDLE hThreadEvent; time_t nextExportTime; void StartThread(bool init); void StopThread(); bool GetNextExportTime(bool init, time_t now); bool ExecuteCurrentTask(time_t now); void GetZipFileTime(const wchar_t *file, uLong *dt); std::wstring ReplaceExt(const std::wstring& file, const wchar_t* ext); bool ZipFiles(const std::wstring& dir, std::wstring zipFilePath, const std::string& password); bool UnzipFiles(const std::wstring& dir, std::wstring& zipFilePath, const std::string& password); bool FtpFiles(const std::wstring& dir, const std::wstring& filePath, const std::wstring& ftpName); bool FtpGetFiles(const std::wstring& dir, const std::list& files, const std::wstring& ftpName); void CreatePath(const wchar_t *szDir); void DoError(const TaskOptions& to, const std::wstring error); static HANDLE hPopupClass; void OptionsSchedulerChanged() { StartThread(false); } static int OnShutdown(WPARAM, LPARAM) { Popup_UnregisterClass(hPopupClass); return 0; } void InitScheduler() { bPopupsEnabled = ServiceExists(MS_POPUP_ADDPOPUPT) || ServiceExists(MS_POPUP_ADDPOPUPCLASS); POPUPCLASS test = { sizeof(test) }; test.flags = PCF_TCHAR; test.hIcon = Skin_LoadIcon(SKINICON_OTHER_HISTORY); test.iSeconds = 10; test.pwszDescription = TranslateT("History task"); test.pszName = MODULE; if (hPopupClass = Popup_RegisterClass(&test)) HookEvent(ME_SYSTEM_SHUTDOWN, OnShutdown); StartThread(true); } void DeinitScheduler() { StopThread(); } int DoLastTask(WPARAM, LPARAM) { for (std::vector::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it) if (it->trigerType == TaskOptions::AtEnd && it->active) DoTask(*it); return 0; } bool IsValidTask(TaskOptions& to, std::list* top, std::wstring* err, std::wstring* errDescr) { if (to.taskName.empty()) { if (err != nullptr) *err = TranslateT("Name"); return false; } if (top != nullptr) { for (std::list::iterator it = top->begin(); it != top->end(); ++it) { if (it->taskName == to.taskName) { if (err != nullptr) *err = TranslateT("Name"); return false; } } } if (!to.isSystem && to.contacts.size() == 0) { if (err != nullptr) *err = TranslateT("Contacts"); if (errDescr != nullptr) *errDescr = TranslateT("At least one contact should be selected."); return false; } bool isImportTask = to.type == TaskOptions::Import || to.type == TaskOptions::ImportAndMarge; if (!isImportTask) { if (to.filterId > 1) { int filter = 0; for (int i = 0; i < (int)Options::instance->customFilters.size(); ++i) { if (to.filterName == Options::instance->customFilters[i].name) { filter = i + 2; break; } } if (filter < 2) { if (err != nullptr) *err = TranslateT("Filter"); return false; } to.filterId = filter; } else if (to.filterId < 0) { if (err != nullptr) *err = TranslateT("Filter"); return false; } } if (to.type == TaskOptions::Delete) return true; if (!Options::FTPAvail() && to.useFtp) { if (err != nullptr) *err = TranslateT("Upload to FTP"); return false; } if (to.filePath.empty()) { if (err != nullptr) *err = TranslateT("Path to output file"); return false; } if (to.useFtp && to.ftpName.empty()) { if (err != nullptr) *err = TranslateT("Session name"); if (errDescr != nullptr) *errDescr = TranslateT("To create session open WinSCP, click New Session, enter data and save with specific name. Remember if FTP server using password you should save it in WinSCP."); return false; } if (to.useFtp && (to.filePath.find('\\') < to.filePath.length() || to.filePath.find(':') < to.filePath.length() || to.filePath[0] != L'/')) { if (err != nullptr) *err = TranslateT("Path to file"); if (errDescr != nullptr) *errDescr = TranslateT("FTP path must contain '/' instead '\\' and start with '/'."); return false; } if (isImportTask && to.filePath.find(L"") < to.filePath.length()) { if (err != nullptr) *err = TranslateT("Path to file"); if (errDescr != nullptr) *errDescr = TranslateT("FTP path cannot contain in import task."); return false; } if (!isImportTask && (to.exportType < IExport::RichHtml || to.exportType > IExport::Dat)) { if (err != nullptr) *err = TranslateT("Export to"); return false; } if (isImportTask && (to.importType < IImport::Binary || to.importType > IImport::Dat)) { if (err != nullptr) *err = TranslateT("Import from"); return false; } if ((to.trigerType == TaskOptions::Daily || to.trigerType == TaskOptions::Weekly || to.trigerType == TaskOptions::Monthly) && (to.dayTime < 0 || to.dayTime >= 24 * 60)) { if (err != nullptr) *err = TranslateT("Time"); return false; } if (to.trigerType == TaskOptions::Weekly && (to.dayOfWeek < 0 || to.dayOfWeek >= 7)) { if (err != nullptr) *err = TranslateT("Day of week"); return false; } if (to.trigerType == TaskOptions::Monthly && (to.dayOfMonth <= 0 || to.dayOfMonth >= 32)) { if (err != nullptr) *err = TranslateT("Day"); return false; } if ((to.trigerType == TaskOptions::DeltaMin || to.trigerType == TaskOptions::DeltaHour) && (to.deltaTime < 0 || to.deltaTime >= 10000)) { if (err != nullptr) *err = TranslateT("Delta time"); return false; } return true; } static void CALLBACK DoRebuildEventsInMainAPCFunc(ULONG_PTR dwParam) { MCONTACT *contacts = (MCONTACT*)dwParam; size_t size = (size_t)contacts[0]; for (size_t i = 1; i <= size; ++i) HistoryWindow::RebuildEvents(contacts[i]); delete[] contacts; } bool DoTask(TaskOptions& to) { std::wstring err; std::wstring errDescr; if (!IsValidTask(to, nullptr, &err, &errDescr)) { wchar_t msg[256]; if (err.empty()) wcscpy_s(msg, TranslateT("Some value is invalid")); else if (errDescr.empty()) mir_snwprintf(msg, TranslateT("Invalid '%s' value."), err.c_str()); else mir_snwprintf(msg, TranslateT("Invalid '%s' value.\n%s"), err.c_str(), errDescr.c_str()); DoError(to, msg); return true; } DWORD now = time(nullptr); long long int t = to.eventDeltaTime * 60; if (to.eventUnit > TaskOptions::Minute) t *= 60LL; if (to.eventUnit > TaskOptions::Hour) t *= 24LL; if (t > 2147483647LL) { DoError(to, TranslateT("Unknown error")); return true; } bool error = false; std::wstring errorStr; std::list managers; if (to.type == TaskOptions::Delete) { if (to.isSystem) { ExportManager *exp = new ExportManager(nullptr, NULL, to.filterId); exp->SetDeleteWithoutExportEvents(t, now); managers.push_back(exp); } for (size_t i = 0; i < to.contacts.size(); ++i) { ExportManager *exp = new ExportManager(nullptr, to.contacts[i], to.filterId); exp->SetDeleteWithoutExportEvents(t, now); managers.push_back(exp); } } else if (to.type == TaskOptions::Import || to.type == TaskOptions::ImportAndMarge) { std::map existingContacts1; ExportManager mExp = ExportManager(nullptr, NULL, 1); std::wstring filePath = to.filePath; std::wstring dir; std::list files; std::vector contacts; if (to.useFtp || to.compress) { std::map existingContacts; wchar_t temp[MAX_PATH]; temp[0] = 0; GetTempPath(MAX_PATH, temp); dir = temp; dir += GetName(filePath); dir = GetFileName(dir, L"", existingContacts, true); dir = ReplaceExt(dir, L""); size_t pos = dir.find_last_of('.'); if (pos < dir.length()) dir = dir.substr(0, pos); DeleteDirectory(dir.c_str()); CreateDirectory(dir.c_str(), nullptr); } const wchar_t* ext = ExportManager::GetExt(to.importType); if (to.isSystem) { std::wstring n = GetFileName(filePath, mExp.GetContactName(), existingContacts1, true); n = ReplaceExt(n, ext); files.push_back(n); contacts.push_back(NULL); } for (size_t i = 0; i < to.contacts.size(); ++i) { mExp.m_hContact = to.contacts[i]; std::wstring n = GetFileName(filePath, mExp.GetContactName(), existingContacts1, true); n = ReplaceExt(n, ext); files.push_back(n); contacts.push_back(to.contacts[i]); } if (to.useFtp) { if (to.compress) { std::map existingContacts; std::wstring n = GetFileName(filePath, L"", existingContacts, true); n = ReplaceExt(n, L"zip"); files.clear(); files.push_back(n); filePath = dir + L"\\" + GetName(filePath); } error = FtpGetFiles(dir, files, to.ftpName); if (error) { if (!errorStr.empty()) errorStr += L"\n"; errorStr += TranslateT("Cannot get FTP file(s)."); } } if (!error && to.compress) { error = UnzipFiles(dir, filePath, to.zipPassword); if (error) { if (!errorStr.empty()) errorStr += L"\n"; errorStr += TranslateT("Cannot unzip file(s)."); } if (to.useFtp) DeleteFile(filePath.c_str()); } if (!error && (to.useFtp || to.compress)) { files.clear(); std::list files1; ListDirectory(dir, L"\\", files1); for (std::list::iterator it = files1.begin(); it != files1.end(); ++it) files.push_back(dir + *it); } if (!error) { std::list contactList; for (std::list::iterator it = files.begin(); it != files.end(); ++it) { mExp.SetAutoImport(*it); int ret = mExp.Import(to.importType, contacts); if (ret == -3) { if (contacts.size() == 1) ret = 0; else { std::map existingContacts; std::wstring name = GetName(*it); for (ret = 0; ret < (int)contacts.size(); ++ret) { mExp.m_hContact = contacts[ret]; std::wstring n = GetFileName(to.filePath, mExp.GetContactName(), existingContacts, true); n = ReplaceExt(n, ext); n = GetName(n); if (n == name) break; } if (ret >= (int)contacts.size()) ret = -1; } } if (ret >= 0) { mExp.m_hContact = contacts[ret]; if (to.type == TaskOptions::Import) { HistoryEventList::AddImporter(mExp.m_hContact, to.importType, *it); contactList.push_back(mExp.m_hContact); } else { std::vector messages; if (mExp.Import(to.importType, messages, nullptr)) { mExp.MargeMessages(messages); contactList.push_back(mExp.m_hContact); } } } else if (ret != -1) { if (!errorStr.empty()) errorStr += L"\n"; wchar_t msg[1024]; mir_snwprintf(msg, TranslateT("Incorrect file format: %s."), GetName(*it).c_str()); errorStr += msg; } else { if (!errorStr.empty()) errorStr += L"\n"; wchar_t msg[1024]; mir_snwprintf(msg, TranslateT("Unknown contact in file: %s."), GetName(*it).c_str()); errorStr += msg; } } if (contactList.size() > 0) { MCONTACT *pContacts = new MCONTACT[contactList.size() + 1]; pContacts[0] = (MCONTACT)contactList.size(); int i = 1; for (std::list::iterator it = contactList.begin(); it != contactList.end(); ++it) pContacts[i++] = *it; QueueUserAPC(DoRebuildEventsInMainAPCFunc, g_hMainThread, (ULONG_PTR)pContacts); } } if (to.useFtp || to.compress) DeleteDirectory(dir.c_str()); } else { std::map existingContacts; std::wstring filePath = to.filePath; std::wstring dir; if (!to.useFtp && !to.compress) { dir = GetDirectoryName(filePath); if (!dir.empty()) CreateDirectory(dir.c_str(), nullptr); } else { filePath = GetName(filePath); wchar_t temp[MAX_PATH]; temp[0] = 0; GetTempPath(MAX_PATH, temp); dir = temp; dir += filePath; dir = GetFileName(dir, L"", existingContacts, true); dir = ReplaceExt(dir, L""); size_t pos = dir.find_last_of('.'); if (pos < dir.length()) dir = dir.substr(0, pos); DeleteDirectory(dir.c_str()); CreateDirectory(dir.c_str(), nullptr); filePath = dir + L"\\" + filePath; } if (to.isSystem) { ExportManager *exp = new ExportManager(nullptr, NULL, to.filterId); exp->SetAutoExport(GetFileName(filePath, exp->GetContactName(), existingContacts, true), t, now); exp->m_useImportedMessages = to.exportImported; if (!exp->Export(to.exportType)) { error = true; if (!errorStr.empty()) errorStr += L"\n"; wchar_t msg[1024]; mir_snwprintf(msg, TranslateT("Cannot export history for contact: %s."), exp->GetContactName().c_str()); errorStr += msg; } if (to.type == TaskOptions::Export) delete exp; else managers.push_back(exp); } if (!error) { for (size_t i = 0; i < to.contacts.size(); ++i) { ExportManager *exp = new ExportManager(nullptr, to.contacts[i], to.filterId); exp->SetAutoExport(GetFileName(filePath, exp->GetContactName(), existingContacts, true), t, now); exp->m_useImportedMessages = to.exportImported; if (!exp->Export(to.exportType)) { error = true; if (!errorStr.empty()) errorStr += L"\n"; wchar_t msg[1024]; mir_snwprintf(msg, TranslateT("Cannot export history for contact: %s."), exp->GetContactName().c_str()); errorStr += msg; break; } if (to.type == TaskOptions::Export) delete exp; else managers.push_back(exp); } } if (error) { if (to.compress && !to.useFtp) DeleteDirectory(dir.c_str()); } else if (to.compress) { std::wstring zipFilePath = to.filePath; std::wstring zipDir = dir; if (!to.useFtp) { zipDir = GetDirectoryName(zipFilePath); if (!zipDir.empty()) CreateDirectory(zipDir.c_str(), nullptr); } else { zipFilePath = GetName(zipFilePath); wchar_t temp[MAX_PATH]; temp[0] = 0; GetTempPath(MAX_PATH, temp); zipDir = temp; zipDir += L"zip"; zipDir = GetFileName(zipDir, L"", existingContacts, true); DeleteDirectory(zipDir.c_str()); CreateDirectory(zipDir.c_str(), nullptr); zipFilePath = zipDir + L"\\" + zipFilePath; } error = ZipFiles(dir + L"\\", zipFilePath, to.zipPassword); dir = zipDir; if (error) { if (!errorStr.empty()) errorStr += L"\n"; errorStr += TranslateT("Cannot compress file(s)."); } } if (to.useFtp) { if (!error) { error = FtpFiles(dir, to.filePath, to.ftpName); if (error) { if (!errorStr.empty()) errorStr += L"\n"; errorStr += TranslateT("Cannot send FTP file(s)."); } } DeleteDirectory(dir.c_str()); } } if (to.type == TaskOptions::Delete || to.type == TaskOptions::ExportAndDelete) { for (std::list::iterator it = managers.begin(); it != managers.end(); ++it) { if (!error) (*it)->DeleteExportedEvents(); delete *it; } } if (error) DoError(to, errorStr.empty() ? TranslateT("Unknown error") : errorStr); return error; } std::wstring GetFileName(const std::wstring &baseName, std::wstring contactName, std::map& existingContacts, bool replaceContact) { std::wstring str = baseName; size_t pos = baseName.find(L""); if (replaceContact && pos < baseName.length()) { str = baseName.substr(0, pos); std::wstring baseName1 = contactName; if (!baseName1.empty()) { std::wstring name = baseName1; int i = 0; wchar_t buf[32]; std::map::iterator it = existingContacts.find(name); while (it != existingContacts.end()) { _itow_s(++i, buf, 10); name = baseName1 + buf; it = existingContacts.find(name); } str += name; existingContacts[name] = true; } str += baseName.substr(pos + 9); } pos = str.find(L""); if (pos < str.length()) { wchar_t time[256]; SYSTEMTIME st; GetLocalTime(&st); mir_snwprintf(time, L"%d-%02d-%02d %02d%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute); std::wstring str1 = str.substr(0, pos); str1 += time; str1 += str.substr(pos + 6); str = str1; } return str; } std::wstring GetDirectoryName(const std::wstring &path) { size_t find = path.find_last_of(L"\\/"); if (find < path.length()) return path.substr(0, find); return L""; } void ListDirectory(const std::wstring &basePath, const std::wstring &path, std::list& files) { WIN32_FIND_DATA findFileData; HANDLE hFind = FindFirstFile((basePath + path + L"*").c_str(), &findFileData); if (hFind == INVALID_HANDLE_VALUE) return; do { if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { std::wstring name = findFileData.cFileName; if (name != L"." && name != L"..") ListDirectory(basePath, path + findFileData.cFileName + L"\\", files); } else files.push_back(path + findFileData.cFileName); } while (FindNextFile(hFind, &findFileData)); FindClose(hFind); } std::wstring ReplaceStr(const std::wstring& str, wchar_t oldCh, wchar_t newCh) { std::wstring ret; size_t start = 0; size_t find; while ((find = str.find_first_of(oldCh, start)) < str.length()) { ret += str.substr(start, find - start); ret += newCh; start = find + 1; } ret += str.substr(start, str.length() - start); return ret; } time_t GetNextExportTime(TaskOptions& to) { tm t; time_t newTime; switch (to.trigerType) { case TaskOptions::Daily: localtime_s(&t, &to.lastExport); t.tm_hour = to.dayTime / 60; t.tm_min = to.dayTime % 60; t.tm_sec = 0; newTime = mktime(&t); if (newTime <= to.lastExport) newTime += 60 * 60 * 24; return newTime; case TaskOptions::Weekly: localtime_s(&t, &to.lastExport); t.tm_hour = to.dayTime / 60; t.tm_min = to.dayTime % 60; t.tm_sec = 0; { int dow = (to.dayOfWeek + 1) % 7; newTime = mktime(&t); while (dow != t.tm_wday) { newTime += 60 * 60 * 24; localtime_s(&t, &newTime); newTime = mktime(&t); } } if (newTime <= to.lastExport) newTime += 7 * 60 * 60 * 24; return newTime; case TaskOptions::Monthly: localtime_s(&t, &to.lastExport); t.tm_hour = to.dayTime / 60; t.tm_min = to.dayTime % 60; t.tm_sec = 0; newTime = mktime(&t); while (to.dayOfMonth != t.tm_mday || newTime <= to.lastExport) { int lastM = t.tm_mon; int lastD = t.tm_mday; newTime += 60 * 60 * 24; localtime_s(&t, &newTime); newTime = mktime(&t); if (to.dayOfMonth > 28 && t.tm_mon != lastM && (newTime - 60 * 60 * 24) > to.lastExport) { lastM = t.tm_mon; if (to.dayOfMonth > lastD) { newTime -= 60 * 60 * 24; break; } } } return newTime; case TaskOptions::DeltaMin: return to.lastExport + to.deltaTime * 60; case TaskOptions::DeltaHour: return to.lastExport + to.deltaTime * 60 * 60; default: return to.lastExport; } } void SchedulerThreadFunc(void*) { if (initTask) { WaitForSingleObject(hThreadEvent, 5 * 1000); initTask = false; } while (!finishThread) { DWORD timeWait; time_t now = time(nullptr); while (nextExportTime <= now) if (!ExecuteCurrentTask(now)) return; time_t dif = nextExportTime - now; timeWait = (dif > 60 * 60 * 24) ? (60 * 60 * 1000) : (60 * 1000); WaitForSingleObject(hThreadEvent, timeWait); } } void StartThread(bool init) { StopThread(); initTask = false; bool isExport = GetNextExportTime(init, time(nullptr)); if (isExport) { finishThread = false; hThreadEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); hThread = mir_forkthread(SchedulerThreadFunc, nullptr); } } void StopThread() { if (hThread == nullptr) return; finishThread = true; SetEvent(hThreadEvent); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThreadEvent); hThread = nullptr; hThreadEvent = nullptr; } bool GetNextExportTime(bool init, time_t now) { mir_cslock lck(Options::instance->criticalSection); bool isExport = false; for (std::vector::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it) { if (it->forceExecute) { nextExportTime = now; isExport = true; initTask = init; break; } else if (it->active && it->trigerType != TaskOptions::AtStart && it->trigerType != TaskOptions::AtEnd) { time_t t = GetNextExportTime(*it); if (isExport) { if (t < nextExportTime) nextExportTime = t; } else { nextExportTime = t; isExport = true; initTask = init; } } else if (it->active && it->trigerType == TaskOptions::AtStart && init) { it->forceExecute = true; it->showMBAfterExecute = false; nextExportTime = now; isExport = true; initTask = true; } } return isExport; } static void CALLBACK DoTaskFinishInMainAPCFunc(ULONG_PTR dwParam) { wchar_t *item = (wchar_t*)dwParam; MessageBox(nullptr, item, TranslateT("Task finished"), MB_OK | MB_ICONINFORMATION); delete[] item; } bool ExecuteCurrentTask(time_t now) { TaskOptions to; bool isExport = false; { mir_cslock lck(Options::instance->criticalSection); for (auto it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it) { if (it->forceExecute) { it->lastExport = time(nullptr); Options::instance->SaveTaskTime(*it); to = *it; isExport = true; break; } else if (it->active && it->trigerType != TaskOptions::AtStart && it->trigerType != TaskOptions::AtEnd) { time_t t = GetNextExportTime(*it); if (t <= now) { it->lastExport = time(nullptr); Options::instance->SaveTaskTime(*it); to = *it; isExport = true; break; } } } } if (isExport) { bool error = DoTask(to); if (to.forceExecute) { { mir_cslock lck(Options::instance->criticalSection); for (auto it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it) { if (it->taskName == to.taskName) { it->forceExecute = false; it->showMBAfterExecute = false; break; } } } if (to.showMBAfterExecute) { size_t size = to.taskName.size() + 1024; wchar_t *name = new wchar_t[size]; if (error) mir_snwprintf(name, size, TranslateT("Task '%s' execution failed"), to.taskName.c_str()); else mir_snwprintf(name, size, TranslateT("Task '%s' finished successfully"), to.taskName.c_str()); QueueUserAPC(DoTaskFinishInMainAPCFunc, g_hMainThread, (ULONG_PTR)name); } } } return GetNextExportTime(false, now); } void GetZipFileTime(const wchar_t *file, uLong *dt) { FILETIME ftLocal; WIN32_FIND_DATA ff32; HANDLE hFind = FindFirstFile(file, &ff32); if (hFind != INVALID_HANDLE_VALUE) { FileTimeToLocalFileTime(&(ff32.ftLastWriteTime), &ftLocal); FileTimeToDosDateTime(&ftLocal, ((LPWORD)dt) + 1, ((LPWORD)dt) + 0); FindClose(hFind); } } /* calculate the CRC32 of a file, because to encrypt a file, we need known the CRC32 of the file before */ bool GetFileCrc(const wchar_t *filenameinzip, unsigned char *buf, unsigned long, unsigned long *result_crc) { unsigned long calculate_crc = 0; bool error = true; HANDLE hFile = CreateFile(filenameinzip, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile != INVALID_HANDLE_VALUE) { DWORD readed; do { if (!ReadFile(hFile, buf, 1024, &readed, nullptr)) { error = false; break; } if (readed > 0) calculate_crc = crc32(calculate_crc, buf, readed); } while (readed > 0); CloseHandle(hFile); } else error = false; *result_crc = calculate_crc; return error; } bool ZipFiles(const std::wstring &dir, std::wstring zipFilePath, const std::string &password) { std::list files; std::map existingContacts; ListDirectory(dir, L"", files); bool error = false; if (files.size() > 0) { zlib_filefunc_def pzlib_filefunc_def; fill_win32_filefunc(&pzlib_filefunc_def); zipFilePath = GetFileName(zipFilePath, L"", existingContacts, true); zipFilePath = ReplaceExt(zipFilePath, L"zip"); zipFile zf = zipOpen2((LPCSTR)(LPTSTR)zipFilePath.c_str(), APPEND_STATUS_CREATE, nullptr, &pzlib_filefunc_def); if (zf != nullptr) { unsigned char buf[1024]; char bufF[MAX_PATH + 20]; while (files.size() > 0) { std::wstring zipDir = *files.begin(); std::wstring localDir = dir + L"\\" + zipDir; zip_fileinfo zi = { 0 }; GetZipFileTime(localDir.c_str(), &zi.dosDate); if (zipDir.size() > MAX_PATH + 19) { error = true; break; } BOOL badChar; WideCharToMultiByte(CP_OEMCP, WC_NO_BEST_FIT_CHARS, zipDir.c_str(), -1, bufF, MAX_PATH + 20, nullptr, &badChar); int flag = 0; if (badChar) { flag = 0x800; // UTF WideCharToMultiByte(CP_UTF8, 0, zipDir.c_str(), -1, bufF, MAX_PATH + 20, nullptr, nullptr); } unsigned long calculate_crc = 0; const char* passwordCh = nullptr; if (password.size() > 0) { if (!GetFileCrc(localDir.c_str(), buf, 1024, &calculate_crc)) { error = true; break; } passwordCh = password.c_str(); } int err = zipOpenNewFileInZip4_64(zf, bufF, &zi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, passwordCh, calculate_crc, 0, flag, 0); if (err == ZIP_OK) { HANDLE hFile = CreateFile(localDir.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); if (hFile != INVALID_HANDLE_VALUE) { DWORD readed; do { err = ZIP_OK; if (!ReadFile(hFile, buf, 1024, &readed, nullptr)) { error = true; break; } if (readed > 0) err = zipWriteInFileInZip(zf, buf, readed); } while ((err == ZIP_OK) && (readed > 0)); CloseHandle(hFile); } if (zipCloseFileInZip(zf) != ZIP_OK) { error = true; break; } } else { error = true; break; } files.pop_front(); } zipClose(zf, nullptr); } else error = true; } DeleteDirectory(dir.c_str()); return error; } bool UnzipFiles(const std::wstring &dir, std::wstring &zipFilePath, const std::string &password) { bool error = false; zlib_filefunc_def pzlib_filefunc_def; fill_win32_filefunc(&pzlib_filefunc_def); std::wstring fileNameInZip; std::map existingContacts; zipFilePath = GetFileName(zipFilePath, L"", existingContacts, true); zipFilePath = ReplaceExt(zipFilePath, L"zip"); unzFile zf = unzOpen2((LPCSTR)(LPTSTR)zipFilePath.c_str(), &pzlib_filefunc_def); if (zf == nullptr) return true; char buf[8192]; char bufF[MAX_PATH + 20]; unz_file_info file_info; do { int err = unzGetCurrentFileInfo(zf, &file_info, bufF, MAX_PATH + 20, buf, 8192, nullptr, 0); if (err == UNZ_OK) { UINT cp = CP_OEMCP; if (file_info.flag & 0x800)// UTF cp = CP_UTF8; // Get Unicode file name for InfoZip style archives, otherwise assume PKZip/WinZip style if (file_info.size_file_extra) { char *p = buf; unsigned long size = min(file_info.size_file_extra, 8192); while (size > 0) { unsigned short id = *(unsigned short*)p; unsigned len = *(unsigned short*)(p + 2); if (size < (len + 4)) break; if (id == 0x7075 && len > 5 && (len - 5) < MAX_PATH + 20 && *(p + 4) == 1) { memcpy(bufF, p + 9, len - 5); bufF[len - 5] = 0; cp = CP_UTF8; break; } size -= len + 4; p += len + 4; } } int sizeC = (int)mir_strlen(bufF); int sizeW = MultiByteToWideChar(cp, 0, bufF, sizeC, nullptr, 0); fileNameInZip.resize(sizeW); MultiByteToWideChar(cp, 0, bufF, sizeC, (wchar_t*)fileNameInZip.c_str(), sizeW); fileNameInZip = dir + L"\\" + fileNameInZip; for (size_t i = 0; i < fileNameInZip.length(); ++i) if (fileNameInZip[i] == L'/') fileNameInZip[i] = L'\\'; if (file_info.external_fa & FILE_ATTRIBUTE_DIRECTORY) CreatePath(fileNameInZip.c_str()); else { const char* passwordCh = nullptr; if (password.size() > 0) passwordCh = password.c_str(); err = unzOpenCurrentFilePassword(zf, passwordCh); if (err == UNZ_OK) { CreatePath(GetDirectoryName(fileNameInZip).c_str()); HANDLE hFile = CreateFile(fileNameInZip.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, 0, nullptr); if (hFile != INVALID_HANDLE_VALUE) { DWORD writed; for (;;) { err = unzReadCurrentFile(zf, buf, 8192); if (err <= 0) break; if (!WriteFile(hFile, buf, err, &writed, FALSE)) { err = -1; break; } } CloseHandle(hFile); if (err < 0) { error = true; break; } } else { unzCloseCurrentFile(zf); error = true; break; } if (unzCloseCurrentFile(zf) != ZIP_OK) { error = true; break; } } else { error = true; break; } } } else { error = true; break; } } while (unzGoToNextFile(zf) == UNZ_OK); unzClose(zf); return error; } bool FtpFiles(const std::wstring& dir, const std::wstring& filePath, const std::wstring& ftpName) { std::list files; std::map existingContacts; ListDirectory(dir, L"\\", files); if (files.size() > 0) { std::wofstream stream((dir + L"\\script.sc").c_str()); if (stream.is_open()) { std::wstring ftpDir = GetDirectoryName(filePath); ftpDir = GetFileName(ftpDir, L"", existingContacts, false); stream << "option batch continue\noption confirm off\nopen \"" << ftpName << "\"\noption transfer binary\n"; std::wstring lastCD; size_t filSize = files.size(); while (files.size() > 0) { std::wstring localDir = *files.begin(); std::wstring currentCD = ftpDir + GetDirectoryName(ReplaceStr(localDir, L'\\', L'/')); if (currentCD != lastCD) { if (!currentCD.empty() && currentCD != L"/") stream << "mkdir \"" << currentCD << "\"\n"; stream << "cd \"" << currentCD << "\"\n"; lastCD = currentCD; } std::wstring name = GetName(localDir); stream << "call MDTM " << name << "\n"; stream << "put \"." << localDir << "\"\n"; stream << "call MDTM " << name << "\n"; files.pop_front(); } stream.close(); std::wstring &log = Options::instance->ftpLogPath; CreateDirectory(GetDirectoryName(log).c_str(), nullptr); DeleteFile(log.c_str()); wchar_t cmdLine[MAX_PATH]; mir_snwprintf(cmdLine, L"\"%s\" /nointeractiveinput /log=\"%s\" /script=script.sc", Options::instance->ftpExePath.c_str(), log.c_str()); STARTUPINFO startupInfo = { 0 }; startupInfo.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION processInfo; if (CreateProcess(nullptr, cmdLine, nullptr, nullptr, FALSE, 0, nullptr, dir.c_str(), &startupInfo, &processInfo)) { WaitForSingleObject(processInfo.hProcess, INFINITE); CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); if (log.empty()) return false; std::wifstream logStream(log.c_str()); if (logStream.is_open()) { bool isInMDTM = false; std::list dates; while (!logStream.eof()) { std::wstring lineStr; std::getline(logStream, lineStr); if (lineStr.length() > 1) { if (lineStr[0] == L'>') { if (isInMDTM) { if (lineStr.find(L"Script:") < lineStr.length()) { dates.push_back(L""); isInMDTM = false; } } if (lineStr.find(L"Script: call MDTM") < lineStr.length()) isInMDTM = true; } else if (isInMDTM && lineStr[0] == L'<') { size_t ss = lineStr.find(L"Script: 213 "); if (ss < lineStr.length()) { ss += 12; if (ss < lineStr.length()) { lineStr = lineStr.substr(ss); if (lineStr.size() == 14) { dates.push_back(lineStr); isInMDTM = false; } } } } } } if (dates.size() > 0 && dates.size() == filSize * 2) { for (std::list::const_iterator it = dates.begin(); it != dates.end(); ++it) { std::wstring date1 = *it++; if (it->empty() || date1 == *it) return true; } return false; } } } } } return true; } bool FtpGetFiles(const std::wstring& dir, const std::list& files, const std::wstring& ftpName) { std::wstring script = dir + L"\\script.sc"; std::wofstream stream(script.c_str()); if (stream.is_open()) { stream << "option batch continue\noption confirm off\nopen \"" << ftpName << "\"\noption transfer binary\n"; std::wstring lastCD; std::list localFiles; for (std::list::const_iterator it = files.begin(); it != files.end(); ++it) { std::wstring fileName = GetName(*it); localFiles.push_back(dir + L"\\" + fileName); std::wstring currentCD = GetDirectoryName(*it); if (currentCD != lastCD) { stream << "cd \"" << currentCD << "\"\n"; lastCD = currentCD; } stream << "get \"" << fileName << "\"\n"; } stream.close(); std::wstring &log = Options::instance->ftpLogPath; CreateDirectory(GetDirectoryName(log).c_str(), nullptr); DeleteFile(log.c_str()); wchar_t cmdLine[MAX_PATH]; mir_snwprintf(cmdLine, L"\"%s\" /nointeractiveinput /log=\"%s\" /script=script.sc", Options::instance->ftpExePath.c_str(), log.c_str()); STARTUPINFO startupInfo = { 0 }; PROCESS_INFORMATION processInfo; startupInfo.cb = sizeof(STARTUPINFO); if (CreateProcess(nullptr, cmdLine, nullptr, nullptr, FALSE, 0, nullptr, dir.c_str(), &startupInfo, &processInfo)) { WaitForSingleObject(processInfo.hProcess, INFINITE); CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); } DeleteFile(script.c_str()); for (std::list::const_iterator it = localFiles.begin(); it != localFiles.end(); ++it) { DWORD atr = GetFileAttributes(it->c_str()); if (atr == INVALID_FILE_ATTRIBUTES || atr & FILE_ATTRIBUTE_DIRECTORY) return true; } return false; } return true; } void CreatePath(const wchar_t *szDir) { if (!szDir) return; DWORD dwAttributes; wchar_t *pszLastBackslash, szTestDir[MAX_PATH]; mir_wstrncpy(szTestDir, szDir, _countof(szTestDir)); if ((dwAttributes = GetFileAttributes(szTestDir)) != INVALID_FILE_ATTRIBUTES && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)) return; pszLastBackslash = wcsrchr(szTestDir, '\\'); if (pszLastBackslash == nullptr) return; *pszLastBackslash = '\0'; CreatePath(szTestDir); *pszLastBackslash = '\\'; CreateDirectory(szTestDir, nullptr); } INT_PTR ExecuteTaskService(WPARAM wParam, LPARAM) { { mir_cslock lck(Options::instance->criticalSection); int taskNr = (int)wParam; if (taskNr < 0 || taskNr >= (int)Options::instance->taskOptions.size()) return FALSE; Options::instance->taskOptions[taskNr].forceExecute = true; Options::instance->taskOptions[taskNr].showMBAfterExecute = true; } StartThread(false); return TRUE; } void DoError(const TaskOptions& to, const std::wstring _error) { wchar_t msg[256]; mir_snwprintf(msg, TranslateT("Task '%s' execution failed:"), to.taskName.c_str()); if (Options::instance->schedulerHistoryAlerts) { std::wstring error = msg; error += L"\n"; error += _error; DBEVENTINFO dbei = {}; dbei.szModule = MODULE; dbei.flags = DBEF_UTF | DBEF_READ; dbei.timestamp = time(nullptr); // For now I do not convert event data from string to blob, and event type must be message to handle it properly dbei.eventType = EVENTTYPE_MESSAGE; int len = (int)error.length() + 1; dbei.cbBlob = WideCharToMultiByte(CP_UTF8, 0, error.c_str(), len, nullptr, 0, nullptr, nullptr); char* buf = new char[dbei.cbBlob]; dbei.cbBlob = WideCharToMultiByte(CP_UTF8, 0, error.c_str(), len, buf, dbei.cbBlob, nullptr, nullptr); dbei.pBlob = (PBYTE)buf; db_event_add(NULL, &dbei); } if (Options::instance->schedulerAlerts) { if (Miranda_IsTerminated()) return; if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) ShowClassPopupT(MODULE, msg, (wchar_t*)_error.c_str()); else if (ServiceExists(MS_POPUP_ADDPOPUPT)) { POPUPDATAT ppd = { 0 }; ppd.lchIcon = Skin_LoadIcon(SKINICON_OTHER_HISTORY); wcscpy_s(ppd.lptzContactName, msg); wcscpy_s(ppd.lptzText, _error.c_str()); CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, 0); } } }