diff --git a/CMakeLists.txt b/CMakeLists.txt index e45ad527f..c34000e71 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,14 +69,6 @@ # Setup basic variables. project(rssguard) -if(WIN32 OR OS2) - # Setup "rssguard_updater" project. - project(rssguard_updater) - - set(UPDATER_SUBFOLDER "updater") - set(UPDATER_EXE_NAME "rssguard_updater") -endif(WIN32 OR OS2) - set(APP_NAME "RSS Guard") set(APP_LOW_NAME "rssguard") set(APP_VERSION "2.0.0.0") @@ -98,6 +90,10 @@ option(USE_QT_5 "Use Qt 5 for building" OFF) option(ENABLE_OS2_RC "Compile application icon on OS/2" OFF) option(BUNDLE_ICON_THEMES "Equip installation with custom icon themes" ON) +if(WIN32) + option(USE_NSIS "Use NSIS generator to produce installer" OFF) +endif(WIN32) + # Set appropriate CMake policies. if(POLICY CMP0012) cmake_policy(SET CMP0012 NEW) @@ -118,6 +114,11 @@ message(STATUS "[${APP_LOW_NAME}] ==== Options summary ====") message(STATUS "[${APP_LOW_NAME}] Use Qt 5 for building -> ${USE_QT_5}") message(STATUS "[${APP_LOW_NAME}] Equip installation with custom icon themes -> ${BUNDLE_ICON_THEMES}") message(STATUS "[${APP_LOW_NAME}] Compile application icon on OS/2 -> ${ENABLE_OS2_RC}") + +if(WIN32) + message(STATUS "[${APP_LOW_NAME}] Use NSIS generator to produce installer -> ${USE_NSIS}") +endif(WIN32) + message(STATUS "[${APP_LOW_NAME}] ==== Options summary ====") message(STATUS "") @@ -126,6 +127,9 @@ message(STATUS "[${APP_LOW_NAME}] Compilation process begins right now.") # Setup misc options. set(CMAKE_COLOR_MAKEFILE ON) +set(CMAKE_MODULE_PATH + ${PROJECT_SOURCE_DIR}/resources/nsis + ${CMAKE_MODULE_PATH}) set(CMAKE_INCLUDE_CURRENT_DIR ON) # Eliminate a warning when building in Windows that relates @@ -538,29 +542,6 @@ if(WIN32) ) endif(WIN32) -# Setup source & header files for "rssguard_updater". -if(WIN32 OR OS2) - set(UPDATER_SOURCES - # QTSINGLEAPPLICATION sources. - src/qtsingleapplication/qtlocalpeer.cpp - src/qtsingleapplication/qtsingleapplication.cpp - - # MAIN sources. - src/miscellaneous/iofactory.cpp - src/updater/formupdater.cpp - src/updater/main.cpp - ) - - set(UPDATER_HEADERS - # QTSINGLEAPPLICATION headers. - src/qtsingleapplication/qtlocalpeer.h - src/qtsingleapplication/qtsingleapplication.h - - # MAIN headers. - src/updater/formupdater.h - ) -endif(WIN32 OR OS2) - # Add custom icon on Mac OS X. if(APPLE) SET (APP_SOURCES ${APP_SOURCES} resources/macosx/rssguard.icns) @@ -596,45 +577,9 @@ include_directories ( ${CMAKE_SOURCE_DIR}/src/gui ${CMAKE_SOURCE_DIR}/src/network-web ${CMAKE_SOURCE_DIR}/src/dynamic-shortcuts - ${CMAKE_SOURCE_DIR}/src/updater ${CMAKE_BINARY_DIR}/src ) -# Compile "rssguard_updater" utility on supported platforms. -if(WIN32 OR OS2) - message(STATUS "[${APP_LOW_NAME}] RSS Guard updater will be compiled.") - - if(${USE_QT_5}) - add_executable(${UPDATER_EXE_NAME} WIN32 - ${UPDATER_SOURCES} - ) - - if(WIN32) - target_link_libraries(${UPDATER_EXE_NAME} Qt5::WinMain) - endif(WIN32) - - # Use modules from Qt. - qt5_use_modules(rssguard_updater - Core - Widgets - Network - ) - else(${USE_QT_5}) - add_executable(${UPDATER_EXE_NAME} WIN32 - ${UPDATER_SOURCES} - ${UPDATER_MOC} - ) - - # Link modules from Qt. - target_link_libraries(${UPDATER_EXE_NAME} - ${QT_QTCORE_LIBRARY} - ${QT_QTGUI_LIBRARY} - ${QT_QTNETWORK_LIBRARY} - ${QT_QTMAIN_LIBRARY} - ) - endif(${USE_QT_5}) -endif(WIN32 OR OS2) - # Compile "rssguard" if(${USE_QT_5}) add_executable(${EXE_NAME} WIN32 MACOSX_BUNDLE @@ -689,24 +634,17 @@ if(WIN32 OR OS2) RUNTIME DESTINATION ./${UPDATER_SUBFOLDER}) # Copy DLLs and other binary files for main installation and updater. - if(WIN32) - # Copy "7za" utility on Windows, OS/2 uses built-in "7za". - install(FILES resources/binaries/windows/7za/7za.exe - resources/binaries/windows/7za/7za_license.txt + if(WIN32 AND NOT ${USE_QT_5}) + install(FILES ${APP_DLLS_QT4_MSVC2010} + DESTINATION ./) + install(FILES ${APP_DLLS_QT4_MSVC2010_IMAGEFORMATS} + DESTINATION ./imageformats) + install(FILES ${APP_DLLS_QT4_MSVC2010_SQLDRIVERS} + DESTINATION ./sqldrivers) + + install(FILES ${APP_DLLS_QT4_MSVC2010} DESTINATION ./${UPDATER_SUBFOLDER}) - - if(NOT ${USE_QT_5}) - install(FILES ${APP_DLLS_QT4_MSVC2010} - DESTINATION ./) - install(FILES ${APP_DLLS_QT4_MSVC2010_IMAGEFORMATS} - DESTINATION ./imageformats) - install(FILES ${APP_DLLS_QT4_MSVC2010_SQLDRIVERS} - DESTINATION ./sqldrivers) - - install(FILES ${APP_DLLS_QT4_MSVC2010} - DESTINATION ./${UPDATER_SUBFOLDER}) - endif(NOT ${USE_QT_5}) - endif(WIN32) + endif(WIN32 AND NOT ${USE_QT_5}) if(BUNDLE_ICON_THEMES) install(DIRECTORY resources/graphics/icons/mini-kfaenza @@ -802,17 +740,40 @@ elseif(UNIX) endif(WIN32 OR OS2) # Custom target for packaging. -set(CPACK_GENERATOR "ZIP") +if(WIN32) + if(USE_NSIS) + set(CPACK_GENERATOR "NSIS") + else(USE_NSIS) + set(CPACK_GENERATOR "ZIP") + endif(USE_NSIS) +else(WIN32) + set(CPACK_GENERATOR "ZIP") +endif(WIN32) + set(CPACK_PACKAGE_NAME ${APP_LOW_NAME}) set(CPACK_PACKAGE_VERSION ${APP_VERSION}) +set(CPACK_PACKAGE_VERSION_PATCH "0") +set(CPACK_PACKAGE_VENDOR ${APP_AUTHOR}) +set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY ${APP_NAME}) set(CPACK_IGNORE_FILES "/resources/aur/;\\\\.psd$;/resources/deployment;/CVS/;/\\\\.svn/;/\\\\.git/;\\\\.swp$;/CMakeLists.txt.user;\\\\.#;/#;\\\\.tar.gz$;/CMakeFiles/;CMakeCache.txt;\\\\.qm$;/build/;\\\\.diff$;.DS_Store'") set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_SOURCE_IGNORE_FILES ${CPACK_IGNORE_FILES}) +set(CPACK_NSIS_INSTALLED_ICON_NAME "${APP_LOW_NAME}.ico") +set(CPACK_NSIS_HELP_LINK ${APP_URL}) +set(CPACK_NSIS_URL_INFO_ABOUT ${APP_URL}) +set(CPACK_NSIS_CONTACT ${APP_EMAIL}) + # Load packaging facilities. include(CPack) +# Configure file with custom definitions for NSIS. +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/resources/nsis/NSIS.definitions.nsh.in + ${CMAKE_CURRENT_BINARY_DIR}/resources/nsis/NSIS.definitions.nsh +) + # make dist implementation. add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) diff --git a/resources/binaries/windows/7za/7za.exe b/resources/binaries/windows/7za/7za.exe deleted file mode 100644 index b27abbdf9..000000000 Binary files a/resources/binaries/windows/7za/7za.exe and /dev/null differ diff --git a/resources/binaries/windows/7za/7za_license.txt b/resources/binaries/windows/7za/7za_license.txt deleted file mode 100644 index a1a56937b..000000000 --- a/resources/binaries/windows/7za/7za_license.txt +++ /dev/null @@ -1,29 +0,0 @@ - 7-Zip Command line version - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - License for use and distribution - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - 7-Zip Copyright (C) 1999-2010 Igor Pavlov. - - 7za.exe is distributed under the GNU LGPL license - - Notes: - You can use 7-Zip on any computer, including a computer in a commercial - organization. You don't need to register or pay for 7-Zip. - - - GNU LGPL information - -------------------- - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You can receive a copy of the GNU Lesser General Public License from - http://www.gnu.org/ diff --git a/resources/nsis/NSIS.InstallOptions.ini.in b/resources/nsis/NSIS.InstallOptions.ini.in new file mode 100755 index 000000000..e69de29bb diff --git a/resources/nsis/NSIS.definitions.nsh.in b/resources/nsis/NSIS.definitions.nsh.in new file mode 100755 index 000000000..7f64b5274 --- /dev/null +++ b/resources/nsis/NSIS.definitions.nsh.in @@ -0,0 +1,27 @@ +; This file is part of RSS Guard. +; +; Copyright (C) 2011-2014 by Martin Rotter +; +; RSS Guard 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 3 of the License, or +; (at your option) any later version. +; +; RSS Guard 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 RSS Guard. If not, see . + + +!define VERSION "@APP_VERSION@" +!define APP_VERSION "@APP_VERSION@" +!define APP_NAME "@APP_NAME@" +!define EXE_NAME "@EXE_NAME@" +!define README_FILE "README" +!define LICENSE_FILE "@PROJECT_SOURCE_DIR@\resources\text\COPYING_GNU_GPL" +!define MUI_ICON "@PROJECT_SOURCE_DIR@\resources\graphics\@APP_LOW_NAME@.ico" +!define MUI_UNICON "@PROJECT_SOURCE_DIR@\resources\graphics\@APP_LOW_NAME@.ico" +!define PATCH "0" \ No newline at end of file diff --git a/resources/nsis/NSIS.template.in b/resources/nsis/NSIS.template.in new file mode 100755 index 000000000..17a8f005c --- /dev/null +++ b/resources/nsis/NSIS.template.in @@ -0,0 +1,246 @@ +; This file is part of RSS Guard. +; +; Copyright (C) 2011-2014 by Martin Rotter +; +; RSS Guard 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 3 of the License, or +; (at your option) any later version. +; +; RSS Guard 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 RSS Guard. If not, see . + + +;-------------------------------- +; Do necessary inclusions. +!include ..\..\..\resources\nsis\NSIS.definitions.nsh +!include MUI2.nsh + +;-------------------------------- +; Basic values definitions. +!define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" + +; Name and file. +Name "${APP_NAME}" +OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + +; Set custom branding text. +BrandingText "${APP_NAME}" + +; Set compression. +SetCompressor /SOLID /FINAL lzma + +; Default installation folder. +InstallDir "@CPACK_NSIS_INSTALL_ROOT@\${APP_NAME}" +InstallDirRegKey HKCU "Software\${APP_NAME}" "Install Directory" + +; Require administrator access. +RequestExecutionLevel admin + +;-------------------------------- +; Interface Settings + +; Show "are you sure" dialog when cancelling installation. +!define MUI_ABORTWARNING + +;-------------------------------- +; Pages + +; Pages for installator. +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "${LICENSE_FILE}" +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_COMPONENTS + +; Start menu folder page configuration. +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" +!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\${APP_NAME}" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + +!insertmacro MUI_PAGE_INSTFILES + +; Offer user to launch the application right when it is installed. +!define MUI_FINISHPAGE_RUN "$INSTDIR\${EXE_NAME}" + +!insertmacro MUI_PAGE_FINISH + +; Pages for uninstallator. +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +; Languages. + +!insertmacro MUI_LANGUAGE "English" + +;-------------------------------- +; Helper macros. + +!macro ExecWaitJob _exec +StrCpy $9 0 +System::Call 'kernel32::CreateIoCompletionPort(i -1,i0,i0,i0)i.r1' +${IfThen} $1 != 0 ${|} IntOp $9 $9 + 1 ${|} +System::Call 'kernel32::CreateJobObject(i0,i0)i.r2' +${IfThen} $2 != 0 ${|} IntOp $9 $9 + 1 ${|} +System::Call '*(i 0,i $1)i.r0' +System::Call 'kernel32::SetInformationJobObject(i $2,i 7,i $0,i 8)i.r3' +${IfThen} $3 != 0 ${|} IntOp $9 $9 + 1 ${|} +System::Free $0 +System::Call '*(i,i,i,i)i.r0' +System::Alloc 72 +pop $4 +System::Call "*$4(i 72)" +System::Call 'kernel32::CreateProcess(i0,t ${_exec},i0,i0,i0,i 0x01000004,i0,i0,i $4,i $0)i.r3' +${IfThen} $3 != 0 ${|} IntOp $9 $9 + 1 ${|} +System::Free $4 +System::Call "*$0(i.r3,i.r4,i,i)" +System::Free $0 +System::Call 'kernel32::AssignProcessToJobObject(i $2,i $3)i.r0' +${IfThen} $0 != 0 ${|} IntOp $9 $9 + 1 ${|} +System::Call 'kernel32::ResumeThread(i $4)i.r0' +${IfThen} $0 != -1 ${|} IntOp $9 $9 + 1 ${|} +System::Call 'kernel32::CloseHandle(i $3)' +System::Call 'kernel32::CloseHandle(i $4)' +!define __ExecWaitJob__ ExecWaitJob${__LINE__} +${__ExecWaitJob__}ioportwait: +System::Call 'kernel32::GetQueuedCompletionStatus(i $1,*i.r3,*i,*i.r4,i -1)i.r0' +${IfThen} $0 = 0 ${|} StrCpy $9 0 ${|} +${IfThen} $3 != 4 ${|} goto ${__ExecWaitJob__}ioportwait ${|} +System::Call 'kernel32::CloseHandle(i $2)' +System::Call 'kernel32::CloseHandle(i $1)' +!undef __ExecWaitJob__ +${IfThen} $9 < 6 ${|} MessageBox mb_iconstop `ExecWaitJob "${_exec}" failed!` ${|} +!macroend + +; If you are using solid compression, files that are required before +; the actual installation should be stored first in the data block, +; because this will make your installer start faster. +!insertmacro MUI_RESERVEFILE_LANGDLL + +;-------------------------------- +; Sections. + +; Installer sections. +Section "!Core" Core + ReadRegStr $R0 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "UninstallString" + IfFileExists $R0 +1 NotInstalled + MessageBox MB_OK|MB_ICONEXCLAMATION "${APP_NAME} is already installed. $\n$\nClick 'OK' to uninstall it then continue with current installation." IDOK Uninstall + +Uninstall: + !insertmacro ExecWaitJob '"$R0 /S"' + +NotInstalled: + SetOutPath "$INSTDIR" + + ; Install core application files. + @CPACK_NSIS_FULL_INSTALL@ + + ; Store installation folder. + WriteRegStr HKCU "Software\${APP_NAME}" "Install Directory" $INSTDIR + + ; Create uninstaller. + WriteUninstaller "$INSTDIR\Uninstall.exe" + + ; Create entry in Windows "Add/Remove programs" panel. + Push "DisplayName" + Push "${APP_NAME}" + Call ConditionalAddToRegisty + Push "DisplayVersion" + Push "${APP_VERSION}" + Call ConditionalAddToRegisty + Push "Publisher" + Push "@CPACK_PACKAGE_VENDOR@" + Call ConditionalAddToRegisty + Push "UninstallString" + Push "$INSTDIR\Uninstall.exe" + Call ConditionalAddToRegisty + Push "NoRepair" + Push "1" + Call ConditionalAddToRegisty + Push "NoModify" + Push "1" + Call ConditionalAddToRegisty + Push "DisplayIcon" + Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" + Call ConditionalAddToRegisty + Push "HelpLink" + Push "@CPACK_NSIS_HELP_LINK@" + Call ConditionalAddToRegisty + Push "URLInfoAbout" + Push "@CPACK_NSIS_URL_INFO_ABOUT@" + Call ConditionalAddToRegisty + Push "Contact" + Push "@CPACK_NSIS_CONTACT@" + Call ConditionalAddToRegisty +SectionEnd + +Section "Desktop Icon" DesktopIcon + CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${EXE_NAME}" +SectionEnd + +Section "Start Menu Shortcuts" StartMenuShortcuts + CreateDirectory "$SMPROGRAMS\${APP_NAME}" + CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\${EXE_NAME}" + CreateShortCut "$SMPROGRAMS\${APP_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe" +SectionEnd + +LangString DESC_Core ${LANG_ENGLISH} "Core installation files for ${APP_NAME}." +LangString DESC_DesktopIcon ${LANG_ENGLISH} "Desktop icon for ${APP_NAME}." +LangString DESC_QuickLaunchIcon ${LANG_ENGLISH} "Quick launch icon for ${APP_NAME}." +LangString DESC_StartMenuShortcuts ${LANG_ENGLISH} "Start Menu Shortcuts for ${APP_NAME}." + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${Core} $(DESC_Core) + !insertmacro MUI_DESCRIPTION_TEXT ${DesktopIcon} $(DESC_DesktopIcon) + !insertmacro MUI_DESCRIPTION_TEXT ${QuickLaunchIcon} $(DESC_QuickLaunchIcon) + !insertmacro MUI_DESCRIPTION_TEXT ${StartMenuShortcuts} $(DESC_StartMenuShortcuts) +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +; Uninstaller section. +Section "Uninstall" + ; Remove core application files & directories. + @CPACK_NSIS_DELETE_FILES@ + @CPACK_NSIS_DELETE_DIRECTORIES@ + + ; Remove uninstaller. + Delete "$INSTDIR\Uninstall.exe" + + ; Remove entry from Windows "Add/Remove programs" panel. + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" + + ; Remove rest of installed files. + ; Custom files are left intact. + RMDir "$INSTDIR" + + Delete "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" + Delete "$SMPROGRAMS\${APP_NAME}\Uninstall.lnk" + RMDir "$SMPROGRAMS\${APP_NAME}" + + Delete "$DESKTOP\${APP_NAME}.lnk" + + DeleteRegKey /ifempty HKCU "Software\${APP_NAME}" +SectionEnd + +;-------------------------------- +; Custom functions. + +; Executed when installer starts. +Function .onInit + IntOp $0 ${SF_SELECTED} | ${SF_RO} + SectionSetFlags ${Core} $0 +FunctionEnd + +; Conditional saving to registry. +Function ConditionalAddToRegisty + Pop $0 + Pop $1 + StrCmp "$0" "" ConditionalAddToRegisty_EmptyString + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "$1" "$0" + ConditionalAddToRegisty_EmptyString: +FunctionEnd \ No newline at end of file diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG index 8d4e5bd07..f3840e07c 100644 --- a/resources/text/CHANGELOG +++ b/resources/text/CHANGELOG @@ -8,7 +8,7 @@ Fixed: Added:
    -
  • +
  • Added NSIS-based "portable" installer for Windows and restructured update process.
Changed: diff --git a/resources/text/UPDATES b/resources/text/UPDATES index 5c6bb2df1..9e66ccc85 100644 --- a/resources/text/UPDATES +++ b/resources/text/UPDATES @@ -1,7 +1,7 @@ - https://bitbucket.org/skunkos/rssguard/downloads/rssguard-1.9.9.9-win32.7z + https://bitbucket.org/skunkos/rssguard/downloads/rssguard-2.0.0.0-win32-test.exe https://bitbucket.org/skunkos/rssguard/downloads/rssguard-1.9.9.9-os2.7z diff --git a/src/definitions/definitions.h.in b/src/definitions/definitions.h.in index 9bbf86c31..4d62b61cb 100755 --- a/src/definitions/definitions.h.in +++ b/src/definitions/definitions.h.in @@ -35,10 +35,6 @@ #define APP_VERSION "@APP_VERSION@" #define APP_USERAGENT QString("@APP_NAME@/@APP_VERSION@ (@APP_URL@) on @CMAKE_SYSTEM@") -#define APP_UPDATER_SUBFOLDER "updater" -#define APP_UPDATER_EXECUTABLE "rssguard_updater.exe" -#define APP_7ZA_EXECUTABLE "7za.exe" - #define RELEASES_LIST "https://bitbucket.org/skunkos/rssguard/raw/master/resources/text/UPDATES?at=master" #define DEFAULT_LOCALE "en_GB" #define DEFAULT_FEED_ENCODING "UTF-8" diff --git a/src/gui/formupdate.cpp b/src/gui/formupdate.cpp index 60f8f04a6..e70a49b93 100755 --- a/src/gui/formupdate.cpp +++ b/src/gui/formupdate.cpp @@ -148,7 +148,7 @@ void FormUpdate::saveUpdateFile(const QByteArray &file_contents) { qDebug("Update file contents was successfuly saved."); - m_updateFilePath = output_file.fileName(); + m_updateFilePath = QDir::toNativeSeparators(output_file.fileName()); m_readyToInstall = true; } else { @@ -199,47 +199,17 @@ void FormUpdate::startUpdate() { // via self-update feature. close(); - // Now we need to copy updater to temporary path and launch it - // with correct arguments from there. -#if QT_VERSION >= 0x050000 - QString temp_directory = QStandardPaths::writableLocation(QStandardPaths::TempLocation); -#else - QString temp_directory = QDesktopServices::storageLocation(QDesktopServices::TempLocation); -#endif + qDebug("Preparing to launch external installer '%s'.", qPrintable(m_updateFilePath)); - QString source_updater_directory = QDir::toNativeSeparators(qApp->applicationDirPath() + QDir::separator() + - APP_UPDATER_SUBFOLDER); - QString target_updater_directory = QDir::toNativeSeparators(temp_directory + QDir::separator() + - APP_UPDATER_SUBFOLDER); - - if (QDir(temp_directory).exists(APP_UPDATER_SUBFOLDER)) { - IOFactory::removeDirectory(target_updater_directory); - } - - IOFactory::copyDirectory(source_updater_directory, target_updater_directory); - - qDebug("Preparing to launch external updater '%s'.", - qPrintable(target_updater_directory + QDir::separator() + APP_UPDATER_EXECUTABLE)); - - if (!QProcess::startDetached(target_updater_directory + QDir::separator() + APP_UPDATER_EXECUTABLE, - QStringList() << - APP_VERSION << - m_updateInfo.m_availableVersion << - QDir::toNativeSeparators(qApp->applicationFilePath()) << - QDir::toNativeSeparators(m_updateFilePath))) { + if (!QProcess::startDetached(m_updateFilePath)) { qDebug("External updater was not launched due to error."); - if (SystemTrayIcon::isSystemTrayActivated()) { - qApp->trayIcon()->showMessage(tr("Cannot update application"), - tr("Cannot launch external updater. Update application manually."), - QSystemTrayIcon::Warning); - } - else { - MessageBox::show(this, - QMessageBox::Warning, - tr("Cannot update application"), - tr("Cannot launch external updater. Update application manually.")); - } + qApp->showGuiMessage(tr("Cannot update application"), + tr("Cannot launch external updater. Update application manually."), + QSystemTrayIcon::Warning, this); + } + else { + qApp->quit(); } } else if (update_for_this_system && isSelfUpdateSupported()) { @@ -264,19 +234,10 @@ void FormUpdate::startUpdate() { } else { // Self-update and package are not available. if (!WebFactory::instance()->openUrlInExternalBrowser(url_file)) { - if (SystemTrayIcon::isSystemTrayActivated()) { - qApp->trayIcon()->showMessage(tr("Cannot update application"), - tr("Cannot navigate to installation file. Check new installation downloads " - "manually on project website."), - QSystemTrayIcon::Warning); - } - else { - MessageBox::show(this, - QMessageBox::Warning, - tr("Cannot update application"), - tr("Cannot navigate to installation file. Check new installation downloads " - "manually on project website.")); - } + qApp->showGuiMessage(tr("Cannot update application"), + tr("Cannot navigate to installation file. Check new installation downloads " + "manually on project website."), + QSystemTrayIcon::Warning, this); } } } diff --git a/src/network-web/downloader.cpp b/src/network-web/downloader.cpp index 645cda9e1..1cb124db5 100755 --- a/src/network-web/downloader.cpp +++ b/src/network-web/downloader.cpp @@ -49,6 +49,8 @@ void Downloader::downloadFile(const QString &url, int timeout, bool protected_co request.setOriginatingObject(&originatingObject); // Set url for this request and fire it up. + m_timer->setInterval(timeout); + request.setUrl(url); runGetRequest(request); } diff --git a/src/updater/formupdater.cpp b/src/updater/formupdater.cpp deleted file mode 100755 index 5edd6bd04..000000000 --- a/src/updater/formupdater.cpp +++ /dev/null @@ -1,382 +0,0 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2014 by Martin Rotter -// -// RSS Guard 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 3 of the License, or -// (at your option) any later version. -// -// RSS Guard 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 RSS Guard. If not, see . - -#include "updater/formupdater.h" - -#include "definitions/definitions.h" -#include "miscellaneous/iofactory.h" -#include "qtsingleapplication/qtsingleapplication.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -FormUpdater *FormUpdater::s_instance; - -FormUpdater::FormUpdater(QWidget *parent) - : QMainWindow(parent, Qt::Dialog | Qt::WindowStaysOnTopHint), - m_state(NoState), - m_txtOutput(new QTextEdit(this)), - m_parsedArguments(QHash()) { - - // Initialize singleton. - s_instance = this; - - m_txtOutput->setAutoFormatting(QTextEdit::AutoNone); - m_txtOutput->setAcceptRichText(true); - m_txtOutput->setFontPointSize(10.0); - m_txtOutput->setReadOnly(true); - m_txtOutput->setFocusPolicy(Qt::StrongFocus); - m_txtOutput->setContextMenuPolicy(Qt::DefaultContextMenu); - m_txtOutput->setTextInteractionFlags(Qt::TextSelectableByMouse | - Qt::TextSelectableByKeyboard | - Qt::LinksAccessibleByKeyboard | - Qt::LinksAccessibleByMouse); - - setCentralWidget(m_txtOutput); - setWindowTitle("RSS Guard updater"); - setWindowIcon(QIcon(APP_ICON_PATH)); - moveToCenterAndResize(); - - connect(this, SIGNAL(debugMessageProduced(QtMsgType,QString)), - this, SLOT(consumeDebugMessage(QtMsgType,QString))); -} - -FormUpdater::~FormUpdater() { -} - -void FormUpdater::startUpgrade() { - qDebug("Started..."); - - printHeading("Welcome to RSS Guard updater"); - printText("Analyzing updater arguments."); - - if (QtSingleApplication::arguments().size() != 5) { - printText("Insufficient arguments passed. Update process cannot proceed."); - printText("\nPress any key to exit updater..."); - - m_state = ExitError; - return; - } - - // Process arguments. - saveArguments(); - printArguments(); - - if (!printUpdateInformation() || !doPreparationCleanup() || !doExtractionAndCopying()) { - printText("\nPress any key to exit updater..."); - - m_state = ExitError; - return; - } - - doFinalCleanup(); - executeMainApplication(); - - printText("\nPress any key to exit updater..."); -} - -void FormUpdater::saveArguments() { - // Obtain parameters. - QStringList arguments = QtSingleApplication::arguments(); - - m_parsedArguments["updater_path"] = QDir::toNativeSeparators(qApp->applicationFilePath()); - m_parsedArguments["current_version"] = arguments.at(1); - m_parsedArguments["next_version"] = arguments.at(2); - m_parsedArguments["rssguard_executable_path"] = QDir::toNativeSeparators(arguments.at(3)); - m_parsedArguments["rssguard_path"] = QDir::toNativeSeparators(QFileInfo(m_parsedArguments["rssguard_executable_path"]).absolutePath()); - m_parsedArguments["update_file_path"] = QDir::toNativeSeparators(arguments.at(4)); - m_parsedArguments["temp_path"] = QDir::toNativeSeparators(QFileInfo(m_parsedArguments["update_file_path"]).absolutePath()); - m_parsedArguments["output_temp_path"] = m_parsedArguments["temp_path"] + QDir::separator() + APP_LOW_NAME; -} - -void FormUpdater::executeMainApplication() { - printText("\nApplication was upgraded without serious errors."); - - if (!QProcess::startDetached(m_parsedArguments["rssguard_executable_path"])) { - printText("RSS Guard was not started successfully. Start it manually."); - m_state = ExitError; - } - else { - m_state = ExitNormal; - } -} - -void FormUpdater::triggerDebugMessageConsumption(QtMsgType type, const QString &message) { - emit debugMessageProduced(type, message); -} - -void FormUpdater::consumeDebugMessage(QtMsgType type, const QString &message) { - switch (type) { - case QtDebugMsg: - printText(message); - break; - - case QtWarningMsg: - printText(QString("WARNING: %1").arg(message)); - break; - - case QtCriticalMsg: - printText(QString("CRITICAL: %1").arg(message)); - break; - - case QtFatalMsg: - printText(QString("FATAL: %1").arg(message)); - qApp->exit(EXIT_FAILURE); - - default: - break; - } -} - -#if QT_VERSION >= 0x050000 -void FormUpdater::debugHandler(QtMsgType type, - const QMessageLogContext &placement, - const QString &message) { -#ifndef QT_NO_DEBUG_OUTPUT - Q_UNUSED(placement) - - s_instance->triggerDebugMessageConsumption(type, message); -#else - Q_UNUSED(type) - Q_UNUSED(placement) - Q_UNUSED(message) -#endif -} -#else -void FormUpdater::debugHandler(QtMsgType type, const char *message) { -#ifndef QT_NO_DEBUG_OUTPUT - s_instance->triggerDebugMessageConsumption(type, QString(message)); -#else - Q_UNUSED(type) - Q_UNUSED(message) -#endif -} -#endif - -void FormUpdater::printArguments() { - printNewline(); - printHeading("Arguments"); - - printText(QString("Updater executable file:\n -> %1").arg(m_parsedArguments["updater_path"])); - printText(QString("Application executable file:\n -> %1").arg(m_parsedArguments["rssguard_executable_path"])); - printText(QString("Temp folder:\n -> %1").arg(m_parsedArguments["temp_path"])); - printText(QString("Application temp folder:\n -> %1").arg(m_parsedArguments["output_temp_path"])); -} - -bool FormUpdater::printUpdateInformation() { - qApp->processEvents(); - - bool update_file_exists = QFile::exists(m_parsedArguments["update_file_path"]); - - printNewline(); - printHeading("Update information"); - - printText(QString("Version change:\n -> %1 --> %2").arg(m_parsedArguments["current_version"], m_parsedArguments["next_version"])); - printText(QString("Update file:\n -> %1").arg(m_parsedArguments["update_file_path"])); - printText(QString("Update file exists:\n -> %1").arg(update_file_exists ? "yes" : "no")); - printText(QString("Update file size:\n -> %1 bytes").arg(QFileInfo(m_parsedArguments["update_file_path"]).size())); - - if (!update_file_exists) { - printText("\nUpdate file does not exist or is corrupted."); - } - - return update_file_exists; -} - -bool FormUpdater::doPreparationCleanup() { - qApp->processEvents(); - - printNewline(); - printHeading("Initial cleanup"); - - // Check if main RSS Guard instance is running. - for (int i = 1; i <= 4; i++) { - qApp->processEvents(); - - if (i == 4) { - printText("Updater made 3 attempts to exit RSS Guard and it failed. Update cannot continue."); - return false; - } - - printText(QString("Check for running instances of RSS Guard, attempt %1.").arg(i)); - - if (static_cast(QCoreApplication::instance())->sendMessage(APP_QUIT_INSTANCE)) { - printText("The main application is running. Quitting it."); - printText("Waiting for 6000 ms for main application to finish."); - - QEventLoop blocker(this); - QTimer::singleShot(6000, &blocker, SLOT(quit())); - blocker.exec(); - } - else { - printText("The main application is not running."); - break; - } - } - - // Remove old folders. - if (QDir(m_parsedArguments["output_temp_path"]).exists()) { - if (!IOFactory::removeDirectory(m_parsedArguments["output_temp_path"])) { - printText("Cleanup of old temporary files failed."); - return false; - } - else { - printText("Cleanup of old temporary files is done."); - } - } - - if (!IOFactory::removeDirectory(m_parsedArguments["rssguard_path"], - QStringList(), - // Keep the folder with settings. - QStringList() << "data")) { - printText("Full cleanup of actual RSS Guard installation failed."); - printText("Some files from old installation may persist."); - } - - return true; -} - -bool FormUpdater::doExtractionAndCopying() { - qApp->processEvents(); - - printNewline(); - printHeading("Extraction of update package"); - - QStringList extractor_arguments; - QProcess process_extractor(this); - - extractor_arguments << "x" << "-r" << "-y" << - QString("-o%1").arg(m_parsedArguments["output_temp_path"]) << - m_parsedArguments["update_file_path"]; - - printText(QString("Calling extractor \'%1\' with these arguments:").arg(APP_7ZA_EXECUTABLE)); - - foreach(const QString &argument, extractor_arguments) { - printText(QString(" -> '%1'").arg(argument)); - } - - process_extractor.setEnvironment(QProcessEnvironment::systemEnvironment().toStringList()); - process_extractor.setWorkingDirectory(qApp->applicationDirPath()); - - process_extractor.start(APP_7ZA_EXECUTABLE, extractor_arguments); - - if (!process_extractor.waitForFinished()) { - process_extractor.close(); - } - - printText(process_extractor.readAll()); - printText(QString("Extractor finished with exit code \'%1\'.").arg(process_extractor.exitCode())); - - if (process_extractor.exitCode() != 0 || process_extractor.exitStatus() != QProcess::NormalExit) { - printText("Extraction failed due errors. Update cannot continue."); - return false; - } - - // Find "rssguard" subfolder path in - QFileInfoList rssguard_temp_root = QDir(m_parsedArguments["output_temp_path"]).entryInfoList(QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks); - - if (rssguard_temp_root.size() != 1) { - printText("Could not find root of downloaded application data."); - return false; - } - - printNewline(); - - QString rssguard_single_temp_root = rssguard_temp_root.at(0).absoluteFilePath(); - - if (!IOFactory::copyDirectory(rssguard_single_temp_root, m_parsedArguments["rssguard_path"])) { - printText("Critical error appeared during copying of application files."); - return false; - } - - return true; -} - -bool FormUpdater::doFinalCleanup() { - bool result_file; - bool result_path; - - qApp->processEvents(); - - printNewline(); - printHeading("Final cleanup"); - - result_path = IOFactory::removeDirectory(m_parsedArguments["output_temp_path"]); - result_file = QFile::remove(m_parsedArguments["update_file_path"]); - - printText(QString("Removing temporary files\n -> %1 -> %2\n -> %3 -> %4").arg( - m_parsedArguments["output_temp_path"], result_path ? "success" : "failure", - m_parsedArguments["update_file_path"], result_file ? "success" : "failure")); - - return result_file && result_path; -} - -void FormUpdater::keyPressEvent(QKeyEvent* event) { - if (event->matches(QKeySequence::Copy)) { - event->accept(); - return; - } - else { - event->ignore(); - } - - switch (m_state) { - case NoState: - break; - - case ExitNormal: - case ExitError: - qApp->quit(); - break; - - default: - break; - } -} - -void FormUpdater::printHeading(const QString &header) { - m_txtOutput->setAlignment(Qt::AlignCenter); - m_txtOutput->append(QString("****** %1 ******\n").arg(header)); -} - -void FormUpdater::printText(const QString &text) { - m_txtOutput->setAlignment(Qt::AlignLeft); - m_txtOutput->append(text); -} - -void FormUpdater::printNewline() { - m_txtOutput->append(""); -} - -void FormUpdater::moveToCenterAndResize() { - resize(600, 400); - move(qApp->desktop()->screenGeometry().center() - rect().center()); -} diff --git a/src/updater/formupdater.h b/src/updater/formupdater.h deleted file mode 100644 index 35cff9480..000000000 --- a/src/updater/formupdater.h +++ /dev/null @@ -1,105 +0,0 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2014 by Martin Rotter -// -// RSS Guard 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 3 of the License, or -// (at your option) any later version. -// -// RSS Guard 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 RSS Guard. If not, see . - -#ifndef FORMUPDATER_H -#define FORMUPDATER_H - -#include - -#include -#include - - -class QTextEdit; -class QKeyEvent; - -class FormUpdater : public QMainWindow { - Q_OBJECT - - public: - // Describes the state of updater. - enum UpdaterState { - NoState, - ExitNormal, - ExitError - }; - - // Constructors and destructors. - explicit FormUpdater(QWidget *parent = 0); - virtual ~FormUpdater(); - - // Prints various texts. - void printText(const QString &text); - void printNewline(); - void printHeading(const QString &header); - - // Starts the whole update process. - void startUpgrade(); - - // Various parts of update process. - void saveArguments(); - void printArguments(); - bool printUpdateInformation(); - bool doPreparationCleanup(); - bool doExtractionAndCopying(); - bool doFinalCleanup(); - void executeMainApplication(); - - // Used to trigger signal informaing about new debug message. - void triggerDebugMessageConsumption(QtMsgType type, const QString &message); - - // Debug handlers for messages. -#if QT_VERSION >= 0x050000 - static void debugHandler(QtMsgType type, - const QMessageLogContext &placement, - const QString &message); -#else - static void debugHandler(QtMsgType type, - const char *message); -#endif - - public slots: - // Should be always called on GUI thread which is enforced - // by signal/slot auto connection. - void consumeDebugMessage(QtMsgType type, const QString &message); - - signals: - // Emitted if new debug messaages is produced and should be printed. - void debugMessageProduced(QtMsgType type, QString message); - - protected: - // Catch the "press any key event" to exit the updater. - void keyPressEvent(QKeyEvent *event); - - // Moves the window into the center of the screen and resizes it. - void moveToCenterAndResize(); - - // File/directory manipulators. - bool copyDirectory(QString source, QString destination); - bool removeDirectory(const QString & directory_name, - const QStringList &exception_file_list = QStringList(), - const QStringList &exception_folder_list = QStringList()); - - private: - UpdaterState m_state; - QTextEdit *m_txtOutput; - QHash m_parsedArguments; - - static FormUpdater *s_instance; -}; - -#endif // FORMUPDATER_H diff --git a/src/updater/main.cpp b/src/updater/main.cpp deleted file mode 100755 index e4fb0101d..000000000 --- a/src/updater/main.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of RSS Guard. -// -// Copyright (C) 2011-2014 by Martin Rotter -// -// RSS Guard 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 3 of the License, or -// (at your option) any later version. -// -// RSS Guard 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 RSS Guard. If not, see . - -#include "definitions/definitions.h" -#include "qtsingleapplication/qtsingleapplication.h" -#include "updater/formupdater.h" - -#include -#include -#include -#include -#include -#include - - -// Main entry point to "rssguard_updater.exe". -// It expects 4 ARGUMENTS: -// 0) - the actual path of this process, -// 1) - string with current version, -// 2) - string with future version, -// 3) - path to RSS Guard ("rssguard.exe") file, -// 4) - path to update file (stored in TEMP folder). -int main(int argc, char *argv[]) { - // Instantiate base application object. - QtSingleApplication application(APP_LOW_NAME, argc, argv); - QtSingleApplication::setQuitOnLastWindowClosed(true); - - FormUpdater main_form; - - // Setup debug output system. -#if QT_VERSION >= 0x050000 - qInstallMessageHandler(FormUpdater::debugHandler); -#else - qInstallMsgHandler(FormUpdater::debugHandler); -#endif - - main_form.show(); - main_form.startUpgrade(); - - return application.exec(); -}