Fix downloading updates if the user directory is non-ASCII.

Use Unicode APIs everywhere related to filenames, including the
temporary directory.

Fixes #26.
This commit is contained in:
Václav Slavík
2014-07-17 19:14:58 +02:00
parent aff3c9744a
commit 6542e90ff6
7 changed files with 75 additions and 51 deletions

View File

@@ -102,10 +102,11 @@ bool GetHttpHeader(HINTERNET handle, DWORD whatToGet, DWORD& output)
) == TRUE;
}
std::string GetURLFileName(const URL_COMPONENTSA& urlc)
std::wstring GetURLFileName(const URL_COMPONENTSA& urlc)
{
const char *lastSlash = strrchr(urlc.lpszUrlPath, '/');
return std::string(lastSlash ? lastSlash + 1 : urlc.lpszUrlPath);
const std::string fn(lastSlash ? lastSlash + 1 : urlc.lpszUrlPath);
return AnsiToWide(fn);
}
} // anonymous namespace
@@ -201,8 +202,7 @@ void DownloadFile(const std::string& url, IDownloadSink *sink, int flags)
else
*ptr2 = 0;
std::string filename( c_filename );
sink->SetFilename(filename);
sink->SetFilename(AnsiToWide(c_filename));
filename_set = true;
}
}

View File

@@ -46,7 +46,7 @@ struct IDownloadSink
/**
Inform the sink of detected filename
*/
virtual void SetFilename(const std::string& filename) = 0;
virtual void SetFilename(const std::wstring& filename) = 0;
/// Add chunk of downloaded data
virtual void Add(const void *data, size_t len) = 0;
@@ -59,7 +59,7 @@ struct StringDownloadSink : public IDownloadSink
{
virtual void SetLength(size_t) {}
virtual void SetFilename(const std::string& filename) {}
virtual void SetFilename(const std::wstring&) {}
virtual void Add(const void *data, size_t len)
{

View File

@@ -178,7 +178,7 @@ std::string Settings::GetDefaultRegistryPath()
namespace
{
void RegistryWrite(const char *name, const char *value)
void RegistryWrite(const char *name, const wchar_t *value)
{
const std::string subkey = Settings::GetRegistryPath();
@@ -198,14 +198,14 @@ void RegistryWrite(const char *name, const char *value)
if ( result != ERROR_SUCCESS )
throw Win32Exception("Cannot write settings to registry");
result = RegSetValueExA
result = RegSetValueEx
(
key,
name,
AnsiToWide(name).c_str(),
0,
REG_SZ,
(const BYTE*)value,
strlen(value) + 1
(wcslen(value) + 1) * sizeof(wchar_t)
);
RegCloseKey(key);
@@ -240,7 +240,7 @@ void RegistryDelete(const char *name)
}
int DoRegistryRead(HKEY root, const char *name, char *buf, size_t len)
int DoRegistryRead(HKEY root, const char *name, wchar_t *buf, size_t len)
{
const std::string subkey = Settings::GetRegistryPath();
@@ -262,10 +262,10 @@ int DoRegistryRead(HKEY root, const char *name, char *buf, size_t len)
DWORD buflen = len;
DWORD type;
result = RegQueryValueExA
result = RegQueryValueEx
(
key,
name,
AnsiToWide(name).c_str(),
0,
&type,
(BYTE*)buf,
@@ -292,7 +292,7 @@ int DoRegistryRead(HKEY root, const char *name, char *buf, size_t len)
}
int RegistryRead(const char *name, char *buf, size_t len)
int RegistryRead(const char *name, wchar_t *buf, size_t len)
{
// Try reading from HKCU first. If that fails, look at HKLM too, in case
// some settings have globally set values (either by the installer or the
@@ -313,7 +313,7 @@ CriticalSection g_csConfigValues;
} // anonymous namespace
void Settings::DoWriteConfigValue(const char *name, const char *value)
void Settings::DoWriteConfigValue(const char *name, const wchar_t *value)
{
CriticalSectionLocker lock(g_csConfigValues);
@@ -321,15 +321,15 @@ void Settings::DoWriteConfigValue(const char *name, const char *value)
}
std::string Settings::DoReadConfigValue(const char *name)
std::wstring Settings::DoReadConfigValue(const char *name)
{
CriticalSectionLocker lock(g_csConfigValues);
char buf[512];
wchar_t buf[512];
if ( RegistryRead(name, buf, sizeof(buf)) )
return buf;
else
return std::string();
return std::wstring();
}
void Settings::DeleteConfigValue(const char *name)

View File

@@ -27,6 +27,7 @@
#define _settings_h_
#include "threads.h"
#include "utils.h"
#include <string>
#include <sstream>
@@ -176,24 +177,49 @@ public:
template<typename T>
static void WriteConfigValue(const char *name, const T& value)
{
std::ostringstream s;
std::wostringstream s;
s << value;
DoWriteConfigValue(name, s.str().c_str());
}
static void WriteConfigValue(const char *name, const std::string& value)
{
DoWriteConfigValue(name, AnsiToWide(value).c_str());
}
static void WriteConfigValue(const char *name, const std::wstring& value)
{
DoWriteConfigValue(name, value.c_str());
}
// Reads a value from registry. Returns true if it was present,
// false otherwise.
template<typename T>
static bool ReadConfigValue(const char *name, T& value)
{
const std::string v = DoReadConfigValue(name);
const std::wstring v = DoReadConfigValue(name);
if ( v.empty() )
return false;
std::istringstream s(v);
std::wistringstream s(v);
s >> value;
return !s.fail();
}
static bool ReadConfigValue(const char *name, std::string& value)
{
const std::wstring v = DoReadConfigValue(name);
if (v.empty())
return false;
value = WideToAnsi(v);
return true;
}
static bool ReadConfigValue(const char *name, std::wstring& value)
{
value = DoReadConfigValue(name);
return !value.empty();
}
// Reads a value from registry, substituting default value if not present.
template<typename T>
static bool ReadConfigValue(const char *name, T& value, const T& defval)
@@ -225,8 +251,8 @@ private:
static std::string GetDefaultRegistryPath();
static void DoWriteConfigValue(const char *name, const char *value);
static std::string DoReadConfigValue(const char *name);
static void DoWriteConfigValue(const char *name, const wchar_t *value);
static std::wstring DoReadConfigValue(const char *name);
private:
// guards the variables below:

View File

@@ -143,9 +143,9 @@ wxIcon GetApplicationIcon()
struct EventPayload
{
Appcast appcast;
size_t sizeDownloaded, sizeTotal;
std::string updateFile;
Appcast appcast;
size_t sizeDownloaded, sizeTotal;
std::wstring updateFile;
};
@@ -283,7 +283,7 @@ public:
// update download progress
void DownloadProgress(size_t downloaded, size_t total);
// change state into "update downloaded"
void StateUpdateDownloaded(const std::string& updateFile);
void StateUpdateDownloaded(const std::wstring& updateFile);
private:
void EnablePulsing(bool enable);
@@ -713,7 +713,7 @@ void UpdateDialog::DownloadProgress(size_t downloaded, size_t total)
}
void UpdateDialog::StateUpdateDownloaded(const std::string& updateFile)
void UpdateDialog::StateUpdateDownloaded(const std::wstring& updateFile)
{
m_downloader->Join();
delete m_downloader;
@@ -1221,7 +1221,7 @@ void UI::NotifyDownloadProgress(size_t downloaded, size_t total)
/*static*/
void UI::NotifyUpdateDownloaded(const std::string& updateFile)
void UI::NotifyUpdateDownloaded(const std::wstring& updateFile)
{
UIThreadAccess uit;
EventPayload payload;

View File

@@ -86,7 +86,7 @@ public:
/**
Notifies the UI that an update was downloaded.
*/
static void NotifyUpdateDownloaded(const std::string& updateFile);
static void NotifyUpdateDownloaded(const std::wstring& updateFile);
/**
Shows the WinSparkle window in "checking for updates..." state.

View File

@@ -45,7 +45,7 @@ namespace winsparkle
namespace
{
std::string CreateUniqueTempDirectory()
std::wstring CreateUniqueTempDirectory()
{
// We need to put downloaded updates into a directory of their own, because
// if we put it in $TMP, some DLLs could be there and interfere with the
@@ -53,25 +53,23 @@ std::string CreateUniqueTempDirectory()
//
// This code creates a new randomized directory name and tries to create it;
// this process is repeated if the directory already exists.
char tmpdir[MAX_PATH+1];
if ( GetTempPathA(sizeof(tmpdir), tmpdir) == 0 )
wchar_t tmpdir[MAX_PATH+1];
if ( GetTempPath(MAX_PATH+1, tmpdir) == 0 )
throw Win32Exception("Cannot create temporary directory");
for ( ;; )
{
std::ostringstream sdir;
sdir << tmpdir << "Update-";
std::wstring dir(tmpdir);
dir += L"Update-";
UUID uuid;
UuidCreate(&uuid);
RPC_CSTR uuidStr;
UuidToStringA(&uuid, &uuidStr);
sdir << uuidStr;
RpcStringFreeA(&uuidStr);
RPC_WSTR uuidStr;
RPC_STATUS status = UuidToString(&uuid, &uuidStr);
dir += reinterpret_cast<wchar_t*>(uuidStr);
RpcStringFree(&uuidStr);
const std::string dir(sdir.str());
if ( CreateDirectoryA(dir.c_str(), NULL) )
if ( CreateDirectory(dir.c_str(), NULL) )
return dir;
else if ( GetLastError() != ERROR_ALREADY_EXISTS )
throw Win32Exception("Cannot create temporary directory");
@@ -80,9 +78,9 @@ std::string CreateUniqueTempDirectory()
struct UpdateDownloadSink : public IDownloadSink
{
UpdateDownloadSink(Thread& thread, const std::string& dir)
UpdateDownloadSink(Thread& thread, const std::wstring& dir)
: m_thread(thread),
m_dir(dir), m_path(""), m_file(NULL),
m_dir(dir), m_file(NULL),
m_downloaded(0), m_total(0), m_lastUpdate(-1)
{}
@@ -97,17 +95,17 @@ struct UpdateDownloadSink : public IDownloadSink
}
}
std::string GetFilePath(void) { return m_path; }
std::wstring GetFilePath(void) { return m_path; }
virtual void SetLength(size_t l) { m_total = l; }
virtual void SetFilename(const std::string& filename)
virtual void SetFilename(const std::wstring& filename)
{
if ( m_file )
throw std::runtime_error("Update file already set");
m_path = m_dir + "\\" + filename;
m_file = fopen(m_path.c_str(), "wb");
m_path = m_dir + L"\\" + filename;
m_file = _wfopen(m_path.c_str(), L"wb");
if ( !m_file )
throw std::runtime_error("Cannot save update file");
}
@@ -136,8 +134,8 @@ struct UpdateDownloadSink : public IDownloadSink
Thread& m_thread;
size_t m_downloaded, m_total;
FILE *m_file;
std::string m_dir;
std::string m_path;
std::wstring m_dir;
std::wstring m_path;
clock_t m_lastUpdate;
};
@@ -166,7 +164,7 @@ void UpdateDownloader::Run()
try
{
const std::string tmpdir = CreateUniqueTempDirectory();
const std::wstring tmpdir = CreateUniqueTempDirectory();
Settings::WriteConfigValue("UpdateTempDir", tmpdir);
UpdateDownloadSink sink(*this, tmpdir);