static PyObject *Rainmeter_RmExecute(RainmeterObject *self, PyObject *args) { PyObject *command; PyArg_ParseTuple(args, "U", &command); wchar_t *commandStr = PyUnicode_AsWideCharString(command, NULL); RmExecute(RmGetSkin(self->rm), commandStr); PyMem_Free(commandStr); Py_INCREF(Py_None); return Py_None; }
DWORD WINAPI NetworkThreadProc(void* pParam) { // NOTE: Do not use CRT functions (since thread was created by CreateThread())! MeasureData* measure = (MeasureData*)pParam; const DWORD bufferSize = sizeof(ICMP_ECHO_REPLY) + 32; BYTE buffer[bufferSize]; double value = 0.0; HANDLE hIcmpFile = IcmpCreateFile(); if (hIcmpFile != INVALID_HANDLE_VALUE) { IcmpSendEcho(hIcmpFile, measure->destAddr, NULL, 0, NULL, buffer, bufferSize, measure->timeout); IcmpCloseHandle(hIcmpFile); ICMP_ECHO_REPLY* reply = (ICMP_ECHO_REPLY*)buffer; value = (reply->Status != IP_SUCCESS) ? measure->timeoutValue : reply->RoundTripTime; if (!measure->finishAction.empty()) { RmExecute(measure->skin, measure->finishAction.c_str()); } } HMODULE module = NULL; EnterCriticalSection(&g_CriticalSection); if (measure->threadActive) { measure->value = value; measure->threadActive = false; } else { // Thread is not attached to an existing measure any longer, so delete // unreferenced data. delete measure; DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; GetModuleHandleEx(flags, (LPCWSTR)DllMain, &module); } LeaveCriticalSection(&g_CriticalSection); if (module) { // Decrement the ref count and possibly unload the module if this is // the last instance. FreeLibraryAndExitThread(module, 0); } return 0; }
PLUGIN_EXPORT double Update(void* data) { Measure* measure = (Measure*)data; Player* player = measure->player; player->UpdateData(); if (player->m_TrackChanged && !measure->trackChangeAction.empty()) RmExecute(RmGetSkin(measure->rm), measure->trackChangeAction.c_str()); if (measure->type == MEASURE_STATUS) return player->GetStatus(); return 0.0; }
// Fetches the data from the net and parses the page unsigned __stdcall NetworkThreadProc(void* pParam) { MeasureData* measure = (MeasureData*)pParam; DWORD dwSize = 0; RmLogF(measure->rm, LOG_DEBUG, L"WebParser: Fetching: %s", measure->url.c_str()); BYTE* data = DownloadUrl(measure->proxy.handle, measure->url, &dwSize, measure->forceReload); if (!data) { ShowError(measure->rm, L"Fetch error"); if (!measure->onConnectErrAction.empty()) { RmExecute(measure->skin, measure->onConnectErrAction.c_str()); } } else { if (measure->debug == 2) { // Dump to a file FILE* file = _wfopen(measure->debugFileLocation.c_str(), L"wb"); if (file) { fwrite(data, sizeof(BYTE), dwSize, file); fclose(file); } else { RmLog(measure->rm, LOG_ERROR, L"WebParser: Failed to dump debug data"); } } ParseData(measure, data, dwSize); free(data); } EnterCriticalSection(&g_CriticalSection); CloseHandle(measure->threadHandle); measure->threadHandle = 0; LeaveCriticalSection(&g_CriticalSection); return 0; // thread completed successfully }
PLUGIN_EXPORT double Update(void* data) { FbxMeasureConf* measureConfig = (FbxMeasureConf*)data; RmLogW(LOG_DEBUG, L"RainFbx.dll: [" + measureConfig->section + L"] Update"); // No measure for [FbxAPIConf] (parent), only for [FbxMeasureConf] (child) */ FbxAPIConf* fbxAPIConf = measureConfig->fbxAPIConf; if (!fbxAPIConf) return 0.0; // Write AppToken in ini file if successfully login if (writeAppToken && (fbx.global_status == LOGIN)) { wstring cmd = L"!WriteKeyValue " + fbxAPIConf->section + L" AppToken \"" + fbx.app_token + L"\""; RmLogW(LOG_DEBUG, L"RainFbx.dll: Write app_token value : " + cmd); RmExecute(fbxAPIConf->skin, _wcsdup(cmd.c_str())); writeAppToken = false; } // Update and execute next call in Freebox API workflow fbx.updateStatus(); fbx.executeNextCall(measureConfig->db, measureConfig->field); // Return numeric value for tuple db::field return fbx.getNumericMeasure(measureConfig->db, measureConfig->field); }
void RunCommand(Measure* measure) { std::unique_lock<std::recursive_mutex> lock(measure->mutex); std::wstring command = measure->program + L" " + measure->parameter; std::wstring folder = measure->folder; WORD state = measure->state; int timeout = measure->timeout; OutputType type = measure->outputType; lock.unlock(); std::wstring result = L""; bool error = false; HANDLE read = INVALID_HANDLE_VALUE; HANDLE write = INVALID_HANDLE_VALUE; /* Instead of trying to keep track of the following handles, use an array to make cleanup easier. HANDLE hOutputReadTmp; 0 HANDLE hOutputWrite; 1 HANDLE hInputWriteTmp; 2 HANDLE hInputRead; 3 HANDLE hErrorWrite; 4 */ HANDLE loadHandles[5]; for (int i = 0; i < sizeof(loadHandles) / sizeof(loadHandles[0]); ++i) { loadHandles[i] = INVALID_HANDLE_VALUE; } SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; HANDLE hProc = GetCurrentProcess(); // Create pipe for stdin, stdout, stderr if (CreatePipe(&loadHandles[0], &loadHandles[1], &sa, 0) && DuplicateHandle(hProc, loadHandles[1], hProc, &loadHandles[4], 0, TRUE, DUPLICATE_SAME_ACCESS) && CreatePipe(&loadHandles[3], &loadHandles[2], &sa, 0) && DuplicateHandle(hProc, loadHandles[0], hProc, &read, 0, FALSE, DUPLICATE_SAME_ACCESS) && DuplicateHandle(hProc, loadHandles[2], hProc, &write, 0, FALSE, DUPLICATE_SAME_ACCESS)) { BYTE buffer[MAX_LINE_LENGTH + 3]; DWORD bytesRead = 0; DWORD totalBytes = 0; DWORD bytesLeft = 0; DWORD exit = 0; PROCESS_INFORMATION pi; SecureZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); STARTUPINFO si; SecureZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = state; si.hStdOutput = loadHandles[1]; si.hStdInput = loadHandles[3]; si.hStdError = loadHandles[4]; if (CreateProcess(NULL, &command[0], NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, NULL, &folder[0], &si, &pi)) { // Store values inside measure for the "Close" or "Kill" command lock.lock(); measure->hProc = pi.hProcess; measure->dwPID = pi.dwProcessId; lock.unlock(); // Send command DWORD written; WriteFile(write, &command[0], MAX_LINE_LENGTH, &written, NULL); std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); // Read output of program (if any) while(true) { auto ReadFileAndSetResult = [&]() -> void { ReadFile(read, buffer, MAX_LINE_LENGTH, &bytesRead, NULL); // Triple "null" the buffer in case an odd number bytes is // converted to a multi-byte string. buffer[bytesRead] = '\0'; buffer[bytesRead + 1] = '\0'; buffer[bytesRead + 2] = '\0'; switch (type) { case OUTPUTTYPE_ANSI: result += StringUtil::Widen((LPCSTR)buffer); break; case OUTPUTTYPE_UTF8: result += StringUtil::WidenUTF8((LPCSTR)buffer); break; default: case OUTPUTTYPE_UTF16: result += (LPCWSTR)buffer; break; } SecureZeroMemory(buffer, sizeof(buffer)); // clear the buffer }; // Wait for a signal from the process (or timeout). // This reduced CPU usage significantly. WaitForSingleObject(pi.hThread, 1); // Check if there is any data to to read PeekNamedPipe(read, buffer, MAX_LINE_LENGTH, &bytesRead, &totalBytes, &bytesLeft); if (bytesRead != 0) { if (totalBytes > MAX_LINE_LENGTH) { while (bytesRead >= MAX_LINE_LENGTH) { ReadFileAndSetResult(); } } else { ReadFileAndSetResult(); } } // If a timeout is defined, attempt to terminate program and detach it from the plugin if ((timeout >= 0 && std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now() - start).count() > timeout)) { if (!TerminateApp(pi.hProcess, pi.dwProcessId, (state == SW_HIDE))) { lock.lock(); measure->value = 105.0f; RmLogF(measure->rm, LOG_ERROR, err_Terminate, measure->program.c_str()); // Could not terminate process (very rare!) error = true; lock.unlock(); } break; } // Check to see if the program is still running GetExitCodeProcess(pi.hProcess, &exit); if (exit != STILL_ACTIVE) break; } // Close process handles CloseHandle(pi.hThread); CloseHandle(pi.hProcess); // Update values in case the "Close" or "Kill" command is called lock.lock(); measure->hProc = INVALID_HANDLE_VALUE; measure->dwPID = 0; lock.unlock(); } else { lock.lock(); measure->value = 103.0f; RmLogF(measure->rm, LOG_ERROR, err_Process, measure->program.c_str()); // Cannot start process error = true; lock.unlock(); } } else { lock.lock(); measure->value = 106.0f; RmLog(measure->rm, LOG_ERROR, err_CreatePipe); // Cannot create pipe error = true; lock.unlock(); } // Close handles for (int i = 0; i < sizeof(loadHandles) / sizeof(loadHandles[0]); ++i) { if (loadHandles[i] != INVALID_HANDLE_VALUE) { CloseHandle(loadHandles[i]); } } CloseHandle(write); CloseHandle(read); // Remove any carriage returns result.erase(std::remove(result.begin(), result.end(), L'\r'), result.end()); HMODULE module = nullptr; lock.lock(); if (measure->threadActive) { measure->result = result; measure->result.shrink_to_fit(); if (!measure->outputFile.empty()) { std::wstring encoding = L"w+"; switch (type) { case OUTPUTTYPE_UTF8: { encoding.append(L", ccs=UTF-8"); break; } case OUTPUTTYPE_UTF16: { encoding.append(L", ccs=UTF-16LE"); break; } } FILE* file; if (_wfopen_s(&file, measure->outputFile.c_str(), encoding.c_str()) == 0) { fputws(result.c_str(), file); } else { measure->value = 104.0f; RmLogF(measure->rm, LOG_ERROR, err_SaveFile, measure->outputFile.c_str()); // Cannot save file error = true; } if (file) { fclose(file); } } // If no errors from the thread, // set number value of measure to 1 to indicate "Success". if (!error) { measure->value = 1.0f; } measure->threadActive = false; lock.unlock(); if (!measure->finishAction.empty()) { RmExecute(measure->skin, measure->finishAction.c_str()); } return; } lock.unlock(); delete measure; DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; GetModuleHandleEx(flags, (LPCWSTR)DllMain, &module); if (module) { // Decrement the ref count and possibly unload the module if this is the last instance. FreeLibraryAndExitThread(module, 0); } }
PLUGIN_EXPORT double Update(void* data) { Measure* measure = (Measure*)data; ParentMeasure* parent = measure->parent; if (!parent) return 0.0; Player* player = parent->player; // Only allow parent measure to update if (parent->data == data) { player->UpdateMeasure(); // Execute TrackChangeAction= if necessary if (!parent->trackChangeAction.empty() && parent->trackCount != player->GetTrackCount()) { RmExecute(parent->skin, parent->trackChangeAction.c_str()); parent->trackCount = player->GetTrackCount(); } } switch (measure->type) { case MEASURE_DURATION: return player->GetDuration(); case MEASURE_POSITION: return player->GetPosition(); case MEASURE_PROGRESS: if (player->GetDuration()) { return (player->GetPosition() * 100) / player->GetDuration(); } return 0.0; case MEASURE_RATING: return player->GetRating(); case MEASURE_VOLUME: return player->GetVolume(); case MEASURE_STATE: return player->GetState(); case MEASURE_STATUS: return player->IsInitialized(); case MEASURE_SHUFFLE: return player->GetShuffle(); case MEASURE_REPEAT: return player->GetRepeat(); case MEASURE_NUMBER: return player->GetNumber(); case MEASURE_YEAR: return player->GetYear(); } return 0.0; }
void ParseData(MeasureData* measure, const BYTE* rawData, DWORD rawSize, bool utf16Data) { const int UTF16_CODEPAGE = 1200; if (measure->codepage == UTF16_CODEPAGE) { utf16Data = true; } const char* error; int erroffset; int ovector[OVECCOUNT]; int rc; bool doErrorAction = false; // Compile the regular expression in the first argument pcre16* re = pcre16_compile( (PCRE_SPTR16)measure->regExp.c_str(), PCRE_UTF16, &error, &erroffset, nullptr); if (re != nullptr) { // Compilation succeeded: match the subject in the second argument std::wstring buffer; auto data = (const WCHAR*)rawData; DWORD dataLength = rawSize / 2; if (!utf16Data) { buffer = StringUtil::Widen((LPCSTR)rawData, rawSize, measure->codepage); data = buffer.c_str(); dataLength = (DWORD)buffer.length(); } rc = pcre16_exec(re, nullptr, (PCRE_SPTR16)data, dataLength, 0, 0, ovector, OVECCOUNT); if (rc >= 0) { if (rc == 0) { // The output vector wasn't big enough RmLog(measure->rm, LOG_ERROR, L"WebParser: Too many substrings"); } else { if (measure->stringIndex < rc) { if (measure->debug != 0) { for (int i = 0; i < rc; ++i) { const WCHAR* match = data + ovector[2 * i]; const int matchLen = min(ovector[2 * i + 1] - ovector[2 * i], 256); RmLogF(measure->rm, LOG_DEBUG, L"WebParser: Index %2d: %.*s", i, matchLen, match); } } const WCHAR* match = data + ovector[2 * measure->stringIndex]; int matchLen = ovector[2 * measure->stringIndex + 1] - ovector[2 * measure->stringIndex]; EnterCriticalSection(&g_CriticalSection); measure->resultString.assign(match, matchLen); DecodeReferences(measure->resultString, measure->decodeCharacterReference); LeaveCriticalSection(&g_CriticalSection); } else { RmLog(measure->rm, LOG_WARNING, L"WebParser: Not enough substrings"); // Clear the old result EnterCriticalSection(&g_CriticalSection); measure->resultString.clear(); if (measure->download) { if (measure->downloadFile.empty()) // cache mode { if (!measure->downloadedFile.empty()) { // Delete old downloaded file DeleteFile(measure->downloadedFile.c_str()); } } measure->downloadedFile.clear(); } LeaveCriticalSection(&g_CriticalSection); } // Update the references std::vector<MeasureData*>::iterator i = g_Measures.begin(); std::wstring compareStr = L"["; compareStr += RmGetMeasureName(measure->rm); compareStr += L']'; for ( ; i != g_Measures.end(); ++i) { if (measure->skin == (*i)->skin && StringUtil::CaseInsensitiveFind((*i)->url, compareStr) != std::wstring::npos) { if ((*i)->stringIndex < rc) { const WCHAR* match = data + ovector[2 * (*i)->stringIndex]; int matchLen = ovector[2 * (*i)->stringIndex + 1] - ovector[2 * (*i)->stringIndex]; if (!(*i)->regExp.empty()) { // Change the index and parse the substring int index = (*i)->stringIndex; (*i)->stringIndex = (*i)->stringIndex2; ParseData((*i), (BYTE*)match, matchLen * 2, true); (*i)->stringIndex = index; } else { // Set the result EnterCriticalSection(&g_CriticalSection); // Substitude the [measure] with result (*i)->resultString = (*i)->url; (*i)->resultString.replace( StringUtil::CaseInsensitiveFind((*i)->resultString, compareStr), compareStr.size(), match, matchLen); DecodeReferences((*i)->resultString, (*i)->decodeCharacterReference); // Start download threads for the references if ((*i)->download) { // Start the download thread unsigned int id; HANDLE threadHandle = (HANDLE)_beginthreadex(nullptr, 0, NetworkDownloadThreadProc, (*i), 0, &id); if (threadHandle) { (*i)->dlThreadHandle = threadHandle; } } LeaveCriticalSection(&g_CriticalSection); } } else { RmLog((*i)->rm, LOG_WARNING, L"WebParser: Not enough substrings"); // Clear the old result EnterCriticalSection(&g_CriticalSection); (*i)->resultString.clear(); if ((*i)->download) { if ((*i)->downloadFile.empty()) // cache mode { if (!(*i)->downloadedFile.empty()) { // Delete old downloaded file DeleteFile((*i)->downloadedFile.c_str()); } } (*i)->downloadedFile.clear(); } LeaveCriticalSection(&g_CriticalSection); } } } } } else { // Matching failed: handle error cases RmLogF(measure->rm, LOG_ERROR, L"WebParser: RegExp matching error (%d)", rc); doErrorAction = true; EnterCriticalSection(&g_CriticalSection); measure->resultString = measure->errorString; // Update the references std::vector<MeasureData*>::iterator i = g_Measures.begin(); std::wstring compareStr = L"["; compareStr += RmGetMeasureName(measure->rm); compareStr += L']'; for ( ; i != g_Measures.end(); ++i) { if ((StringUtil::CaseInsensitiveFind((*i)->url, compareStr) != std::wstring::npos) && (measure->skin == (*i)->skin)) { (*i)->resultString = (*i)->errorString; } } LeaveCriticalSection(&g_CriticalSection); } // Release memory used for the compiled pattern pcre16_free(re); } else { // Compilation failed. RmLogF(measure->rm, LOG_ERROR, L"WebParser: RegExp error at offset %d: %S", erroffset, error); doErrorAction = true; } if (measure->download) { // Start the download thread unsigned int id; HANDLE threadHandle = (HANDLE)_beginthreadex(nullptr, 0, NetworkDownloadThreadProc, measure, 0, &id); if (threadHandle) { measure->dlThreadHandle = threadHandle; } } if (doErrorAction && !measure->onRegExpErrAction.empty()) { RmExecute(measure->skin, measure->onRegExpErrAction.c_str()); } else if (!measure->download && !measure->finishAction.empty()) { RmExecute(measure->skin, measure->finishAction.c_str()); } }
// Downloads file from the net unsigned __stdcall NetworkDownloadThreadProc(void* pParam) { MeasureData* measure = (MeasureData*)pParam; const bool download = !measure->downloadFile.empty(); bool ready = false; std::wstring url; if (measure->regExp.empty() && measure->resultString.empty()) { if (!measure->url.empty() && measure->url[0] != L'[') { url = measure->url; } } else { EnterCriticalSection(&g_CriticalSection); url = measure->resultString; LeaveCriticalSection(&g_CriticalSection); std::wstring::size_type pos = url.find(L':'); if (pos == std::wstring::npos && !url.empty()) // No protocol { // Add the base url to the string if (url[0] == L'/') { // Absolute path pos = measure->url.find(L'/', 7); // Assume "http://" (=7) if (pos != std::wstring::npos) { std::wstring path(measure->url.substr(0, pos)); url = path + url; } } else { // Relative path pos = measure->url.rfind(L'/'); if (pos != std::wstring::npos) { std::wstring path(measure->url.substr(0, pos + 1)); url = path + url; } } } } if (!url.empty()) { // Create the filename WCHAR buffer[MAX_PATH] = {0}; std::wstring fullpath, directory; if (download) // download mode { PathCanonicalize(buffer, measure->downloadFile.c_str()); std::wstring path = buffer; std::wstring::size_type pos = path.find_first_not_of(L'\\'); if (pos != std::wstring::npos) { path.erase(0, pos); } PathCanonicalize(buffer, measure->downloadFolder.c_str()); CreateDirectory(buffer, nullptr); // Make sure that the folder exists wcscat(buffer, path.c_str()); if (buffer[wcslen(buffer)-1] != L'\\') // path is a file { fullpath = buffer; PathRemoveFileSpec(buffer); } PathAddBackslash(buffer); } else // cache mode { GetTempPath(MAX_PATH, buffer); wcscat(buffer, L"Rainmeter-Cache\\"); // "%TEMP%\Rainmeter-Cache\" } CreateDirectory(buffer, nullptr); // Make sure that the folder exists directory = buffer; if (fullpath.empty()) { fullpath = directory; std::wstring::size_type pos2 = url.find_first_of(L"?#"); std::wstring::size_type pos1 = url.find_last_of(L'/', pos2); pos1 = (pos1 != std::wstring::npos) ? pos1 + 1 : 0; std::wstring name; if (pos2 != std::wstring::npos) { name.assign(url, pos1, pos2 - pos1); } else { name.assign(url, pos1, url.length() - pos1); } if (!name.empty()) { // Replace reserved characters to "_" pos1 = 0; while ((pos1 = name.find_first_of(L"\\/:*?\"<>|", pos1)) != std::wstring::npos) { name[pos1] = L'_'; } fullpath += name; } else { fullpath += L"index"; } } ready = true; if (download) // download mode { if (!PathFileExists(directory.c_str()) || !PathIsDirectory(directory.c_str())) { ready = false; RmLogF( measure->rm, LOG_ERROR, L"WebParser: Directory does not exist: %s", directory.c_str()); } else if (PathIsDirectory(fullpath.c_str())) { ready = false; RmLogF( measure->rm, LOG_ERROR, L"WebParser: Path is a directory, not a file: %s", fullpath.c_str()); } else if (PathFileExists(fullpath.c_str())) { DWORD attr = GetFileAttributes(fullpath.c_str()); if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_READONLY)) { ready = false; RmLogF( measure->rm, LOG_ERROR, L"WebParser: File is read-only: %s", fullpath.c_str()); } } } else // cache mode { EnterCriticalSection(&g_CriticalSection); if (PathFileExists(fullpath.c_str())) { std::wstring::size_type pos = fullpath.find_last_of(L'.'); std::wstring path, ext; if (pos != std::wstring::npos) { path.assign(fullpath, 0, pos); ext.assign(fullpath, pos, fullpath.length() - pos); } else { path = fullpath; } // Assign a serial number int i = 1; do { wsprintf(buffer, L"_%i", i++); fullpath = path; fullpath += buffer; if (!ext.empty()) { fullpath += ext; } } while (PathFileExists(fullpath.c_str())); } // Create empty file HANDLE hFile = CreateFile(fullpath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); LeaveCriticalSection(&g_CriticalSection); } if (ready) { // Delete IE cache before download if "SyncMode5" is not 3 (every visit to the page) { // Check "Temporary Internet Files" sync mode (SyncMode5) // Values: // Every visit to the page 3 // Every time you start Internet Explorer 2 // Automatically (default) 4 // Never 0 // http://support.microsoft.com/kb/263070/en HKEY hKey; LONG ret; DWORD mode; ret = RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hKey); if (ret == ERROR_SUCCESS) { DWORD size = sizeof(mode); ret = RegQueryValueEx(hKey, L"SyncMode5", nullptr, nullptr, (LPBYTE)&mode, &size); RegCloseKey(hKey); } if (ret != ERROR_SUCCESS || mode != 3) { std::wstring::size_type pos = url.find_first_of(L'#'); if (pos != std::wstring::npos) { DeleteUrlCacheEntry(url.substr(0, pos).c_str()); } else { DeleteUrlCacheEntry(url.c_str()); } } } RmLogF( measure->rm, LOG_DEBUG, L"WebParser: Downloading url '%s' to: %s", url.c_str(), fullpath.c_str()); HRESULT resultCoInitialize = CoInitialize(nullptr); // requires before calling URLDownloadToFile function // Download the file HRESULT result = URLDownloadToFile(nullptr, url.c_str(), fullpath.c_str(), 0, nullptr); if (result == S_OK) { EnterCriticalSection(&g_CriticalSection); if (!download) // cache mode { if (!measure->downloadedFile.empty()) { // Delete old downloaded file DeleteFile(measure->downloadedFile.c_str()); } } // Convert LFN to 8.3 filename if the path contains blank character if (fullpath.find_first_of(L' ') != std::wstring::npos) { DWORD size = GetShortPathName(fullpath.c_str(), buffer, MAX_PATH); if (size > 0 && size <= MAX_PATH) { fullpath = buffer; } } measure->downloadedFile = fullpath; LeaveCriticalSection(&g_CriticalSection); if (!measure->finishAction.empty()) { RmExecute(measure->skin, measure->finishAction.c_str()); } } else { ready = false; if (!download) // cache mode { // Delete empty file DeleteFile(fullpath.c_str()); } RmLogF( measure->rm, LOG_ERROR, L"WebParser: Download failed (res=0x%08X, COM=0x%08X): %s", result, resultCoInitialize, url.c_str()); if (!measure->onDownloadErrAction.empty()) { RmExecute(measure->skin, measure->onDownloadErrAction.c_str()); } } if (SUCCEEDED(resultCoInitialize)) { CoUninitialize(); } } else { RmLogF(measure->rm, LOG_ERROR, L"WebParser: Download failed: %s", url.c_str()); if (!measure->onDownloadErrAction.empty()) { RmExecute(measure->skin, measure->onDownloadErrAction.c_str()); } } } else { RmLog(measure->rm, LOG_ERROR, L"WebParser: Url is empty"); } if (!ready) // download failed { EnterCriticalSection(&g_CriticalSection); if (!download) // cache mode { if (!measure->downloadedFile.empty()) { // Delete old downloaded file DeleteFile(measure->downloadedFile.c_str()); } } // Clear old downloaded filename measure->downloadedFile.clear(); LeaveCriticalSection(&g_CriticalSection); } EnterCriticalSection(&g_CriticalSection); CloseHandle(measure->dlThreadHandle); measure->dlThreadHandle = 0; LeaveCriticalSection(&g_CriticalSection); return 0; // thread completed successfully }
void ParseData(MeasureData* measure, LPCSTR parseData, DWORD dwSize) { // Parse the value from the data pcre* re; const char* error; int erroffset; int ovector[OVECCOUNT]; int rc; int flags = PCRE_UTF8; if (measure->codepage == 0) { flags = 0; } // Compile the regular expression in the first argument re = pcre_compile( StringUtil::NarrowUTF8(measure->regExp).c_str(), // the pattern flags, // default options &error, // for error message &erroffset, // for error offset nullptr); // use default character tables if (re != nullptr) { // Compilation succeeded: match the subject in the second argument std::string utf8Data; if (measure->codepage == 1200) // 1200 = UTF-16LE { // Must convert the data to utf8 utf8Data = StringUtil::NarrowUTF8((LPCWSTR)parseData, dwSize / 2); parseData = utf8Data.c_str(); dwSize = (DWORD)utf8Data.length(); } else if (measure->codepage != CP_UTF8 && measure->codepage != 0) // 0 = CP_ACP { // Must convert the data to utf8 utf8Data = ConvertAsciiToUTF8(parseData, dwSize, measure->codepage); parseData = utf8Data.c_str(); dwSize = (DWORD)utf8Data.length(); } rc = pcre_exec( re, // the compiled pattern nullptr, // no extra data - we didn't study the pattern parseData, // the subject string dwSize, // the length of the subject 0, // start at offset 0 in the subject 0, // default options ovector, // output vector for substring information OVECCOUNT); // number of elements in the output vector if (rc >= 0) { if (rc == 0) { // The output vector wasn't big enough RmLog(measure->rm, LOG_ERROR, L"WebParser: Too many substrings"); } else { if (measure->stringIndex < rc) { if (measure->debug != 0) { for (int i = 0; i < rc; ++i) { const char* substring_start = parseData + ovector[2 * i]; int substring_length = ovector[2 * i + 1] - ovector[2 * i]; substring_length = min(substring_length, 256); const std::wstring value = StringUtil::WidenUTF8(substring_start, substring_length); RmLogF(measure->rm, LOG_DEBUG, L"WebParser: Index %2d: %s", i, value.c_str()); } } const char* substring_start = parseData + ovector[2 * measure->stringIndex]; int substring_length = ovector[2 * measure->stringIndex + 1] - ovector[2 * measure->stringIndex]; EnterCriticalSection(&g_CriticalSection); measure->resultString = StringUtil::WidenUTF8(substring_start, substring_length); DecodeReferences(measure->resultString, measure->decodeCharacterReference); LeaveCriticalSection(&g_CriticalSection); } else { RmLog(measure->rm, LOG_WARNING, L"WebParser: Not enough substrings"); // Clear the old result EnterCriticalSection(&g_CriticalSection); measure->resultString.clear(); if (measure->download) { if (measure->downloadFile.empty()) // cache mode { if (!measure->downloadedFile.empty()) { // Delete old downloaded file DeleteFile(measure->downloadedFile.c_str()); } } measure->downloadedFile.clear(); } LeaveCriticalSection(&g_CriticalSection); } // Update the references std::vector<MeasureData*>::iterator i = g_Measures.begin(); std::wstring compareStr = L"["; compareStr += RmGetMeasureName(measure->rm); compareStr += L']'; for ( ; i != g_Measures.end(); ++i) { if (measure->skin == (*i)->skin && (*i)->url.find(compareStr) != std::wstring::npos) { if ((*i)->stringIndex < rc) { const char* substring_start = parseData + ovector[2 * (*i)->stringIndex]; int substring_length = ovector[2 * (*i)->stringIndex + 1] - ovector[2 * (*i)->stringIndex]; if (!(*i)->regExp.empty()) { // Change the index and parse the substring int index = (*i)->stringIndex; (*i)->stringIndex = (*i)->stringIndex2; ParseData((*i), substring_start, substring_length); (*i)->stringIndex = index; } else { // Set the result EnterCriticalSection(&g_CriticalSection); // Substitude the [measure] with result std::wstring result = StringUtil::WidenUTF8(substring_start, substring_length); (*i)->resultString = (*i)->url; (*i)->resultString.replace((*i)->resultString.find(compareStr), compareStr.size(), result); DecodeReferences((*i)->resultString, (*i)->decodeCharacterReference); // Start download threads for the references if ((*i)->download) { // Start the download thread unsigned int id; HANDLE threadHandle = (HANDLE)_beginthreadex(nullptr, 0, NetworkDownloadThreadProc, (*i), 0, &id); if (threadHandle) { (*i)->dlThreadHandle = threadHandle; } } LeaveCriticalSection(&g_CriticalSection); } } else { RmLog((*i)->rm, LOG_WARNING, L"WebParser: Not enough substrings"); // Clear the old result EnterCriticalSection(&g_CriticalSection); (*i)->resultString.clear(); if ((*i)->download) { if ((*i)->downloadFile.empty()) // cache mode { if (!(*i)->downloadedFile.empty()) { // Delete old downloaded file DeleteFile((*i)->downloadedFile.c_str()); } } (*i)->downloadedFile.clear(); } LeaveCriticalSection(&g_CriticalSection); } } } } } else { // Matching failed: handle error cases RmLogF(measure->rm, LOG_ERROR, L"WebParser: RegExp matching error (%d)", rc); EnterCriticalSection(&g_CriticalSection); measure->resultString = measure->errorString; // Update the references std::vector<MeasureData*>::iterator i = g_Measures.begin(); std::wstring compareStr = L"["; compareStr += RmGetMeasureName(measure->rm); compareStr += L']'; for ( ; i != g_Measures.end(); ++i) { if (((*i)->url.find(compareStr) != std::wstring::npos) && (measure->skin == (*i)->skin)) { (*i)->resultString = (*i)->errorString; } } LeaveCriticalSection(&g_CriticalSection); } // Release memory used for the compiled pattern pcre_free(re); } else { // Compilation failed. RmLogF(measure->rm, LOG_ERROR, L"WebParser: RegExp error at offset %d: %S", erroffset, error); } if (measure->download) { // Start the download thread unsigned int id; HANDLE threadHandle = (HANDLE)_beginthreadex(nullptr, 0, NetworkDownloadThreadProc, measure, 0, &id); if (threadHandle) { measure->dlThreadHandle = threadHandle; } } else { if (!measure->finishAction.empty()) { RmExecute(measure->skin, measure->finishAction.c_str()); } } }