Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
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;
}
Пример #4
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
}
Пример #5
0
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);
}
Пример #6
0
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);
	}
}
Пример #7
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;
}
Пример #8
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());
	}
}
Пример #9
0
// 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
}
Пример #10
0
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());
		}
	}
}