From d51294e088e86adfed04059e2d1ee204450f6af0 Mon Sep 17 00:00:00 2001 From: lordmulder Date: Mon, 9 Dec 2013 22:19:32 +0100 Subject: [PATCH] Actually use updater thread in the update dialog (not completed yet!) --- src/global.cpp | 353 ++++++++---- src/global.h | 18 +- src/thread_updater.cpp | 713 +++++++++++++++++++++++++ src/thread_updater.h | 107 ++++ src/version.h | 2 +- src/win_updater.cpp | 138 +++-- src/win_updater.h | 8 +- x264_launcher_MSVC2013.vcxproj | 14 +- x264_launcher_MSVC2013.vcxproj.filters | 9 + z_build.bat | 3 + 10 files changed, 1227 insertions(+), 138 deletions(-) create mode 100644 src/thread_updater.cpp create mode 100644 src/thread_updater.h diff --git a/src/global.cpp b/src/global.cpp index ab5f8b6..d256258 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include //C++ includes #include @@ -86,6 +88,10 @@ #include #endif +//Global types +typedef HRESULT (WINAPI *SHGetKnownFolderPath_t)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath); +typedef HRESULT (WINAPI *SHGetFolderPath_t)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath); + //Global vars static bool g_x264_console_attached = false; static QMutex g_x264_message_mutex; @@ -144,6 +150,8 @@ g_x264_os_version; static struct { QMap *knownFolders; + SHGetKnownFolderPath_t getKnownFolderPath; + SHGetFolderPath_t getFolderPath; QReadWriteLock lock; } g_x264_known_folder; @@ -342,6 +350,74 @@ void x264_invalid_param_handler(const wchar_t*, const wchar_t*, const wchar_t*, x264_fatal_exit(L"Invalid parameter handler invoked, application will exit!"); } +/* + * Get a random string + */ +QString x264_rand_str(const bool bLong) +{ + const QUuid uuid = QUuid::createUuid().toString(); + + const unsigned int u1 = uuid.data1; + const unsigned int u2 = (((unsigned int)(uuid.data2)) << 16) | ((unsigned int)(uuid.data3)); + const unsigned int u3 = (((unsigned int)(uuid.data4[0])) << 24) | (((unsigned int)(uuid.data4[1])) << 16) | (((unsigned int)(uuid.data4[2])) << 8) | ((unsigned int)(uuid.data4[3])); + const unsigned int u4 = (((unsigned int)(uuid.data4[4])) << 24) | (((unsigned int)(uuid.data4[5])) << 16) | (((unsigned int)(uuid.data4[6])) << 8) | ((unsigned int)(uuid.data4[7])); + + return bLong ? QString().sprintf("%08x%08x%08x%08x", u1, u2, u3, u4) : QString().sprintf("%08x%08x", (u1 ^ u2), (u3 ^ u4)); +} + +/* + * Robert Jenkins' 96 bit Mix Function + * Source: http://www.concentric.net/~Ttwang/tech/inthash.htm + */ +static unsigned int x264_mix(const unsigned int x, const unsigned int y, const unsigned int z) +{ + unsigned int a = x; + unsigned int b = y; + unsigned int c = z; + + a=a-b; a=a-c; a=a^(c >> 13); + b=b-c; b=b-a; b=b^(a << 8); + c=c-a; c=c-b; c=c^(b >> 13); + a=a-b; a=a-c; a=a^(c >> 12); + b=b-c; b=b-a; b=b^(a << 16); + c=c-a; c=c-b; c=c^(b >> 5); + a=a-b; a=a-c; a=a^(c >> 3); + b=b-c; b=b-a; b=b^(a << 10); + c=c-a; c=c-b; c=c^(b >> 15); + + return c; +} + +/* + * Seeds the random number generator + * Note: Altough rand_s() doesn't need a seed, this must be called pripr to lamexp_rand(), just to to be sure! + */ +void x264_seed_rand(void) +{ + qsrand(x264_mix(clock(), time(NULL), _getpid())); +} + +/* + * Returns a randum number + * Note: This function uses rand_s() if available, but falls back to qrand() otherwise + */ +unsigned int x264_rand(void) +{ + quint32 rnd = 0; + + if(rand_s(&rnd) == 0) + { + return rnd; + } + + for(size_t i = 0; i < sizeof(unsigned int); i++) + { + rnd = (rnd << 8) ^ qrand(); + } + + return rnd; +} + /* * Change console text color */ @@ -354,6 +430,69 @@ static void x264_console_color(FILE* file, WORD attributes) } } +/* + * Determines the current date, resistant against certain manipulations + */ +QDate x264_current_date_safe(void) +{ + const DWORD MAX_PROC = 1024; + DWORD *processes = new DWORD[MAX_PROC]; + DWORD bytesReturned = 0; + + if(!EnumProcesses(processes, sizeof(DWORD) * MAX_PROC, &bytesReturned)) + { + X264_DELETE_ARRAY(processes); + return QDate::currentDate(); + } + + const DWORD procCount = bytesReturned / sizeof(DWORD); + ULARGE_INTEGER lastStartTime; + memset(&lastStartTime, 0, sizeof(ULARGE_INTEGER)); + + for(DWORD i = 0; i < procCount; i++) + { + HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[i]); + if(hProc) + { + FILETIME processTime[4]; + if(GetProcessTimes(hProc, &processTime[0], &processTime[1], &processTime[2], &processTime[3])) + { + ULARGE_INTEGER timeCreation; + timeCreation.LowPart = processTime[0].dwLowDateTime; + timeCreation.HighPart = processTime[0].dwHighDateTime; + if(timeCreation.QuadPart > lastStartTime.QuadPart) + { + lastStartTime.QuadPart = timeCreation.QuadPart; + } + } + CloseHandle(hProc); + } + } + + X264_DELETE_ARRAY(processes); + + FILETIME lastStartTime_fileTime; + lastStartTime_fileTime.dwHighDateTime = lastStartTime.HighPart; + lastStartTime_fileTime.dwLowDateTime = lastStartTime.LowPart; + + FILETIME lastStartTime_localTime; + if(!FileTimeToLocalFileTime(&lastStartTime_fileTime, &lastStartTime_localTime)) + { + memcpy(&lastStartTime_localTime, &lastStartTime_fileTime, sizeof(FILETIME)); + } + + SYSTEMTIME lastStartTime_system; + if(!FileTimeToSystemTime(&lastStartTime_localTime, &lastStartTime_system)) + { + memset(&lastStartTime_system, 0, sizeof(SYSTEMTIME)); + lastStartTime_system.wYear = 1970; lastStartTime_system.wMonth = lastStartTime_system.wDay = 1; + } + + const QDate currentDate = QDate::currentDate(); + const QDate processDate = QDate(lastStartTime_system.wYear, lastStartTime_system.wMonth, lastStartTime_system.wDay); + return (currentDate >= processDate) ? currentDate : processDate; +} + /* * Output logging message to console */ @@ -753,21 +892,6 @@ const QStringList &x264_arguments(void) return (*g_x264_argv.list); } -/* - * Get a random string - */ -static QString x264_rand_str(const bool bLong = false) -{ - const QUuid uuid = QUuid::createUuid().toString(); - - const unsigned int u1 = uuid.data1; - const unsigned int u2 = (((unsigned int)(uuid.data2)) << 16) | ((unsigned int)(uuid.data3)); - const unsigned int u3 = (((unsigned int)(uuid.data4[0])) << 24) | (((unsigned int)(uuid.data4[1])) << 16) | (((unsigned int)(uuid.data4[2])) << 8) | ((unsigned int)(uuid.data4[3])); - const unsigned int u4 = (((unsigned int)(uuid.data4[4])) << 24) | (((unsigned int)(uuid.data4[5])) << 16) | (((unsigned int)(uuid.data4[6])) << 8) | ((unsigned int)(uuid.data4[7])); - - return bLong ? QString().sprintf("%08x%08x%08x%08x", u1, u2, u3, u4) : QString().sprintf("%08x%08x", (u1 ^ u2), (u3 ^ u4)); -} - /* * Check for portable mode */ @@ -1693,52 +1817,46 @@ QString x264_query_reg_string(const bool bUser, const QString &path, const QStri */ const QString &x264_known_folder(x264_known_folder_t folder_id) { - typedef HRESULT (WINAPI *SHGetKnownFolderPathFun)(__in const GUID &rfid, __in DWORD dwFlags, __in HANDLE hToken, __out PWSTR *ppszPath); - typedef HRESULT (WINAPI *SHGetFolderPathFun)(__in HWND hwndOwner, __in int nFolder, __in HANDLE hToken, __in DWORD dwFlags, __out LPWSTR pszPath); + static const int CSIDL_FLAG_CREATE = 0x8000; + typedef enum { KF_FLAG_CREATE = 0x00008000 } kf_flags_t; + + struct + { + const int csidl; + const GUID guid; + } + static s_folders[] = + { + { 0x001c, {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}} }, //CSIDL_LOCAL_APPDATA + { 0x0026, {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}} }, //CSIDL_PROGRAM_FILES + { 0x0024, {0xF38BF404,0x1D43,0x42F2,{0x93,0x05,0x67,0xDE,0x0B,0x28,0xFC,0x23}} }, //CSIDL_WINDOWS_FOLDER + { 0x0025, {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}} }, //CSIDL_SYSTEM_FOLDER + }; - static const int CSIDL_LOCAL_APPDATA = 0x001c; - static const int CSIDL_PROGRAM_FILES = 0x0026; - static const int CSIDL_SYSTEM_FOLDER = 0x0025; - static const GUID GUID_LOCAL_APPDATA = {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}}; - static const GUID GUID_LOCAL_APPDATA_LOW = {0xA520A1A4,0x1780,0x4FF6,{0xBD,0x18,0x16,0x73,0x43,0xC5,0xAF,0x16}}; - static const GUID GUID_PROGRAM_FILES = {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}}; - static const GUID GUID_SYSTEM_FOLDER = {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}}; - - QReadLocker readLock(&g_x264_known_folder.lock); - - int folderCSIDL = -1; - GUID folderGUID = {0x0000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}; - size_t folderCacheId = size_t(-1); + size_t folderId = size_t(-1); switch(folder_id) { - case x264_folder_localappdata: - folderCacheId = 0; - folderCSIDL = CSIDL_LOCAL_APPDATA; - folderGUID = GUID_LOCAL_APPDATA; - break; - case x264_folder_programfiles: - folderCacheId = 1; - folderCSIDL = CSIDL_PROGRAM_FILES; - folderGUID = GUID_PROGRAM_FILES; - break; - case x264_folder_systemfolder: - folderCacheId = 2; - folderCSIDL = CSIDL_SYSTEM_FOLDER; - folderGUID = GUID_SYSTEM_FOLDER; - break; - default: + case x264_folder_localappdata: folderId = 0; break; + case x264_folder_programfiles: folderId = 1; break; + case x264_folder_systroot_dir: folderId = 2; break; + case x264_folder_systemfolder: folderId = 3; break; + } + + if(folderId == size_t(-1)) + { qWarning("Invalid 'known' folder was requested!"); return *reinterpret_cast(NULL); - break; } + QReadLocker readLock(&g_x264_known_folder.lock); + //Already in cache? if(g_x264_known_folder.knownFolders) { - if(g_x264_known_folder.knownFolders->contains(folderCacheId)) + if(g_x264_known_folder.knownFolders->contains(folderId)) { - return (*g_x264_known_folder.knownFolders)[folderCacheId]; + return (*g_x264_known_folder.knownFolders)[folderId]; } } @@ -1749,75 +1867,59 @@ const QString &x264_known_folder(x264_known_folder_t folder_id) //Still not in cache? if(g_x264_known_folder.knownFolders) { - if(g_x264_known_folder.knownFolders->contains(folderCacheId)) + if(g_x264_known_folder.knownFolders->contains(folderId)) { - return (*g_x264_known_folder.knownFolders)[folderCacheId]; + return (*g_x264_known_folder.knownFolders)[folderId]; } } - static SHGetKnownFolderPathFun SHGetKnownFolderPathPtr = NULL; - static SHGetFolderPathFun SHGetFolderPathPtr = NULL; - - //Lookup functions - if((!SHGetKnownFolderPathPtr) && (!SHGetFolderPathPtr)) + //Initialize on first call + if(!g_x264_known_folder.knownFolders) { - QLibrary kernel32Lib("shell32.dll"); - if(kernel32Lib.load()) + QLibrary shell32("shell32.dll"); + if(shell32.load()) { - SHGetKnownFolderPathPtr = (SHGetKnownFolderPathFun) kernel32Lib.resolve("SHGetKnownFolderPath"); - SHGetFolderPathPtr = (SHGetFolderPathFun) kernel32Lib.resolve("SHGetFolderPathW"); + g_x264_known_folder.getFolderPath = (SHGetFolderPath_t) shell32.resolve("SHGetFolderPathW"); + g_x264_known_folder.getKnownFolderPath = (SHGetKnownFolderPath_t) shell32.resolve("SHGetKnownFolderPath"); } + g_x264_known_folder.knownFolders = new QMap(); } - QString folder; + QString folderPath; //Now try to get the folder path! - if(SHGetKnownFolderPathPtr) + if(g_x264_known_folder.getKnownFolderPath) { WCHAR *path = NULL; - if(SHGetKnownFolderPathPtr(folderGUID, 0x00008000, NULL, &path) == S_OK) + if(g_x264_known_folder.getKnownFolderPath(s_folders[folderId].guid, KF_FLAG_CREATE, NULL, &path) == S_OK) { //MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST); QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast(path)))); - if(!folderTemp.exists()) - { - folderTemp.mkpath("."); - } if(folderTemp.exists()) { - folder = folderTemp.canonicalPath(); + folderPath = folderTemp.canonicalPath(); } CoTaskMemFree(path); } } - else if(SHGetFolderPathPtr) + else if(g_x264_known_folder.getFolderPath) { WCHAR *path = new WCHAR[4096]; - if(SHGetFolderPathPtr(NULL, folderCSIDL, NULL, NULL, path) == S_OK) + if(g_x264_known_folder.getFolderPath(NULL, s_folders[folderId].csidl | CSIDL_FLAG_CREATE, NULL, NULL, path) == S_OK) { //MessageBoxW(0, path, L"SHGetFolderPathW", MB_TOPMOST); QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast(path)))); - if(!folderTemp.exists()) - { - folderTemp.mkpath("."); - } if(folderTemp.exists()) { - folder = folderTemp.canonicalPath(); + folderPath = folderTemp.canonicalPath(); } } - delete [] path; - } - - //Create cache - if(!g_x264_known_folder.knownFolders) - { - g_x264_known_folder.knownFolders = new QMap(); + X264_DELETE_ARRAY(path); } //Update cache - g_x264_known_folder.knownFolders->insert(folderCacheId, folder); - return (*g_x264_known_folder.knownFolders)[folderCacheId]; + g_x264_known_folder.knownFolders->insert(folderId, folderPath); + return (*g_x264_known_folder.knownFolders)[folderId]; } /* @@ -1886,14 +1988,18 @@ const QString &x264_temp_directory(void) if(g_x264_temp_folder.path->isEmpty()) { qWarning("%%TEMP%% directory not found -> falling back to %%LOCALAPPDATA%%"); - const QString &localAppData = x264_known_folder(x264_folder_localappdata); - if(!localAppData.isEmpty()) + static const x264_known_folder_t folderId[2] = { x264_folder_localappdata, x264_folder_systroot_dir }; + for(size_t id = 0; (g_x264_temp_folder.path->isEmpty() && (id < 2)); id++) { - *g_x264_temp_folder.path = x264_try_init_folder(QString("%1/Temp").arg(localAppData)); - } - else - { - qWarning("%%LOCALAPPDATA%% directory could not be found!"); + const QString &localAppData = x264_known_folder(x264_folder_localappdata); + if(!localAppData.isEmpty()) + { + *g_x264_temp_folder.path = x264_try_init_folder(QString("%1/Temp").arg(localAppData)); + } + else + { + qWarning("%%LOCALAPPDATA%% directory could not be found!"); + } } } @@ -1922,6 +2028,9 @@ bool x264_enable_close_button(const QWidget *win, const bool bEnable) return ok; } +/* + * Play beep sound + */ bool x264_beep(int beepType) { switch(beepType) @@ -1960,6 +2069,68 @@ bool x264_shutdown_computer(const QString &message, const unsigned long timeout, return false; } +/* + * Check the network connection status + */ +int x264_network_status(void) +{ + DWORD dwFlags; + const BOOL ret = (IsNetworkAlive(&dwFlags) == TRUE); + if(GetLastError() == 0) + { + return (ret == TRUE) ? x264_network_yes : x264_network_non; + } + return x264_network_err; +} + +/* + * Setup QPorcess object + */ +void x264_init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir) +{ + //Environment variable names + static const char *const s_envvar_names_temp[] = + { + "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL + }; + static const char *const s_envvar_names_remove[] = + { + "WGETRC", "SYSTEM_WGETRC", "HTTP_PROXY", "FTP_PROXY", "NO_PROXY", "GNUPGHOME", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LANG", NULL + }; + + //Initialize environment + QProcessEnvironment env = process.processEnvironment(); + if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment(); + + //Clean a number of enviroment variables that might affect our tools + for(size_t i = 0; s_envvar_names_remove[i]; i++) + { + env.remove(QString::fromLatin1(s_envvar_names_remove[i])); + env.remove(QString::fromLatin1(s_envvar_names_remove[i]).toLower()); + } + + const QString tempDir = QDir::toNativeSeparators(x264_temp_directory()); + + //Replace TEMP directory in environment + if(bReplaceTempDir) + { + for(size_t i = 0; s_envvar_names_temp[i]; i++) + { + env.insert(s_envvar_names_temp[i], tempDir); + } + } + + //Setup PATH variable + const QString path = env.value("PATH", QString()).trimmed(); + env.insert("PATH", path.isEmpty() ? tempDir : QString("%1;%2").arg(tempDir, path)); + + //Setup QPorcess object + process.setWorkingDirectory(wokringDir); + process.setProcessChannelMode(QProcess::MergedChannels); + process.setReadChannel(QProcess::StandardOutput); + process.setProcessEnvironment(env); +} + /* * Inform the system that it is in use, thereby preventing the system from entering sleep */ diff --git a/src/global.h b/src/global.h index ec68828..e378c76 100644 --- a/src/global.h +++ b/src/global.h @@ -90,10 +90,20 @@ typedef enum { x264_folder_localappdata = 0, x264_folder_programfiles = 2, - x264_folder_systemfolder = 3 + x264_folder_systemfolder = 3, + x264_folder_systroot_dir = 4 } x264_known_folder_t; +//Network connection types +typedef enum +{ + x264_network_err = 0, /*unknown*/ + x264_network_non = 1, /*not connected*/ + x264_network_yes = 2 /*connected*/ +} +x264_network_t; + //Known Windows versions extern const x264_os_version_t x264_winver_win2k; extern const x264_os_version_t x264_winver_winxp; @@ -114,6 +124,7 @@ bool x264_bring_to_front(const QWidget *win); bool x264_change_process_priority(const QProcess *proc, const int priority); bool x264_change_process_priority(const int priority); bool x264_change_process_priority(void *hProcess, const int priority); +QDate x264_current_date_safe(void); const QString &x264_data_path(void); size_t x264_dbg_private_bytes(void); x264_cpu_t x264_detect_cpu_features(const QStringList &argv); @@ -121,16 +132,21 @@ bool x264_enable_close_button(const QWidget *win, const bool bEnable); void x264_fatal_exit(const wchar_t* exitMessage, const wchar_t* errorBoxMessage = NULL); void x264_finalization(void); void x264_init_console(int argc, char* argv[]); +void x264_init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir = true); bool x264_init_qt(int argc, char* argv[]); bool x264_is_executable(const QString &path); bool x264_is_prerelease(void); const QString &x264_known_folder(x264_known_folder_t folder_id); void x264_message_handler(QtMsgType type, const char *msg); +int x264_network_status(void); QString x264_path2ansi(const QString &longPath, bool makeLowercase = false); bool x264_play_sound(const unsigned short uiSoundIdx, const bool bAsync, const wchar_t *alias = NULL); bool x264_portable(void); unsigned int x264_process_id(void); QString x264_query_reg_string(const bool bUser, const QString &path, const QString &name); +unsigned int x264_rand(void); +QString x264_rand_str(const bool bLong = false); +void x264_seed_rand(void); bool x264_set_thread_execution_state(const bool systemRequired); bool x264_shutdown_computer(const QString &message, const unsigned long timeout, const bool forceShutdown); void x264_sleep(const unsigned int delay); diff --git a/src/thread_updater.cpp b/src/thread_updater.cpp new file mode 100644 index 0000000..5cc75e8 --- /dev/null +++ b/src/thread_updater.cpp @@ -0,0 +1,713 @@ +/////////////////////////////////////////////////////////////////////////////// +// Simple x264 Launcher +// Copyright (C) 2004-2013 LoRd_MuldeR +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#include "thread_updater.h" + +#include "global.h" + +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// CONSTANTS +/////////////////////////////////////////////////////////////////////////////// + +static const char *header_id = "!Update"; +static const char *section_id = "x264_launcher"; + +static const char *mirror_url_postfix[] = +{ + "update.ver", + "update_beta.ver", + NULL +}; + +static const char *update_mirrors_prim[] = +{ + "http://muldersoft.com/", + "http://mulder.bplaced.net/", + "http://mulder.cwsurf.de/", + "http://mulder.6te.net/", + "http://mulder.webuda.com/", + "http://mulder.byethost13.com/", + "http://muldersoft.kilu.de/", + "http://muldersoft.zxq.net/", + "http://lamexp.sourceforge.net/", + "http://lamexp.berlios.de/", + "http://lordmulder.github.io/LameXP/", + "http://lord_mulder.bitbucket.org/", + "http://www.tricksoft.de/", + NULL +}; + +static const char *update_mirrors_back[] = +{ + "http://mplayer.savedonthe.net/", + NULL +}; + +static const char *known_hosts[] = //Taken form: http://www.alexa.com/topsites !!! +{ + "http://www.163.com/", + "http://www.ac3filter.net/", + "http://www.amazon.com/", + "http://www.aol.com/", + "http://www.apache.org/", + "http://www.apple.com/", + "http://www.adobe.com/", + "http://www.avidemux.org/", + "http://www.babylon.com/", + "http://www.baidu.com/", + "http://www.bbc.co.uk/", + "http://www.berlios.de/", + "http://www.bing.com/", + "http://www.cnet.com/", + "http://cnzz.com/", + "http://codecs.com/", + "http://www.codeplex.com/", + "http://qt.digia.com/", + "http://www.ebay.com/", + "http://www.equation.com/", + "http://fc2.com/", + "http://www.ffmpeg.org/", + "http://www.flickr.com/", + "http://blog.gitorious.org/", + "http://git-scm.com/", + "http://www.gnome.org/", + "http://www.gnu.org/", + "http://go.com/", + "http://code.google.com/", + "http://www.heise.de/", + "http://www.huffingtonpost.co.uk/", + "http://www.iana.org/", + "http://www.imdb.com/", + "http://www.imgburn.com/", + "http://imgur.com/", + "http://en.jd.com/", + "http://mirrors.kernel.org/", + "http://lame.sourceforge.net/", + "http://www.libav.org/", + "http://www.linkedin.com/about-us", + "http://www.linuxmint.com/", + "http://www.livedoor.com/", + "http://www.livejournal.com/", + "http://mail.ru/", + "http://www.mediafire.com/", + "http://www.mozilla.org/en-US/", + "http://mplayerhq.hu/", + "http://www.msn.com/?st=1", + "http://oss.netfarm.it/", + "http://www.nytimes.com/", + "http://www.opera.com/", + "http://www.portablefreeware.com/", + "http://qt-project.org/", + "http://www.quakelive.com/", + "http://www.seamonkey-project.org/", + "http://www.sina.com.cn/", + "http://www.sohu.com/", + "http://www.soso.com/", + "http://sourceforge.net/", + "http://www.spiegel.de/", + "http://tdm-gcc.tdragon.net/", + "http://www.tdrsmusic.com/", + "http://www.ubuntu.com/", + "http://status.twitter.com/", + "http://www.uol.com.br/", + "http://www.videohelp.com/", + "http://www.videolan.org/", + "http://virtualdub.org/", + "http://www.weibo.com/", + "http://www.wikipedia.org/", + "http://www.winamp.com/", + "http://wordpress.com/", + "http://xiph.org/", + "http://us.yahoo.com/", + "http://www.yandex.ru/", + "http://www.youtube.com/", + "http://www.zedo.com/", + "http://ffmpeg.zeranoe.com/", + NULL +}; + +static const int MIN_CONNSCORE = 8; +static const int VERSION_INFO_EXPIRES_MONTHS = 6; +static char *USER_AGENT_STR = "Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20111106 IceCat/7.0.1"; + +//Helper function +static int getMaxProgress(void) +{ + int counter = MIN_CONNSCORE + 2; + for(int i = 0; update_mirrors_prim[i]; i++) counter++; + for(int i = 0; update_mirrors_back[i]; i++) counter++; + return counter; +} + +//////////////////////////////////////////////////////////// +// Update Info Class +//////////////////////////////////////////////////////////// + +UpdateInfo::UpdateInfo(void) +{ + resetInfo(); +} + +void UpdateInfo::resetInfo(void) +{ + m_buildNo = 0; + m_buildDate.setDate(1900, 1, 1); + m_downloadSite.clear(); + m_downloadAddress.clear(); + m_downloadFilename.clear(); + m_downloadFilecode.clear(); +} + +//////////////////////////////////////////////////////////// +// Constructor & Destructor +//////////////////////////////////////////////////////////// + +UpdateCheckThread::UpdateCheckThread(const QString &binWGet, const QString &binGnuPG, const QString &binKeys, const bool betaUpdates, const bool testMode) +: + m_updateInfo(new UpdateInfo()), + m_binaryWGet(binWGet), + m_binaryGnuPG(binGnuPG), + m_binaryKeys(binKeys), + m_betaUpdates(betaUpdates), + m_testMode(testMode), + m_maxProgress(getMaxProgress()) +{ + m_success = false; + m_status = UpdateStatus_NotStartedYet; + m_progress = 0; + + if(m_binaryWGet.isEmpty() || m_binaryGnuPG.isEmpty() || m_binaryKeys.isEmpty()) + { + THROW("Tools not initialized correctly!"); + } +} + +UpdateCheckThread::~UpdateCheckThread(void) +{ + delete m_updateInfo; +} + +//////////////////////////////////////////////////////////// +// Protected functions +//////////////////////////////////////////////////////////// + +void UpdateCheckThread::run(void) +{ + qDebug("Update checker thread started!"); + + try + { + m_testMode ? testKnownHosts() : checkForUpdates(); + } + catch(const std::exception &error) + { + fflush(stdout); fflush(stderr); + fprintf(stderr, "\nGURU MEDITATION !!!\n\nException error:\n%s\n", error.what()); + x264_fatal_exit(L"Unhandeled C++ exception error, application will exit!"); + } + catch(...) + { + fflush(stdout); fflush(stderr); + fprintf(stderr, "\nGURU MEDITATION !!!\n\nUnknown exception error!\n"); + x264_fatal_exit(L"Unhandeled C++ exception error, application will exit!"); + } + + qDebug("Update checker thread completed."); +} + +void UpdateCheckThread::checkForUpdates(void) +{ + // ----- Initialization ----- // + + m_success = false; + m_updateInfo->resetInfo(); + setProgress(0); + + // ----- Test Internet Connection ----- // + + int connectionScore = 0; + + log("Checking internet connection..."); + setStatus(UpdateStatus_CheckingConnection); + + const int networkStatus = x264_network_status(); + if(networkStatus == x264_network_non) + { + log("", "Operating system reports that the computer is currently offline !!!"); + setProgress(m_maxProgress); + setStatus(UpdateStatus_ErrorNoConnection); + return; + } + + setProgress(1); + + // ----- Test Known Hosts Connectivity ----- // + + QStringList hostList; + for(int i = 0; known_hosts[i]; i++) + { + hostList << QString::fromLatin1(known_hosts[i]); + } + + x264_seed_rand(); + while(!hostList.isEmpty()) + { + QString currentHost = hostList.takeAt(x264_rand() % hostList.count()); + if(connectionScore < MIN_CONNSCORE) + { + log("", "Testing host:", currentHost); + QString outFile = QString("%1/%2.htm").arg(x264_temp_directory(), x264_rand_str()); + bool httpOk = false; + if(getFile(currentHost, outFile, 0, &httpOk)) + { + connectionScore++; + setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1)); + x264_sleep(64); + } + if(httpOk) + { + connectionScore++; + setProgress(qBound(1, connectionScore + 1, MIN_CONNSCORE + 1)); + x264_sleep(64); + } + QFile::remove(outFile); + } + } + + if(connectionScore < MIN_CONNSCORE) + { + setProgress(m_maxProgress); + setStatus(UpdateStatus_ErrorConnectionTestFailed); + return; + } + + // ----- Build Mirror List ----- // + + log("", "----", "", "Checking for updates online..."); + setStatus(UpdateStatus_FetchingUpdates); + + QStringList mirrorList; + for(int index = 0; update_mirrors_prim[index]; index++) + { + mirrorList << QString::fromLatin1(update_mirrors_prim[index]); + } + + x264_seed_rand(); + if(const int len = mirrorList.count()) + { + const int rounds = len * 1097; + for(int i = 0; i < rounds; i++) + { + mirrorList.swap(i % len, x264_rand() % len); + } + } + + for(int index = 0; update_mirrors_back[index]; index++) + { + mirrorList << QString::fromLatin1(update_mirrors_back[index]); + } + + // ----- Fetch Update Info From Server ----- // + + while(!mirrorList.isEmpty()) + { + QString currentMirror = mirrorList.takeFirst(); + setProgress(m_progress + 1); + if(!m_success) + { + if(tryUpdateMirror(m_updateInfo, currentMirror)) + { + m_success = true; + } + } + else + { + x264_sleep(64); + } + } + + setProgress(m_maxProgress); + + if(m_success) + { + if(m_updateInfo->m_buildNo > x264_version_build()) + { + setStatus(UpdateStatus_CompletedUpdateAvailable); + } + else if(m_updateInfo->m_buildNo == x264_version_build()) + { + setStatus(UpdateStatus_CompletedNoUpdates); + } + else + { + setStatus(UpdateStatus_CompletedNewVersionOlder); + } + } + else + { + setStatus(UpdateStatus_ErrorFetchUpdateInfo); + } +} + +void UpdateCheckThread::testKnownHosts(void) +{ + QStringList hostList; + for(int i = 0; known_hosts[i]; i++) + { + hostList << QString::fromLatin1(known_hosts[i]); + } + + qDebug("\n[Known Hosts]"); + log("Testing all known hosts...", "", "---"); + + int hostCount = hostList.count(); + while(!hostList.isEmpty()) + { + QString currentHost = hostList.takeFirst(); + qDebug("Testing: %s", currentHost.toLatin1().constData()); + log("", "Testing:", currentHost, ""); + QString outFile = QString("%1/%2.htm").arg(x264_temp_directory(), x264_rand_str()); + bool httpOk = false; + if(!getFile(currentHost, outFile, 0, &httpOk)) + { + if(httpOk) + { + qWarning("\nConnectivity test was SLOW on the following site:\n%s\n", currentHost.toLatin1().constData()); + } + else + { + qWarning("\nConnectivity test FAILED on the following site:\n%s\n", currentHost.toLatin1().constData()); + } + } + log("", "---"); + QFile::remove(outFile); + } +} + +//////////////////////////////////////////////////////////// +// PRIVATE FUNCTIONS +//////////////////////////////////////////////////////////// + +void UpdateCheckThread::setStatus(const int status) +{ + if(m_status != status) + { + m_status = status; + emit statusChanged(status); + } +} + +void UpdateCheckThread::setProgress(const int progress) +{ + if(m_progress != progress) + { + m_progress = progress; + emit progressChanged(progress); + } +} + +void UpdateCheckThread::log(const QString &str1, const QString &str2, const QString &str3, const QString &str4) +{ + if(!str1.isNull()) emit messageLogged(str1); + if(!str2.isNull()) emit messageLogged(str2); + if(!str3.isNull()) emit messageLogged(str3); + if(!str4.isNull()) emit messageLogged(str4); +} + +bool UpdateCheckThread::tryUpdateMirror(UpdateInfo *updateInfo, const QString &url) +{ + bool success = false; + log("", "Trying mirror:", url); + + QString randPart = x264_rand_str(); + QString outFileVersionInfo = QString("%1/%2.ver").arg(x264_temp_directory(), randPart); + QString outFileSignature = QString("%1/%2.sig").arg(x264_temp_directory(), randPart); + + log("", "Downloading update info:"); + bool ok1 = getFile(QString("%1%2").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileVersionInfo); + + log("", "Downloading signature:"); + bool ok2 = getFile(QString("%1%2.sig").arg(url, mirror_url_postfix[m_betaUpdates ? 1 : 0]), outFileSignature); + + if(ok1 && ok2) + { + log("", "Download okay, checking signature:"); + if(checkSignature(outFileVersionInfo, outFileSignature)) + { + log("", "Signature okay, parsing info:"); + success = parseVersionInfo(outFileVersionInfo, updateInfo); + } + else + { + log("", "Bad signature, take care!"); + } + } + else + { + log("", "Download has failed!"); + } + + QFile::remove(outFileVersionInfo); + QFile::remove(outFileSignature); + + return success; +} + +bool UpdateCheckThread::getFile(const QString &url, const QString &outFile, unsigned int maxRedir, bool *httpOk) +{ + QFileInfo output(outFile); + output.setCaching(false); + if(httpOk) *httpOk = false; + + if(output.exists()) + { + QFile::remove(output.canonicalFilePath()); + if(output.exists()) + { + return false; + } + } + + QProcess process; + x264_init_process(process, output.absolutePath()); + + QStringList args; + args << "--no-cache" << "--no-dns-cache" << QString().sprintf("--max-redirect=%u", maxRedir); + args << QString("--referer=%1://%2/").arg(QUrl(url).scheme(), QUrl(url).host()) << "-U" << USER_AGENT_STR; + args << "-O" << output.fileName() << url; + + QEventLoop loop; + connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); + + QTimer timer; + timer.setSingleShot(true); + timer.setInterval(25000); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + const QRegExp httpResponseOK("200 OK$"); + + process.start(m_binaryWGet, args); + + if(!process.waitForStarted()) + { + return false; + } + + timer.start(); + + while(process.state() == QProcess::Running) + { + loop.exec(); + bool bTimeOut = (!timer.isActive()); + while(process.canReadLine()) + { + QString line = QString::fromLatin1(process.readLine()).simplified(); + if(line.contains(httpResponseOK)) + { + line.append(" [OK]"); + if(httpOk) *httpOk = true; + } + log(line); + } + if(bTimeOut) + { + qWarning("WGet process timed out <-- killing!"); + process.kill(); + process.waitForFinished(); + log("!!! TIMEOUT !!!"); + return false; + } + } + + timer.stop(); + timer.disconnect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + log(QString().sprintf("Exited with code %d", process.exitCode())); + return (process.exitCode() == 0) && output.exists() && output.isFile(); +} + +bool UpdateCheckThread::checkSignature(const QString &file, const QString &signature) +{ + if(QFileInfo(file).absolutePath().compare(QFileInfo(signature).absolutePath(), Qt::CaseInsensitive) != 0) + { + qWarning("CheckSignature: File and signature should be in same folder!"); + return false; + } + if(QFileInfo(file).absolutePath().compare(QFileInfo(m_binaryKeys).absolutePath(), Qt::CaseInsensitive) != 0) + { + qWarning("CheckSignature: File and keyring should be in same folder!"); + return false; + } + + QProcess process; + x264_init_process(process, QFileInfo(file).absolutePath()); + + QEventLoop loop; + connect(&process, SIGNAL(error(QProcess::ProcessError)), &loop, SLOT(quit())); + connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit())); + connect(&process, SIGNAL(readyRead()), &loop, SLOT(quit())); + + process.start(m_binaryGnuPG, QStringList() << "--homedir" << "." << "--keyring" << QFileInfo(m_binaryKeys).fileName() << QFileInfo(signature).fileName() << QFileInfo(file).fileName()); + + if(!process.waitForStarted()) + { + return false; + } + + while(process.state() == QProcess::Running) + { + loop.exec(); + while(process.canReadLine()) + { + log(QString::fromLatin1(process.readLine()).simplified()); + } + } + + log(QString().sprintf("Exited with code %d", process.exitCode())); + return (process.exitCode() == 0); +} + +bool UpdateCheckThread::parseVersionInfo(const QString &file, UpdateInfo *updateInfo) +{ + QRegExp value("^(\\w+)=(.+)$"); + QRegExp section("^\\[(.+)\\]$"); + + QDate updateInfoDate; + updateInfo->resetInfo(); + + QFile data(file); + if(!data.open(QIODevice::ReadOnly)) + { + qWarning("Cannot open update info file for reading!"); + return false; + } + + bool inHeader = false; + bool inSection = false; + + while(!data.atEnd()) + { + QString line = QString::fromLatin1(data.readLine()).trimmed(); + if(section.indexIn(line) >= 0) + { + log(QString("Sec: [%1]").arg(section.cap(1))); + inSection = (section.cap(1).compare(section_id, Qt::CaseInsensitive) == 0); + inHeader = (section.cap(1).compare(header_id, Qt::CaseInsensitive) == 0); + continue; + } + if(inSection && (value.indexIn(line) >= 0)) + { + log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2))); + if(value.cap(1).compare("BuildNo", Qt::CaseInsensitive) == 0) + { + bool ok = false; + unsigned int temp = value.cap(2).toUInt(&ok); + if(ok) updateInfo->m_buildNo = temp; + } + else if(value.cap(1).compare("BuildDate", Qt::CaseInsensitive) == 0) + { + QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); + if(temp.isValid()) updateInfo->m_buildDate = temp; + } + else if(value.cap(1).compare("DownloadSite", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadSite = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadAddress", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadAddress = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadFilename", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadFilename = value.cap(2).trimmed(); + } + else if(value.cap(1).compare("DownloadFilecode", Qt::CaseInsensitive) == 0) + { + updateInfo->m_downloadFilecode = value.cap(2).trimmed(); + } + } + if(inHeader && (value.indexIn(line) >= 0)) + { + log(QString("Val: '%1' ==> '%2").arg(value.cap(1), value.cap(2))); + if(value.cap(1).compare("TimestampCreated", Qt::CaseInsensitive) == 0) + { + QDate temp = QDate::fromString(value.cap(2).trimmed(), Qt::ISODate); + if(temp.isValid()) updateInfoDate = temp; + } + } + } + + if(!updateInfoDate.isValid()) + { + updateInfo->resetInfo(); + log("WARNING: Version info timestamp is missing!"); + return false; + } + else if(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS) < x264_current_date_safe()) + { + updateInfo->resetInfo(); + log(QString::fromLatin1("WARNING: This version info has expired at %1!").arg(updateInfoDate.addMonths(VERSION_INFO_EXPIRES_MONTHS).toString(Qt::ISODate))); + return false; + } + else if(x264_current_date_safe() < updateInfoDate) + { + log("Version info is from the future, take care!"); + qWarning("Version info is from the future, take care!"); + } + + bool complete = true; + + if(!(updateInfo->m_buildNo > 0)) complete = false; + if(!(updateInfo->m_buildDate.year() >= 2010)) complete = false; + if(updateInfo->m_downloadSite.isEmpty()) complete = false; + if(updateInfo->m_downloadAddress.isEmpty()) complete = false; + if(updateInfo->m_downloadFilename.isEmpty()) complete = false; + if(updateInfo->m_downloadFilecode.isEmpty()) complete = false; + + if(!complete) + { + log("WARNING: Version info is incomplete!"); + } + + return complete; +} + +//////////////////////////////////////////////////////////// +// SLOTS +//////////////////////////////////////////////////////////// + +/*NONE*/ + +//////////////////////////////////////////////////////////// +// EVENTS +//////////////////////////////////////////////////////////// + +/*NONE*/ diff --git a/src/thread_updater.h b/src/thread_updater.h new file mode 100644 index 0000000..1881b57 --- /dev/null +++ b/src/thread_updater.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////// +// Simple x264 Launcher +// Copyright (C) 2004-2013 LoRd_MuldeR +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +class UpdateInfo +{ +public: + UpdateInfo(void); + void resetInfo(void); + + unsigned int m_buildNo; + QDate m_buildDate; + QString m_downloadSite; + QString m_downloadAddress; + QString m_downloadFilename; + QString m_downloadFilecode; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class UpdateCheckThread : public QThread +{ + Q_OBJECT + +public: + enum + { + UpdateStatus_NotStartedYet = 0, + UpdateStatus_CheckingConnection = 1, + UpdateStatus_FetchingUpdates = 2, + UpdateStatus_CompletedUpdateAvailable = 3, + UpdateStatus_CompletedNoUpdates = 4, + UpdateStatus_CompletedNewVersionOlder = 5, + UpdateStatus_ErrorNoConnection = 6, + UpdateStatus_ErrorConnectionTestFailed = 7, + UpdateStatus_ErrorFetchUpdateInfo = 8 + } + update_status_t; + + UpdateCheckThread(const QString &binWGet, const QString &binGnuPG, const QString &binKeys, const bool betaUpdates, const bool testMode = false); + ~UpdateCheckThread(void); + + const int getUpdateStatus(void) const { return m_status; } + const bool getSuccess(void) const { return m_success; }; + const int getMaximumProgress(void) const { return m_maxProgress; }; + const int getCurrentProgress(void) const { return m_progress; }; + const UpdateInfo *getUpdateInfo(void) const { return m_updateInfo; } + +protected: + void run(void); + void checkForUpdates(void); + void testKnownHosts(void); + +signals: + void statusChanged(const int status); + void progressChanged(const int progress); + void messageLogged(const QString &text); + +private: + const int m_maxProgress; + UpdateInfo *const m_updateInfo; + + const bool m_betaUpdates; + const bool m_testMode; + + const QString m_binaryWGet; + const QString m_binaryGnuPG; + const QString m_binaryKeys; + + volatile bool m_success; + + int m_status; + int m_progress; + + inline void setStatus(const int status); + inline void setProgress(const int progress); + inline void log(const QString &str1, const QString &str2 = QString(), const QString &str3 = QString(), const QString &str4 = QString()); + + bool getFile(const QString &url, const QString &outFile, unsigned int maxRedir = 5, bool *httpOk = NULL); + bool tryUpdateMirror(UpdateInfo *updateInfo, const QString &url); + bool checkSignature(const QString &file, const QString &signature); + bool parseVersionInfo(const QString &file, UpdateInfo *updateInfo); +}; diff --git a/src/version.h b/src/version.h index a2444c5..c36c07c 100644 --- a/src/version.h +++ b/src/version.h @@ -26,7 +26,7 @@ #define VER_X264_MAJOR 2 #define VER_X264_MINOR 2 #define VER_X264_PATCH 7 -#define VER_X264_BUILD 678 +#define VER_X264_BUILD 686 #define VER_X264_MINIMUM_REV 2363 #define VER_X264_CURRENT_API 140 diff --git a/src/win_updater.cpp b/src/win_updater.cpp index 07f92b9..f030b49 100644 --- a/src/win_updater.cpp +++ b/src/win_updater.cpp @@ -23,6 +23,7 @@ #include "uic_win_updater.h" #include "global.h" +#include "thread_updater.h" #include "checksum.h" #include @@ -55,7 +56,7 @@ UpdaterDialog::UpdaterDialog(QWidget *parent, const QString &binDir) QDialog(parent), ui(new Ui::UpdaterDialog()), m_binDir(binDir), - m_state(0), + m_thread(NULL), m_firstShow(true) { //Init the dialog, from the .ui file @@ -82,7 +83,18 @@ UpdaterDialog::UpdaterDialog(QWidget *parent, const QString &binDir) UpdaterDialog::~UpdaterDialog(void) { + if(m_thread) + { + if(!m_thread->wait(1000)) + { + m_thread->terminate(); + m_thread->wait(); + } + } + + X264_DELETE(m_thread); X264_DELETE(m_animator); + delete ui; } @@ -119,8 +131,43 @@ void UpdaterDialog::closeEvent(QCloseEvent *e) void UpdaterDialog::initUpdate(void) { - //Restet text - ui->retranslateUi(this); + + //Reset icons + + + //Show animation + + //Check binary files + QStringList binaries; + if(!checkBinaries(binaries)) + { + ui->buttonCancel->setEnabled(true); + QMessageBox::critical(this, tr("File Error"), tr("At least one file required by web-update is missing or corrupted.
Please re-install this application and then try again!")); + close(); + return; + } + + //Create and setup thread + if(!m_thread) + { + m_thread = new UpdateCheckThread(binaries[0], binaries[1], binaries[2], false); + connect(m_thread, SIGNAL(statusChanged(int)), this, SLOT(threadStatusChanged(int))); + connect(m_thread, SIGNAL(progressChanged(int)), this, SLOT(threadProgressChanged(int))); + connect(m_thread, SIGNAL(messageLogged(QString)), this, SLOT(threadMessageLogged(QString))); + connect(m_thread, SIGNAL(finished()), this, SLOT(threadFinished())); + connect(m_thread, SIGNAL(terminated()), this, SLOT(threadFinished())); + } + + //Begin updater test run + QTimer::singleShot(0, this, SLOT(checkForUpdates())); +} + +void UpdaterDialog::checkForUpdates(void) +{ + if((!m_thread) || m_thread->isRunning()) + { + qWarning("Update in progress, cannot check for updates now!"); + } //Init buttons ui->buttonCancel->setEnabled(false); @@ -130,56 +177,61 @@ void UpdaterDialog::initUpdate(void) //Hide labels ui->labelInfo->hide(); ui->labelUrl->hide(); + + //Update status + threadStatusChanged(UpdateCheckThread::UpdateStatus_NotStartedYet); - //Reset icons - UPDATE_ICON(1, "clock"); - UPDATE_ICON(2, "clock"); - UPDATE_ICON(3, "clock"); + //Update cursor + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + QApplication::setOverrideCursor(Qt::WaitCursor); - //Show animation - SHOW_ANIMATION(true); - - //Check binary files - if(!checkBinaries()) - { - ui->buttonCancel->setEnabled(true); - QMessageBox::critical(this, tr("File Error"), tr("At least one file required by web-update is missing or corrupted.
Please re-install this application and then try again!")); - close(); - return; - } - - //Begin updater test run - m_state = 0; - QTimer::singleShot(333, this, SLOT(updateState())); + //Start the updater thread + m_thread->start(); } -void UpdaterDialog::updateState(void) +void UpdaterDialog::threadStatusChanged(int status) { - switch(m_state++) + switch(status) { - case 0: - UPDATE_ICON(1, "play"); - QTimer::singleShot(6666, this, SLOT(updateState())); + case UpdateCheckThread::UpdateStatus_NotStartedYet: + UPDATE_ICON(1, "clock"); + UPDATE_ICON(2, "clock"); + UPDATE_ICON(3, "clock"); + ui->retranslateUi(this); break; - case 1: + case UpdateCheckThread::UpdateStatus_CheckingConnection: + UPDATE_ICON(1, "play"); + break; + case UpdateCheckThread::UpdateStatus_FetchingUpdates: UPDATE_ICON(1, "shield_green"); UPDATE_TEXT(1, tr("Internet connection is working.")); UPDATE_ICON(2, "play"); - QTimer::singleShot(6666, this, SLOT(updateState())); break; - case 2: - UPDATE_ICON(2, "shield_green"); - UPDATE_TEXT(2, tr("Update-information was received successfully.")); - UPDATE_ICON(3, "play"); - QTimer::singleShot(6666, this, SLOT(updateState())); - break; - case 3: + case UpdateCheckThread::UpdateStatus_CompletedUpdateAvailable: UPDATE_ICON(3, "shield_exclamation"); UPDATE_TEXT(3, tr("A newer version is available!")); - QTimer::singleShot(6666, this, SLOT(updateState())); + break; + case UpdateCheckThread::UpdateStatus_ErrorNoConnection: + case UpdateCheckThread::UpdateStatus_ErrorConnectionTestFailed: + case UpdateCheckThread::UpdateStatus_ErrorFetchUpdateInfo: + break; + default: + throw "Unknown status code!"; + } + + switch(status) + { + case UpdateCheckThread::UpdateStatus_CompletedUpdateAvailable: + case UpdateCheckThread::UpdateStatus_CompletedNoUpdates: + case UpdateCheckThread::UpdateStatus_CompletedNewVersionOlder: + UPDATE_ICON(2, "shield_green"); + UPDATE_TEXT(2, tr("Update-information was received successfully.")); + ui->buttonDownload->show(); + case UpdateCheckThread::UpdateStatus_ErrorNoConnection: + case UpdateCheckThread::UpdateStatus_ErrorConnectionTestFailed: + case UpdateCheckThread::UpdateStatus_ErrorFetchUpdateInfo: SHOW_ANIMATION(false); ui->buttonCancel->setEnabled(true); - ui->buttonDownload->show(); break; } } @@ -188,7 +240,7 @@ void UpdaterDialog::updateState(void) // Private Functions /////////////////////////////////////////////////////////////////////////////// -bool UpdaterDialog::checkBinaries(void) +bool UpdaterDialog::checkBinaries(QStringList &binaries) { qDebug("[File Verification]"); @@ -206,9 +258,15 @@ bool UpdaterDialog::checkBinaries(void) }; bool okay = true; + binaries.clear(); + for(size_t i = 0; FILE_INFO[i].name; i++) { - okay = okay && checkFileHash(QString("%1/common/%2").arg(m_binDir, QString::fromLatin1(FILE_INFO[i].name)), FILE_INFO[i].hash); + const QString binPath = QString("%1/common/%2").arg(m_binDir, QString::fromLatin1(FILE_INFO[i].name)); + if(okay = okay && checkFileHash(binPath, FILE_INFO[i].hash)) + { + binaries << binPath; + } } if(okay) diff --git a/src/win_updater.h b/src/win_updater.h index 41161e9..bb3f62d 100644 --- a/src/win_updater.h +++ b/src/win_updater.h @@ -24,6 +24,7 @@ #include class QMovie; +class UpdateCheckThread; namespace Ui { @@ -44,16 +45,17 @@ protected: private slots: void initUpdate(void); - void updateState(void); + void checkForUpdates(void); + void threadStatusChanged(int status); private: Ui::UpdaterDialog *const ui; - bool checkBinaries(void); + bool checkBinaries(QStringList &binaries); bool checkFileHash(const QString &filePath, const char *expectedHash); bool m_firstShow; const QString m_binDir; + UpdateCheckThread *m_thread; QMovie *m_animator; - int m_state; }; diff --git a/x264_launcher_MSVC2013.vcxproj b/x264_launcher_MSVC2013.vcxproj index 6e68511..1bc44fe 100644 --- a/x264_launcher_MSVC2013.vcxproj +++ b/x264_launcher_MSVC2013.vcxproj @@ -63,7 +63,7 @@ Windows true $(QTDIR)\lib;$(SolutionDir)etc\vld\lib\Win32;%(AdditionalLibraryDirectories) - QtCored4.lib;QtGuid4.lib;Winmm.lib;psapi.lib;%(AdditionalDependencies) + QtCored4.lib;QtGuid4.lib;Winmm.lib;Psapi.lib;SensAPI.lib;%(AdditionalDependencies) x264_entry_point @@ -129,7 +129,7 @@ copy /Y "$(SolutionDir)etc\vld\bin\Win32\*.manifest" "$(TargetDir)" true true $(QTDIR)\lib;%(AdditionalLibraryDirectories) - QtCore4.lib;QtGui4.lib;Winmm.lib;dwmapi.lib;%(AdditionalDependencies) + QtCore4.lib;QtGui4.lib;Winmm.lib;dwmapi.lib;Psapi.lib;SensAPI.lib;%(AdditionalDependencies) UseLinkTimeCodeGeneration false x264_entry_point @@ -342,6 +342,14 @@ copy /Y "$(QTDIR)\plugins\imageformats\qgif4.dll" "$(TargetDir)\imageformats" $(SolutionDir)tmp\moc\moc_%(Filename).cpp;%(Outputs) $(SolutionDir)tmp\moc\moc_%(Filename).cpp;%(Outputs) + + "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)tmp\moc\moc_%(Filename).cpp" "%(FullPath)" + "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)tmp\moc\moc_%(Filename).cpp" "%(FullPath)" + MOC "$(SolutionDir)tmp\moc\moc_%(Filename).cpp" + MOC "$(SolutionDir)tmp\moc\moc_%(Filename).cpp" + $(SolutionDir)tmp\moc\moc_%(Filename).cpp;%(Outputs) + $(SolutionDir)tmp\moc\moc_%(Filename).cpp;%(Outputs) + "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)tmp\moc\moc_%(Filename).cpp" "%(FullPath)" @@ -368,6 +376,7 @@ copy /Y "$(QTDIR)\plugins\imageformats\qgif4.dll" "$(TargetDir)\imageformats" + @@ -380,6 +389,7 @@ copy /Y "$(QTDIR)\plugins\imageformats\qgif4.dll" "$(TargetDir)\imageformats" + diff --git a/x264_launcher_MSVC2013.vcxproj.filters b/x264_launcher_MSVC2013.vcxproj.filters index 447ea5a..c22f958 100644 --- a/x264_launcher_MSVC2013.vcxproj.filters +++ b/x264_launcher_MSVC2013.vcxproj.filters @@ -182,6 +182,12 @@ Source Files\3rd Party + + Source Files + + + Generated Files + @@ -241,6 +247,9 @@ Header Files + + Header Files + diff --git a/z_build.bat b/z_build.bat index 9504518..e3d0016 100644 --- a/z_build.bat +++ b/z_build.bat @@ -85,11 +85,14 @@ mkdir "%PACK_PATH%" mkdir "%PACK_PATH%\imageformats" mkdir "%PACK_PATH%\toolset\x86" mkdir "%PACK_PATH%\toolset\x64" +mkdir "%PACK_PATH%\toolset\common" copy "%~dp0\bin\Release\*.exe" "%PACK_PATH%" copy "%~dp0\bin\Release\toolset\x86\*.exe" "%PACK_PATH%\toolset\x86" copy "%~dp0\bin\Release\toolset\x86\*.dll" "%PACK_PATH%\toolset\x86" copy "%~dp0\bin\Release\toolset\x64\*.exe" "%PACK_PATH%\toolset\x64" copy "%~dp0\bin\Release\toolset\x64\*.dll" "%PACK_PATH%\toolset\x64" +copy "%~dp0\bin\Release\toolset\common\*.exe" "%PACK_PATH%\toolset\common" +copy "%~dp0\bin\Release\toolset\common\*.gpg" "%PACK_PATH%\toolset\common" copy "%~dp0\*.txt" "%PACK_PATH%" REM ///////////////////////////////////////////////////////////////////////////