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; ) == TRUE;
} }
std::string GetURLFileName(const URL_COMPONENTSA& urlc) std::wstring GetURLFileName(const URL_COMPONENTSA& urlc)
{ {
const char *lastSlash = strrchr(urlc.lpszUrlPath, '/'); 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 } // anonymous namespace
@@ -201,8 +202,7 @@ void DownloadFile(const std::string& url, IDownloadSink *sink, int flags)
else else
*ptr2 = 0; *ptr2 = 0;
std::string filename( c_filename ); sink->SetFilename(AnsiToWide(c_filename));
sink->SetFilename(filename);
filename_set = true; filename_set = true;
} }
} }

View File

@@ -46,7 +46,7 @@ struct IDownloadSink
/** /**
Inform the sink of detected filename 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 /// Add chunk of downloaded data
virtual void Add(const void *data, size_t len) = 0; 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 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) virtual void Add(const void *data, size_t len)
{ {

View File

@@ -178,7 +178,7 @@ std::string Settings::GetDefaultRegistryPath()
namespace namespace
{ {
void RegistryWrite(const char *name, const char *value) void RegistryWrite(const char *name, const wchar_t *value)
{ {
const std::string subkey = Settings::GetRegistryPath(); const std::string subkey = Settings::GetRegistryPath();
@@ -198,14 +198,14 @@ void RegistryWrite(const char *name, const char *value)
if ( result != ERROR_SUCCESS ) if ( result != ERROR_SUCCESS )
throw Win32Exception("Cannot write settings to registry"); throw Win32Exception("Cannot write settings to registry");
result = RegSetValueExA result = RegSetValueEx
( (
key, key,
name, AnsiToWide(name).c_str(),
0, 0,
REG_SZ, REG_SZ,
(const BYTE*)value, (const BYTE*)value,
strlen(value) + 1 (wcslen(value) + 1) * sizeof(wchar_t)
); );
RegCloseKey(key); 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(); 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 buflen = len;
DWORD type; DWORD type;
result = RegQueryValueExA result = RegQueryValueEx
( (
key, key,
name, AnsiToWide(name).c_str(),
0, 0,
&type, &type,
(BYTE*)buf, (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 // 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 // some settings have globally set values (either by the installer or the
@@ -313,7 +313,7 @@ CriticalSection g_csConfigValues;
} // anonymous namespace } // 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); 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); CriticalSectionLocker lock(g_csConfigValues);
char buf[512]; wchar_t buf[512];
if ( RegistryRead(name, buf, sizeof(buf)) ) if ( RegistryRead(name, buf, sizeof(buf)) )
return buf; return buf;
else else
return std::string(); return std::wstring();
} }
void Settings::DeleteConfigValue(const char *name) void Settings::DeleteConfigValue(const char *name)

View File

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

View File

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

View File

@@ -86,7 +86,7 @@ public:
/** /**
Notifies the UI that an update was downloaded. 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. Shows the WinSparkle window in "checking for updates..." state.

View File

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