[WebParser]

Solved problems:

    * Multiple files of the same file name cannot be downloaded. (Issue 132)
    * The file including the character that cannot be used for the filename cannot be downloaded. (Issue 13)
    * As for the same file downloaded from the same URL, cache is not cleared and the file is not updated until the skin is restarted. (Issue 87)
    * Some kinds of files cannot be downloaded in Windows in which IE of an old version is installed. (because API used in WebParser depends on IE)

--------------------

Added new "Download" option:
The user can now download the file to the "DownloadFile" folder in a current skin folder as a file name defined in "DownloadFile=...".
If you define it as "DownloadFile=image.jpg", a "DownloadFile" folder will be created in the current skin path then the downloaded file is saved as "image.jpg"
Since this file is not a temporary file, it is not deleted even if it close a skin. And, when the refresh of skin or update of Webparser, the file will be overwrited by the latest file.
The user can also make the subfolder in the "DownloadFile" folder. If you define as "DownloadFile=photo\image.jpg", the subfolder "photo" is created and a file is saved in it.

As for specification of path, it is not possible to specify by the absolute path and also specification to the outside of a "DownloadFile" folder is not possible. It means that save of a file is limited to under the "DownloadFile" directory, and this does not permit save to other directories. This limitation is the measures that considered security.

Valid specification examples:

    * DownloadFile=filename.jpg
    * DownloadFile=subfolder\filename.jpg

these specification is invalid:

    * DownloadFile=C:\foo\filename.jpg
    * DownloadFile=#CURRENTPATH#filename.jpg
    * DownloadFile=..\filename.jpg
This commit is contained in:
spx 2010-01-12 21:45:47 +00:00
parent cc3ad487a7
commit 5ae810d00a
2 changed files with 256 additions and 37 deletions

View File

@ -258,7 +258,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="Urlmon.lib Wininet.lib odbc32.lib odbccp32.lib Rainmeter.lib"
AdditionalDependencies="Urlmon.lib Wininet.lib Shlwapi.lib odbc32.lib odbccp32.lib Rainmeter.lib"
OutputFile="../../TestBench/x32/Release/Plugins/WebParser.dll"
LinkIncremental="1"
SuppressStartupBanner="true"
@ -532,7 +532,7 @@
<Tool
Name="VCLinkerTool"
AdditionalOptions="/machine:AMD64 /LTCG "
AdditionalDependencies="Urlmon.lib Wininet.lib odbc32.lib odbccp32.lib Rainmeter.lib"
AdditionalDependencies="Urlmon.lib Wininet.lib Shlwapi.lib odbc32.lib odbccp32.lib Rainmeter.lib"
OutputFile="../../TestBench/x64/Release/Plugins/WebParser.dll"
LinkIncremental="1"
SuppressStartupBanner="true"

View File

@ -28,6 +28,7 @@
#include <map>
#include <vector>
#include <Wininet.h>
#include <shlwapi.h>
#include "pcre-6.4/pcre.h"
#include "..\..\Library\Export.h" // Rainmeter's exported functions
@ -58,6 +59,7 @@ struct UrlData
std::wstring finishAction;
bool download;
bool forceReload;
std::wstring downloadFile;
std::wstring downloadedFile;
std::wstring iniFile;
int debug;
@ -215,9 +217,10 @@ UINT Initialize(HMODULE instance, LPCTSTR iniFile, LPCTSTR section, UINT id)
data->finishAction = ReadConfigString(section, L"FinishAction", L"");
data->errorString = ReadConfigString(section, L"ErrorString", L"");
data->proxy = ReadConfigString(section, L"ProxyServer", L"");
data->downloadFile = ReadConfigString(section, L"DownloadFile", L"");
data->debugFileLocation = ReadConfigString(section, L"Debug2File", L"c:\\WebParserDump.txt");
if(data->debugFileLocation.find(L"\\") == -1)
if(data->debugFileLocation.find(L"\\") == std::wstring::npos)
{
std::wstring str = data->iniFile.substr(0,data->iniFile.find_last_of(L"\\")+1);
str += data->debugFileLocation;
@ -487,7 +490,7 @@ void ParseData(UrlData* urlData, LPCSTR parseData)
int substring_length = ovector[2 * i + 1] - ovector[2 * i];
substring_length = min(substring_length, 256);
std::string tmpStr(substring_start, substring_length);
wsprintf(buffer, L"WebParser: (Index %2d) %hs", i, tmpStr.c_str());
wsprintf(buffer, L"WebParser: (Index %2d) %s", i, ConvertToWide(tmpStr.c_str()).c_str());
Log(buffer);
}
}
@ -613,7 +616,7 @@ void ParseData(UrlData* urlData, LPCSTR parseData)
copyData.cbData = (DWORD)(urlData->finishAction.size() + 1) * sizeof(WCHAR);
copyData.lpData = (void*)urlData->finishAction.c_str();
// Send the bang to the Rainlendar window
// Send the bang to the Rainmeter window
SendMessage(wnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&copyData);
}
}
@ -639,15 +642,15 @@ DWORD WINAPI NetworkDownloadThreadProc(LPVOID pParam)
url = urlData->resultString;
LeaveCriticalSection(&g_CriticalSection);
size_t pos = url.find(':');
if (pos == -1 && !url.empty()) // No protocol
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] == '/')
if (url[0] == L'/')
{
// Absolute path
pos = urlData->url.find('/', 7); // Assume "http://" (=7)
if (pos != -1)
pos = urlData->url.find(L'/', 7); // Assume "http://" (=7)
if (pos != std::wstring::npos)
{
std::wstring path(urlData->url.substr(0, pos));
url = path + url;
@ -657,8 +660,8 @@ DWORD WINAPI NetworkDownloadThreadProc(LPVOID pParam)
{
// Relative path
pos = urlData->url.rfind('/');
if (pos != -1)
pos = urlData->url.rfind(L'/');
if (pos != std::wstring::npos)
{
std::wstring path(urlData->url.substr(0, pos + 1));
url = path + url;
@ -669,37 +672,236 @@ DWORD WINAPI NetworkDownloadThreadProc(LPVOID pParam)
if (!url.empty())
{
bool download = !urlData->downloadFile.empty();
// Create the filename
WCHAR buffer[MAX_PATH];
GetTempPath(MAX_PATH, buffer);
wcscat(buffer, L"Rainmeter-Cache\\");
CreateDirectory(buffer, NULL); // Make sure that the folder exists
WCHAR buffer[MAX_PATH] = {0};
std::wstring fullpath, directory;
size_t pos = url.rfind('/');
if (pos != -1)
if (download) // download mode
{
std::wstring filename(url.substr(pos + 1));
wcscat(buffer, filename.c_str());
PathCanonicalize(buffer, urlData->downloadFile.c_str());
// Write some log info
std::wstring info;
info = L"WebParser: Downloading url ";
info += url;
info += L" to ";
info += buffer;
info += L"\n";
Log(info.c_str());
std::wstring path = buffer;
std::wstring::size_type pos = path.find_first_not_of(L'\\');
if (pos != std::wstring::npos)
{
path = path.substr(pos);
}
PathCanonicalize(buffer, urlData->iniFile.substr(0, urlData->iniFile.find_last_of(L'\\') + 1).c_str()); // "#CURRENTPATH#"
wcscat(buffer, L"DownloadFile\\"); // "#CURRENTPATH#DownloadFile\"
CreateDirectory(buffer, NULL); // 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, NULL); // 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 = url.substr(pos1, pos2 - pos1);
}
else
{
name = url.substr(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";
}
}
bool ready = true;
if (download) // download mode
{
std::wstring error;
if (!PathFileExists(directory.c_str()) || !PathIsDirectory(directory.c_str()))
{
ready = false;
error = L"WebParser: Directory not exists: ";
error += directory;
error += L"\n";
Log(error.c_str());
}
else if (PathIsDirectory(fullpath.c_str()))
{
ready = false;
error = L"WebParser: Path is a directory, not a file: ";
error += fullpath;
error += L"\n";
Log(error.c_str());
}
else
{
DeleteFile(fullpath.c_str());
if (PathFileExists(fullpath.c_str()))
{
ready = false;
DWORD attr = GetFileAttributes(fullpath.c_str());
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_READONLY))
{
error = L"WebParser: File is READ-ONLY: ";
}
else
{
error = L"WebParser: Failed to delete file: ";
}
error += fullpath;
error += L"\n";
Log(error.c_str());
}
}
}
else // cache mode
{
if (!urlData->downloadedFile.empty())
{
DeleteFile(urlData->downloadedFile.c_str());
}
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 = fullpath.substr(0, pos);
ext = fullpath.substr(pos);
}
else
{
path = fullpath;
}
// Assign a serial number
size_t 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, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
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", NULL, NULL, (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());
}
}
}
// Write some log info
std::wstring info = L"WebParser: Downloading url ";
info += url;
info += L" to ";
info += fullpath;
info += L"\n";
Log(info.c_str());
HRESULT resultCoInitialize = CoInitialize(NULL); // requires before calling URLDownloadToFile function
// Download the file
if (S_OK == URLDownloadToFile(NULL, url.c_str(), buffer, NULL, NULL))
HRESULT result = URLDownloadToFile(NULL, url.c_str(), fullpath.c_str(), NULL, NULL);
if (result == S_OK)
{
EnterCriticalSection(&g_CriticalSection);
urlData->downloadedFile = buffer;
// 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;
}
}
urlData->downloadedFile = fullpath;
LeaveCriticalSection(&g_CriticalSection);
if (!urlData->finishAction.empty())
@ -714,21 +916,35 @@ DWORD WINAPI NetworkDownloadThreadProc(LPVOID pParam)
copyData.cbData = (DWORD)(urlData->finishAction.size() + 1) * sizeof(WCHAR);
copyData.lpData = (void*)urlData->finishAction.c_str();
// Send the bang to the Rainlendar window
// Send the bang to the Rainmeter window
SendMessage(wnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&copyData);
}
}
}
else
{
std::wstring error = L"WebParser: Download failed: ";
if (!download) // cache mode
{
// Delete empty file
DeleteFile(fullpath.c_str());
}
wsprintf(buffer, L"result=0x%08X, COM=0x%08X", result, resultCoInitialize);
std::wstring error = L"WebParser: Download failed (";
error += buffer;
error += L"): ";
error += url;
Log(error.c_str());
}
if (SUCCEEDED(resultCoInitialize))
{
CoUninitialize();
}
}
else
{
std::wstring error = L"WebParser: No filename in the url: ";
std::wstring error = L"WebParser: Download failed: ";
error += url;
Log(error.c_str());
}
@ -808,10 +1024,13 @@ void Finalize(HMODULE instance, UINT id)
LeaveCriticalSection(&g_CriticalSection);
}
if (!((*urlIter).second)->downloadedFile.empty())
if (((*urlIter).second)->downloadFile.empty()) // cache mode
{
// Delete the file
DeleteFile(((*urlIter).second)->downloadedFile.c_str());
if (!((*urlIter).second)->downloadedFile.empty())
{
// Delete the file
DeleteFile(((*urlIter).second)->downloadedFile.c_str());
}
}
delete (*urlIter).second;
@ -1015,7 +1234,7 @@ void Log(const WCHAR* string)
UINT GetPluginVersion()
{
return 1010;
return 1012;
}
LPCTSTR GetPluginAuthor()