/* 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 = NULL, std::wstring* err = NULL, std::wstring* errDescr = NULL); 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 = NULL; 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 TCHAR *file, uLong *dt); std::wstring ReplaceExt(const std::wstring& file, const TCHAR* 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 TCHAR *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 = LoadSkinnedIcon(SKINICON_OTHER_HISTORY); test.iSeconds = 10; test.ptszDescription = 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 != NULL) *err = TranslateT("Name"); return false; } if (top != NULL) { for(std::list::iterator it = top->begin(); it != top->end(); ++it) { if (it->taskName == to.taskName) { if (err != NULL) *err = TranslateT("Name"); return false; } } } if (!to.isSystem && to.contacts.size() == 0) { if (err != NULL) *err = TranslateT("Contacts"); if (errDescr != NULL) *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 != NULL) *err = TranslateT("Filter"); return false; } to.filterId = filter; } else if (to.filterId < 0) { if (err != NULL) *err = TranslateT("Filter"); return false; } } if (to.type == TaskOptions::Delete) { return true; } if (!Options::FTPAvail() && to.useFtp) { if (err != NULL) *err = TranslateT("Upload to FTP"); return false; } if (to.filePath.empty()) { if (err != NULL) *err = TranslateT("Path to output file"); return false; } if (to.useFtp && to.ftpName.empty()) { if (err != NULL) *err = TranslateT("Session name"); if (errDescr != NULL) *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(_T('\\')) < to.filePath.length() || to.filePath.find(_T(':')) < to.filePath.length() || to.filePath[0] != L'/')) { if (err != NULL) *err = TranslateT("Path to file"); if (errDescr != NULL) *errDescr = TranslateT("FTP path must contains '/' instead '\\' and starts from '/'."); return false; } if (isImportTask && to.filePath.find(_T("")) < to.filePath.length()) { if (err != NULL) *err = TranslateT("Path to file"); if (errDescr != NULL) *errDescr = TranslateT("FTP path cannot contain in import task."); return false; } if (!isImportTask && (to.exportType < IExport::RichHtml || to.exportType > IExport::Dat)) { if (err != NULL) *err = TranslateT("Export to"); return false; } if (isImportTask && (to.importType < IImport::Binary || to.importType > IImport::Dat)) { if (err != NULL) *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 != NULL) *err = TranslateT("Time"); return false; } if (to.trigerType == TaskOptions::Weekly && (to.dayOfWeek < 0 || to.dayOfWeek >= 7)) { if (err != NULL) *err = TranslateT("Day of week"); return false; } if (to.trigerType == TaskOptions::Monthly && (to.dayOfMonth <= 0 || to.dayOfMonth >= 32)) { if (err != NULL) *err = TranslateT("Day"); return false; } if ((to.trigerType == TaskOptions::DeltaMin || to.trigerType == TaskOptions::DeltaHour) && (to.deltaTime < 0 || to.deltaTime >= 10000)) { if (err != NULL) *err = TranslateT("Delta time"); return false; } return true; } static void CALLBACK DoRebuildEventsInMainAPCFunc(ULONG_PTR dwParam) { HANDLE* contacts = (HANDLE*) 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, NULL, &err, &errDescr)) { TCHAR msg[256]; if (err.empty()) _tcscpy_s(msg, TranslateT("Some value is invalid")); else if (errDescr.empty()) { mir_sntprintf(msg, SIZEOF(msg), TranslateT("Invalid '%s' value."), err.c_str()); } else { mir_sntprintf(msg, SIZEOF(msg), TranslateT("Invalid '%s' value.\n%s"), err.c_str(), errDescr.c_str()); } DoError(to, msg); return true; } DWORD now = time(NULL); 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(NULL, 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(NULL, 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(NULL, NULL, 1); std::wstring filePath = to.filePath; std::wstring dir; std::list files; std::vector contacts; if (to.useFtp || to.compress) { std::map existingContacts; TCHAR 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(_T('.')); if (pos < dir.length()) { dir = dir.substr(0, pos); } DeleteDirectory(dir.c_str()); CreateDirectory(dir.c_str(), NULL); } const TCHAR* 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.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.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.hContact = contacts[ret]; if (to.type == TaskOptions::Import) { EventList::AddImporter(mExp.hContact, to.importType, *it); contactList.push_back(mExp.hContact); } else { std::vector messages; if (mExp.Import(to.importType, messages, NULL)) { mExp.MargeMessages(messages); contactList.push_back(mExp.hContact); } } } else if (ret != -1) { if (!errorStr.empty()) { errorStr += L"\n"; } TCHAR msg[1024]; mir_sntprintf(msg, SIZEOF(msg), TranslateT("Incorrect file format: %s."), GetName(*it).c_str()); errorStr += msg; } else { if (!errorStr.empty()) { errorStr += L"\n"; } TCHAR msg[1024]; mir_sntprintf(msg, SIZEOF(msg), TranslateT("Unknown contact in file: %s."), GetName(*it).c_str()); errorStr += msg; } } if (contactList.size() > 0) { HANDLE* contacts = new HANDLE[contactList.size() + 1]; contacts[0] = (HANDLE)contactList.size(); int i = 1; for(std::list::iterator it = contactList.begin(); it != contactList.end(); ++it) { contacts[i++] = *it; } QueueUserAPC(DoRebuildEventsInMainAPCFunc, g_hMainThread, (ULONG_PTR) contacts); } } 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(), NULL); } } else { filePath = GetName(filePath); TCHAR 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(_T('.')); if (pos < dir.length()) { dir = dir.substr(0, pos); } DeleteDirectory(dir.c_str()); CreateDirectory(dir.c_str(), NULL); filePath = dir + L"\\" + filePath; } if (to.isSystem) { ExportManager *exp = new ExportManager(NULL, NULL, to.filterId); exp->SetAutoExport(GetFileName(filePath, exp->GetContactName(), existingContacts, true), t, now); exp->useImportedMessages = to.exportImported; if (!exp->Export(to.exportType)) { error = true; if (!errorStr.empty()) { errorStr += L"\n"; } TCHAR msg[1024]; mir_sntprintf(msg, SIZEOF(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(NULL, to.contacts[i], to.filterId); exp->SetAutoExport(GetFileName(filePath, exp->GetContactName(), existingContacts, true), t, now); exp->useImportedMessages = to.exportImported; if (!exp->Export(to.exportType)) { error = true; if (!errorStr.empty()) { errorStr += L"\n"; } TCHAR msg[1024]; mir_sntprintf(msg, SIZEOF(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(), NULL); } } else { zipFilePath = GetName(zipFilePath); TCHAR 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(), NULL); 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(_T("")); if (replaceContact && pos < baseName.length()) { str = baseName.substr(0, pos); std::wstring baseName1 = contactName; if (!baseName1.empty()) { std::wstring name = baseName1; int i = 0; TCHAR buf[32]; std::map::iterator it = existingContacts.find(name); while(it != existingContacts.end()) { _itot_s(++i, buf, 10); name = baseName1 + buf; it = existingContacts.find(name); } str += name; existingContacts[name] = true; } str += baseName.substr(pos + 9); } pos = str.find(_T("")); if (pos < str.length()) { TCHAR time[256]; SYSTEMTIME st; GetLocalTime(&st); mir_sntprintf(time, SIZEOF(time), _T("%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 + _T("*")).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 + _T("\\"), 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) { switch(to.trigerType) { case TaskOptions::Daily: { tm t; localtime_s(&t, &to.lastExport); t.tm_hour = to.dayTime/60; t.tm_min = to.dayTime%60; t.tm_sec = 0; time_t newTime = mktime(&t); if (newTime <= to.lastExport) { newTime += 60 * 60 * 24; } return newTime; } case TaskOptions::Weekly: { tm t; 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; time_t 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: { tm t; localtime_s(&t, &to.lastExport); t.tm_hour = to.dayTime/60; t.tm_min = to.dayTime%60; t.tm_sec = 0; time_t newTime = mktime(&t); int lastM = t.tm_mon; int lastD; while(to.dayOfMonth != t.tm_mday || newTime <= to.lastExport) { 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(NULL); 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(NULL)); if (isExport) { finishThread = false; hThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); hThread = mir_forkthread(SchedulerThreadFunc, NULL); } } void StopThread() { if (hThread == NULL) return; finishThread = true; SetEvent(hThreadEvent); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThreadEvent); hThread = NULL; hThreadEvent = NULL; } bool GetNextExportTime(bool init, time_t now) { EnterCriticalSection(&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; } } LeaveCriticalSection(&Options::instance->criticalSection); return isExport; } static void CALLBACK DoTaskFinishInMainAPCFunc(ULONG_PTR dwParam) { TCHAR *item = (TCHAR*) dwParam; MessageBox(NULL, item, TranslateT("Task finished"), MB_OK | MB_ICONINFORMATION); delete[] item; } bool ExecuteCurrentTask(time_t now) { EnterCriticalSection(&Options::instance->criticalSection); TaskOptions to; bool isExport = false; for(std::vector::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it) { if (it->forceExecute) { it->lastExport = time(NULL); 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(NULL); Options::instance->SaveTaskTime(*it); to = *it; isExport = true; break; } } } LeaveCriticalSection(&Options::instance->criticalSection); if (isExport) { bool error = DoTask(to); if (to.forceExecute) { EnterCriticalSection(&Options::instance->criticalSection); for(std::vector::iterator it = Options::instance->taskOptions.begin(); it != Options::instance->taskOptions.end(); ++it) { if (it->taskName == to.taskName) { it->forceExecute = false; it->showMBAfterExecute = false; break; } } LeaveCriticalSection(&Options::instance->criticalSection); if (to.showMBAfterExecute) { size_t size = to.taskName.size() + 1024; TCHAR* name = new TCHAR[size]; if (error) { mir_sntprintf(name, size, TranslateT("Task '%s' execution failed"), to.taskName.c_str()); } else { mir_sntprintf(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 TCHAR *file, uLong *dt) { FILETIME ftLocal; HANDLE hFind; WIN32_FIND_DATA ff32; 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 TCHAR* filenameinzip, unsigned char* buf, unsigned long size_buf, unsigned long* result_crc) { unsigned long calculate_crc = 0; bool error = true; HANDLE hFile = CreateFile(filenameinzip, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD readed; do { if (!ReadFile(hFile, buf, 1024, &readed, NULL)) { 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, NULL, &pzlib_filefunc_def); if (zf != NULL) { 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, NULL, &badChar); int flag = 0; if (badChar) { flag = 0x800; // UTF WideCharToMultiByte(CP_UTF8, 0, zipDir.c_str(), -1, bufF, MAX_PATH + 20, NULL, NULL); } unsigned long calculate_crc = 0; const char* passwordCh = NULL; 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, NULL, 0, NULL, 0, NULL, 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, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD readed; do { err = ZIP_OK; if (!ReadFile(hFile, buf, 1024, &readed, NULL)) { 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, NULL); } else { error = true; } } DeleteDirectory(dir.c_str()); return error; } bool UnzipFiles(const std::wstring& dir, std::wstring& zipFilePath, const std::string& password) { std::list files; 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 != NULL) { 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, NULL, 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)strlen(bufF); int sizeW = MultiByteToWideChar(cp, 0, bufF, sizeC, NULL, 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 = NULL; 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, NULL, CREATE_ALWAYS, 0, NULL); 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); } else { error = true; } 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 + _T("\\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(), NULL); DeleteFile(log.c_str()); TCHAR cmdLine[MAX_PATH]; mir_sntprintf(cmdLine, SIZEOF(cmdLine), _T("\"%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(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, 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::map existingContacts; std::wstring script = dir + _T("\\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(), NULL); DeleteFile(log.c_str()); TCHAR cmdLine[MAX_PATH]; mir_sntprintf(cmdLine, SIZEOF(cmdLine), _T("\"%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(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, 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 TCHAR *szDir) { if (!szDir) return; DWORD dwAttributes; TCHAR *pszLastBackslash, szTestDir[ MAX_PATH ]; lstrcpyn( szTestDir, szDir, SIZEOF( szTestDir )); if (( dwAttributes = GetFileAttributes( szTestDir )) != INVALID_FILE_ATTRIBUTES && ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY )) return; pszLastBackslash = _tcsrchr( szTestDir, '\\' ); if ( pszLastBackslash == NULL ) return; *pszLastBackslash = '\0'; CreatePath( szTestDir ); *pszLastBackslash = '\\'; CreateDirectory( szTestDir, NULL ); } INT_PTR ExecuteTaskService(WPARAM wParam, LPARAM lParam) { EnterCriticalSection(&Options::instance->criticalSection); int taskNr = (int)wParam; if (taskNr < 0 || taskNr >= (int)Options::instance->taskOptions.size()) { LeaveCriticalSection(&Options::instance->criticalSection); return FALSE; } Options::instance->taskOptions[taskNr].forceExecute = true; Options::instance->taskOptions[taskNr].showMBAfterExecute = true; LeaveCriticalSection(&Options::instance->criticalSection); StartThread(false); return TRUE; } void DoError(const TaskOptions& to, const std::wstring _error) { TCHAR msg[256]; mir_sntprintf(msg, SIZEOF(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 = { sizeof(DBEVENTINFO) }; dbei.szModule = MODULE; dbei.flags = DBEF_UTF | DBEF_READ; dbei.timestamp = time(NULL); // 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, NULL, 0, NULL, NULL); char* buf = new char[dbei.cbBlob]; dbei.cbBlob = WideCharToMultiByte(CP_UTF8, 0, error.c_str(), len, buf, dbei.cbBlob, NULL, NULL); dbei.pBlob = (PBYTE)buf; db_event_add(NULL, &dbei); } if (Options::instance->schedulerAlerts) { if (CallService(MS_SYSTEM_TERMINATED, 0, 0)) 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 = LoadSkinnedIcon(SKINICON_OTHER_HISTORY); _tcscpy_s(ppd.lptzContactName, msg); _tcscpy_s(ppd.lptzText, _error.c_str()); CallService(MS_POPUP_ADDPOPUPT, (WPARAM)&ppd, 0); } } }