diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..fe952f5 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,320 @@ +name: Deploy + +on: + push: + branches: + - master + +jobs: + Check: + runs-on: ubuntu-16.04 + outputs: + deploy: ${{ steps.check.outputs.check }} + steps: + - uses: actions/checkout@v2 + - uses: lukka/get-cmake@latest + + - name: Install Python3 + run: | + sudo apt install -y python3 + + - id: check + name: Check Commit Message + run: | + git clone https://github.com/antony-jr/AppImageUpdater + cd AppImageUpdater + git tag > /tmp/tags.txt + cd .. + rm -rf AppImageUpdater + cat /tmp/tags.txt + result=$(python3 scripts/check.py "$(git log -1 --pretty=%B)" "/tmp/tags.txt") + echo "::set-output name=check::$result" + BuildAndDeploy: + name: "Build And Deploy" + runs-on: ubuntu-16.04 + needs: Check + steps: + - uses: actions/checkout@v2 + - uses: lukka/get-cmake@latest + + - name: Install Qt and other Dependencies + run: | + sudo add-apt-repository ppa:beineri/opt-qt-5.12.9-xenial -y + sudo apt-get update -qq + sudo apt-get -y install build-essential libgl1-mesa-dev xvfb qt512base \ + qt512quickcontrols2 qt512quickcontrols qt512declarative \ + automake libtool \ + libcairo-dev libfuse-dev zsync desktop-file-utils \ + ca-certificates autoconf libtool patch wget \ + vim-common desktop-file-utils pkg-config \ + libarchive-dev librsvg2-dev librsvg2-bin liblzma-dev + + - name: Install OpenSSL + run: | + wget -q "https://www.openssl.org/source/openssl-1.1.1h.tar.gz" + tar -xf openssl-1.1.1h.tar.gz + cd openssl-1.1.1h + ./config shared --prefix=/usr/ --openssldir=/usr/ + make -j$(nproc) > /dev/null + sudo make install > /dev/null + cd .. + rm -rf openssl-1.1.1h + + - name: Install Boost 1.75.0 + run: | + wget -q "https://dl.bintray.com/boostorg/release/1.75.0/source/boost_1_75_0.tar.gz" + tar -xf boost_1_75_0.tar.gz + cd boost_1_75_0 + ./bootstrap.sh + sudo cp b2 /usr/bin/ + echo "using gcc ;" > ~/user-config.jam + export BOOST_ROOT=$PWD + export BOOST_BUILD_PATH=$PWD/tools/build + sudo BOOST_ROOT=$PWD BOOST_BUILD_PATH=$PWD/tools/build b2 cxxflags="-std=c++14" variant=release link=static install -j$(nproc) --with-system --with-chrono --with-random > /dev/null + cd .. + sudo rm -rf boost_1_75_0 + + - name: Install Torrent Rasterbar + run: | + wget -q "https://github.com/arvidn/libtorrent/releases/download/libtorrent-1.2.8/libtorrent-rasterbar-1.2.8.tar.gz" + tar -xvf libtorrent-rasterbar-1.2.8.tar.gz + cd libtorrent-rasterbar-1.2.8 + cmake . + make -j$(nproc) + sudo make install -j$(nproc) + # sudo ln -s /usr/local/lib/libtorrent-rasterbar.so.1.2.8 /usr/lib/libtorrent-rasterbar.so.10 + cd .. + rm -rf libtorrent-rasterbar-1.2.8 + + - name: Install QAppImageUpdate + run: | + source /opt/qt*/bin/qt*-env.sh || true + git clone https://github.com/antony-jr/QAppImageUpdate + cd QAppImageUpdate + git checkout v2.0.2 + source /opt/qt*/bin/qt*-env.sh || true + cmake -DDECENTRALIZED_UPDATE_ENABLED=ON . + make -j$(nproc) + sudo make install + cd .. + rm -rf QAppImageUpdate + + - name: Install libappimage + run: | + git clone https://github.com/AppImage/libappimage --recursive + cd libappimage + git checkout 176f8885690eee830a226ee350e023c6e5e8f9d7 + cmake . -DBUILD_TESTING:bool=False + make -j$(nproc) + sudo make install + cd .. + rm -rf libappimage + + - name: Build AppImage Updater + id: appimage_build + run: | + git submodule init + git submodule update # Download all required submodules. + export VERSION=$(git rev-parse --short HEAD) + source /opt/qt*/bin/qt*-env.sh || true + cmake -DAPPIMAGE_UPDATER_VERSION=2.0.0 \ + -DAPPIMAGE_UPDATER_COMMIT=$(git rev-parse --short HEAD) \ + -DAPPIMAGE_UPDATER_BUILD_TIME="$(date +'%A, %B %d %Y %T')" \ + -DAPPIMAGE_UPDATER_BUILD_NO=$GITHUB_RUN_NUMBER . + make -j$(nproc) + cd cli + cmake -DAPPIMAGE_UPDATER_VERSION=2.0.0 \ + -DAPPIMAGE_UPDATER_COMMIT=$(git rev-parse --short HEAD) \ + -DAPPIMAGE_UPDATER_BUILD_TIME="$(date +'%A, %B %d %Y %T')" \ + -DAPPIMAGE_UPDATER_BUILD_NO=$GITHUB_RUN_NUMBER . + make -j$(nproc) + cp appimageupdater .. + cd .. + + mkdir -p appdir/usr/lib + mkdir -p appdir/usr/bin ; + cp -r appdir appdir-cli + strip AppImageUpdater ; cp AppImageUpdater appdir/usr/bin/ + strip appimageupdater ; cp appimageupdater appdir-cli/usr/bin + mkdir -p appdir/usr/share/applications ; + mkdir -p appdir-cli/usr/share/applications; + cp build_resources/AppImageUpdater.desktop appdir/usr/share/applications/ + cp build_resources/appimageupdater.desktop appdir-cli/usr/share/applications/ + mkdir -p appdir/usr/share/icons/hicolor/256x256/apps/ ; + mkdir -p appdir-cli/usr/share/icons/hicolor/256x256/apps/ ; + cp build_resources/AppImageUpdater.png appdir/usr/share/icons/hicolor/256x256/apps/AppImageUpdater.png + cp build_resources/AppImageUpdater.png appdir-cli/usr/share/icons/hicolor/256x256/apps/AppImageUpdater.png + wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" + chmod a+x linuxdeployqt-continuous-x86_64.AppImage + unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH + export VERSION=$(git rev-parse --short HEAD) + ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -qmldir=$(qmake -query QT_INSTALL_QML) -bundle-non-qt-libs + ./linuxdeployqt-continuous-x86_64.AppImage appdir-cli/usr/share/applications/*.desktop -bundle-non-qt-libs + + # Copy QtQuickShapes stuff. + cp -r $(qmake -query QT_INSTALL_QML)/QtQuick/Shapes appdir/usr/qml/QtQuick + cp -r $(qmake -query QT_HOST_LIBS)/libQt5QuickShapes.so* appdir/usr/lib/ + + wget -c -nv "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" + chmod +x appimagetool-x86_64.AppImage + + deploy=${{ needs.Check.outputs.deploy }} + if [ "$deploy" = "false" ] + then + export tag="development" + else + export tag="latest" + fi + + ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|antony-jr|AppImageUpdater|$tag|AppImageUpdater*-x86_64.AppImage.zsync" appdir + ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|antony-jr|AppImageUpdater|$tag|appimageupdatercli*-x86_64.AppImage.zsync" appdir-cli + + wget -q "https://github.com/antony-jr/MakeAppImageTorrent/releases/download/Mk-2/MakeAppImageTorrent-609383f-x86_64.AppImage" + chmod +x MakeAppImageTorrent-609383f-x86_64.AppImage + + mv AppImage_Updater-$VERSION-x86_64.AppImage AppImageUpdater-$VERSION-x86_64.AppImage + rm -rf AppImage_Updater-$VERSION-x86_64.AppImage.zsync + zsyncmake AppImageUpdater-$VERSION-x86_64.AppImage + + ./MakeAppImageTorrent-609383f-x86_64.AppImage AppImageUpdater-*-x86_64.AppImage + ./MakeAppImageTorrent-609383f-x86_64.AppImage appimageupdatercli-*-x86_64.AppImage + + echo ::set-output name=appimagefile::AppImageUpdater-$VERSION-x86_64.AppImage + echo ::set-output name=appimagefilecli::appimageupdatercli-$VERSION-x86_64.AppImage + echo ::set-output name=zsyncfile::AppImageUpdater-$VERSION-x86_64.AppImage.zsync + echo ::set-output name=zsyncfilecli::appimageupdatercli-$VERSION-x86_64.AppImage.zsync + echo ::set-output name=torrentfile::AppImageUpdater-$VERSION-x86_64.AppImage.torrent + echo ::set-output name=torrentfilecli::appimageupdatercli-$VERSION-x86_64.AppImage.torrent + + # Remove development appimages. + rm -rf MakeAppImageTorrent* + rm -rf appimagetool* + rm -rf linuxdeployqt* + + ls + + - name: Delete Old Development GUI Builds + if: needs.Check.outputs.deploy == 'false' + uses: mknejp/delete-release-assets@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: development + assets: AppImageUpdater-*-x86_64.AppImage* + fail-if-no-assets: false + fail-if-no-release: false + + - name: Delete Old Development CLI Builds + if: needs.Check.outputs.deploy == 'false' + uses: mknejp/delete-release-assets@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: development + assets: appimageupdatercli-*-x86_64.AppImage* + fail-if-no-assets: false + fail-if-no-release: false + + - name: Upload Development GUI Build + if: needs.Check.outputs.deploy == 'false' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./AppImageUpdater-*-x86_64.AppImage* + tag: development + overwrite: true + file_glob: true + prerelease: true + release_name: "AppImage Updater Development Builds" + body: "The latest and greatest of AppImage Updater!" + + - name: Upload Development CLI Build + if: needs.Check.outputs.deploy == 'false' + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ./appimageupdatercli-*-x86_64.AppImage* + tag: development + overwrite: true + file_glob: true + prerelease: true + release_name: "AppImage Updater Development Builds" + body: "The latest and greatest of AppImage Updater!" + + - name: Create Release + if: needs.Check.outputs.deploy != 'false' + id: create_release + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.Check.outputs.deploy }} + release_name: Release ${{ needs.Check.outputs.deploy }} + body_path: ./release_notes/${{ needs.Check.outputs.deploy }}.md + draft: false + prerelease: false + + - name: Upload AppImage Release Asset + if: needs.Check.outputs.deploy != 'false' + uses: actions/upload-release-asset@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{steps.appimage_build.outputs.appimagefile}} + asset_name: ${{steps.appimage_build.outputs.appimagefile}} + asset_content_type: application/octet-stream + + - name: Upload AppImage CLI Release Asset + if: needs.Check.outputs.deploy != 'false' + uses: actions/upload-release-asset@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{steps.appimage_build.outputs.appimagefilecli}} + asset_name: ${{steps.appimage_build.outputs.appimagefilecli}} + asset_content_type: application/octet-stream + + - name: Upload AppImage Zsync File Release Asset + if: needs.Check.outputs.deploy != 'false' + uses: actions/upload-release-asset@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{steps.appimage_build.outputs.zsyncfile}} + asset_name: ${{steps.appimage_build.outputs.zsyncfile}} + asset_content_type: application/octet-stream + + + - name: Upload AppImage Zsync File CLI Release Asset + if: needs.Check.outputs.deploy != 'false' + uses: actions/upload-release-asset@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{steps.appimage_build.outputs.zsyncfilecli}} + asset_name: ${{steps.appimage_build.outputs.zsyncfilecli}} + asset_content_type: application/octet-stream + + - name: Upload AppImage Torrent File Release Asset + if: needs.Check.outputs.deploy != 'false' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{steps.appimage_build.outputs.torrentfile}} + asset_name: ${{steps.appimage_build.outputs.torrentfile}} + asset_content_type: application/octet-stream + + - name: Upload AppImage Torrent File CLI Release Asset + if: needs.Check.outputs.deploy != 'false' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./${{steps.appimage_build.outputs.torrentfilecli}} + asset_name: ${{steps.appimage_build.outputs.torrentfilecli}} + asset_content_type: application/octet-stream diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/.gitmodules b/.gitmodules index d268f03..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "AppImageUpdaterBridge"] - path = AppImageUpdaterBridge - url = https://github.com/antony-jr/AppImageUpdaterBridge diff --git a/.img/poster.png b/.img/poster.png index 9a2a391..de04795 100644 Binary files a/.img/poster.png and b/.img/poster.png differ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6b79f20..0000000 --- a/.travis.yml +++ /dev/null @@ -1,52 +0,0 @@ -language: cpp -compiler: gcc -sudo: require -dist: xenial - -before_install: - - sudo apt-get update -qq - -install: - - sudo bash build_resources/build_qt_static.sh - -script: - - export VERSION=$(git rev-parse --short HEAD) - - export APPIMAGE_UPDATER_VERSION=1-alpha - - export APPIMAGE_UPDATER_COMMIT=$(git rev-parse --short HEAD) - - export APPIMAGE_UPDATER_BUILD_NO=$TRAVIS_BUILD_NUMBER - - export APPIMAGE_UPDATER_BUILD_TIME=$(date) - - /usr/local/Qt-5.12.8/bin/qmake . - - make -j$(nproc) - - cd cli - - /usr/local/Qt-5.12.8/bin/qmake . - - make -j$(nproc) - - mkdir -p appdir/usr/bin ; strip appimageupdater ; cp appimageupdater appdir/usr/bin/ - - cd .. - - mkdir -p appdir/usr/bin ; strip AppImageUpdater ; cp AppImageUpdater appdir/usr/bin/ - - mkdir -p appdir/usr/share/applications ; cp build_resources/AppImageUpdater.desktop appdir/usr/share/applications/ - - mkdir -p appdir/usr/share/icons/hicolor/256x256/apps/ ; cp build_resources/AppImageUpdater.png appdir/usr/share/icons/hicolor/256x256/apps/AppImageUpdater.png - - wget -c -nv "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" - - chmod a+x linuxdeployqt-continuous-x86_64.AppImage - - unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH - - export VERSION=$(git rev-parse --short HEAD) - - ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -bundle-non-qt-libs - - wget -c -nv "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" - - chmod +x appimagetool-x86_64.AppImage - - ./appimagetool-x86_64.AppImage -u "gh-releases-zsync|antony-jr|AppImageUpdater|continuous|AppImageUpdater*-x86_64.AppImage.zsync" appdir - - cd cli - - ../linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -bundle-non-qt-libs - - ../appimagetool-x86_64.AppImage -u "gh-releases-zsync|antony-jr|AppImageUpdater|continuous-cli|appimageupdater*-x86_64.AppImage.zsync" appdir - - cp appimageupdater*.AppImage .. - - cp *zsync .. - - cd .. - - ls # for debug - -after_success: - - wget "https://github.com/probonopd/uploadtool/raw/master/upload.sh" - - bash upload.sh AppImageUpdater*.AppImage* - - UPLOADTOOL_SUFFIX=cli bash upload.sh appimageupdater*.AppImage* - -branches: - except: - - # Do not build tags that we create when we upload to GitHub Releases - - /^(?i:continuous)/ diff --git a/AppImageImageProvider.cc b/AppImageImageProvider.cc new file mode 100644 index 0000000..2df2f27 --- /dev/null +++ b/AppImageImageProvider.cc @@ -0,0 +1,43 @@ +#include "AppImageImageProvider.hpp" + +AppImageImageProvider::AppImageImageProvider() + : QQuickImageProvider(QQuickImageProvider::Pixmap) { } +AppImageImageProvider::~AppImageImageProvider() { + for(auto it = m_DB.begin(), + end = m_DB.end(); + it != end; + ++it) { + delete (*it); + } +} + + +void AppImageImageProvider::addImage(const QString &id, QByteArray *data) { + if(m_DB.contains(id)){ + /// Remove old one and add this. + auto ptr = m_DB.take(id); + delete ptr; + m_DB[id] = data; + }else{ + m_DB[id] = data; + } +} + + +QPixmap AppImageImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) { + int width = 100; + int height = 100; + + if (size) + *size = QSize(width, height); + QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width, + requestedSize.height() > 0 ? requestedSize.height() : height); + + if(!m_DB.contains(id)){ + return pixmap; + } + + auto data = m_DB.value(id); + pixmap.loadFromData(*data); + return pixmap; +} diff --git a/AppImageImageProvider.hpp b/AppImageImageProvider.hpp new file mode 100644 index 0000000..1e3e91b --- /dev/null +++ b/AppImageImageProvider.hpp @@ -0,0 +1,17 @@ +#ifndef APPIMAGE_IMAGE_PROVIDER_HPP_INCLUDED +#define APPIMAGE_IMAGE_PROVIDER_HPP_INCLUDED +#include +#include +#include +#include +class AppImageImageProvider : public QQuickImageProvider +{ + QHash m_DB; +public: + AppImageImageProvider(); + ~AppImageImageProvider(); + void addImage(const QString&, QByteArray*); + QPixmap requestPixmap(const QString&, QSize*, const QSize&) override; +}; + +#endif diff --git a/AppImageUpdater.cc b/AppImageUpdater.cc deleted file mode 100644 index 7b33e23..0000000 --- a/AppImageUpdater.cc +++ /dev/null @@ -1,390 +0,0 @@ -#include -#include -#include -#include - -using namespace AppImageUpdaterBridge; - -#define SELF_UPDATER_IDLE_TIME 4000 // 4 Secs for now - -/* DR => Dereference pointers. */ -#define DR(pointer) (*pointer) - -/* Access UserInterface. */ -#define AUI(Object) DR(_pUi.Object) - -/* AppImage Delta Revisioner Signals. */ -#define ADRUA_SIGNAL &AppImageDeltaRevisioner::updateAvailable - -AppImageUpdater::AppImageUpdater(bool minimized, QWidget *parent) - : QWidget(parent, Qt::WindowStaysOnTopHint) -{ - _pUi.setupUi(this); - centerPos = QGuiApplication::primaryScreen()->geometry().center(); - setAcceptDrops(true); - - /* Construct tray icon. */ - _pTIcon = new QSystemTrayIcon(this); - _pTIcon->setIcon(QIcon(QPixmap(QString::fromUtf8(":/logo.png")))); - // Menu context for the tray icon - QMenu *menu = new QMenu(this); - menu->addAction(QString::fromUtf8("Show / Hide"), - this, - &AppImageUpdater::showHide); - menu->addAction(QString::fromUtf8("Quit"), - this, - &AppImageUpdater::quit); - - _pTIcon->setContextMenu(menu); - connect(_pTIcon, &QSystemTrayIcon::activated, this, &AppImageUpdater::showHideWindow); - _pTIcon->show(); - - // Set window flags to stay on top for About Dialog - _pAboutMessageBox.setWindowFlags(Qt::WindowStaysOnTopHint); - - /* Do not show the app if it ran on startup. */ - if(_pSettings.isRunOnStartup() && minimized) { - this->hide(); - } else { - this->move(centerPos - this->rect().center()); - this->show(); - } - - /* This is oftenly used pixmap , Thus to reduce overhead we are caching it. */ - _pDropHere = QPixmap(QString::fromUtf8(":/dotted_square_drop.png")); - _pDropNorm = QPixmap(QString::fromUtf8(":/dotted_square.png")); - _pWindowIcon = QIcon(QPixmap(QString::fromUtf8(":/logo.png"))); - - /* Build about message box. */ - { - QString AboutText; - AboutText.append("AppImage Updater version "); - AboutText.append(QString(APPIMAGE_UPDATER_VERSION)); - AboutText.append(" (commit "); - AboutText.append(QString(APPIMAGE_UPDATER_COMMIT)); - AboutText.append(") , Build "); - AboutText.append(QString(APPIMAGE_UPDATER_BUILD_NO)); - AboutText.append(" , Built on "); - AboutText.append(QString(APPIMAGE_UPDATER_BUILD_TIME)); - AboutText.append(".

"); - AboutText.append("The GNU Lesser General Public License v3.
"); - AboutText.append("Copyright © 2018 , Antony Jr.

"); - AboutText.append("Icons by FlatIcon.
"); - AboutText.append("Released under Creative Commons.

"); - AboutText.append("Report issues at github.
"); - AboutText.append("This project is maintained by Antony Jr."); - _pAboutMessageBox.setTextFormat(Qt::RichText); - _pAboutMessageBox.setText(AboutText); - _pAboutMessageBox.setIconPixmap(QPixmap(QString::fromUtf8(":/logo.png")).scaled( 128, 128, Qt::KeepAspectRatio)); - } - - - /* Check for updates. */ - _bUpdateStarted = true; - - m_Updater = new AppImageDeltaRevisioner(/*single threaded=*/false, this); - m_Updater->setShowLog(true); - - int flags = 0; - if(_pSettings.isShowUpdateDialogs()){ - flags = (AppImageUpdaterDialog::Default ^ - AppImageUpdaterDialog::ShowErrorDialog ^ - AppImageUpdaterDialog::ShowBeforeProgress ^ - AppImageUpdaterDialog::NotifyWhenNoUpdateIsAvailable) | AppImageUpdaterDialog::AlertWhenAuthorizationIsRequired; - } - - _pUpdateDialog = new AppImageUpdaterDialog(QPixmap(QString::fromUtf8(":/default_icon.png")),this,flags); - _pUpdateDialog->move(centerPos - _pUpdateDialog->rect().center()); - - /* Special connect */ - connect(_pUpdateDialog, &AppImageUpdaterDialog::quit, this, &AppImageUpdater::quit, Qt::DirectConnection); - - _pUpdateDialog->init(m_Updater); - - /* Connect buttons. */ - connect(_pUi.settingsBtn, &QPushButton::pressed, &_pSettings, &QDialog::exec); - connect(_pUi.exitBtn, &QPushButton::pressed, this, &AppImageUpdater::quit, Qt::DirectConnection); - connect(_pUi.aboutBtn, &QPushButton::pressed, this, &AppImageUpdater::showAbout); - - /* Program logic. */ - connect(_pUpdateDialog, &AppImageUpdaterDialog::requiresAuthorization, &_pAuthorizationDialog, &AuthorizationDialog::handleAuthorization); - connect(&_pAuthorizationDialog, &AuthorizationDialog::finished, this, &AppImageUpdater::handleAuthorizationFinished, Qt::UniqueConnection); - connect(_pUpdateDialog, &AppImageUpdaterDialog::started, this, &AppImageUpdater::handleStarted); - connect(_pUpdateDialog, &AppImageUpdaterDialog::canceled, this, &AppImageUpdater::handleCanceled); - connect(_pUpdateDialog, &AppImageUpdaterDialog::error, this, &AppImageUpdater::handleAutoUpdateError); - connect(_pUpdateDialog, &AppImageUpdaterDialog::finished, this, &AppImageUpdater::handleFinished); - return; -} - -AppImageUpdater::~AppImageUpdater() -{ - - return; -} - -void AppImageUpdater::gracefulShow(void) -{ - this->hide(); - this->move(centerPos - this->rect().center()); - this->show(); - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage(QString::fromUtf8("AppImage Updater"), - QString::fromUtf8("The Application is running already , Please close this instance to start a new one.")); - } - return; -} - -void AppImageUpdater::closeEvent(QCloseEvent *e) -{ - this->hide(); - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage(QString::fromUtf8("AppImage Updater"), - QString::fromUtf8("The Application is running in the background , Click on the system tray icon to use it.")); - } - e->ignore(); - return; -} - -void AppImageUpdater::handleStarted(void) -{ - _bUpdateStarted = true; - return; -} - -void AppImageUpdater::handleCanceled(void) -{ - _bUpdateStarted = false; - updateAppImagesInQueue(); - return; -} - -void AppImageUpdater::handleAutoUpdateError(QString eStr, short errorCode){ - (void)eStr; - (void)errorCode; - _bUpdateStarted = false; - updateAppImagesInQueue(); - -} - -void AppImageUpdater::handleError(short errorCode) -{ - // Ignore all permission errors. - if(errorCode == AppImageUpdaterBridge::NoReadPermission || - errorCode == AppImageUpdaterBridge::NoPermissionToReadSourceFile || - errorCode == AppImageUpdaterBridge::NoPermissionToReadWriteTargetFile) { - return; - } - if(_pSettings.isShowUpdateDialogs()){ - QMessageBox box(this); - box.setWindowTitle(QString::fromUtf8("Update Failed")); - box.setIcon(QMessageBox::Critical); - box.setText(QString::fromUtf8("Update failed for '") + - QFileInfo(_pCurrentAppImagePath).fileName() + - QString::fromUtf8("': ") + AppImageUpdaterBridge::errorCodeToDescriptionString(errorCode)); - box.exec(); - } - - _bUpdateStarted = false; - updateAppImagesInQueue(); - return; -} - -void AppImageUpdater::handleFinished(QJsonObject info) -{ - auto r = (AppImageUpdaterDialog*)QObject::sender(); - r->deleteLater(); - (void)info; - _bUpdateStarted = false; - updateAppImagesInQueue(); - return; -} - -void AppImageUpdater::handleAuthorizationFinished(QJsonObject info) -{ - (void)info; - _bUpdateStarted = false; - updateAppImagesInQueue(); - return; -} - -/* Show hide window. */ -void AppImageUpdater::showHide(void){ - this->move(centerPos - this->rect().center()); - if(this->isVisible()) { - this->hide(); - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage(QString::fromUtf8("AppImage Updater"), - QString::fromUtf8("Click on the system tray icon to use AppImage Updater.")); - } - } else { - this->show(); - } - -} -void AppImageUpdater::showHideWindow(QSystemTrayIcon::ActivationReason reason) -{ - if(reason == QSystemTrayIcon::Trigger) { - this->move(centerPos - this->rect().center()); - if(this->isVisible()) { - this->hide(); - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage(QString::fromUtf8("AppImage Updater"), - QString::fromUtf8("Click on the system tray icon to use AppImage Updater.")); - } - } else { - this->show(); - } - } - return; -} - -/* Show about dialog box. */ -void AppImageUpdater::showAbout(void) -{ - _pAboutMessageBox.exec(); - return; -} - -void AppImageUpdater::handleAppImageInformation(QJsonObject info){ - int flags = 0; - QString applicationName; - QJsonObject updInfo = (info["UpdateInformation"]).toObject(); - QString transportType = (updInfo["transport"]).toString(); - - - if(_pSettings.isShowUpdateDialogs()){ - flags = (AppImageUpdaterDialog::Default ^ - AppImageUpdaterDialog::ShowErrorDialog ^ - AppImageUpdaterDialog::ShowBeforeProgress) | - AppImageUpdaterDialog::AlertWhenAuthorizationIsRequired; - } - - _pUpdateDialog = new AppImageUpdaterDialog(QPixmap(QString::fromUtf8(":/default_icon.png")),this,flags); - _pUpdateDialog->move(centerPos - _pUpdateDialog->rect().center()); - - if(transportType == QString::fromUtf8("gh-releases-zsync") || - transportType == QString::fromUtf8("bintray-zsync")){ - applicationName = (updInfo["repo"]).toString(); - }else{ - applicationName = QFileInfo(_pCurrentAppImagePath).baseName(); - } - - _pUpdateDialog->init(m_Updater, applicationName); - - connect(m_Updater, &AppImageDeltaRevisioner::error, this, &AppImageUpdater::handleError, - Qt::UniqueConnection); - - /* Program logic. */ - connect(&_pAuthorizationDialog, &AuthorizationDialog::started, _pUpdateDialog, &QDialog::hide, Qt::DirectConnection); - connect(_pUpdateDialog, &AppImageUpdaterDialog::requiresAuthorization, &_pAuthorizationDialog, &AuthorizationDialog::handleAuthorization); - connect(&_pAuthorizationDialog, &AuthorizationDialog::finished, this, &AppImageUpdater::handleAuthorizationFinished, Qt::UniqueConnection); - connect(_pUpdateDialog, &AppImageUpdaterDialog::started, this, &AppImageUpdater::handleStarted); - connect(_pUpdateDialog, &AppImageUpdaterDialog::canceled, this, &AppImageUpdater::handleCanceled); - connect(_pUpdateDialog, &AppImageUpdaterDialog::finished, this, &AppImageUpdater::handleFinished); - return; - -} - -/* Updates AppImages in queue. */ -void AppImageUpdater::updateAppImagesInQueue(void) -{ - if(_bUpdateStarted == true) { - return; - } - if(_pAppImagePaths.isEmpty()) { - AUI(statusLbl).setText(QString::fromUtf8("Nothing is Queued for Update.")); - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage("AppImage Updater", "Finished all queued updates."); - } - return; - } - - _bUpdateStarted = true; - _pCurrentAppImagePath = _pAppImagePaths.dequeue(); - - const QString msg("Updating %1 , Queued %2 AppImage(s) for Update."); - QString AppImageSName = QFileInfo(_pCurrentAppImagePath).fileName(); - if(AppImageSName.length() > 20) { - AppImageSName = AppImageSName.left(18); - AppImageSName += QString::fromUtf8(".."); - } - AUI(statusLbl).setText(msg.arg(AppImageSName).arg(_pAppImagePaths.size())); - - QObject::connect(m_Updater, - &AppImageDeltaRevisioner::embededInformation, - this, &AppImageUpdater::handleAppImageInformation, - Qt::UniqueConnection); - - m_Updater->setAppImage(_pCurrentAppImagePath); - m_Updater->getAppImageEmbededInformation(); - return; -} - -/* drop move event override. */ -void AppImageUpdater::dragMoveEvent(QDragMoveEvent *e) -{ - e->accept(); - /* Lets react to the user's drag. */ - AUI(dragAndDropArea).setPixmap(_pDropHere); - return; -} - -/* drop leave event override. */ -void AppImageUpdater::dragLeaveEvent(QDragLeaveEvent *e) -{ - Q_UNUSED(e); - /* Reset back to normal. */ - AUI(dragAndDropArea).setPixmap(_pDropNorm); - return; -} - -/* drop enter event override. */ -void AppImageUpdater::dragEnterEvent(QDragEnterEvent *e) -{ - if (e->mimeData()->hasUrls()) { - e->acceptProposedAction(); - } - return; -} - -/* handle drop event. */ -void AppImageUpdater::dropEvent(QDropEvent *e) -{ - /* Reset back to normal. */ - AUI(dragAndDropArea).setPixmap(_pDropNorm); - - /* Notification message template. */ - const QString statusMsg("%1 AppImage(s) Queued for Update."); - const QString msg("'%1' is queued for update."); - const QString msgDir("The contents of '%1' has been recursively queued for update!"); - foreach (const QUrl &url, e->mimeData()->urls()) { - QString fileName = url.toLocalFile(); - if(QFileInfo(fileName).isDir()) { - QDirIterator dirIt(fileName, - QStringList() << "*.AppImage" - << "*.desktop", - QDir::Files, - QDirIterator::Subdirectories); - while (dirIt.hasNext()) { - auto file = dirIt.next(); - _pAppImagePaths.enqueue(file); - QCoreApplication::processEvents(); - } - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage(QString::fromUtf8("AppImage Updater"), msgDir.arg(fileName)); - } - continue; - } - _pAppImagePaths.enqueue(fileName); - if(_pSettings.isShowSystemTrayNotificationMessages()) { - _pTIcon->showMessage(QString::fromUtf8("AppImage Updater"), msg.arg(fileName)); - } - AUI(statusLbl).setText(statusMsg.arg(_pAppImagePaths.size())); - QCoreApplication::processEvents(); - } - - if(_bUpdateStarted != true) { - updateAppImagesInQueue(); - } - return; -} diff --git a/AppImageUpdater.hpp b/AppImageUpdater.hpp deleted file mode 100644 index 6c9c993..0000000 --- a/AppImageUpdater.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef APPIMAGEUPDATER_HPP_INCLUDED -#define APPIMAGEUPDATER_HPP_INCLUDED - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class AppImageUpdater : public QWidget -{ - Q_OBJECT -public: - AppImageUpdater(bool minimized, QWidget *parent = nullptr); - ~AppImageUpdater(); -public Q_SLOTS: - void gracefulShow(void); -private Q_SLOTS: - void updateAppImagesInQueue(void); - void showAbout(void); - void showHide(void); - void showHideWindow(QSystemTrayIcon::ActivationReason); - void handleAppImageInformation(QJsonObject); - void handleAuthorizationFinished(QJsonObject); - void handleFinished(QJsonObject); - void handleError(short); - void handleAutoUpdateError(QString, short); - void handleStarted(void); - void handleCanceled(void); -Q_SIGNALS: - void quit(); -private: - SettingsDialog _pSettings; - AuthorizationDialog _pAuthorizationDialog; - Ui::MainWidget _pUi; - QPoint centerPos; - QAtomicInteger _bUpdateStarted = false; - QMessageBox _pAboutMessageBox; - QString _pCurrentAppImagePath; - QPixmap _pDropHere, - _pDropNorm; - QIcon _pWindowIcon; - QQueue _pAppImagePaths; - AppImageUpdaterBridge::AppImageDeltaRevisioner *m_Updater = nullptr; - AppImageUpdaterBridge::AppImageUpdaterDialog *_pUpdateDialog = nullptr; - QSystemTrayIcon *_pTIcon = nullptr; -protected: - void closeEvent(QCloseEvent *); - void dragMoveEvent(QDragMoveEvent *); - void dragLeaveEvent(QDragLeaveEvent *); - void dragEnterEvent(QDragEnterEvent *); - void dropEvent(QDropEvent *); -}; - -#endif // APPIMAGEUPDATER_HPP_INCLUDED diff --git a/AppImageUpdater.pro b/AppImageUpdater.pro deleted file mode 100644 index e072c61..0000000 --- a/AppImageUpdater.pro +++ /dev/null @@ -1,20 +0,0 @@ -include(AppImageUpdaterBridge/AppImageUpdaterBridge.pri) -INCLUDEPATH += . $(PWD) -TEMPLATE = app -LIBS += -lutil -CONFIG += release static -TARGET = AppImageUpdater -QT += core network gui widgets concurrent - -# Build time variables which will be used inside the application. -DEFINES += "APPIMAGE_UPDATER_VERSION=\"\\\"$$(APPIMAGE_UPDATER_VERSION)\\\"\"" -DEFINES += "APPIMAGE_UPDATER_COMMIT=\"\\\"$$(APPIMAGE_UPDATER_COMMIT)\\\"\"" -DEFINES += "APPIMAGE_UPDATER_BUILD_NO=\"\\\"$$(APPIMAGE_UPDATER_BUILD_NO)\\\"\"" -DEFINES += "APPIMAGE_UPDATER_BUILD_TIME=\"\\\"$$(APPIMAGE_UPDATER_BUILD_TIME)\\\"\"" - -DEFINES += QT_DEPRECATED_WARNINGS - -HEADERS += AppImageUpdater.hpp AppImageUpdaterStandalone.hpp SettingsDialog.hpp AuthorizationDialog.hpp -SOURCES += AppImageUpdater.cc AppImageUpdaterStandalone.cc SettingsDialog.cc AuthorizationDialog.cc main.cc -RESOURCES += app_resources/resources.qrc -FORMS += AppImageUpdater.ui SettingsDialog.ui AuthorizationDialog.ui diff --git a/AppImageUpdater.ui b/AppImageUpdater.ui deleted file mode 100644 index 4d696c6..0000000 --- a/AppImageUpdater.ui +++ /dev/null @@ -1,127 +0,0 @@ - - - MainWidget - - - - 0 - 0 - 480 - 320 - - - - - 480 - 320 - - - - AppImage Updater - - - - :/logo.png:/logo.png - - - - - - - - - Qt::AlignCenter - - - - - - - About - - - - :/about.png:/about.png - - - - - - - Exit - - - - :/exit.png:/exit.png - - - - - - - - 12 - false - - - - false - - - - - - Drag 'n' Drop AppImage(s) to Update! - - - Qt::AlignCenter - - - true - - - - - - - Settings - - - - :/check.png:/check.png - - - - - - - - 240 - 200 - - - - - - - - - - :/dotted_square.png - - - true - - - Qt::AlignCenter - - - - - - - - - - - diff --git a/AppImageUpdaterBridge b/AppImageUpdaterBridge deleted file mode 160000 index f8eb89d..0000000 --- a/AppImageUpdaterBridge +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f8eb89d409e08ed912e6efdef7cd0dda2f93352d diff --git a/AppImageUpdaterStandalone.cc b/AppImageUpdaterStandalone.cc index 773f86f..077e904 100644 --- a/AppImageUpdaterStandalone.cc +++ b/AppImageUpdaterStandalone.cc @@ -2,9 +2,144 @@ #include #include #include +#include -using AppImageUpdaterBridge::AppImageUpdaterDialog; -using AppImageUpdaterBridge::AppImageDeltaRevisioner; +#include +#include +#include + +static std::vector split_string(const std::string& str, + const std::string& delimiter) +{ + std::vector strings; + + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = str.find(delimiter, prev)) != std::string::npos) + { + strings.push_back(str.substr(prev, pos - prev)); + prev = pos + 1; + } + + strings.push_back(str.substr(prev)); + return strings; +} + +static QPair getAppImageNameAndIcon(const QString &appimagePath) { + QPair r; + r.first = QString(); + r.second = QByteArray(); + + appimage::core::AppImage *appimage; + appimage::utils::ResourcesExtractor *res; + try { + appimage = new appimage::core::AppImage(appimagePath.toStdString()); + }catch(...) { + qDebug() << "Cannot contstruct AppImage"; + return r; + } + + try { + res = new appimage::utils::ResourcesExtractor(*appimage); + }catch(...) { + if(appimage) { + delete appimage; + } + qDebug() << "Cannot Resource Extract"; + return r; + } + + if(!res) { + if(appimage) { + delete appimage; + return r; + } + } + + auto desktopFile = res->extract(res->getDesktopEntryPath()); + auto data = desktopFile.data(); + + std::stringstream ss(data); + std::string dest; + while(std::getline(ss, dest, '\n')){ + if(dest == "[Desktop Entry]") { + continue; + } + + auto entry = split_string(dest, "="); + if(entry.size() != 2) { + continue; + } + + if(entry[0] == "Name") { + r.first = QString::fromStdString(entry[1]); + if(!r.second.isEmpty()) { + break; + } + } + if(entry[0] == "Icon") { + //// Some crazy Application developers don't follow the + //// Rox AppDir format so the icon can be in the top dir + //// itself. So we will check for that. + + std::vector _iconData; + QPixmap image; + QByteArray imageData; + bool cannotFind = false; + try { + _iconData = res->extract(entry[1] + ".png"); + imageData = QByteArray(_iconData.data(), _iconData.size()); + image.loadFromData(imageData); + }catch(...) { + cannotFind = true; + } + + //// libappimage does not provide anything to check if the given + //// data is a valid png image, We can do some Qt magic here. + if(!cannotFind && !image.isNull()) { + r.second = imageData; + }else { + + auto paths = res->getIconFilePaths(entry[1]); + if(paths.size()) { + auto iconData = res->extract(paths[paths.size()-1]); + r.second = QByteArray(iconData.data(), iconData.size()); + } + } + if(!r.first.isEmpty()) { + break; + } + } + QCoreApplication::processEvents(); + } + + if(r.first.isEmpty()) { + r.first = QFileInfo(appimagePath).baseName(); + } + + if(r.second.isEmpty()) { + /// Give Default Icon + QPixmap *pixmap = new QPixmap(":logo.png"); + QByteArray icon; + QBuffer buffer(&icon); + buffer.open(QIODevice::WriteOnly); + pixmap->save(&buffer, "PNG"); // writes pixmap into bytes in PNG format + r.second = icon; + delete pixmap; + } + + delete res; + delete appimage; + return r; +} + +static QByteArray getByteArrayFromPixmap(const QPixmap &icon) { + QByteArray iconData; + QBuffer iconBuffer(&iconData); + iconBuffer.open(QIODevice::WriteOnly); + icon.save(&iconBuffer, "PNG"); + return iconData; +} AppImageUpdaterStandalone::AppImageUpdaterStandalone(QString AppImagePath, int flags, QObject *parent) : QObject(parent), @@ -12,96 +147,69 @@ AppImageUpdaterStandalone::AppImageUpdaterStandalone(QString AppImagePath, int f m_AppImagePath(AppImagePath) { - m_Updater = new AppImageDeltaRevisioner(AppImagePath , this); + + m_Updater = new QAppImageUpdate(m_AppImagePath, /*singleThreaded=*/false, /*parent=*/this); m_Updater->setShowLog(true); - connect(m_Updater, - &AppImageDeltaRevisioner::embededInformation, - this, &AppImageUpdaterStandalone::handleAppImageInformation, - Qt::UniqueConnection); - connect(m_Updater, &AppImageDeltaRevisioner::error, this, &AppImageUpdaterStandalone::handleError); + + auto appimageInformation = getAppImageNameAndIcon(m_AppImagePath); + if(appimageInformation.first.isEmpty() || + appimageInformation.second.isEmpty()) { + QPixmap defaultIcon(QString::fromUtf8(":/logo.png")); + m_Updater->setIcon(getByteArrayFromPixmap(defaultIcon)); + m_Updater->setApplicationName(QFileInfo(m_AppImagePath).baseName()); + } else { + QPixmap pixmap(100, 100); + pixmap.loadFromData(appimageInformation.second); + m_Updater->setIcon(getByteArrayFromPixmap(pixmap)); + m_Updater->setApplicationName(appimageInformation.first); + } + + m_Updater->setGuiFlag(flags); + qDebug() << "Flags:: " << flags; + + connect(m_Updater, &QAppImageUpdate::error, this, &AppImageUpdaterStandalone::handleError); + connect(&m_AuthorizationDialog, &AuthorizationDialog::finished, this, &AppImageUpdaterStandalone::quit); + connect(m_Updater, &QAppImageUpdate::canceled, this, &AppImageUpdaterStandalone::handleCanceled); + connect(m_Updater, &QAppImageUpdate::finished, this, &AppImageUpdaterStandalone::handleFinished); + } AppImageUpdaterStandalone::~AppImageUpdaterStandalone() { - if(_pUpdateDialog){ - _pUpdateDialog->hide(); - _pUpdateDialog->deleteLater(); - } + m_Updater->deleteLater(); return; } void AppImageUpdaterStandalone::init(){ - m_Updater->getAppImageEmbededInformation(); + m_Updater->start(QAppImageUpdate::Action::UpdateWithGUI); } -void AppImageUpdaterStandalone::handleAppImageInformation(QJsonObject info){ - QString applicationName; - QJsonObject updInfo = (info["UpdateInformation"]).toObject(); - QString transportType = (updInfo["transport"]).toString(); - - _pUpdateDialog = new AppImageUpdaterDialog(QPixmap(QString::fromUtf8(":/default_icon.png")), nullptr, - (flags ^ AppImageUpdaterDialog::ShowErrorDialog) | - AppImageUpdaterDialog::AlertWhenAuthorizationIsRequired); - _pUpdateDialog->setWindowFlags(Qt::WindowStaysOnTopHint); - _pUpdateDialog->move(QGuiApplication::primaryScreen()->geometry().center() - _pUpdateDialog->rect().center()); - - if(transportType == QString::fromUtf8("gh-releases-zsync") || - transportType == QString::fromUtf8("bintray-zsync")){ - applicationName = (updInfo["repo"]).toString(); - }else{ - applicationName = QFileInfo(m_AppImagePath).baseName(); - } - - _pUpdateDialog->init(m_Updater, applicationName); - - //Program logic. - connect(&_pAuthorizationDialog, &AuthorizationDialog::started, _pUpdateDialog, &QDialog::hide, Qt::DirectConnection); - connect(_pUpdateDialog, &AppImageUpdaterDialog::requiresAuthorization, &_pAuthorizationDialog, &AuthorizationDialog::handleAuthorization); - connect(&_pAuthorizationDialog, &AuthorizationDialog::finished, this, &AppImageUpdaterStandalone::quit); - connect(_pUpdateDialog, &AppImageUpdaterDialog::canceled, this, &AppImageUpdaterStandalone::handleCanceled); - connect(_pUpdateDialog, &AppImageUpdaterDialog::finished, this, &AppImageUpdaterStandalone::handleFinished); - return; - -} - - - -void AppImageUpdaterStandalone::handleError(short errorCode) +void AppImageUpdaterStandalone::handleError(short errorCode, short action) { + Q_UNUSED(action); // Ignore all permission errors. - if(errorCode == AppImageUpdaterBridge::NoReadPermission || - errorCode == AppImageUpdaterBridge::NoPermissionToReadSourceFile || - errorCode == AppImageUpdaterBridge::NoPermissionToReadWriteTargetFile) { + if(errorCode == QAppImageUpdate::Error::NoReadPermission || + errorCode == QAppImageUpdate::Error::NoPermissionToReadSourceFile || + errorCode == QAppImageUpdate::Error::NoPermissionToReadWriteTargetFile) { + + m_AuthorizationDialog.handleAuthorization(QAppImageUpdate::errorCodeToDescriptionString(errorCode),m_AppImagePath); return; } - - if(flags & AppImageUpdaterDialog::ShowErrorDialog){ - QMessageBox box; - box.setWindowTitle(QString::fromUtf8("Update Failed")); - box.setIcon(QMessageBox::Critical); - box.setText(QString::fromUtf8("Update failed for '") + - QFileInfo(m_AppImagePath).fileName() + - QString::fromUtf8("': ") + AppImageUpdaterBridge::errorCodeToDescriptionString(errorCode)); - box.exec(); - - } emit quit(); return; } -void AppImageUpdaterStandalone::handleFinished(QJsonObject info) +void AppImageUpdaterStandalone::handleFinished(QJsonObject info, short action) { - (void)info; + Q_UNUSED(info); + Q_UNUSED(action); emit quit(); return; } -void AppImageUpdaterStandalone::handleCanceled(void) +void AppImageUpdaterStandalone::handleCanceled(short action) { - if(_pUpdateDialog){ - _pUpdateDialog->hide(); - m_Updater->deleteLater(); - } + Q_UNUSED(action); emit quit(); return; } diff --git a/AppImageUpdaterStandalone.hpp b/AppImageUpdaterStandalone.hpp index 0175b92..2e92136 100644 --- a/AppImageUpdaterStandalone.hpp +++ b/AppImageUpdaterStandalone.hpp @@ -1,7 +1,6 @@ #ifndef APPIMAGE_UPDATER_STANDALONE_HPP_INCLUDED #define APPIMAGE_UPDATER_STANDALONE_HPP_INCLUDED -#include -#include +#include #include #include @@ -13,19 +12,17 @@ class AppImageUpdaterStandalone : public QObject int flags; QString m_AppImagePath; - AppImageUpdaterBridge::AppImageDeltaRevisioner *m_Updater = nullptr; - AppImageUpdaterBridge::AppImageUpdaterDialog *_pUpdateDialog = nullptr; - AuthorizationDialog _pAuthorizationDialog; + QAppImageUpdate *m_Updater; + AuthorizationDialog m_AuthorizationDialog; public: AppImageUpdaterStandalone(QString, int, QObject *parent = nullptr); ~AppImageUpdaterStandalone(); void init(); private Q_SLOTS: - void handleError(short); - void handleFinished(QJsonObject); - void handleCanceled(void); - void handleAppImageInformation(QJsonObject); + void handleError(short, short); + void handleFinished(QJsonObject, short); + void handleCanceled(short); Q_SIGNALS: void quit(); }; diff --git a/AuthorizationDialog.cc b/AuthorizationDialog.cc index 49f2a13..d537990 100644 --- a/AuthorizationDialog.cc +++ b/AuthorizationDialog.cc @@ -1,10 +1,8 @@ #include -#include -#include +#include +#include #include -#include -#include -#include +#include #include #include @@ -23,6 +21,7 @@ #include #define SU_COMMAND "/usr/bin/sudo" +#define XDG_SU_COMMAND "/usr/bin/xdg-su" AuthorizationDialog::AuthorizationDialog(QWidget *parent) : QDialog(parent, Qt::WindowStaysOnTopHint) @@ -38,15 +37,6 @@ AuthorizationDialog::~AuthorizationDialog() { } -void AuthorizationDialog::handleAuthorization(QString errorString, short errorCode, QString appimagePath) -{ - QFuture future = QtConcurrent::run([this, errorString, errorCode, appimagePath]() { - doAuthorize(errorString, errorCode, appimagePath); - return; - }); - return; -} - void AuthorizationDialog::showError(QString eStr) { QMessageBox box(this); @@ -57,10 +47,8 @@ void AuthorizationDialog::showError(QString eStr) return; } -void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QString appimagePath) +void AuthorizationDialog::handleAuthorization(QString errorString, QString appimagePath) { - Q_UNUSED(errorCode); - emit started(); arguments.clear(); // Also append all the program arguments to this @@ -71,6 +59,9 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr skipNext = false; continue; } + if(arg == "--show-checking-for-update-dialog" || arg == "-c") { + continue; + } if(arg == "--standalone-update-dialog" || arg == "-d"){ skipNext = true; continue; @@ -81,7 +72,20 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr << appimagePath << QString::fromUtf8("--noconfirm"); - qDebug() << "Will be invoking updater as root with args: " << arguments; + /// Check if xdg-su command is available then just use it. + if(QFile::exists(XDG_SU_COMMAND)) { + QString commandString = program + " "; + for(const QString &arg : arguments) { + commandString += arg + " "; + } + + QStringList commandArgs; + commandArgs << "-c"; + commandArgs << commandString; + QProcess::startDetached(XDG_SU_COMMAND, commandArgs); + emit finished(); + return; + } const QString fallback = program + QLatin1String(" ") + arguments.join(QLatin1String(" ")); (_pUi.reasonLbl)->setText(QString::fromUtf8("Authorization is required for %1 because %2").arg(appimagePath, errorString)); @@ -93,24 +97,16 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr int masterFD = -1; int slaveFD = -1; char ptsn[ PATH_MAX ]; - auto metaObject = this->metaObject(); - QEventLoop loop; - + if (::openpty(&masterFD, &slaveFD, ptsn, 0, 0)) { - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error)."))); - - emit finished(QJsonObject()); - return; + emit finished(); + return; } masterFD = ::posix_openpt(O_RDWR | O_NOCTTY); if (masterFD < 0) { - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error)."))); - - - emit finished(QJsonObject()); + showError(QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error).")); + emit finished(); return; } @@ -118,11 +114,8 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr if (::grantpt(masterFD)) { ::close(masterFD); - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error)."))); - - - emit finished(QJsonObject()); + showError(QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error).")); + emit finished(); return; } @@ -131,11 +124,8 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY); if (slaveFD < 0) { ::close(masterFD); - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error)."))); - - - emit finished(QJsonObject()); + showError(QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error).")); + emit finished(); return; } @@ -143,11 +133,8 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC); int pipedData[2]; if (pipe(pipedData) != 0) { - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error)."))); - - - emit finished(QJsonObject()); + showError(QString::fromUtf8("we cannot setup a pseudo-terminal(Internal error).")); + emit finished(); return; } @@ -162,11 +149,8 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr ::close(slaveFD); ::close(pipedData[0]); ::close(pipedData[1]); - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, QString::fromUtf8("we cannot fork a child process(Internal error)."))); - - - emit finished(QJsonObject()); + showError(QString::fromUtf8("we cannot fork a child process(Internal error).")); + emit finished(); return; } @@ -188,14 +172,10 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr bytes = ::read(masterFD, buf, 1023); if (bytes > 0) { const QString line = QString::fromLatin1(buf, bytes); - if (re.indexIn(line) != -1) { - connect(this, &QDialog::accepted, &loop, &QEventLoop::quit); - connect(this, &QDialog::rejected, &loop, &QEventLoop::quit); - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("exec(void)"))) - .invoke(this); - loop.exec(); - - const QString password = (_pUi.passwordTxt)->text(); + if (re.indexIn(line) != -1) { + this->exec(); + + const QString password = (_pUi.passwordTxt)->text(); if (this->result() != QDialog::Accepted) { /* Note: * Do not break here , if we do break here then on the end of this @@ -203,28 +183,25 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr * So we simply close and exit. */ ::close(pipedData[1]); - emit finished(QJsonObject()); + emit finished(); return; } - QByteArray pwd = password.toLatin1(); + QByteArray pwd = password.toUtf8(); ::write(masterFD, pwd.data(), pwd.length()); ::write(masterFD, "\n", 1); ::read(masterFD, buf, pwd.length() + 1); - } + } } if (bytes == 0) ::usleep(100000); } - int status; + int status = 0; child = ::wait(&status); ::close(pipedData[1]); if (status) { - metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature("showError(QString)"))) - .invoke(this, Q_ARG(QString, - QString::fromUtf8("the password you gave has been rejected."))); - + showError(QString::fromUtf8("the password you gave has been rejected.")); } - emit finished(QJsonObject()); + emit finished(); return; } @@ -246,7 +223,7 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr ::dup2(slaveFD, 1); ::dup2(pipedData[1], 2); - // close all file descriptors + // close all file descriptors struct rlimit rlp; getrlimit(RLIMIT_NOFILE, &rlp); for (int i = 3; i < static_cast(rlp.rlim_cur); ++i) @@ -270,7 +247,7 @@ void AuthorizationDialog::doAuthorize(QString errorString, short errorCode, QStr ::execv(SU_COMMAND, argp); _exit(0); - emit finished(QJsonObject()); + emit finished(); return; } diff --git a/AuthorizationDialog.hpp b/AuthorizationDialog.hpp index 1b0bb7c..3cef420 100644 --- a/AuthorizationDialog.hpp +++ b/AuthorizationDialog.hpp @@ -1,10 +1,7 @@ #ifndef AUTHORIZATION_DIALOG_HPP_INCLUDED #define AUTHORIZATION_DIALOG_HPP_INCLUDED -#include -#include -#include #include -#include +#include #include class AuthorizationDialog : public QDialog @@ -17,12 +14,10 @@ class AuthorizationDialog : public QDialog AuthorizationDialog(QWidget *parent = nullptr); ~AuthorizationDialog(); public Q_SLOTS: - void handleAuthorization(QString, short, QString); + void handleAuthorization(QString, QString); private Q_SLOTS: void showError(QString); - void doAuthorize(QString, short, QString); Q_SIGNALS: - void started(void); - void finished(QJsonObject); + void finished(); }; #endif // AUTHORIZATION_DIALOG_HPP_INCLUDED diff --git a/BuildConstants.cc b/BuildConstants.cc new file mode 100644 index 0000000..544036d --- /dev/null +++ b/BuildConstants.cc @@ -0,0 +1,41 @@ +#include +#include +#include + +#ifndef APPIMAGE_UPDATER_VERSION +#define APPIMAGE_UPDATER_VERSION "2" +#endif +#ifndef APPIMAGE_UPDATER_COMMIT +#define APPIMAGE_UPDATER_COMMIT "none" +#endif +#ifndef APPIMAGE_UPDATER_BUILD_NO +#define APPIMAGE_UPDATER_BUILD_NO "1" +#endif +#ifndef APPIMAGE_UPDATER_BUILD_TIME +#define APPIMAGE_UPDATER_BUILD_TIME __DATE__ +#endif + + +QString BuildConstants::getCommit() { + return QString::fromUtf8(APPIMAGE_UPDATER_COMMIT); +} + +QString BuildConstants::getBuildNo() { + return QString::fromUtf8(APPIMAGE_UPDATER_BUILD_NO); +} + +QString BuildConstants::getBuildTime() { + return QString::fromUtf8(APPIMAGE_UPDATER_BUILD_TIME); +} + +QString BuildConstants::getQAppImageUpdateVersion() { + return QAppImageUpdate::versionString(); +} + +QString BuildConstants::getTorrentVersion() { + return QString::fromUtf8(LIBTORRENT_VERSION); +} + +QString BuildConstants::getVersion() { + return QString::fromUtf8(APPIMAGE_UPDATER_VERSION); +} diff --git a/BuildConstants.hpp b/BuildConstants.hpp new file mode 100644 index 0000000..dc7983e --- /dev/null +++ b/BuildConstants.hpp @@ -0,0 +1,16 @@ +#ifndef BUILD_CONSTANTS_HPP_INCLUDED +#define BUILD_CONSTANTS_HPP_INCLUDED +#include + +class BuildConstants : public QObject { + Q_OBJECT + public: + Q_INVOKABLE QString getCommit(); + Q_INVOKABLE QString getBuildNo(); + Q_INVOKABLE QString getBuildTime(); + Q_INVOKABLE QString getQAppImageUpdateVersion(); + Q_INVOKABLE QString getTorrentVersion(); + Q_INVOKABLE QString getVersion(); +}; + +#endif // BUILD_CONSTANTS_HPP_INCLUDED diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..af90d2f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.2) +project(AppImageUpdater) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) +set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication") + +set(CMAKE_CXX_FLAGS "-Wall -Wextra") +set(CMAKE_CXX_FLAGS_DEBUG "-g") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +find_package(LibtorrentRasterbar) +find_package(QAppImageUpdate) +find_package(libappimage) +find_package(Qt5Gui) +find_package(Qt5Qml) +find_package(Qt5QuickControls2) + +# Include Directories. +include_directories(${CMAKE_BINARY_DIR}) + +if(APPIMAGE_UPDATER_VERSION) + add_definitions(-DAPPIMAGE_UPDATER_VERSION="${APPIMAGE_UPDATER_VERSION}") +endif() +if(APPIMAGE_UPDATER_COMMIT) + add_definitions(-DAPPIMAGE_UPDATER_COMMIT="${APPIMAGE_UPDATER_COMMIT}") +endif() +if(APPIMAGE_UPDATER_BUILD_NO) + add_definitions(-DAPPIMAGE_UPDATER_BUILD_NO="${APPIMAGE_UPDATER_BUILD_NO}") +endif() +if(APPIMAGE_UPDATER_BUILD_TIME) + add_definitions(-DAPPIMAGE_UPDATER_BUILD_TIME="${APPIMAGE_UPDATER_BUILD_TIME}") +endif() + +add_executable(AppImageUpdater main.cc + global.hpp + AppImageUpdaterStandalone.hpp + AppImageUpdaterStandalone.cc + AuthorizationDialog.hpp + AuthorizationDialog.cc + Updater_p.hpp + Updater_p.cc + Updater.hpp + Updater.cc + SeedManager_p.hpp + SeedManager_p.cc + SeedManager.hpp + SeedManager.cc + AppImageImageProvider.hpp + AppImageImageProvider.cc + DropItemParser.hpp + DropItemParser.cc + BuildConstants.hpp + BuildConstants.cc + SettingsManager.hpp + SettingsManager.cc + SystemTray.hpp + SystemTray.cc + Executer_p.hpp + Executer_p.cc + Executer.hpp + Executer.cc + Helpers.hpp + Helpers.cc + app_resources/resources.qrc + qml/qml.qrc) +target_link_libraries(AppImageUpdater PUBLIC + Qt5::Gui + Qt5::Qml + Qt5::QuickControls2 + util) +target_link_libraries(AppImageUpdater PRIVATE QAppImageUpdate) +target_link_libraries(AppImageUpdater PRIVATE libappimage) +target_link_libraries(AppImageUpdater PRIVATE LibtorrentRasterbar::torrent-rasterbar) diff --git a/DropItemParser.cc b/DropItemParser.cc new file mode 100644 index 0000000..b70a97e --- /dev/null +++ b/DropItemParser.cc @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static std::vector split_string(const std::string& str, + const std::string& delimiter) +{ + std::vector strings; + + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = str.find(delimiter, prev)) != std::string::npos) + { + strings.push_back(str.substr(prev, pos - prev)); + prev = pos + 1; + } + + strings.push_back(str.substr(prev)); + return strings; +} +static QPair getAppImageNameAndIcon(const QString &appimagePath) { + QPair r; + r.first = QString(); + r.second = QByteArray(); + + appimage::core::AppImage *appimage; + appimage::utils::ResourcesExtractor *res; + try { + appimage = new appimage::core::AppImage(appimagePath.toStdString()); + }catch(...) { + qDebug() << "Cannot contstruct AppImage"; + return r; + } + + try { + res = new appimage::utils::ResourcesExtractor(*appimage); + }catch(...) { + if(appimage) { + delete appimage; + } + qDebug() << "Cannot Resource Extract"; + return r; + } + + if(!res) { + if(appimage) { + delete appimage; + return r; + } + } + + auto desktopFile = res->extract(res->getDesktopEntryPath()); + auto data = desktopFile.data(); + + std::stringstream ss(data); + std::string dest; + while(std::getline(ss, dest, '\n')){ + if(dest == "[Desktop Entry]") { + continue; + } + + auto entry = split_string(dest, "="); + if(entry.size() != 2) { + continue; + } + + if(entry[0] == "Name") { + r.first = QString::fromStdString(entry[1]); + if(!r.second.isEmpty()) { + break; + } + } + if(entry[0] == "Icon") { + //// Some crazy Application developers don't follow the + //// Rox AppDir format so the icon can be in the top dir + //// itself. So we will check for that. + + std::vector _iconData; + QPixmap image; + QByteArray imageData; + bool cannotFind = false; + try { + _iconData = res->extract(entry[1] + ".png"); + imageData = QByteArray(_iconData.data(), _iconData.size()); + image.loadFromData(imageData); + }catch(...) { + cannotFind = true; + } + + //// libappimage does not provide anything to check if the given + //// data is a valid png image, We can do some Qt magic here. + if(!cannotFind && !image.isNull()) { + r.second = imageData; + }else { + + auto paths = res->getIconFilePaths(entry[1]); + if(paths.size()) { + auto iconData = res->extract(paths[paths.size()-1]); + r.second = QByteArray(iconData.data(), iconData.size()); + } + } + if(!r.first.isEmpty()) { + break; + } + } + QCoreApplication::processEvents(); + } + + if(r.first.isEmpty()) { + r.first = QFileInfo(appimagePath).baseName(); + } + + if(r.second.isEmpty()) { + /// Give Default Icon + QPixmap *pixmap = new QPixmap(":logo.png"); + QByteArray icon; + QBuffer buffer(&icon); + buffer.open(QIODevice::WriteOnly); + pixmap->save(&buffer, "PNG"); // writes pixmap into bytes in PNG format + r.second = icon; + delete pixmap; + } + + delete res; + delete appimage; + return r; +} + +static QByteArray readLine(QFile *IO) { + QByteArray ret; + char c = 0; + while(IO->getChar(&c) && c != '\n') { + ret.append(c); + } + return ret; +} + +static QByteArray getExecPathFromDesktopFile(QFile *file) { + QByteArray line; + qint64 prevPos = file->pos(); + file->seek(0); + while(!(line = readLine(file)).isEmpty()) { + if(line.contains("Exec")) { + for(auto i = 0; i < line.size() ; ++i) { + if(line[i] == '=') { + line = line.mid(i+1); + break; + } + } + break; + } + } + file->seek(prevPos); + return line; +} + + +DropItemParser::DropItemParser(QObject *parent) + : QObject(parent) { } + +DropItemParser::~DropItemParser() { } + + + +/// Public Qt Slots +void DropItemParser::clearBuffer() { + m_Buffer.clear(); +} + +void DropItemParser::appendToBuffer(const QString &path) { + m_Buffer.append(path); +} + +void DropItemParser::start() { + if(m_Buffer.isEmpty()) { + return; + } + emit loading(); + int enqueued = 0; + for(auto i = 0; i < m_Buffer.size(); ++i) { + QFileInfo info(QUrl(m_Buffer.at(i)).toLocalFile()); + //// Let's check if it's a file or a directory + if(info.isDir()) { + //// If it's a directory then add AppImages recursively. + QDirIterator dirIt(info.absoluteFilePath(), + QStringList() << "*.AppImage", + QDir::Files, + QDirIterator::Subdirectories); + while (dirIt.hasNext()) { + auto file = dirIt.next(); + + /// Check if the file is actually an AppImage. + auto appimageInformation = getAppImageNameAndIcon(file); + if(appimageInformation.first.isEmpty() || + appimageInformation.second.isEmpty()) { + continue; + } + ++enqueued; + emit enqueue(file, appimageInformation.first, QVariant(appimageInformation.second)); + QCoreApplication::processEvents(); + } + continue; + } + + /// Posible permission error, + /// Just spawn a new standalone updater to handle this. + if(!info.isReadable() || !info.isWritable()) { + auto arguments = QCoreApplication::arguments(); + auto program = QFileInfo(arguments.at(0)).absolutePath() + + QString::fromUtf8("/") + + QFileInfo(arguments.at(0)).fileName(); + + QStringList args; + args << "-c" + << "-d" + << info.absoluteFilePath(); + emit hideApp(); + QProcess::startDetached(program, args); + emit finished(); + return; + } + + /// It is a file then try loading it as AppImage. + auto appimageInformation = getAppImageNameAndIcon(info.absoluteFilePath()); + if(appimageInformation.first.isEmpty() || + appimageInformation.second.isEmpty()) { + qDebug() << "Empty"; + /// Most probably not an AppImage. Let's see if it's Desktop file. + + QFile file(info.absoluteFilePath()); + if(!file.open(QIODevice::ReadOnly)) { + continue; + } + auto signature = file.read(15); + if(signature != "[Desktop Entry]") { + continue; + } + auto path = QString(getExecPathFromDesktopFile(&file)); + if(path.isEmpty()) { + continue; + } + + QFileInfo pathInfo(path); + + //// The Exec field may be a command. + if(!pathInfo.isFile() || !pathInfo.exists()) { + continue; + } + + //// If it's just relative then just fail. + //// We can test the working dir entry in the + //// Desktop file but for now let's just do this. + if(QFileInfo(path).isRelative()) { + continue; + } + + file.close(); + + appimageInformation = getAppImageNameAndIcon(path); + if(appimageInformation.first.isEmpty() || + appimageInformation.second.isEmpty()) { + continue; + } + ++enqueued; + + emit enqueue(path, appimageInformation.first, QVariant(appimageInformation.second)); + }else{ + ++enqueued; + emit enqueue(info.absoluteFilePath(), appimageInformation.first, QVariant(appimageInformation.second)); + } + QCoreApplication::processEvents(); + } + + if(enqueued == 0){ + emit failed(); + return; + } + emit finished(); +} diff --git a/DropItemParser.hpp b/DropItemParser.hpp new file mode 100644 index 0000000..d5ca371 --- /dev/null +++ b/DropItemParser.hpp @@ -0,0 +1,27 @@ +#ifndef DROP_PARSER_HPP_INCLUDED +#define DROP_PARSER_HPP_INCLUDED +#include +#include +#include +#include +#include +#include +class DropItemParser : public QObject { + Q_OBJECT + QStringList m_Buffer; +public: + DropItemParser(QObject *parent = nullptr); + ~DropItemParser(); +public Q_SLOTS: + void clearBuffer(); + void appendToBuffer(const QString&); + void start(); +Q_SIGNALS: + void enqueue(QString absolutePath, QString appName, QVariant icon); + void loading(); + void failed(); + void finished(); + void hideApp(); +}; + +#endif // DROP_PARSER_HPP_INCLUDED diff --git a/Executer.cc b/Executer.cc new file mode 100644 index 0000000..fc6f879 --- /dev/null +++ b/Executer.cc @@ -0,0 +1,56 @@ +#include +#include + +#include "Executer_p.hpp" +#include "Executer.hpp" + +static QMetaMethod getMethod(QObject *object, const char *function) { + auto metaObject = object->metaObject(); + return metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature(function))); +} + + +Executer::Executer(QObject *parent) + : QObject(parent) { + m_Private = new ExecuterPrivate; + m_Thread = new QThread; + m_Thread->start(); + + m_Private->moveToThread(m_Thread); + + connect(m_Private, &ExecuterPrivate::loading, + this, &Executer::loading, + Qt::DirectConnection); + + connect(m_Private, &ExecuterPrivate::terminalApp, + this, &Executer::terminalApp, + Qt::DirectConnection); + + connect(m_Private, &ExecuterPrivate::finished, + this, &Executer::finished, + Qt::DirectConnection); +} +Executer::~Executer() { + m_Private->disconnect(); + m_Private->deleteLater(); + + m_Thread->quit(); + m_Thread->wait(); + m_Thread->deleteLater(); +} + +void Executer::exec(const QString &hash, const QString &path) { + getMethod(m_Private, "exec(const QString&, const QString&)") + .invoke(m_Private, + Qt::QueuedConnection, + Q_ARG(QString, hash), + Q_ARG(QString, path)); + +} + +void Executer::openDirectory(const QString &path) { + getMethod(m_Private, "openDirectory(const QString&)") + .invoke(m_Private, + Qt::QueuedConnection, + Q_ARG(QString, path)); +} diff --git a/Executer.hpp b/Executer.hpp new file mode 100644 index 0000000..bcc1a48 --- /dev/null +++ b/Executer.hpp @@ -0,0 +1,24 @@ +#ifndef EXECUTER_HPP_INCLUDED +#define EXECUTER_HPP_INCLUDED +#include +#include +#include +class ExecuterPrivate; +class Executer : public QObject { + Q_OBJECT + ExecuterPrivate *m_Private; + QThread *m_Thread; + +public: + Executer(QObject *parent = nullptr); + ~Executer(); +public Q_SLOTS: + void exec(const QString&, const QString&); + void openDirectory(const QString&); +Q_SIGNALS: + void loading(QString hash); + void terminalApp(QString hash); + void finished(QString hash); +}; + +#endif // EXECUTER_HPP_INCLUDED diff --git a/Executer_p.cc b/Executer_p.cc new file mode 100644 index 0000000..0300fda --- /dev/null +++ b/Executer_p.cc @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Executer_p.hpp" + +#include +#include +#include +#include +#include + +static std::vector split_string(const std::string& str, + const std::string& delimiter) +{ + std::vector strings; + + std::string::size_type pos = 0; + std::string::size_type prev = 0; + while ((pos = str.find(delimiter, prev)) != std::string::npos) + { + strings.push_back(str.substr(prev, pos - prev)); + prev = pos + 1; + } + + strings.push_back(str.substr(prev)); + return strings; +} + +static bool isTerminalApplication(const QString &appimagePath) { + appimage::core::AppImage *appimage; + appimage::utils::ResourcesExtractor *res; + try { + appimage = new appimage::core::AppImage(appimagePath.toStdString()); + }catch(...) { + qDebug() << "Cannot contstruct AppImage"; + return true; // If it's Terminal App then we will not execute it. + /// It's better not execute something that is not an AppImage. + } + + try { + res = new appimage::utils::ResourcesExtractor(*appimage); + }catch(...) { + if(appimage) { + delete appimage; + } + qDebug() << "Cannot Resource Extract"; + return true; + } + + if(!res) { + if(appimage) { + delete appimage; + return true; + } + } + + auto desktopFile = res->extract(res->getDesktopEntryPath()); + auto data = desktopFile.data(); + + std::stringstream ss(data); + std::string dest; + + bool terminalApp = true; + while(std::getline(ss, dest, '\n')){ + if(dest == "[Desktop Entry]") { + continue; + } + + auto entry = split_string(dest, "="); + if(entry.size() != 2) { + continue; + } + + if(entry[0] == "Terminal") { + auto terminalValue = QString::fromStdString(entry[1]); + if(terminalValue.toLower() == "false") { + terminalApp = false; + } + break; + } + QCoreApplication::processEvents(); + } + + delete res; + delete appimage; + return terminalApp; +} + + + +ExecuterPrivate::ExecuterPrivate(QObject *parent) + : QObject(parent) { +} + +ExecuterPrivate::~ExecuterPrivate() { + +} + +void ExecuterPrivate::openDirectory(const QString &path) { + auto toOpen = QFileInfo(path).path(); + QDesktopServices::openUrl(QUrl(toOpen)); +} + +void ExecuterPrivate::exec(const QString &hash, const QString &path) { + m_Buffer.enqueue(qMakePair(hash, path)); + + if(m_CurrentExec.first.isEmpty()) { + execNext(); + } +} + +void ExecuterPrivate::finishExec() { + emit finished(m_CurrentExec.first); + execNext(); +} + +void ExecuterPrivate::execNext() { + if(m_Buffer.isEmpty()) { + m_CurrentExec.first.clear(); + m_CurrentExec.second.clear(); + return; + } + + emit loading(m_CurrentExec.first); + m_CurrentExec = m_Buffer.dequeue(); + + if(isTerminalApplication(m_CurrentExec.second)) { + emit terminalApp(m_CurrentExec.first); + execNext(); + return; + } + + + /// Make it executable if it's not. + QFileInfo info(m_CurrentExec.second); + if(!info.isExecutable()) { + { + QFile file(m_CurrentExec.second); + file.setPermissions(QFileDevice::ExeUser | + QFileDevice::ExeOther| + QFileDevice::ExeGroup| + info.permissions()); + } + } + + QProcess::startDetached(m_CurrentExec.second); + QTimer::singleShot(2000, this, &ExecuterPrivate::finishExec); +} diff --git a/Executer_p.hpp b/Executer_p.hpp new file mode 100644 index 0000000..f3dcead --- /dev/null +++ b/Executer_p.hpp @@ -0,0 +1,28 @@ +#ifndef EXECUTER_PRIVATE_HPP_INCLUDED +#define EXECUTER_PRIVATE_HPP_INCLUDED +#include +#include +#include +#include +#include +class ExecuterPrivate : public QObject { + Q_OBJECT + QPair m_CurrentExec; + QQueue> m_Buffer; +public: + ExecuterPrivate(QObject *parent = nullptr); + ~ExecuterPrivate(); +public Q_SLOTS: + void exec(const QString&, const QString&); + void openDirectory(const QString&); +private Q_SLOTS: + void finishExec(); +private: + void execNext(); +Q_SIGNALS: + void loading(QString hash); + void terminalApp(QString hash); + void finished(QString hash); +}; + +#endif // EXECUTER_PRIVATE_HPP_INCLUDED diff --git a/Helpers.cc b/Helpers.cc new file mode 100644 index 0000000..01755cc --- /dev/null +++ b/Helpers.cc @@ -0,0 +1,21 @@ +#include +#include + +#include "Helpers.hpp" + +QString Helpers::fileNameFromPath(const QString &filePath) const { + return QFileInfo(filePath).fileName(); +} + +bool Helpers::isMinimized() const { + auto arguments = QCoreApplication::arguments(); + if(arguments.size() != 1 && + arguments.at(1).toLower() == QString::fromUtf8("--minimized")) { + return true; + } + return false; +} + +bool Helpers::removeFile(const QString &filePath) const { + return QFile::remove(filePath); +} diff --git a/Helpers.hpp b/Helpers.hpp new file mode 100644 index 0000000..91a3789 --- /dev/null +++ b/Helpers.hpp @@ -0,0 +1,14 @@ +#ifndef HELPERS_HPP_INCLUDED +#define HELPERS_HPP_INCLUDED +#include +#include + +class Helpers : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE QString fileNameFromPath(const QString &filePath) const; + Q_INVOKABLE bool isMinimized() const; + Q_INVOKABLE bool removeFile(const QString &filePath) const; +}; +#endif /// HELPERS_HPP_INCLUDED diff --git a/README.md b/README.md index b0d0f75..825c9ab 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ stars license - -

+ +

@@ -16,7 +16,7 @@ @@ -27,27 +27,23 @@

- AppImage Updater
+ AppImage Updater

+AppImage Updater updates AppImages using the infamous zsync algorithm. The zsync algorithm allows delta updates, i.e You get to download only the data that is required to construct the latest version of the AppImage. This helps you reduce bandwidth, network usage and typically faster than traditional updates. -> :rocket: A Unofficial AppImage Updater written using AppImageUpdaterBridge , Written :black_nib: in C++ using Qt5. -> -- Antony Jr. - - -This is built to **Test** AppImageUpdaterBridge and also for people who like to have a nice looking AppImage Updater. +**This updater is not the official updater, this updater is a complete rewrite to create a more flexible development of the Application.** -**Note** : This is not the official Update tool , this is just a example to help developers to understand **AppImageUpdaterBridge** better. -and also for people who like Qt. +This Application is made with Google Material Design and it's easy on Humans. This project aims to create a AppImage Updater that is more user friendly and beautiful. -Please see [AppImage Updater Bridge](https://github.com/antony-jr/AppImageUpdaterBridge) for more information. +Please see [QAppImageUpdate](https://github.com/antony-jr/QAppImageUpdate) for more information on the library used. # Support [![Twitter](https://img.shields.io/twitter/url/https/github.com/antony-jr/AIUpdateInformation.svg?style=social)](https://twitter.com/intent/tweet?text=Checkout%20the%20new%20AppImage%20Updater%20by%20%40antonyjr0%20%2C%20its%20really%20cool%20%2C%20Get%20it%20at%20https%3A%2F%2Fgithub.com%2Fantony-jr%2FAppImageUpdater) If you think that this project is **cool** then you can give it a :star: or :fork_and_knife: it if you want to improve it with me. I really :heart: stars though! -You can also tweet about me on twitter , get connected with me [@antonyjr0](https://twitter.com/antonyjr0) +You can also tweet about me on twitter, get connected with me [@antonyjr0](https://twitter.com/antonyjr0) Thank You! :smiley_cat: @@ -55,4 +51,4 @@ Thank You! :smiley_cat: The GNU Lesser General Public License V3. -Copyright (C) 2018 , Antony Jr. +Copyright (C) 2018-2021, Antony Jr. diff --git a/SeedManager.cc b/SeedManager.cc new file mode 100644 index 0000000..86f8f90 --- /dev/null +++ b/SeedManager.cc @@ -0,0 +1,63 @@ +#include +#include + +#include "SeedManager.hpp" +#include "SeedManager_p.hpp" + +static QMetaMethod getMethod(QObject *object, const char *function) { + auto metaObject = object->metaObject(); + return metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature(function))); +} + +SeedManager::SeedManager(QObject *parent) + : QObject(parent) { + m_Private = QSharedPointer(new SeedManagerPrivate); + auto obj = m_Private.data(); + + connect(obj, &SeedManagerPrivate::errorSeeding, + this, &SeedManager::errorSeeding, + Qt::DirectConnection); + + connect(obj, &SeedManagerPrivate::queuedSeeding, + this, &SeedManager::queuedSeeding, + Qt::DirectConnection); + + connect(obj, &SeedManagerPrivate::startedSeeding, + this, &SeedManager::startedSeeding, + Qt::DirectConnection); + + connect(obj, &SeedManagerPrivate::removingSeeding, + this, &SeedManager::removingSeeding, + Qt::DirectConnection); + + connect(obj, &SeedManagerPrivate::stoppedSeeding, + this, &SeedManager::stoppedSeeding, + Qt::DirectConnection); + + connect(obj, &SeedManagerPrivate::torrentStatus, + this, &SeedManager::torrentStatus, + Qt::DirectConnection); +} + +void SeedManager::updateProxy() { + getMethod(m_Private.data(), "updateProxy(void)") + .invoke(m_Private.data(), + Qt::QueuedConnection); +} + +void SeedManager::startSeeding(QString hash, QString path, QUrl torrentFileUrl) { + getMethod(m_Private.data(), "startSeeding(QString, QString, QUrl)") + .invoke(m_Private.data(), + Qt::QueuedConnection, + Q_ARG(QString, hash), + Q_ARG(QString, path), + Q_ARG(QUrl, torrentFileUrl)); + +} + +void SeedManager::stopSeeding(QString hash) { + getMethod(m_Private.data(), "stopSeeding(QString)") + .invoke(m_Private.data(), + Qt::QueuedConnection, + Q_ARG(QString, hash)); +} diff --git a/SeedManager.hpp b/SeedManager.hpp new file mode 100644 index 0000000..fccbf8d --- /dev/null +++ b/SeedManager.hpp @@ -0,0 +1,29 @@ +#ifndef SEED_MANAGER_HPP_INCLUDED +#define SEED_MANAGER_HPP_INCLUDED +#include +#include +#include +#include +#include + +class SeedManagerPrivate; + +class SeedManager : public QObject { + Q_OBJECT + QSharedPointer m_Private; + public: + SeedManager(QObject *parent = nullptr); + public Q_SLOTS: + void updateProxy(); + + void startSeeding(QString, QString, QUrl); + void stopSeeding(QString); + Q_SIGNALS: + void errorSeeding(QString hash); + void queuedSeeding(QString hash); + void startedSeeding(QString hash); + void removingSeeding(QString hash); + void stoppedSeeding(QString hash); + void torrentStatus(QString hash, QString statusText); +}; +#endif // SEED_MANAGER_HPP_INCLUDED diff --git a/SeedManager_p.cc b/SeedManager_p.cc new file mode 100644 index 0000000..9897301 --- /dev/null +++ b/SeedManager_p.cc @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "SeedManager_p.hpp" + +SeedManagerPrivate::SeedManagerPrivate() + : QObject() { + + m_Manager.reset(new QNetworkAccessManager); + lt::session_params p = lt::session_params(); + p.settings.set_int(lt::settings_pack::alert_mask, + lt::alert_category::status | + lt::alert_category::error | + lt::alert_category::storage); + + m_Session.reset(new lt::session(p)); + m_TorrentMeta.reset(new QByteArray); + + m_Timer.setSingleShot(false); + m_Timer.setInterval(100); // 1ms? + connect(&m_Timer, &QTimer::timeout, + this, &SeedManagerPrivate::torrentLoop, + Qt::QueuedConnection); + + updateProxy(); + + m_Timer.setSingleShot(false); + m_Timer.setInterval(100); + m_Timer.start(); +} + +SeedManagerPrivate::~SeedManagerPrivate() { + m_Session->abort(); +} + +void SeedManagerPrivate::updateProxy() { + QNetworkProxy proxy; + lt::settings_pack sp; + QSettings settings; + settings.sync(); + + /// Get Proxy Settings + auto useProxy = settings.value("V2.isProxyEnabled").toBool(); + auto phost = settings.value("V2.ProxyHost").toString(); + auto pport = settings.value("V2.ProxyPort").toInt(); + auto ptype = settings.value("V2.ProxyType").toString(); + auto puser = settings.value("V2.ProxyUser").toString(); + auto ppass = settings.value("V2.ProxyPass").toString(); + + if(!useProxy) { + if(!m_Manager.isNull()) { + m_Manager->setProxy(proxy); + } + + if(!m_Session.isNull()) { + m_Session->apply_settings(sp); + } + return; + } + + /// Check if proxy provided + if(!phost.isEmpty() && pport != 0) { + + //// Set proxy for QNetworkAccessManager. + proxy.setHostName(phost); + proxy.setPort(pport); + + //// Set proxy for libtorrent. + sp.set_str(lt::settings_pack::proxy_hostname, + phost.toStdString()); + sp.set_int(lt::settings_pack::proxy_port, + (int)pport); + + if(ptype == "Socks 5") { + proxy.setType(QNetworkProxy::Socks5Proxy); + sp.set_int(lt::settings_pack::proxy_type, + lt::settings_pack::socks5_pw); + + } else { + proxy.setType(QNetworkProxy::HttpProxy); + sp.set_int(lt::settings_pack::proxy_type, + lt::settings_pack::http_pw); + + } + proxy.setHostName(phost); + proxy.setPort(pport); + + if(!puser.isEmpty() && !ppass.isEmpty()) { + proxy.setUser(puser); + proxy.setPassword(ppass); + + sp.set_str(lt::settings_pack::proxy_username, + puser.toStdString()); + sp.set_str(lt::settings_pack::proxy_password, + ppass.toStdString()); + + } + + if(!m_Manager.isNull()) { + m_Manager->setProxy(proxy); + } + + if(!m_Session.isNull()) { + m_Session->apply_settings(sp); + } + } +} + +void SeedManagerPrivate::startSeeding(QString hash, QString path, QUrl torrentFile) { + if(m_Torrents.contains(hash)) { + return; + } + + AppImageSeedInfo info; + info.hash = hash; + info.path = path; + info.torrentFileUrl = torrentFile; + info.empty = false; + + m_QueuedTorrents.enqueue(info); + emit queuedSeeding(info.hash); + + if(m_CurrentTorrent.empty) { + getNextTorrentMeta(); + } + return; +} + +void SeedManagerPrivate::stopSeeding(QString hash) { + if(!m_Torrents.contains(hash)) { + return; + } + + m_ReqTorrentForRemoval.append(hash); + emit removingSeeding(hash); +} + +void SeedManagerPrivate::getNextTorrentMeta() { + if(m_QueuedTorrents.isEmpty()) { + AppImageSeedInfo empty; + m_CurrentTorrent = empty; + return; + } + + m_CurrentTorrent = m_QueuedTorrents.dequeue(); + + m_TorrentMeta->clear(); + updateProxy(); + + QNetworkRequest request; + request.setUrl(m_CurrentTorrent.torrentFileUrl); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + auto reply = m_Manager->get(request); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(handleTorrentFileError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), + this, SLOT(handleTorrentFileData(qint64, qint64))); + connect(reply, SIGNAL(finished()), + this, SLOT(handleTorrentFileFinish())); +} + +void SeedManagerPrivate::handleTorrentFileError(QNetworkReply::NetworkError code) { + QNetworkReply *reply = qobject_cast(QObject::sender()); + Q_UNUSED(code); + if(!reply) { + return; + } + + reply->disconnect(); + reply->deleteLater(); + + emit errorSeeding(m_CurrentTorrent.hash); + + getNextTorrentMeta(); +} + +void SeedManagerPrivate::handleTorrentFileData(qint64 br, qint64 bt) { + Q_UNUSED(br); + Q_UNUSED(bt); + + auto reply = qobject_cast(QObject::sender()); + if(!reply) { + return; + } + + if(reply->error() != QNetworkReply::NoError) { + return; + } + + if(reply->isReadable()) { + m_TorrentMeta->append(reply->readAll()); + } +} + +void SeedManagerPrivate::handleTorrentFileFinish() { + auto reply = qobject_cast(QObject::sender()); + m_TorrentMeta->append(reply->readAll()); + + reply->disconnect(); + reply->deleteLater(); + + lt::add_torrent_params params; + QString savePath = QFileInfo(m_CurrentTorrent.path).path() + "/"; + + params.save_path = savePath.toStdString(); + auto ti = std::make_shared(m_TorrentMeta->constData(), (int)m_TorrentMeta->size()); + + /// We know that MakeAppImageTorrent only packs a single file that is the + /// the Target AppImage. So We just need to check if it is bundled correctly. + if(ti->num_files() != 1) { + emit errorSeeding(m_CurrentTorrent.hash); + getNextTorrentMeta(); + return; + } + + /// Since only 1 file is packaged in the torrent, we can + /// assume that the file index for our Target AppImage is 0 + ti->rename_file(0, + QFileInfo(m_CurrentTorrent.path).fileName().toStdString()); + + params.ti = ti; + auto handle = m_Session->add_torrent(params); + if(!handle.is_valid()) { + emit errorSeeding(m_CurrentTorrent.hash); + getNextTorrentMeta(); + return; + } + + m_Torrents.insert(m_CurrentTorrent.hash, handle); + emit startedSeeding(m_CurrentTorrent.hash); + + getNextTorrentMeta(); + return; +} + +void SeedManagerPrivate::torrentLoop() { + if(m_Session.isNull()) { + return; + } + + if(m_Torrents.isEmpty()) { + return; + } + + QList toRemove; + QString torrentStatusTextFormat = QString::fromUtf8("Seeding at %1 KiB/s to %2 peer(s), uploaded %3 MiB in total."); + + for(auto key: m_Torrents.keys()) { + auto handle = m_Torrents[key]; + auto status = handle.status(); + + emit torrentStatus(key, + torrentStatusTextFormat.arg(status.upload_rate/1024).arg(status.num_peers).arg(status.all_time_upload/1048576)); + if(m_ReqTorrentForRemoval.contains(key)) { + m_Session->remove_torrent(handle); + toRemove.append(key); + m_ReqTorrentForRemoval.removeAll(key); + emit stoppedSeeding(key); + continue; + } + if(status.state == lt::torrent_status::downloading) { + /// If it is not seeding but downloading then something is wrong. + emit errorSeeding(key); + m_Session->remove_torrent(handle); + toRemove.append(key); + } + QCoreApplication::processEvents(); + } + + /// Remove all defered torrent seeding + for(auto key: toRemove) { + m_Torrents.remove(key); + } +} diff --git a/SeedManager_p.hpp b/SeedManager_p.hpp new file mode 100644 index 0000000..4f73794 --- /dev/null +++ b/SeedManager_p.hpp @@ -0,0 +1,68 @@ +#ifndef SEEDER_MANAGER_PRIVATE_HPP_INCLUDED +#define SEEDER_MANAGER_PRIVATE_HPP_INCLUDED +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class SeedManagerPrivate : public QObject { + Q_OBJECT + public: + SeedManagerPrivate(); + ~SeedManagerPrivate(); + public Q_SLOTS: + void updateProxy(); + + void startSeeding(QString, QString, QUrl); + void stopSeeding(QString); + + private Q_SLOTS: + void getNextTorrentMeta(); + void handleTorrentFileData(qint64, qint64); + void handleTorrentFileError(QNetworkReply::NetworkError); + void handleTorrentFileFinish(); + + void torrentLoop(); + + Q_SIGNALS: + void errorSeeding(QString); + void queuedSeeding(QString); + void startedSeeding(QString); + void removingSeeding(QString); + void stoppedSeeding(QString); + void torrentStatus(QString, QString); + private: + struct AppImageSeedInfo { + QString hash; + QString path; + QUrl torrentFileUrl; + bool empty = true; + }; + + QTimer m_Timer; + AppImageSeedInfo m_CurrentTorrent; + QQueue m_QueuedTorrents; + QList m_ReqTorrentForRemoval; + QMap m_Torrents; + QScopedPointer m_Manager; + QScopedPointer m_TorrentMeta; + QScopedPointer m_Session; +}; +#endif // SEED_MANAGER_PRIVATE_HPP_INCLUDED diff --git a/SettingsDialog.cc b/SettingsDialog.cc deleted file mode 100644 index f8522e5..0000000 --- a/SettingsDialog.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define IS_CHECKED(checkbox) (checkbox->checkState() == Qt::Checked) ? true : false -#define BOOL_CHECKED(B) (B == true) ? Qt::Checked : Qt::Unchecked - -SettingsDialog::SettingsDialog(QWidget *parent) - : QDialog(parent , Qt::WindowStaysOnTopHint) -{ - _mUi.setupUi(this); - (_mUi.doNotShowUpdateDialogs)->setCheckState(BOOL_CHECKED(_mSettings.value("doNotShowUpdateDialogs").toBool())); - (_mUi.doNotShowSystemTrayMsgs)->setCheckState(BOOL_CHECKED(_mSettings.value("doNotShowSystemTrayMsgs").toBool())); - (_mUi.runOnStartup)->setCheckState(BOOL_CHECKED(_mSettings.value("runOnStartup").toBool())); - - auto arguments = QCoreApplication::arguments(); - _mDEntry = _mDEntry.arg(QFileInfo(arguments.at(0)).absolutePath() + - QString::fromUtf8("/") + - QFileInfo(arguments.at(0)).fileName()); - connect(this, &QDialog::accepted, this, &SettingsDialog::handleAccepted); - return; -} - -SettingsDialog::~SettingsDialog() -{ - return; -} - -bool SettingsDialog::isShowUpdateDialogs(void) const -{ - return !IS_CHECKED(_mUi.doNotShowUpdateDialogs); -} - -bool SettingsDialog::isShowSystemTrayNotificationMessages(void) const -{ - return !IS_CHECKED(_mUi.doNotShowSystemTrayMsgs); -} - -bool SettingsDialog::isRunOnStartup(void) const -{ - return IS_CHECKED(_mUi.runOnStartup); -} - -void SettingsDialog::handleAccepted(void) -{ - _mSettings.setValue("doNotShowUpdateDialogs", IS_CHECKED(_mUi.doNotShowUpdateDialogs)); - _mSettings.setValue("doNotShowSystemTrayMsgs", IS_CHECKED(_mUi.doNotShowSystemTrayMsgs)); - - if(IS_CHECKED(_mUi.runOnStartup)) { - QFile entryFile(QDir::homePath() + QString::fromUtf8("/.config/autostart/AppImageUpdater.desktop")); - if(!entryFile.open(QIODevice::WriteOnly)) { - return; - } - entryFile.write(_mDEntry.toLatin1()); - entryFile.close(); - _mSettings.setValue("runOnStartup", true); - } else { - QFile::remove(QDir::homePath() + QString::fromUtf8("/.config/autostart/AppImageUpdater.desktop")); - _mSettings.setValue("runOnStartup", false); - } - return; -} diff --git a/SettingsDialog.hpp b/SettingsDialog.hpp deleted file mode 100644 index 7ed3cd3..0000000 --- a/SettingsDialog.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SETTINGS_DIALOG_HPP_INCLUDED -#define SETTINGS_DIALOG_HPP_INCLUDED -#include -#include - -class SettingsDialog : public QDialog -{ - Q_OBJECT - Ui::AppImageUpdaterSettingsDialog _mUi; - QSettings _mSettings; - - /* - * AutoStart Dynamic Desktop Entry. - * This desktop entry will be written in to - * $HOME/.config/autostart/AppImageUpdater.desktop. - * Thus the lastest instance will replace this file whenever - * the user checks the run on startup checkbox in the gui. - */ - - QString _mDEntry = - QString::fromUtf8("[Desktop Entry]\nName=AppImageUpdater\nType=Application\nExec=%1 --minimized\nTerminal=false"); -public: - SettingsDialog(QWidget *parent = nullptr); - ~SettingsDialog(); - - bool isShowUpdateDialogs() const; - bool isShowSystemTrayNotificationMessages() const; - bool isRunOnStartup() const; -private Q_SLOTS: - void handleAccepted(void); -}; -#endif // SETTINGS_HPP_INCLUDED diff --git a/SettingsDialog.ui b/SettingsDialog.ui deleted file mode 100644 index 7eaab82..0000000 --- a/SettingsDialog.ui +++ /dev/null @@ -1,106 +0,0 @@ - - - AppImageUpdaterSettingsDialog - - - Qt::ApplicationModal - - - - 0 - 0 - 403 - 130 - - - - - 403 - 130 - - - - - 403 - 130 - - - - Settings - - - - :/logo.png:/logo.png - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - Do not show system tray notification messages. - - - - - - - Auto start updater on system startup. - - - - - - - Do not show any Update Dialogs. - - - - - - - - - - - buttonBox - accepted() - AppImageUpdaterSettingsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - AppImageUpdaterSettingsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/SettingsManager.cc b/SettingsManager.cc new file mode 100644 index 0000000..5af0317 --- /dev/null +++ b/SettingsManager.cc @@ -0,0 +1,156 @@ +#include +#include +#include + +#include + +static bool getBool(const QSettings &settings, const QString &key) { + return settings.value(key).toBool(); +} + +SettingsManager::SettingsManager(QObject *parent) + : QObject(parent) + +{ + m_Settings.sync(); + b_isDarkMode = getBool(m_Settings, "V2.isDarkMode"); + b_allowSystemNotifications = getBool(m_Settings, "V2.isAllowSystemNotifications"); + b_RunOnStartup = getBool(m_Settings, "V2.isRunOnStartup"); + b_BTEnabled = getBool(m_Settings, "V2.isDecentralizedUpdateEnabled"); + b_ProxyEnabled = getBool(m_Settings, "V2.isProxyEnabled"); + + /// Get Proxy Settings + m_ProxyHost = m_Settings.value("V2.ProxyHost").toString(); + m_ProxyPort = m_Settings.value("V2.ProxyPort").toInt(); + m_ProxyType = m_Settings.value("V2.ProxyType").toString(); + m_ProxyUser = m_Settings.value("V2.ProxyUser").toString(); + m_ProxyPass = m_Settings.value("V2.ProxyPass").toString(); + + /// Setup Desktop Entry for Startup + auto arguments = QCoreApplication::arguments(); + m_DesktopEntry = QString::fromUtf8("[Desktop Entry]\n"); + m_DesktopEntry += QString::fromUtf8("Name=AppImageUpdater\n"); + m_DesktopEntry += QString::fromUtf8("Type=Application\n"); + m_DesktopEntry += QString::fromUtf8("Exec=%1 --minimized\n"); + m_DesktopEntry += QString::fromUtf8("Terminal=false\n"); + m_DesktopEntry = m_DesktopEntry.arg( + QFileInfo(arguments.at(0)).absolutePath() + + QString::fromUtf8("/") + + QFileInfo(arguments.at(0)).fileName()); +} + +SettingsManager::~SettingsManager() +{ + m_Settings.sync(); +} + + +void SettingsManager::setIsDarkMode(bool value) { + b_isDarkMode = value; + m_Settings.setValue("V2.isDarkMode", b_isDarkMode); + emit isDarkModeChanged(); +} + +bool SettingsManager::isDarkMode() const { + return b_isDarkMode; +} + +void SettingsManager::setASN(bool value) { + b_allowSystemNotifications = value; + m_Settings.setValue("V2.isAllowSystemNotifications", b_allowSystemNotifications); + emit isASNChanged(); +} +bool SettingsManager::isASN() const { + return b_allowSystemNotifications; +} + +void SettingsManager::setStartup(bool value) { + b_RunOnStartup = value; + m_Settings.setValue("V2.isRunOnStartup", b_RunOnStartup); + if(b_RunOnStartup) { + QFile entryFile( + QDir::homePath() + + QString::fromUtf8("/.config/autostart/AppImageUpdater.desktop")); + if(!entryFile.open(QIODevice::WriteOnly)) { + b_RunOnStartup = false; + m_Settings.setValue("V2.isRunOnStartup", false); + return; + } + entryFile.write(m_DesktopEntry.toLatin1()); + entryFile.close(); + } else { + QFile::remove( + QDir::homePath() + + QString::fromUtf8("/.config/autostart/AppImageUpdater.desktop")); + } + emit startupChanged(); +} + +bool SettingsManager::isStartup() const { + return b_RunOnStartup; +} + +void SettingsManager::setBTEnabled(bool value) { + b_BTEnabled = value; + m_Settings.setValue("V2.isDecentralizedUpdateEnabled", b_BTEnabled); + emit btEnabledChanged(); +} +bool SettingsManager::isBTEnabled() const { + return b_BTEnabled; +} + +void SettingsManager::setProxyEnabled(bool value) { + b_ProxyEnabled = value; + m_Settings.setValue("V2.isProxyEnabled", b_ProxyEnabled); + emit proxyChanged(); +} + +bool SettingsManager::isProxyEnabled() const { + return b_ProxyEnabled; +} + +void SettingsManager::setProxyHost(const QString &value) { + m_ProxyHost = value; + m_Settings.setValue("V2.ProxyHost", m_ProxyHost); + emit proxyHostChanged(); +} +void SettingsManager::setProxyPort(int port) { + m_ProxyPort = port; + m_Settings.setValue("V2.ProxyPort", m_ProxyPort); + emit proxyPortChanged(); +} +void SettingsManager::setProxyType(const QString &value) { + m_ProxyType = value; + m_Settings.setValue("V2.ProxyType", value); + emit proxyTypeChanged(); +} +void SettingsManager::setProxyUser(const QString &value) { + m_ProxyUser = value; + m_Settings.setValue("V2.ProxyUser", value); + emit proxyUserChanged(); +} +void SettingsManager::setProxyPass(const QString &value) { + m_ProxyPass = value; + m_Settings.setValue("V2.ProxyPass", value); + emit proxyPassChanged(); +} + +QString SettingsManager::getProxyHost() const { + return m_ProxyHost; +} + +int SettingsManager::getProxyPort() const { + return m_ProxyPort; +} + +QString SettingsManager::getProxyType() const { + return m_ProxyType; +} + +QString SettingsManager::getProxyUser() const { + return m_ProxyUser; +} + +QString SettingsManager::getProxyPass() const { + return m_ProxyPass; +} diff --git a/SettingsManager.hpp b/SettingsManager.hpp new file mode 100644 index 0000000..c125cdd --- /dev/null +++ b/SettingsManager.hpp @@ -0,0 +1,78 @@ +#ifndef SETTINGS_MANAGER_HPP_INCLUDED +#define SETTINGS_MANAGER_HPP_INCLUDED +#include +#include + +class SettingsManager : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isDarkMode READ isDarkMode WRITE setIsDarkMode NOTIFY isDarkModeChanged) + Q_PROPERTY(bool isAllowSystemTrayNotification READ isASN WRITE setASN NOTIFY isASNChanged) + Q_PROPERTY(bool isRunOnStartup READ isStartup WRITE setStartup NOTIFY startupChanged) + Q_PROPERTY(bool isDecentralizedUpdateEnabled READ isBTEnabled WRITE setBTEnabled NOTIFY btEnabledChanged) + Q_PROPERTY(bool isProxyEnabled READ isProxyEnabled WRITE setProxyEnabled NOTIFY proxyChanged) + Q_PROPERTY(QString ProxyHost READ getProxyHost WRITE setProxyHost NOTIFY proxyHostChanged) + Q_PROPERTY(int ProxyPort READ getProxyPort WRITE setProxyPort NOTIFY proxyPortChanged) + Q_PROPERTY(QString ProxyType READ getProxyType WRITE setProxyType NOTIFY proxyTypeChanged) + Q_PROPERTY(QString ProxyUser READ getProxyUser WRITE setProxyUser NOTIFY proxyUserChanged) + Q_PROPERTY(QString ProxyPass READ getProxyPass WRITE setProxyPass NOTIFY proxyPassChanged) + + QString m_DesktopEntry; +public: + explicit SettingsManager(QObject *parent = nullptr); + ~SettingsManager() override; + + void setIsDarkMode(bool); + bool isDarkMode() const; + + void setASN(bool); + bool isASN() const; + + void setStartup(bool); + bool isStartup() const; + + void setBTEnabled(bool); + bool isBTEnabled() const; + + void setProxyEnabled(bool); + bool isProxyEnabled() const; + + void setProxyHost(const QString&); + void setProxyPort(int); + void setProxyType(const QString&); + void setProxyUser(const QString&); + void setProxyPass(const QString&); + + QString getProxyHost() const; + int getProxyPort() const; + QString getProxyType() const; + QString getProxyUser() const; + QString getProxyPass() const; + + + +signals: + void isDarkModeChanged(); + void isASNChanged(); + void startupChanged(); + void btEnabledChanged(); + void proxyChanged(); + void proxyHostChanged(); + void proxyPortChanged(); + void proxyTypeChanged(); + void proxyUserChanged(); + void proxyPassChanged(); +private: + bool b_isDarkMode, + b_allowSystemNotifications, + b_RunOnStartup, + b_BTEnabled, + b_ProxyEnabled; + QString m_ProxyHost, + m_ProxyType, + m_ProxyUser, + m_ProxyPass; + int m_ProxyPort; + QSettings m_Settings; +}; +#endif /// SETTINGS_MANAGER_HPP_INCLUDED diff --git a/SystemTray.cc b/SystemTray.cc new file mode 100644 index 0000000..7eb5f00 --- /dev/null +++ b/SystemTray.cc @@ -0,0 +1,53 @@ +#include + +#include + +SystemTray::SystemTray(QObject *parent) + : QObject(parent) + +{ + auto arguments = QCoreApplication::arguments(); + if(arguments.size() != 1 && + arguments.at(1).toLower() == QString::fromUtf8("--minimized")) { + emit forceHide(); + } + + m_TIcon = new QSystemTrayIcon; + m_TIcon->setIcon(QIcon(QPixmap(QString::fromUtf8(":/logo.png")))); + + auto menu = new QMenu; + menu->addAction(QString::fromUtf8("Show / Hide"), this, &SystemTray::showOrHide); + menu->addAction(QString::fromUtf8("Quit"), this, &SystemTray::quit); + m_CurrentContextMenu = menu; + + m_TIcon->setContextMenu(menu); + + connect(m_TIcon, &QSystemTrayIcon::activated, this, &SystemTray::raise); + m_TIcon->show(); +} + +SystemTray::~SystemTray() +{ + m_TIcon->deleteLater(); + m_CurrentContextMenu->deleteLater(); +} + +void SystemTray::raise() { + emit raiseApp(); +} + +void SystemTray::notify(const QString &message) { + m_TIcon->showMessage(QString::fromUtf8("AppImage Updater"), message); +} + +void SystemTray::changeTrayIconToBlue() { + m_TIcon->setIcon(QIcon(QPixmap(QString::fromUtf8(":/logo_blue.png")))); +} + +void SystemTray::changeTrayIconToRed() { + m_TIcon->setIcon(QIcon(QPixmap(QString::fromUtf8(":/logo_red.png")))); +} + +void SystemTray::changeTrayIconDefault() { + m_TIcon->setIcon(QIcon(QPixmap(QString::fromUtf8(":/logo.png")))); +} diff --git a/SystemTray.hpp b/SystemTray.hpp new file mode 100644 index 0000000..bd2c522 --- /dev/null +++ b/SystemTray.hpp @@ -0,0 +1,30 @@ +#ifndef SYSTEM_TRAY_HPP_INCLUDED +#define SYSTEM_TRAY_HPP_INCLUDED +#include +#include +#include +#include + +class SystemTray : public QObject +{ + Q_OBJECT +public: + explicit SystemTray(QObject *parent = nullptr); + ~SystemTray() override; + +public Q_SLOTS: + void raise(); + void notify(const QString&); + void changeTrayIconToBlue(); + void changeTrayIconToRed(); + void changeTrayIconDefault(); +signals: + void raiseApp(); + void showOrHide(); + void forceHide(); + void quit(); +private: + QMenu *m_CurrentContextMenu = nullptr; + QSystemTrayIcon *m_TIcon = nullptr; +}; +#endif /// SYSTEM_TRAY_HPP_INCLUDED diff --git a/Updater.cc b/Updater.cc new file mode 100644 index 0000000..2339dd6 --- /dev/null +++ b/Updater.cc @@ -0,0 +1,138 @@ +#include +#include + +#include "Updater.hpp" +#include "Updater_p.hpp" + +static QMetaMethod getMethod(QObject *object, const char *function) { + auto metaObject = object->metaObject(); + return metaObject->method(metaObject->indexOfMethod(QMetaObject::normalizedSignature(function))); +} + + + +Updater::Updater(QObject *parent) + : QObject(parent) { + b_NoConfirm = false; + m_Private = new UpdaterPrivate; + m_Thread = new QThread; + m_Thread->start(); + + m_Private->moveToThread(m_Thread); + + connect(m_Private, &UpdaterPrivate::removedFromQueue, + this, &Updater::removedFromQueue); + + connect(m_Private, &UpdaterPrivate::queuedCountChanged, + this, &Updater::queuedCountChanged); + + connect(m_Private, &UpdaterPrivate::failedCountChanged, + this, &Updater::failedCountChanged); + + connect(m_Private, &UpdaterPrivate::completedCountChanged, + this, &Updater::completedCountChanged); + + connect(m_Private, &UpdaterPrivate::progressText, + this, &Updater::progressText); + + connect(m_Private, &UpdaterPrivate::torrentStatus, + this, &Updater::torrentStatus); + + connect(m_Private, &UpdaterPrivate::loading, + this, &Updater::loading); + + connect(m_Private, &UpdaterPrivate::metaInfo, + this, &Updater::metaInfo); + + connect(m_Private, &UpdaterPrivate::queued, + this, &Updater::queued); + + connect(m_Private, &UpdaterPrivate::failed, + this, &Updater::failed); + + connect(m_Private, &UpdaterPrivate::started, + this, &Updater::started); + + connect(m_Private, &UpdaterPrivate::finished, + this, &Updater::finished); + + connect(m_Private, &UpdaterPrivate::canceled, + this, &Updater::canceled); + + + connect(m_Private, &UpdaterPrivate::retrySent, + this, &Updater::retrySent); + + + connect(m_Private, &UpdaterPrivate::finishedAll, + this, &Updater::finishedAll); + + connect(m_Private, &UpdaterPrivate::noConfirmState, + this, &Updater::handleNoConfirmState); +} + +Updater::~Updater() { + m_Private->disconnect(); + m_Private->cancelAll(); + m_Private->deleteLater(); + + m_Thread->quit(); + m_Thread->wait(); + m_Thread->deleteLater(); +} + +bool Updater::isNoConfirm() const { + return b_NoConfirm; +} + +void Updater::handleNoConfirmState(bool value) { + b_NoConfirm = value; + emit isNoConfirmEnabledChanged(); +} + +void Updater::retry(const QJsonObject &json) { + getMethod(m_Private, "retry(const QJsonObject&)") + .invoke(m_Private, + Qt::QueuedConnection, + Q_ARG(QJsonObject, json)); +} + +void Updater::removeFromQueue(const QString &hash) { + getMethod(m_Private, "removeFromQueue(const QString&)") + .invoke(m_Private, + Qt::QueuedConnection, + Q_ARG(QString, hash)); +} + +void Updater::queue(const QString &path, const QString &name, QVariant icon) { + getMethod(m_Private, "queue(const QString&, const QString&, QVariant)") + .invoke(m_Private, + Qt::QueuedConnection, + Q_ARG(QString, path), + Q_ARG(QString, name), + Q_ARG(QVariant,icon)); +} + +void Updater::toggleNoConfirm() { + getMethod(m_Private, "toggleNoConfirm()") + .invoke(m_Private, + Qt::QueuedConnection); +} + +void Updater::continueCurrentUpdate() { + getMethod(m_Private, "continueCurrentUpdate()") + .invoke(m_Private, + Qt::QueuedConnection); +} + +void Updater::cancelCurrentUpdate() { + getMethod(m_Private, "cancelCurrentUpdate()") + .invoke(m_Private, + Qt::QueuedConnection); +} + +void Updater::cancelAll() { + getMethod(m_Private, "cancelAll()") + .invoke(m_Private, + Qt::QueuedConnection); +} diff --git a/Updater.hpp b/Updater.hpp new file mode 100644 index 0000000..c971c55 --- /dev/null +++ b/Updater.hpp @@ -0,0 +1,52 @@ +#ifndef UPDATER_HPP_INCLUDED +#define UPDATER_HPP_INCLUDED +#include +#include +#include +#include +#include +#include +class UpdaterPrivate; +class Updater : public QObject { + Q_OBJECT + Q_PROPERTY(bool isNoConfirmEnabled READ isNoConfirm NOTIFY isNoConfirmEnabledChanged) + + bool b_NoConfirm; + UpdaterPrivate *m_Private; + QThread *m_Thread; +public: + Updater(QObject *parent = nullptr); + ~Updater(); + + bool isNoConfirm() const; +private Q_SLOTS: + void handleNoConfirmState(bool); +public Q_SLOTS: + void removeFromQueue(const QString&); + void queue(const QString&, const QString&, QVariant icon); + void retry(const QJsonObject&); + void toggleNoConfirm(); + void continueCurrentUpdate(); + void cancelCurrentUpdate(); + void cancelAll(); +Q_SIGNALS: + void isNoConfirmEnabledChanged(); + void removedFromQueue(QString hash); + void queuedCountChanged(int n); + void failedCountChanged(int n); + void completedCountChanged(int n); + + void progressText(QString progressTextString, int progressValue); + void torrentStatus(int seeders, int peers); + void loading(); + void metaInfo(QJsonObject info); + void queued(QJsonObject info); + void failed(QJsonObject info); + void started(); + void finished(QJsonObject info); + void canceled(QString hash); + void retrySent(QString hash); + void finishedAll(); +}; + +#endif // UPDATER_HPP_INCLUDED diff --git a/Updater_p.cc b/Updater_p.cc new file mode 100644 index 0000000..c1a1469 --- /dev/null +++ b/Updater_p.cc @@ -0,0 +1,373 @@ +#include +#include +#include +#include +#include + +#include "Updater_p.hpp" +#include "global.hpp" +#include "AppImageImageProvider.hpp" + +#include + +#ifndef APPIMAGE_UPDATER_COMMIT +#define APPIMAGE_UPDATER_COMMIT "none" +#endif +#ifndef APPIMAGE_UPDATER_BUILD_NO +#define APPIMAGE_UPDATER_BUILD_NO "1" +#endif + +static QString getSystemInformation() { + QString r = "OS: %1 (%2)\n"; + r += "CPU Architecture: %3\n"; + r += "Kernel Version: %4\n"; + r += "QAppImageUpdate Version: %5\n"; + r += "LibTorrent Rasterbar Version: %6\n"; + r += "AppImage Updater Commit: %7\n"; + r += "AppImage Updater Build Number: %8\n\n"; + + return r.arg(QSysInfo::prettyProductName()) + .arg(QSysInfo::productVersion()) + .arg(QSysInfo::buildCpuArchitecture()) + .arg(QSysInfo::kernelVersion()) + .arg(QAppImageUpdate::versionString()) + .arg(QString::fromUtf8(LIBTORRENT_VERSION)) + .arg(APPIMAGE_UPDATER_COMMIT) + .arg(APPIMAGE_UPDATER_BUILD_NO); +} + +UpdaterPrivate::UpdaterPrivate(QObject *parent) + : QObject(parent), + n_Queued(0), + n_Failed(0), + n_Completed(0), + b_NoConfirm(false), + b_Running(false) +{ + m_Updater = new QAppImageUpdate(/*single threaded=*/false, /*parent=*/this); + + connect(m_Updater, &QAppImageUpdate::progress, + this, &UpdaterPrivate::onProgress, + Qt::DirectConnection); + + connect(m_Updater, &QAppImageUpdate::logger, + this, &UpdaterPrivate::onLog, + Qt::QueuedConnection); + + connect(m_Updater, &QAppImageUpdate::torrentStatus, + this, &UpdaterPrivate::torrentStatus, + Qt::DirectConnection); + + connect(m_Updater, &QAppImageUpdate::finished, + this, &UpdaterPrivate::onFinishAction, + Qt::QueuedConnection); + connect(m_Updater, &QAppImageUpdate::error, + this, &UpdaterPrivate::onErrorAction, + Qt::QueuedConnection); + connect(m_Updater, &QAppImageUpdate::started, + this, &UpdaterPrivate::onStartAction, + Qt::QueuedConnection); + connect(m_Updater, &QAppImageUpdate::canceled, + this, &UpdaterPrivate::onCancelAction, + Qt::QueuedConnection); + + qInfo().noquote() << getSystemInformation(); +} + +UpdaterPrivate::~UpdaterPrivate() { + cancelAll(); +} + +void UpdaterPrivate::retry(const QJsonObject &json) { + emit retrySent(json["Hash"].toString()); + AppImage app; + app.hash = json["Hash"].toString(); + app.path = json["AbsolutePath"].toString(); + app.name = json["Name"].toString(); + app.image_id = json["ImageId"].toString(); + + m_AppImages.enqueue(app); + ++n_Queued; + emit queuedCountChanged(n_Queued); + + --n_Failed; + emit failedCountChanged(n_Failed); + + QJsonObject r { + {"Hash", app.hash }, + {"AbsolutePath" , app.path}, + {"Name", app.name}, + {"ImageId", app.image_id} + }; + emit queued(r); + + if(m_CurrentAppImage.isEmpty()) { + updateNextAppImage(); + } + +} + +void UpdaterPrivate::removeFromQueue(const QString &hash) { + for(auto i = 0; i < m_AppImages.size(); ++i) { + auto app = m_AppImages.at(i); + if(app.hash == hash) { + m_AppImages.removeAt(i); + --n_Queued; + emit removedFromQueue(hash); + emit queuedCountChanged(n_Queued); + break; + } + } +} + +void UpdaterPrivate::queue(const QString &path, const QString &name, QVariant icon) { + if(path.isEmpty()) { + return; + } + AppImage app; + app.path = path; + app.name = name; + + /// Provide the icon to the image provider. + /// This is global variable, should be little careful with this. + if(g_AppImageImageProvider) { + QByteArray *array = new QByteArray(icon.toByteArray()); + auto unique_time_str = QDateTime::currentDateTime().toString(Qt::ISODate); + auto to_hash = path + unique_time_str; + auto hash = QCryptographicHash::hash(to_hash.toUtf8(), QCryptographicHash::Md4).toHex(); + auto image_hash = QCryptographicHash::hash(path.toUtf8(), QCryptographicHash::Md4).toHex(); + app.hash = QString::fromUtf8(hash); + app.image_id = QString::fromUtf8(image_hash); + g_AppImageImageProvider->addImage(app.image_id, + array); /// Will be using md4 hash of path as the id. + } + + m_AppImages.enqueue(app); + + ++n_Queued; + emit queuedCountChanged(n_Queued); + + QJsonObject r { + {"Hash", app.hash }, + {"AbsolutePath" , app.path}, + {"Name", app.name}, + {"ImageId", app.image_id} + }; + emit queued(r); + + if(m_CurrentAppImage.isEmpty()) { + updateNextAppImage(); + } +} + +void UpdaterPrivate::toggleNoConfirm() { + b_NoConfirm = !b_NoConfirm; + emit noConfirmState(b_NoConfirm); +} + +void UpdaterPrivate::continueCurrentUpdate() { + m_Settings.sync(); + b_Running = true; + bool useBt = m_Settings.value("V2.isDecentralizedUpdateEnabled").toBool(); + if(useBt) { + m_Updater->start(QAppImageUpdate::Action::UpdateWithTorrent); + }else{ + m_Updater->start(); + } +} + +void UpdaterPrivate::cancelCurrentUpdate() { + if(m_CurrentAppImage.isEmpty()) { + return; + } + + if(b_Running) { + m_Updater->cancel(); + }else { + emit removedFromQueue(m_CurrentAppImage.hash); + updateNextAppImage(); + } +} + +void UpdaterPrivate::cancelAll() { + if(m_CurrentAppImage.isEmpty()) { + return; + } + + AppImage app; + m_CurrentAppImage = app; + m_AppImages.clear(); + m_Updater->cancel(); +} + +//// Private Qt Slots. +void UpdaterPrivate::onLog(const QString &message,const QString &AppImagePath) { + Q_UNUSED(AppImagePath); + + qInfo().noquote() << message; +} + +void UpdaterPrivate::onProgress( + int percentage , qint64 bytesReceived , qint64 bytesTotal , + double speed , QString speedUnits, short action) { + + if(action != QAppImageUpdate::Action::UpdateWithTorrent && + action != QAppImageUpdate::Action::Update) { + return; + } + + auto megaBytesRec = bytesReceived / 1048576; + auto totalMegaBytes = bytesTotal / 1048576; + + QString progressStr = "Revising %1 of %2 MiB at %3 %4"; + + emit progressText(progressStr.arg(megaBytesRec).arg(totalMegaBytes).arg(speed).arg(speedUnits), percentage); +} +void UpdaterPrivate::onFinishAction(QJsonObject info, short action) { + b_Running = false; + qDebug() << "Finished Action"; + if(action == QAppImageUpdate::Action::Update || + action == QAppImageUpdate::Action::UpdateWithTorrent) { + ++n_Completed; + QJsonObject r { + {"Updated" , true}, + {"NewAbsPath" , info["NewVersionPath"].toString() }, + {"OldAbsPath" , m_CurrentAppImage.path }, + {"Hash", m_CurrentAppImage.hash}, + {"ImageId" , m_CurrentAppImage.image_id}, + {"Name" , m_CurrentAppImage.name }, + {"UsedTorrent", info["UsedTorrent"].toBool()}, + {"TorrentFileUrl", info["TorrentFileUrl"].toString()} + }; + emit finished(r); + emit completedCountChanged(n_Completed); + updateNextAppImage(); + }else if(action == QAppImageUpdate::Action::CheckForUpdate) { + if(!info["UpdateAvailable"].toBool()) { + ++n_Completed; + QJsonObject rt { + {"Updated" , false}, + {"NewAbsPath" , m_CurrentAppImage.path }, + {"OldAbsPath" , m_CurrentAppImage.path }, + {"Hash", m_CurrentAppImage.hash}, + {"ImageId" , m_CurrentAppImage.image_id}, + {"Name" , m_CurrentAppImage.name }, + {"UsedTorrent", info["TorrentSupported"].toBool()}, + {"TorrentFileUrl", info["TorrentFileUrl"].toString()} + }; + + emit finished(rt); + emit completedCountChanged(n_Completed); + updateNextAppImage(); + return; + } + QJsonObject r { + {"AbsolutePath", m_CurrentAppImage.path }, + {"ReleaseNotes", info["ReleaseNotes"].toString() }, + {"Hash", m_CurrentAppImage.hash}, + {"ImageId" , m_CurrentAppImage.image_id}, + {"Name", m_CurrentAppImage.name } + }; + emit metaInfo(r); + + if(b_NoConfirm) { + m_Settings.sync(); + b_Running = true; + bool useBt = m_Settings.value("V2.isDecentralizedUpdateEnabled").toBool(); + + if(useBt){ + m_Updater->start(QAppImageUpdate::Action::UpdateWithTorrent); + }else{ + m_Updater->start(); + } + } + } +} + +void UpdaterPrivate::onStartAction(short action) { + if(action == QAppImageUpdate::Action::UpdateWithTorrent || + action == QAppImageUpdate::Action::Update) { + emit started(); + } +} + +void UpdaterPrivate::onErrorAction(short code, short action) { + Q_UNUSED(action); + b_Running = false; + QJsonObject r { + { "ErrorMsg" , QAppImageUpdate::errorCodeToDescriptionString(code) }, + { "AbsolutePath" , m_CurrentAppImage.path} , + { "Hash", m_CurrentAppImage.hash}, + { "ImageId" , m_CurrentAppImage.image_id}, + { "Name" , m_CurrentAppImage.name}, + }; + + ++n_Failed; + emit failed(r); + + + qDebug() << "Failed:: " << n_Failed; + emit failedCountChanged(n_Failed); + + updateNextAppImage(); +} + +void UpdaterPrivate::onCancelAction(short action) { + b_Running = false; + + if(action == QAppImageUpdate::Action::Update || + action == QAppImageUpdate::Action::UpdateWithTorrent) { + emit canceled(m_CurrentAppImage.hash); + } + updateNextAppImage(); +} + +//// Private Methods +void UpdaterPrivate::updateNextAppImage() { + emit loading(); + if(m_AppImages.isEmpty()) { + n_Queued = 0; + AppImage app; + m_CurrentAppImage = app; + emit queuedCountChanged(0); + emit finishedAll(); + return; + } + + m_CurrentAppImage = m_AppImages.dequeue(); + --n_Queued; + emit queuedCountChanged(n_Queued); + + m_Updater->clear(); + m_Updater->setAppImage(m_CurrentAppImage.path); + m_Settings.sync(); + bool useProxy = m_Settings.value("V2.isProxyEnabled").toBool(); + + if(useProxy) { + /// Get Proxy Settings + m_Settings.sync(); + auto proxyHost = m_Settings.value("V2.ProxyHost").toString(); + auto proxyPort = m_Settings.value("V2.ProxyPort").toInt(); + auto proxyType = m_Settings.value("V2.ProxyType").toString(); + auto username = m_Settings.value("V2.ProxyUser").toString(); + auto passwd = m_Settings.value("V2.ProxyPass").toString(); + + QNetworkProxy proxy; + if(proxyType == "Socks 5") { + proxy.setType(QNetworkProxy::Socks5Proxy); + }else{ + proxy.setType(QNetworkProxy::HttpProxy); + } + proxy.setHostName(proxyHost); + proxy.setPort(proxyPort); + if(!username.isEmpty() && !passwd.isEmpty()) { + proxy.setUser(username); + proxy.setPassword(passwd); + } + m_Updater->setProxy(proxy); + }else { + m_Updater->setProxy(QNetworkProxy()); + } + b_Running = true; + m_Updater->start(QAppImageUpdate::Action::CheckForUpdate); +} diff --git a/Updater_p.hpp b/Updater_p.hpp new file mode 100644 index 0000000..47b56c6 --- /dev/null +++ b/Updater_p.hpp @@ -0,0 +1,68 @@ +#ifndef UPDATER_PRIVATE_HPP_INCLUDED +#define UPDATER_PRIVATE_HPP_INCLUDED +#include +#include +#include +#include +#include +class UpdaterPrivate : public QObject { + Q_OBJECT + int n_Queued, + n_Failed, + n_Completed; + bool b_NoConfirm; + bool b_Running; + struct AppImage { + QString hash; + QString name; + QString path; + QString image_id; + bool isEmpty() { + return (path.isEmpty()); + } + }; + AppImage m_CurrentAppImage; + QQueue m_AppImages; + QAppImageUpdate *m_Updater; + QSettings m_Settings; +public: + UpdaterPrivate(QObject *parent = nullptr); + ~UpdaterPrivate(); +public Q_SLOTS: + void retry(const QJsonObject&); + void removeFromQueue(const QString&); + void queue(const QString&, const QString&, QVariant); + void toggleNoConfirm(); + void continueCurrentUpdate(); + void cancelCurrentUpdate(); + void cancelAll(); +private Q_SLOTS: + void onProgress(int, qint64, qint64, double, QString, short); + void onFinishAction(QJsonObject, short); + void onStartAction(short); + void onCancelAction(short); + void onErrorAction(short, short); + void onLog(const QString&, const QString&); +private: + void updateNextAppImage(); +Q_SIGNALS: + void noConfirmState(bool); + void removedFromQueue(const QString&); + void queuedCountChanged(int); + void failedCountChanged(int); + void completedCountChanged(int); + + void progressText(QString, int); + void torrentStatus(int, int); + void loading(); + void metaInfo(QJsonObject); + void queued(QJsonObject); + void failed(QJsonObject); + void started(); + void finished(QJsonObject); + void canceled(QString); + void retrySent(QString hash); + void finishedAll(); +}; + +#endif // UPDATER_PRIVATE_HPP_INCLUDED diff --git a/app_resources/about.png b/app_resources/about.png deleted file mode 100644 index 8f1336b..0000000 Binary files a/app_resources/about.png and /dev/null differ diff --git a/app_resources/ajr_logo.png b/app_resources/ajr_logo.png new file mode 100644 index 0000000..ef9af37 Binary files /dev/null and b/app_resources/ajr_logo.png differ diff --git a/app_resources/alert-triangle.png b/app_resources/alert-triangle.png new file mode 100644 index 0000000..06a0fff Binary files /dev/null and b/app_resources/alert-triangle.png differ diff --git a/app_resources/check.png b/app_resources/check.png deleted file mode 100644 index 57e095a..0000000 Binary files a/app_resources/check.png and /dev/null differ diff --git a/app_resources/default_icon.png b/app_resources/default_icon.png deleted file mode 100644 index 8947c31..0000000 Binary files a/app_resources/default_icon.png and /dev/null differ diff --git a/app_resources/dotted_square.png b/app_resources/dotted_square.png index ab0517f..f9821f9 100644 Binary files a/app_resources/dotted_square.png and b/app_resources/dotted_square.png differ diff --git a/app_resources/drop_image.png b/app_resources/drop_image.png new file mode 100644 index 0000000..7eda80f Binary files /dev/null and b/app_resources/drop_image.png differ diff --git a/app_resources/exit.png b/app_resources/exit.png deleted file mode 100644 index 82bf58c..0000000 Binary files a/app_resources/exit.png and /dev/null differ diff --git a/app_resources/less_info_icon.png b/app_resources/less_info_icon.png deleted file mode 100644 index 866075f..0000000 Binary files a/app_resources/less_info_icon.png and /dev/null differ diff --git a/app_resources/logo.png b/app_resources/logo.png index 6c235b6..e5feae0 100644 Binary files a/app_resources/logo.png and b/app_resources/logo.png differ diff --git a/app_resources/logo_blue.png b/app_resources/logo_blue.png new file mode 100644 index 0000000..af4f167 Binary files /dev/null and b/app_resources/logo_blue.png differ diff --git a/app_resources/logo_red.png b/app_resources/logo_red.png new file mode 100644 index 0000000..b6b9bba Binary files /dev/null and b/app_resources/logo_red.png differ diff --git a/app_resources/resources.qrc b/app_resources/resources.qrc index 53b16c6..a678b11 100644 --- a/app_resources/resources.qrc +++ b/app_resources/resources.qrc @@ -1,12 +1,13 @@ - shield.png logo.png - check.png - default_icon.png + logo_blue.png + logo_red.png dotted_square.png dotted_square_drop.png - about.png - exit.png + ajr_logo.png + alert-triangle.png + drop_image.png + shield.png diff --git a/appdir/etc/ssl/misc/CA.pl b/appdir/etc/ssl/misc/CA.pl deleted file mode 100755 index bb166d2..0000000 --- a/appdir/etc/ssl/misc/CA.pl +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/perl -# -# CA - wrapper around ca to make it easier to use ... basically ca requires -# some setup stuff to be done before you can use it and this makes -# things easier between now and when Eric is convinced to fix it :-) -# -# CA -newca ... will setup the right stuff -# CA -newreq[-nodes] ... will generate a certificate request -# CA -sign ... will sign the generated request and output -# -# At the end of that grab newreq.pem and newcert.pem (one has the key -# and the other the certificate) and cat them together and that is what -# you want/need ... I'll make even this a little cleaner later. -# -# -# 12-Jan-96 tjh Added more things ... including CA -signcert which -# converts a certificate to a request and then signs it. -# 10-Jan-96 eay Fixed a few more bugs and added the SSLEAY_CONFIG -# environment variable so this can be driven from -# a script. -# 25-Jul-96 eay Cleaned up filenames some more. -# 11-Jun-96 eay Fixed a few filename missmatches. -# 03-May-96 eay Modified to use 'ssleay cmd' instead of 'cmd'. -# 18-Apr-96 tjh Original hacking -# -# Tim Hudson -# tjh@cryptsoft.com -# - -# 27-Apr-98 snh Translation into perl, fix existing CA bug. -# -# -# Steve Henson -# shenson@bigfoot.com - -# default openssl.cnf file has setup as per the following -# demoCA ... where everything is stored - -my $openssl; -if(defined $ENV{OPENSSL}) { - $openssl = $ENV{OPENSSL}; -} else { - $openssl = "openssl"; - $ENV{OPENSSL} = $openssl; -} - -$SSLEAY_CONFIG=$ENV{"SSLEAY_CONFIG"}; -$DAYS="-days 365"; # 1 year -$CADAYS="-days 1095"; # 3 years -$REQ="$openssl req $SSLEAY_CONFIG"; -$CA="$openssl ca $SSLEAY_CONFIG"; -$VERIFY="$openssl verify"; -$X509="$openssl x509"; -$PKCS12="$openssl pkcs12"; - -$CATOP="/etc/ssl"; -$CAKEY="cakey.pem"; -$CAREQ="careq.pem"; -$CACERT="cacert.pem"; - -$DIRMODE = 0777; - -$RET = 0; - -foreach (@ARGV) { - if ( /^(-\?|-h|-help)$/ ) { - print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n"; - exit 0; - } elsif (/^-newcert$/) { - # create a certificate - system ("$REQ -new -x509 -keyout newkey.pem -out newcert.pem $DAYS"); - $RET=$?; - print "Certificate is in newcert.pem, private key is in newkey.pem\n" - } elsif (/^-newreq$/) { - # create a certificate request - system ("$REQ -new -keyout newkey.pem -out newreq.pem $DAYS"); - $RET=$?; - print "Request is in newreq.pem, private key is in newkey.pem\n"; - } elsif (/^-newreq-nodes$/) { - # create a certificate request - system ("$REQ -new -nodes -keyout newkey.pem -out newreq.pem $DAYS"); - $RET=$?; - print "Request is in newreq.pem, private key is in newkey.pem\n"; - } elsif (/^-newca$/) { - # if explicitly asked for or it doesn't exist then setup the - # directory structure that Eric likes to manage things - $NEW="1"; - if ( "$NEW" || ! -f "${CATOP}/serial" ) { - # create the directory hierarchy - mkdir $CATOP, $DIRMODE; - mkdir "${CATOP}/certs", $DIRMODE; - mkdir "${CATOP}/crl", $DIRMODE ; - mkdir "${CATOP}/newcerts", $DIRMODE; - mkdir "${CATOP}/private", $DIRMODE; - open OUT, ">${CATOP}/index.txt"; - close OUT; - open OUT, ">${CATOP}/crlnumber"; - print OUT "01\n"; - close OUT; - } - if ( ! -f "${CATOP}/private/$CAKEY" ) { - print "CA certificate filename (or enter to create)\n"; - $FILE = ; - - chop $FILE; - - # ask user for existing CA certificate - if ($FILE) { - cp_pem($FILE,"${CATOP}/private/$CAKEY", "PRIVATE"); - cp_pem($FILE,"${CATOP}/$CACERT", "CERTIFICATE"); - $RET=$?; - } else { - print "Making CA certificate ...\n"; - system ("$REQ -new -keyout " . - "${CATOP}/private/$CAKEY -out ${CATOP}/$CAREQ"); - system ("$CA -create_serial " . - "-out ${CATOP}/$CACERT $CADAYS -batch " . - "-keyfile ${CATOP}/private/$CAKEY -selfsign " . - "-extensions v3_ca " . - "-infiles ${CATOP}/$CAREQ "); - $RET=$?; - } - } - } elsif (/^-pkcs12$/) { - my $cname = $ARGV[1]; - $cname = "My Certificate" unless defined $cname; - system ("$PKCS12 -in newcert.pem -inkey newkey.pem " . - "-certfile ${CATOP}/$CACERT -out newcert.p12 " . - "-export -name \"$cname\""); - $RET=$?; - print "PKCS #12 file is in newcert.p12\n"; - exit $RET; - } elsif (/^-xsign$/) { - system ("$CA -policy policy_anything -infiles newreq.pem"); - $RET=$?; - } elsif (/^(-sign|-signreq)$/) { - system ("$CA -policy policy_anything -out newcert.pem " . - "-infiles newreq.pem"); - $RET=$?; - print "Signed certificate is in newcert.pem\n"; - } elsif (/^(-signCA)$/) { - system ("$CA -policy policy_anything -out newcert.pem " . - "-extensions v3_ca -infiles newreq.pem"); - $RET=$?; - print "Signed CA certificate is in newcert.pem\n"; - } elsif (/^-signcert$/) { - system ("$X509 -x509toreq -in newreq.pem -signkey newreq.pem " . - "-out tmp.pem"); - system ("$CA -policy policy_anything -out newcert.pem " . - "-infiles tmp.pem"); - $RET = $?; - print "Signed certificate is in newcert.pem\n"; - } elsif (/^-verify$/) { - if (shift) { - foreach $j (@ARGV) { - system ("$VERIFY -CAfile $CATOP/$CACERT $j"); - $RET=$? if ($? != 0); - } - exit $RET; - } else { - system ("$VERIFY -CAfile $CATOP/$CACERT newcert.pem"); - $RET=$?; - exit 0; - } - } else { - print STDERR "Unknown arg $_\n"; - print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n"; - exit 1; - } -} - -exit $RET; - -sub cp_pem { -my ($infile, $outfile, $bound) = @_; -open IN, $infile; -open OUT, ">$outfile"; -my $flag = 0; -while () { - $flag = 1 if (/^-----BEGIN.*$bound/) ; - print OUT $_ if ($flag); - if (/^-----END.*$bound/) { - close IN; - close OUT; - return; - } -} -} - diff --git a/appdir/etc/ssl/misc/CA.sh b/appdir/etc/ssl/misc/CA.sh deleted file mode 100755 index d5b4d8f..0000000 --- a/appdir/etc/ssl/misc/CA.sh +++ /dev/null @@ -1,198 +0,0 @@ -#!/bin/sh -# -# CA - wrapper around ca to make it easier to use ... basically ca requires -# some setup stuff to be done before you can use it and this makes -# things easier between now and when Eric is convinced to fix it :-) -# -# CA -newca ... will setup the right stuff -# CA -newreq ... will generate a certificate request -# CA -sign ... will sign the generated request and output -# -# At the end of that grab newreq.pem and newcert.pem (one has the key -# and the other the certificate) and cat them together and that is what -# you want/need ... I'll make even this a little cleaner later. -# -# -# 12-Jan-96 tjh Added more things ... including CA -signcert which -# converts a certificate to a request and then signs it. -# 10-Jan-96 eay Fixed a few more bugs and added the SSLEAY_CONFIG -# environment variable so this can be driven from -# a script. -# 25-Jul-96 eay Cleaned up filenames some more. -# 11-Jun-96 eay Fixed a few filename missmatches. -# 03-May-96 eay Modified to use 'ssleay cmd' instead of 'cmd'. -# 18-Apr-96 tjh Original hacking -# -# Tim Hudson -# tjh@cryptsoft.com -# - -# default openssl.cnf file has setup as per the following -# demoCA ... where everything is stored -cp_pem() { - infile=$1 - outfile=$2 - bound=$3 - flag=0 - exec <$infile; - while read line; do - if [ $flag -eq 1 ]; then - echo $line|grep "^-----END.*$bound" 2>/dev/null 1>/dev/null - if [ $? -eq 0 ] ; then - echo $line >>$outfile - break - else - echo $line >>$outfile - fi - fi - - echo $line|grep "^-----BEGIN.*$bound" 2>/dev/null 1>/dev/null - if [ $? -eq 0 ]; then - echo $line >$outfile - flag=1 - fi - done -} - -usage() { - echo "usage: $0 -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify" >&2 -} - -if [ -z "$OPENSSL" ]; then OPENSSL=openssl; fi - -if [ -z "$DAYS" ] ; then DAYS="-days 365" ; fi # 1 year -CADAYS="-days 1095" # 3 years -REQ="$OPENSSL req $SSLEAY_CONFIG" -CA="$OPENSSL ca $SSLEAY_CONFIG" -VERIFY="$OPENSSL verify" -X509="$OPENSSL x509" -PKCS12="openssl pkcs12" - -if [ -z "$CATOP" ] ; then CATOP=/etc/ssl ; fi -CAKEY=./cakey.pem -CAREQ=./careq.pem -CACERT=./cacert.pem - -RET=0 - -while [ "$1" != "" ] ; do -case $1 in --\?|-h|-help) - usage - exit 0 - ;; --newcert) - # create a certificate - $REQ -new -x509 -keyout newkey.pem -out newcert.pem $DAYS - RET=$? - echo "Certificate is in newcert.pem, private key is in newkey.pem" - ;; --newreq) - # create a certificate request - $REQ -new -keyout newkey.pem -out newreq.pem $DAYS - RET=$? - echo "Request is in newreq.pem, private key is in newkey.pem" - ;; --newreq-nodes) - # create a certificate request - $REQ -new -nodes -keyout newreq.pem -out newreq.pem $DAYS - RET=$? - echo "Request (and private key) is in newreq.pem" - ;; --newca) - # if explicitly asked for or it doesn't exist then setup the directory - # structure that Eric likes to manage things - NEW="1" - if [ "$NEW" -o ! -f ${CATOP}/serial ]; then - # create the directory hierarchy - mkdir -p ${CATOP} - mkdir -p ${CATOP}/certs - mkdir -p ${CATOP}/crl - mkdir -p ${CATOP}/newcerts - mkdir -p ${CATOP}/private - touch ${CATOP}/index.txt - fi - if [ ! -f ${CATOP}/private/$CAKEY ]; then - echo "CA certificate filename (or enter to create)" - read FILE - - # ask user for existing CA certificate - if [ "$FILE" ]; then - cp_pem $FILE ${CATOP}/private/$CAKEY PRIVATE - cp_pem $FILE ${CATOP}/$CACERT CERTIFICATE - RET=$? - if [ ! -f "${CATOP}/serial" ]; then - $X509 -in ${CATOP}/$CACERT -noout -next_serial \ - -out ${CATOP}/serial - fi - else - echo "Making CA certificate ..." - $REQ -new -keyout ${CATOP}/private/$CAKEY \ - -out ${CATOP}/$CAREQ - $CA -create_serial -out ${CATOP}/$CACERT $CADAYS -batch \ - -keyfile ${CATOP}/private/$CAKEY -selfsign \ - -extensions v3_ca \ - -infiles ${CATOP}/$CAREQ - RET=$? - fi - fi - ;; --xsign) - $CA -policy policy_anything -infiles newreq.pem - RET=$? - ;; --pkcs12) - if [ -z "$2" ] ; then - CNAME="My Certificate" - else - CNAME="$2" - fi - $PKCS12 -in newcert.pem -inkey newreq.pem -certfile ${CATOP}/$CACERT \ - -out newcert.p12 -export -name "$CNAME" - RET=$? - exit $RET - ;; --sign|-signreq) - $CA -policy policy_anything -out newcert.pem -infiles newreq.pem - RET=$? - cat newcert.pem - echo "Signed certificate is in newcert.pem" - ;; --signCA) - $CA -policy policy_anything -out newcert.pem -extensions v3_ca -infiles newreq.pem - RET=$? - echo "Signed CA certificate is in newcert.pem" - ;; --signcert) - echo "Cert passphrase will be requested twice - bug?" - $X509 -x509toreq -in newreq.pem -signkey newreq.pem -out tmp.pem - $CA -policy policy_anything -out newcert.pem -infiles tmp.pem - RET=$? - cat newcert.pem - echo "Signed certificate is in newcert.pem" - ;; --verify) - shift - if [ -z "$1" ]; then - $VERIFY -CAfile $CATOP/$CACERT newcert.pem - RET=$? - else - for j - do - $VERIFY -CAfile $CATOP/$CACERT $j - if [ $? != 0 ]; then - RET=$? - fi - done - fi - exit $RET - ;; -*) - echo "Unknown arg $i" >&2 - usage - exit 1 - ;; -esac -shift -done -exit $RET diff --git a/appdir/etc/ssl/misc/c_hash b/appdir/etc/ssl/misc/c_hash deleted file mode 100755 index 5e0a908..0000000 --- a/appdir/etc/ssl/misc/c_hash +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# print out the hash values -# - -for i in $* -do - h=`openssl x509 -hash -noout -in $i` - echo "$h.0 => $i" -done diff --git a/appdir/etc/ssl/misc/c_info b/appdir/etc/ssl/misc/c_info deleted file mode 100755 index 0e1e633..0000000 --- a/appdir/etc/ssl/misc/c_info +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# print the subject -# - -for i in $* -do - n=`openssl x509 -subject -issuer -enddate -noout -in $i` - echo "$i" - echo "$n" - echo "--------" -done diff --git a/appdir/etc/ssl/misc/c_issuer b/appdir/etc/ssl/misc/c_issuer deleted file mode 100755 index 55821ab..0000000 --- a/appdir/etc/ssl/misc/c_issuer +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -# -# print out the issuer -# - -for i in $* -do - n=`openssl x509 -issuer -noout -in $i` - echo "$i $n" -done diff --git a/appdir/etc/ssl/misc/c_name b/appdir/etc/ssl/misc/c_name deleted file mode 100755 index 28800c0..0000000 --- a/appdir/etc/ssl/misc/c_name +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -# -# print the subject -# - -for i in $* -do - n=`openssl x509 -subject -noout -in $i` - echo "$i $n" -done diff --git a/appdir/etc/ssl/misc/tsget b/appdir/etc/ssl/misc/tsget deleted file mode 100755 index 0d54e9f..0000000 --- a/appdir/etc/ssl/misc/tsget +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/perl -w -# Written by Zoltan Glozik . -# Copyright (c) 2002 The OpenTSA Project. All rights reserved. -$::version = '$Id: tsget,v 1.1.2.2 2009/09/07 17:57:02 steve Exp $'; - -use strict; -use IO::Handle; -use Getopt::Std; -use File::Basename; -use WWW::Curl::Easy; - -use vars qw(%options); - -# Callback for reading the body. -sub read_body { - my ($maxlength, $state) = @_; - my $return_data = ""; - my $data_len = length ${$state->{data}}; - if ($state->{bytes} < $data_len) { - $data_len = $data_len - $state->{bytes}; - $data_len = $maxlength if $data_len > $maxlength; - $return_data = substr ${$state->{data}}, $state->{bytes}, $data_len; - $state->{bytes} += $data_len; - } - return $return_data; -} - -# Callback for writing the body into a variable. -sub write_body { - my ($data, $pointer) = @_; - ${$pointer} .= $data; - return length($data); -} - -# Initialise a new Curl object. -sub create_curl { - my $url = shift; - - # Create Curl object. - my $curl = WWW::Curl::Easy::new(); - - # Error-handling related options. - $curl->setopt(CURLOPT_VERBOSE, 1) if $options{d}; - $curl->setopt(CURLOPT_FAILONERROR, 1); - $curl->setopt(CURLOPT_USERAGENT, "OpenTSA tsget.pl/" . (split / /, $::version)[2]); - - # Options for POST method. - $curl->setopt(CURLOPT_UPLOAD, 1); - $curl->setopt(CURLOPT_CUSTOMREQUEST, "POST"); - $curl->setopt(CURLOPT_HTTPHEADER, - ["Content-Type: application/timestamp-query", - "Accept: application/timestamp-reply,application/timestamp-response"]); - $curl->setopt(CURLOPT_READFUNCTION, \&read_body); - $curl->setopt(CURLOPT_HEADERFUNCTION, sub { return length($_[0]); }); - - # Options for getting the result. - $curl->setopt(CURLOPT_WRITEFUNCTION, \&write_body); - - # SSL related options. - $curl->setopt(CURLOPT_SSLKEYTYPE, "PEM"); - $curl->setopt(CURLOPT_SSL_VERIFYPEER, 1); # Verify server's certificate. - $curl->setopt(CURLOPT_SSL_VERIFYHOST, 2); # Check server's CN. - $curl->setopt(CURLOPT_SSLKEY, $options{k}) if defined($options{k}); - $curl->setopt(CURLOPT_SSLKEYPASSWD, $options{p}) if defined($options{p}); - $curl->setopt(CURLOPT_SSLCERT, $options{c}) if defined($options{c}); - $curl->setopt(CURLOPT_CAINFO, $options{C}) if defined($options{C}); - $curl->setopt(CURLOPT_CAPATH, $options{P}) if defined($options{P}); - $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r}); - $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g}); - - # Setting destination. - $curl->setopt(CURLOPT_URL, $url); - - return $curl; -} - -# Send a request and returns the body back. -sub get_timestamp { - my $curl = shift; - my $body = shift; - my $ts_body; - local $::error_buf; - - # Error-handling related options. - $curl->setopt(CURLOPT_ERRORBUFFER, "::error_buf"); - - # Options for POST method. - $curl->setopt(CURLOPT_INFILE, {data => $body, bytes => 0}); - $curl->setopt(CURLOPT_INFILESIZE, length(${$body})); - - # Options for getting the result. - $curl->setopt(CURLOPT_FILE, \$ts_body); - - # Send the request... - my $error_code = $curl->perform(); - my $error_string; - if ($error_code != 0) { - my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE); - $error_string = "could not get timestamp"; - $error_string .= ", http code: $http_code" unless $http_code == 0; - $error_string .= ", curl code: $error_code"; - $error_string .= " ($::error_buf)" if defined($::error_buf); - } else { - my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE); - if (lc($ct) ne "application/timestamp-reply" - && lc($ct) ne "application/timestamp-response") { - $error_string = "unexpected content type returned: $ct"; - } - } - return ($ts_body, $error_string); - -} - -# Print usage information and exists. -sub usage { - - print STDERR "usage: $0 -h [-e ] [-o ] "; - print STDERR "[-v] [-d] [-k ] [-p ] "; - print STDERR "[-c ] [-C ] [-P ] "; - print STDERR "[-r ] [-g ] []...\n"; - exit 1; -} - -# ---------------------------------------------------------------------- -# Main program -# ---------------------------------------------------------------------- - -# Getting command-line options (default comes from TSGET environment variable). -my $getopt_arg = "h:e:o:vdk:p:c:C:P:r:g:"; -if (exists $ENV{TSGET}) { - my @old_argv = @ARGV; - @ARGV = split /\s+/, $ENV{TSGET}; - getopts($getopt_arg, \%options) or usage; - @ARGV = @old_argv; -} -getopts($getopt_arg, \%options) or usage; - -# Checking argument consistency. -if (!exists($options{h}) || (@ARGV == 0 && !exists($options{o})) - || (@ARGV > 1 && exists($options{o}))) { - print STDERR "Inconsistent command line options.\n"; - usage; -} -# Setting defaults. -@ARGV = ("-") unless @ARGV != 0; -$options{e} = ".tsr" unless defined($options{e}); - -# Processing requests. -my $curl = create_curl $options{h}; -undef $/; # For reading whole files. -REQUEST: foreach (@ARGV) { - my $input = $_; - my ($base, $path) = fileparse($input, '\.[^.]*'); - my $output_base = $base . $options{e}; - my $output = defined($options{o}) ? $options{o} : $path . $output_base; - - STDERR->printflush("$input: ") if $options{v}; - # Read request. - my $body; - if ($input eq "-") { - # Read the request from STDIN; - $body = ; - } else { - # Read the request from file. - open INPUT, "<" . $input - or warn("$input: could not open input file: $!\n"), next REQUEST; - $body = ; - close INPUT - or warn("$input: could not close input file: $!\n"), next REQUEST; - } - - # Send request. - STDERR->printflush("sending request") if $options{v}; - - my ($ts_body, $error) = get_timestamp $curl, \$body; - if (defined($error)) { - die "$input: fatal error: $error\n"; - } - STDERR->printflush(", reply received") if $options{v}; - - # Write response. - if ($output eq "-") { - # Write to STDOUT. - print $ts_body; - } else { - # Write to file. - open OUTPUT, ">", $output - or warn("$output: could not open output file: $!\n"), next REQUEST; - print OUTPUT $ts_body; - close OUTPUT - or warn("$output: could not close output file: $!\n"), next REQUEST; - } - STDERR->printflush(", $output written.\n") if $options{v}; -} -$curl->cleanup(); -WWW::Curl::Easy::global_cleanup(); diff --git a/appdir/etc/ssl/openssl.cnf b/appdir/etc/ssl/openssl.cnf deleted file mode 100644 index 290dd7b..0000000 --- a/appdir/etc/ssl/openssl.cnf +++ /dev/null @@ -1,350 +0,0 @@ -# -# OpenSSL example configuration file. -# This is mostly being used for generation of certificate requests. -# - -# This definition stops the following lines choking if HOME isn't -# defined. -HOME = . -RANDFILE = $ENV::HOME/.rnd - -# Extra OBJECT IDENTIFIER info: -#oid_file = $ENV::HOME/.oid -oid_section = new_oids - -# To use this configuration file with the "-extfile" option of the -# "openssl x509" utility, name here the section containing the -# X.509v3 extensions to use: -# extensions = -# (Alternatively, use a configuration file that has only -# X.509v3 extensions in its main [= default] section.) - -[ new_oids ] - -# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. -# Add a simple OID like this: -# testoid1=1.2.3.4 -# Or use config file substitution like this: -# testoid2=${testoid1}.5.6 - -# Policies used by the TSA examples. -tsa_policy1 = 1.2.3.4.1 -tsa_policy2 = 1.2.3.4.5.6 -tsa_policy3 = 1.2.3.4.5.7 - -#################################################################### -[ ca ] -default_ca = CA_default # The default ca section - -#################################################################### -[ CA_default ] - -dir = /etc/ssl # Where everything is kept -certs = $dir/certs # Where the issued certs are kept -crl_dir = $dir/crl # Where the issued crl are kept -database = $dir/index.txt # database index file. -#unique_subject = no # Set to 'no' to allow creation of - # several ctificates with same subject. -new_certs_dir = $dir/newcerts # default place for new certs. - -certificate = $dir/cacert.pem # The CA certificate -serial = $dir/serial # The current serial number -crlnumber = $dir/crlnumber # the current crl number - # must be commented out to leave a V1 CRL -crl = $dir/crl.pem # The current CRL -private_key = $dir/private/cakey.pem# The private key -RANDFILE = $dir/private/.rand # private random number file - -x509_extensions = usr_cert # The extentions to add to the cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -# Extension copying option: use with caution. -# copy_extensions = copy - -# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs -# so this is commented out by default to leave a V1 CRL. -# crlnumber must also be commented out to leave a V1 CRL. -# crl_extensions = crl_ext - -default_days = 365 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = default # use public key default MD -preserve = no # keep passed DN ordering - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_match - -# For the CA policy -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 1024 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extentions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -# req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgits Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - - -# Extensions for a typical CA - - -# PKIX recommendation. - -subjectKeyIdentifier=hash - -authorityKeyIdentifier=keyid:always,issuer - -# This is what PKIX recommends but some broken software chokes on critical -# extensions. -#basicConstraints = critical,CA:true -# So we do this instead. -basicConstraints = CA:true - -# Key usage: this is typical for a CA certificate. However since it will -# prevent it being used as an test self-signed certificate it is best -# left out by default. -# keyUsage = cRLSign, keyCertSign - -# Some might want this also -# nsCertType = sslCA, emailCA - -# Include email address in subject alt name: another PKIX recommendation -# subjectAltName=email:copy -# Copy issuer details -# issuerAltName=issuer:copy - -# DER hex encoding of an extension: beware experts only! -# obj=DER:02:03 -# Where 'obj' is a standard or added object -# You can even override a supported extension: -# basicConstraints= critical, DER:30:03:01:01:FF - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ proxy_cert_ext ] -# These extensions should be added when creating a proxy certificate - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This really needs to be in place for it to be a proxy certificate. -proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo - -#################################################################### -[ tsa ] - -default_tsa = tsa_config1 # the default TSA section - -[ tsa_config1 ] - -# These are used by the TSA reply generation only. -dir = ./demoCA # TSA root directory -serial = $dir/tsaserial # The current serial number (mandatory) -crypto_device = builtin # OpenSSL engine to use for signing -signer_cert = $dir/tsacert.pem # The TSA signing certificate - # (optional) -certs = $dir/cacert.pem # Certificate chain to include in reply - # (optional) -signer_key = $dir/private/tsakey.pem # The TSA private key (optional) - -default_policy = tsa_policy1 # Policy if request did not specify it - # (optional) -other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) -digests = md5, sha1 # Acceptable message digests (mandatory) -accuracy = secs:1, millisecs:500, microsecs:100 # (optional) -clock_precision_digits = 0 # number of digits after dot. (optional) -ordering = yes # Is ordering defined for timestamps? - # (optional, default: no) -tsa_name = yes # Must the TSA name be included in the reply? - # (optional, default: no) -ess_cert_id_chain = no # Must the ESS cert id chain be included? - # (optional, default: no) diff --git a/appdir/usr/lib/engines/lib4758cca.so b/appdir/usr/lib/engines/lib4758cca.so deleted file mode 100755 index ab665c3..0000000 Binary files a/appdir/usr/lib/engines/lib4758cca.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libaep.so b/appdir/usr/lib/engines/libaep.so deleted file mode 100755 index f17cbdc..0000000 Binary files a/appdir/usr/lib/engines/libaep.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libatalla.so b/appdir/usr/lib/engines/libatalla.so deleted file mode 100755 index 8b56c8e..0000000 Binary files a/appdir/usr/lib/engines/libatalla.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libcapi.so b/appdir/usr/lib/engines/libcapi.so deleted file mode 100755 index 956d1ec..0000000 Binary files a/appdir/usr/lib/engines/libcapi.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libchil.so b/appdir/usr/lib/engines/libchil.so deleted file mode 100755 index 5e28b6f..0000000 Binary files a/appdir/usr/lib/engines/libchil.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libcswift.so b/appdir/usr/lib/engines/libcswift.so deleted file mode 100755 index 7a6622f..0000000 Binary files a/appdir/usr/lib/engines/libcswift.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libgmp.so b/appdir/usr/lib/engines/libgmp.so deleted file mode 100755 index a909210..0000000 Binary files a/appdir/usr/lib/engines/libgmp.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libgost.so b/appdir/usr/lib/engines/libgost.so deleted file mode 100755 index ec65faf..0000000 Binary files a/appdir/usr/lib/engines/libgost.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libnuron.so b/appdir/usr/lib/engines/libnuron.so deleted file mode 100755 index 68a7a5c..0000000 Binary files a/appdir/usr/lib/engines/libnuron.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libpadlock.so b/appdir/usr/lib/engines/libpadlock.so deleted file mode 100755 index f8af5c8..0000000 Binary files a/appdir/usr/lib/engines/libpadlock.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libsureware.so b/appdir/usr/lib/engines/libsureware.so deleted file mode 100755 index 6e1b23d..0000000 Binary files a/appdir/usr/lib/engines/libsureware.so and /dev/null differ diff --git a/appdir/usr/lib/engines/libubsec.so b/appdir/usr/lib/engines/libubsec.so deleted file mode 100755 index 938e2f3..0000000 Binary files a/appdir/usr/lib/engines/libubsec.so and /dev/null differ diff --git a/appdir/usr/lib/libcrypto.so b/appdir/usr/lib/libcrypto.so deleted file mode 120000 index c049566..0000000 --- a/appdir/usr/lib/libcrypto.so +++ /dev/null @@ -1 +0,0 @@ -libcrypto.so.1.0.0 \ No newline at end of file diff --git a/appdir/usr/lib/libcrypto.so.1.0.0 b/appdir/usr/lib/libcrypto.so.1.0.0 deleted file mode 100755 index 4d8bcb1..0000000 Binary files a/appdir/usr/lib/libcrypto.so.1.0.0 and /dev/null differ diff --git a/appdir/usr/lib/libssl.so b/appdir/usr/lib/libssl.so deleted file mode 120000 index 8a897d0..0000000 --- a/appdir/usr/lib/libssl.so +++ /dev/null @@ -1 +0,0 @@ -libssl.so.1.0.0 \ No newline at end of file diff --git a/appdir/usr/lib/libssl.so.1.0.0 b/appdir/usr/lib/libssl.so.1.0.0 deleted file mode 100755 index d9657c7..0000000 Binary files a/appdir/usr/lib/libssl.so.1.0.0 and /dev/null differ diff --git a/build_resources/AppImageUpdater.desktop b/build_resources/AppImageUpdater.desktop index e136922..e525fd5 100644 --- a/build_resources/AppImageUpdater.desktop +++ b/build_resources/AppImageUpdater.desktop @@ -1,8 +1,8 @@ [Desktop Entry] -Name=AppImageUpdater +Name=AppImage Updater Type=Application Exec=AppImageUpdater Icon=AppImageUpdater Terminal=false Categories=System; -Comment=Update your AppImages Like a Pro. +Comment=AppImage Updater for Humans. diff --git a/build_resources/AppImageUpdater.png b/build_resources/AppImageUpdater.png index 6c235b6..e5feae0 100644 Binary files a/build_resources/AppImageUpdater.png and b/build_resources/AppImageUpdater.png differ diff --git a/cli/appdir/usr/share/applications/appimageupdater.desktop b/build_resources/appimageupdater.desktop similarity index 61% rename from cli/appdir/usr/share/applications/appimageupdater.desktop rename to build_resources/appimageupdater.desktop index f3e12bf..4a7a5bc 100644 --- a/cli/appdir/usr/share/applications/appimageupdater.desktop +++ b/build_resources/appimageupdater.desktop @@ -1,8 +1,8 @@ [Desktop Entry] -Name=appimageupdater +Name=appimageupdatercli Type=Application Exec=appimageupdater Icon=AppImageUpdater Terminal=true Categories=System; -Comment=Update your AppImages Like a Pro. +Comment=AppImage Updater for Humans (CLI). diff --git a/build_resources/build_qt_static.sh b/build_resources/build_qt_static.sh index 2ab3d88..21e2977 100644 --- a/build_resources/build_qt_static.sh +++ b/build_resources/build_qt_static.sh @@ -1,11 +1,11 @@ if [ ! -d "/usr/local/Qt-5.12.8" ]; then sudo apt-get -y install gcc g++ make wget sudo apt-get -y install build-essential - sudo apt-get -y install "^libxcb.*" libx11-dev libx11-xcb-dev libxcursor-dev libxrender-dev libxrandr-dev libxext-dev libxi-dev libxss-dev libxt-dev libxv-dev libxxf86vm-dev libxinerama-dev libxkbcommon-dev libfontconfig1-dev libharfbuzz-dev libasound2-dev libpulse-dev libdbus-1-dev udev mtdev-tools webp libudev-dev libglm-dev libwayland-dev libegl1-mesa-dev mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev libgles2-mesa libgles2-mesa-dev libmirclient-dev libproxy-dev libgtk2.0-dev libgtk-3-dev libcups2-dev + sudo apt-get -y install "^libxcb.*" libx11-dev libx11-xcb-dev libxcursor-dev libxrender-dev libxrandr-dev libxext-dev libxi-dev libxss-dev libxt-dev libxv-dev libxxf86vm-dev libxinerama-dev libxkbcommon-dev libfontconfig1-dev libharfbuzz-dev libasound2-dev libpulse-dev libdbus-1-dev udev mtdev-tools webp libudev-dev libglm-dev libwayland-dev libegl1-mesa-dev mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev libgles2-mesa libgles2-mesa-dev libmirclient-dev libproxy-dev libgtk2.0-dev libgtk-3-dev libcups2-dev libssl-dev openssl wget -c "https://download.qt.io/archive/qt/5.12/5.12.8/submodules/qtbase-everywhere-src-5.12.8.tar.xz" tar -xvf qtbase-everywhere-src-5.12.8.tar.xz cd qtbase-everywhere-src-5.12.8 - ./configure -static -release -optimize-size -silent -opensource -confirm-license -opengl -nomake examples -qt-xcb -sm -qt-libpng -no-libjpeg -no-icu -qt-zlib -qt-pcre -gtk -system-freetype -qt-harfbuzz + ./configure -static -release -optimize-size -silent -opensource -confirm-license -opengl -openssl-linked -nomake examples -qt-xcb -sm -qt-libpng -no-libjpeg -no-icu -qt-zlib -qt-pcre -gtk -system-freetype -qt-harfbuzz make -j$(nproc) && sudo make install fi diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt new file mode 100644 index 0000000..3b4a9ef --- /dev/null +++ b/cli/CMakeLists.txt @@ -0,0 +1,36 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.2) +project(appimageupdater) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_FLAGS "-Wall -Wextra") +set(CMAKE_CXX_FLAGS_DEBUG "-g") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +find_package(LibtorrentRasterbar) +find_package(QAppImageUpdate) + +# Include Directories. +include_directories(${CMAKE_BINARY_DIR}) + +if(APPIMAGE_UPDATER_VERSION) + add_definitions(-DAPPIMAGE_UPDATER_VERSION="${APPIMAGE_UPDATER_VERSION}") +endif() +if(APPIMAGE_UPDATER_COMMIT) + add_definitions(-DAPPIMAGE_UPDATER_COMMIT="${APPIMAGE_UPDATER_COMMIT}") +endif() +if(APPIMAGE_UPDATER_BUILD_NO) + add_definitions(-DAPPIMAGE_UPDATER_BUILD_NO="${APPIMAGE_UPDATER_BUILD_NO}") +endif() +if(APPIMAGE_UPDATER_BUILD_TIME) + add_definitions(-DAPPIMAGE_UPDATER_BUILD_TIME="${APPIMAGE_UPDATER_BUILD_TIME}") +endif() + +add_executable(appimageupdater main.cc cutelog.c cutelog.h) +target_link_libraries(appimageupdater PUBLIC Qt5::Core) +target_link_libraries(appimageupdater PRIVATE QAppImageUpdate) +target_link_libraries(appimageupdater PRIVATE LibtorrentRasterbar::torrent-rasterbar) diff --git a/cli/appdir/etc/ssl/misc/CA.pl b/cli/appdir/etc/ssl/misc/CA.pl deleted file mode 100755 index bb166d2..0000000 --- a/cli/appdir/etc/ssl/misc/CA.pl +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/perl -# -# CA - wrapper around ca to make it easier to use ... basically ca requires -# some setup stuff to be done before you can use it and this makes -# things easier between now and when Eric is convinced to fix it :-) -# -# CA -newca ... will setup the right stuff -# CA -newreq[-nodes] ... will generate a certificate request -# CA -sign ... will sign the generated request and output -# -# At the end of that grab newreq.pem and newcert.pem (one has the key -# and the other the certificate) and cat them together and that is what -# you want/need ... I'll make even this a little cleaner later. -# -# -# 12-Jan-96 tjh Added more things ... including CA -signcert which -# converts a certificate to a request and then signs it. -# 10-Jan-96 eay Fixed a few more bugs and added the SSLEAY_CONFIG -# environment variable so this can be driven from -# a script. -# 25-Jul-96 eay Cleaned up filenames some more. -# 11-Jun-96 eay Fixed a few filename missmatches. -# 03-May-96 eay Modified to use 'ssleay cmd' instead of 'cmd'. -# 18-Apr-96 tjh Original hacking -# -# Tim Hudson -# tjh@cryptsoft.com -# - -# 27-Apr-98 snh Translation into perl, fix existing CA bug. -# -# -# Steve Henson -# shenson@bigfoot.com - -# default openssl.cnf file has setup as per the following -# demoCA ... where everything is stored - -my $openssl; -if(defined $ENV{OPENSSL}) { - $openssl = $ENV{OPENSSL}; -} else { - $openssl = "openssl"; - $ENV{OPENSSL} = $openssl; -} - -$SSLEAY_CONFIG=$ENV{"SSLEAY_CONFIG"}; -$DAYS="-days 365"; # 1 year -$CADAYS="-days 1095"; # 3 years -$REQ="$openssl req $SSLEAY_CONFIG"; -$CA="$openssl ca $SSLEAY_CONFIG"; -$VERIFY="$openssl verify"; -$X509="$openssl x509"; -$PKCS12="$openssl pkcs12"; - -$CATOP="/etc/ssl"; -$CAKEY="cakey.pem"; -$CAREQ="careq.pem"; -$CACERT="cacert.pem"; - -$DIRMODE = 0777; - -$RET = 0; - -foreach (@ARGV) { - if ( /^(-\?|-h|-help)$/ ) { - print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n"; - exit 0; - } elsif (/^-newcert$/) { - # create a certificate - system ("$REQ -new -x509 -keyout newkey.pem -out newcert.pem $DAYS"); - $RET=$?; - print "Certificate is in newcert.pem, private key is in newkey.pem\n" - } elsif (/^-newreq$/) { - # create a certificate request - system ("$REQ -new -keyout newkey.pem -out newreq.pem $DAYS"); - $RET=$?; - print "Request is in newreq.pem, private key is in newkey.pem\n"; - } elsif (/^-newreq-nodes$/) { - # create a certificate request - system ("$REQ -new -nodes -keyout newkey.pem -out newreq.pem $DAYS"); - $RET=$?; - print "Request is in newreq.pem, private key is in newkey.pem\n"; - } elsif (/^-newca$/) { - # if explicitly asked for or it doesn't exist then setup the - # directory structure that Eric likes to manage things - $NEW="1"; - if ( "$NEW" || ! -f "${CATOP}/serial" ) { - # create the directory hierarchy - mkdir $CATOP, $DIRMODE; - mkdir "${CATOP}/certs", $DIRMODE; - mkdir "${CATOP}/crl", $DIRMODE ; - mkdir "${CATOP}/newcerts", $DIRMODE; - mkdir "${CATOP}/private", $DIRMODE; - open OUT, ">${CATOP}/index.txt"; - close OUT; - open OUT, ">${CATOP}/crlnumber"; - print OUT "01\n"; - close OUT; - } - if ( ! -f "${CATOP}/private/$CAKEY" ) { - print "CA certificate filename (or enter to create)\n"; - $FILE = ; - - chop $FILE; - - # ask user for existing CA certificate - if ($FILE) { - cp_pem($FILE,"${CATOP}/private/$CAKEY", "PRIVATE"); - cp_pem($FILE,"${CATOP}/$CACERT", "CERTIFICATE"); - $RET=$?; - } else { - print "Making CA certificate ...\n"; - system ("$REQ -new -keyout " . - "${CATOP}/private/$CAKEY -out ${CATOP}/$CAREQ"); - system ("$CA -create_serial " . - "-out ${CATOP}/$CACERT $CADAYS -batch " . - "-keyfile ${CATOP}/private/$CAKEY -selfsign " . - "-extensions v3_ca " . - "-infiles ${CATOP}/$CAREQ "); - $RET=$?; - } - } - } elsif (/^-pkcs12$/) { - my $cname = $ARGV[1]; - $cname = "My Certificate" unless defined $cname; - system ("$PKCS12 -in newcert.pem -inkey newkey.pem " . - "-certfile ${CATOP}/$CACERT -out newcert.p12 " . - "-export -name \"$cname\""); - $RET=$?; - print "PKCS #12 file is in newcert.p12\n"; - exit $RET; - } elsif (/^-xsign$/) { - system ("$CA -policy policy_anything -infiles newreq.pem"); - $RET=$?; - } elsif (/^(-sign|-signreq)$/) { - system ("$CA -policy policy_anything -out newcert.pem " . - "-infiles newreq.pem"); - $RET=$?; - print "Signed certificate is in newcert.pem\n"; - } elsif (/^(-signCA)$/) { - system ("$CA -policy policy_anything -out newcert.pem " . - "-extensions v3_ca -infiles newreq.pem"); - $RET=$?; - print "Signed CA certificate is in newcert.pem\n"; - } elsif (/^-signcert$/) { - system ("$X509 -x509toreq -in newreq.pem -signkey newreq.pem " . - "-out tmp.pem"); - system ("$CA -policy policy_anything -out newcert.pem " . - "-infiles tmp.pem"); - $RET = $?; - print "Signed certificate is in newcert.pem\n"; - } elsif (/^-verify$/) { - if (shift) { - foreach $j (@ARGV) { - system ("$VERIFY -CAfile $CATOP/$CACERT $j"); - $RET=$? if ($? != 0); - } - exit $RET; - } else { - system ("$VERIFY -CAfile $CATOP/$CACERT newcert.pem"); - $RET=$?; - exit 0; - } - } else { - print STDERR "Unknown arg $_\n"; - print STDERR "usage: CA -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify\n"; - exit 1; - } -} - -exit $RET; - -sub cp_pem { -my ($infile, $outfile, $bound) = @_; -open IN, $infile; -open OUT, ">$outfile"; -my $flag = 0; -while () { - $flag = 1 if (/^-----BEGIN.*$bound/) ; - print OUT $_ if ($flag); - if (/^-----END.*$bound/) { - close IN; - close OUT; - return; - } -} -} - diff --git a/cli/appdir/etc/ssl/misc/CA.sh b/cli/appdir/etc/ssl/misc/CA.sh deleted file mode 100755 index d5b4d8f..0000000 --- a/cli/appdir/etc/ssl/misc/CA.sh +++ /dev/null @@ -1,198 +0,0 @@ -#!/bin/sh -# -# CA - wrapper around ca to make it easier to use ... basically ca requires -# some setup stuff to be done before you can use it and this makes -# things easier between now and when Eric is convinced to fix it :-) -# -# CA -newca ... will setup the right stuff -# CA -newreq ... will generate a certificate request -# CA -sign ... will sign the generated request and output -# -# At the end of that grab newreq.pem and newcert.pem (one has the key -# and the other the certificate) and cat them together and that is what -# you want/need ... I'll make even this a little cleaner later. -# -# -# 12-Jan-96 tjh Added more things ... including CA -signcert which -# converts a certificate to a request and then signs it. -# 10-Jan-96 eay Fixed a few more bugs and added the SSLEAY_CONFIG -# environment variable so this can be driven from -# a script. -# 25-Jul-96 eay Cleaned up filenames some more. -# 11-Jun-96 eay Fixed a few filename missmatches. -# 03-May-96 eay Modified to use 'ssleay cmd' instead of 'cmd'. -# 18-Apr-96 tjh Original hacking -# -# Tim Hudson -# tjh@cryptsoft.com -# - -# default openssl.cnf file has setup as per the following -# demoCA ... where everything is stored -cp_pem() { - infile=$1 - outfile=$2 - bound=$3 - flag=0 - exec <$infile; - while read line; do - if [ $flag -eq 1 ]; then - echo $line|grep "^-----END.*$bound" 2>/dev/null 1>/dev/null - if [ $? -eq 0 ] ; then - echo $line >>$outfile - break - else - echo $line >>$outfile - fi - fi - - echo $line|grep "^-----BEGIN.*$bound" 2>/dev/null 1>/dev/null - if [ $? -eq 0 ]; then - echo $line >$outfile - flag=1 - fi - done -} - -usage() { - echo "usage: $0 -newcert|-newreq|-newreq-nodes|-newca|-sign|-verify" >&2 -} - -if [ -z "$OPENSSL" ]; then OPENSSL=openssl; fi - -if [ -z "$DAYS" ] ; then DAYS="-days 365" ; fi # 1 year -CADAYS="-days 1095" # 3 years -REQ="$OPENSSL req $SSLEAY_CONFIG" -CA="$OPENSSL ca $SSLEAY_CONFIG" -VERIFY="$OPENSSL verify" -X509="$OPENSSL x509" -PKCS12="openssl pkcs12" - -if [ -z "$CATOP" ] ; then CATOP=/etc/ssl ; fi -CAKEY=./cakey.pem -CAREQ=./careq.pem -CACERT=./cacert.pem - -RET=0 - -while [ "$1" != "" ] ; do -case $1 in --\?|-h|-help) - usage - exit 0 - ;; --newcert) - # create a certificate - $REQ -new -x509 -keyout newkey.pem -out newcert.pem $DAYS - RET=$? - echo "Certificate is in newcert.pem, private key is in newkey.pem" - ;; --newreq) - # create a certificate request - $REQ -new -keyout newkey.pem -out newreq.pem $DAYS - RET=$? - echo "Request is in newreq.pem, private key is in newkey.pem" - ;; --newreq-nodes) - # create a certificate request - $REQ -new -nodes -keyout newreq.pem -out newreq.pem $DAYS - RET=$? - echo "Request (and private key) is in newreq.pem" - ;; --newca) - # if explicitly asked for or it doesn't exist then setup the directory - # structure that Eric likes to manage things - NEW="1" - if [ "$NEW" -o ! -f ${CATOP}/serial ]; then - # create the directory hierarchy - mkdir -p ${CATOP} - mkdir -p ${CATOP}/certs - mkdir -p ${CATOP}/crl - mkdir -p ${CATOP}/newcerts - mkdir -p ${CATOP}/private - touch ${CATOP}/index.txt - fi - if [ ! -f ${CATOP}/private/$CAKEY ]; then - echo "CA certificate filename (or enter to create)" - read FILE - - # ask user for existing CA certificate - if [ "$FILE" ]; then - cp_pem $FILE ${CATOP}/private/$CAKEY PRIVATE - cp_pem $FILE ${CATOP}/$CACERT CERTIFICATE - RET=$? - if [ ! -f "${CATOP}/serial" ]; then - $X509 -in ${CATOP}/$CACERT -noout -next_serial \ - -out ${CATOP}/serial - fi - else - echo "Making CA certificate ..." - $REQ -new -keyout ${CATOP}/private/$CAKEY \ - -out ${CATOP}/$CAREQ - $CA -create_serial -out ${CATOP}/$CACERT $CADAYS -batch \ - -keyfile ${CATOP}/private/$CAKEY -selfsign \ - -extensions v3_ca \ - -infiles ${CATOP}/$CAREQ - RET=$? - fi - fi - ;; --xsign) - $CA -policy policy_anything -infiles newreq.pem - RET=$? - ;; --pkcs12) - if [ -z "$2" ] ; then - CNAME="My Certificate" - else - CNAME="$2" - fi - $PKCS12 -in newcert.pem -inkey newreq.pem -certfile ${CATOP}/$CACERT \ - -out newcert.p12 -export -name "$CNAME" - RET=$? - exit $RET - ;; --sign|-signreq) - $CA -policy policy_anything -out newcert.pem -infiles newreq.pem - RET=$? - cat newcert.pem - echo "Signed certificate is in newcert.pem" - ;; --signCA) - $CA -policy policy_anything -out newcert.pem -extensions v3_ca -infiles newreq.pem - RET=$? - echo "Signed CA certificate is in newcert.pem" - ;; --signcert) - echo "Cert passphrase will be requested twice - bug?" - $X509 -x509toreq -in newreq.pem -signkey newreq.pem -out tmp.pem - $CA -policy policy_anything -out newcert.pem -infiles tmp.pem - RET=$? - cat newcert.pem - echo "Signed certificate is in newcert.pem" - ;; --verify) - shift - if [ -z "$1" ]; then - $VERIFY -CAfile $CATOP/$CACERT newcert.pem - RET=$? - else - for j - do - $VERIFY -CAfile $CATOP/$CACERT $j - if [ $? != 0 ]; then - RET=$? - fi - done - fi - exit $RET - ;; -*) - echo "Unknown arg $i" >&2 - usage - exit 1 - ;; -esac -shift -done -exit $RET diff --git a/cli/appdir/etc/ssl/misc/c_hash b/cli/appdir/etc/ssl/misc/c_hash deleted file mode 100755 index 5e0a908..0000000 --- a/cli/appdir/etc/ssl/misc/c_hash +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# print out the hash values -# - -for i in $* -do - h=`openssl x509 -hash -noout -in $i` - echo "$h.0 => $i" -done diff --git a/cli/appdir/etc/ssl/misc/c_info b/cli/appdir/etc/ssl/misc/c_info deleted file mode 100755 index 0e1e633..0000000 --- a/cli/appdir/etc/ssl/misc/c_info +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# -# print the subject -# - -for i in $* -do - n=`openssl x509 -subject -issuer -enddate -noout -in $i` - echo "$i" - echo "$n" - echo "--------" -done diff --git a/cli/appdir/etc/ssl/misc/c_issuer b/cli/appdir/etc/ssl/misc/c_issuer deleted file mode 100755 index 55821ab..0000000 --- a/cli/appdir/etc/ssl/misc/c_issuer +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -# -# print out the issuer -# - -for i in $* -do - n=`openssl x509 -issuer -noout -in $i` - echo "$i $n" -done diff --git a/cli/appdir/etc/ssl/misc/c_name b/cli/appdir/etc/ssl/misc/c_name deleted file mode 100755 index 28800c0..0000000 --- a/cli/appdir/etc/ssl/misc/c_name +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -# -# print the subject -# - -for i in $* -do - n=`openssl x509 -subject -noout -in $i` - echo "$i $n" -done diff --git a/cli/appdir/etc/ssl/misc/tsget b/cli/appdir/etc/ssl/misc/tsget deleted file mode 100755 index 0d54e9f..0000000 --- a/cli/appdir/etc/ssl/misc/tsget +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/perl -w -# Written by Zoltan Glozik . -# Copyright (c) 2002 The OpenTSA Project. All rights reserved. -$::version = '$Id: tsget,v 1.1.2.2 2009/09/07 17:57:02 steve Exp $'; - -use strict; -use IO::Handle; -use Getopt::Std; -use File::Basename; -use WWW::Curl::Easy; - -use vars qw(%options); - -# Callback for reading the body. -sub read_body { - my ($maxlength, $state) = @_; - my $return_data = ""; - my $data_len = length ${$state->{data}}; - if ($state->{bytes} < $data_len) { - $data_len = $data_len - $state->{bytes}; - $data_len = $maxlength if $data_len > $maxlength; - $return_data = substr ${$state->{data}}, $state->{bytes}, $data_len; - $state->{bytes} += $data_len; - } - return $return_data; -} - -# Callback for writing the body into a variable. -sub write_body { - my ($data, $pointer) = @_; - ${$pointer} .= $data; - return length($data); -} - -# Initialise a new Curl object. -sub create_curl { - my $url = shift; - - # Create Curl object. - my $curl = WWW::Curl::Easy::new(); - - # Error-handling related options. - $curl->setopt(CURLOPT_VERBOSE, 1) if $options{d}; - $curl->setopt(CURLOPT_FAILONERROR, 1); - $curl->setopt(CURLOPT_USERAGENT, "OpenTSA tsget.pl/" . (split / /, $::version)[2]); - - # Options for POST method. - $curl->setopt(CURLOPT_UPLOAD, 1); - $curl->setopt(CURLOPT_CUSTOMREQUEST, "POST"); - $curl->setopt(CURLOPT_HTTPHEADER, - ["Content-Type: application/timestamp-query", - "Accept: application/timestamp-reply,application/timestamp-response"]); - $curl->setopt(CURLOPT_READFUNCTION, \&read_body); - $curl->setopt(CURLOPT_HEADERFUNCTION, sub { return length($_[0]); }); - - # Options for getting the result. - $curl->setopt(CURLOPT_WRITEFUNCTION, \&write_body); - - # SSL related options. - $curl->setopt(CURLOPT_SSLKEYTYPE, "PEM"); - $curl->setopt(CURLOPT_SSL_VERIFYPEER, 1); # Verify server's certificate. - $curl->setopt(CURLOPT_SSL_VERIFYHOST, 2); # Check server's CN. - $curl->setopt(CURLOPT_SSLKEY, $options{k}) if defined($options{k}); - $curl->setopt(CURLOPT_SSLKEYPASSWD, $options{p}) if defined($options{p}); - $curl->setopt(CURLOPT_SSLCERT, $options{c}) if defined($options{c}); - $curl->setopt(CURLOPT_CAINFO, $options{C}) if defined($options{C}); - $curl->setopt(CURLOPT_CAPATH, $options{P}) if defined($options{P}); - $curl->setopt(CURLOPT_RANDOM_FILE, $options{r}) if defined($options{r}); - $curl->setopt(CURLOPT_EGDSOCKET, $options{g}) if defined($options{g}); - - # Setting destination. - $curl->setopt(CURLOPT_URL, $url); - - return $curl; -} - -# Send a request and returns the body back. -sub get_timestamp { - my $curl = shift; - my $body = shift; - my $ts_body; - local $::error_buf; - - # Error-handling related options. - $curl->setopt(CURLOPT_ERRORBUFFER, "::error_buf"); - - # Options for POST method. - $curl->setopt(CURLOPT_INFILE, {data => $body, bytes => 0}); - $curl->setopt(CURLOPT_INFILESIZE, length(${$body})); - - # Options for getting the result. - $curl->setopt(CURLOPT_FILE, \$ts_body); - - # Send the request... - my $error_code = $curl->perform(); - my $error_string; - if ($error_code != 0) { - my $http_code = $curl->getinfo(CURLINFO_HTTP_CODE); - $error_string = "could not get timestamp"; - $error_string .= ", http code: $http_code" unless $http_code == 0; - $error_string .= ", curl code: $error_code"; - $error_string .= " ($::error_buf)" if defined($::error_buf); - } else { - my $ct = $curl->getinfo(CURLINFO_CONTENT_TYPE); - if (lc($ct) ne "application/timestamp-reply" - && lc($ct) ne "application/timestamp-response") { - $error_string = "unexpected content type returned: $ct"; - } - } - return ($ts_body, $error_string); - -} - -# Print usage information and exists. -sub usage { - - print STDERR "usage: $0 -h [-e ] [-o ] "; - print STDERR "[-v] [-d] [-k ] [-p ] "; - print STDERR "[-c ] [-C ] [-P ] "; - print STDERR "[-r ] [-g ] []...\n"; - exit 1; -} - -# ---------------------------------------------------------------------- -# Main program -# ---------------------------------------------------------------------- - -# Getting command-line options (default comes from TSGET environment variable). -my $getopt_arg = "h:e:o:vdk:p:c:C:P:r:g:"; -if (exists $ENV{TSGET}) { - my @old_argv = @ARGV; - @ARGV = split /\s+/, $ENV{TSGET}; - getopts($getopt_arg, \%options) or usage; - @ARGV = @old_argv; -} -getopts($getopt_arg, \%options) or usage; - -# Checking argument consistency. -if (!exists($options{h}) || (@ARGV == 0 && !exists($options{o})) - || (@ARGV > 1 && exists($options{o}))) { - print STDERR "Inconsistent command line options.\n"; - usage; -} -# Setting defaults. -@ARGV = ("-") unless @ARGV != 0; -$options{e} = ".tsr" unless defined($options{e}); - -# Processing requests. -my $curl = create_curl $options{h}; -undef $/; # For reading whole files. -REQUEST: foreach (@ARGV) { - my $input = $_; - my ($base, $path) = fileparse($input, '\.[^.]*'); - my $output_base = $base . $options{e}; - my $output = defined($options{o}) ? $options{o} : $path . $output_base; - - STDERR->printflush("$input: ") if $options{v}; - # Read request. - my $body; - if ($input eq "-") { - # Read the request from STDIN; - $body = ; - } else { - # Read the request from file. - open INPUT, "<" . $input - or warn("$input: could not open input file: $!\n"), next REQUEST; - $body = ; - close INPUT - or warn("$input: could not close input file: $!\n"), next REQUEST; - } - - # Send request. - STDERR->printflush("sending request") if $options{v}; - - my ($ts_body, $error) = get_timestamp $curl, \$body; - if (defined($error)) { - die "$input: fatal error: $error\n"; - } - STDERR->printflush(", reply received") if $options{v}; - - # Write response. - if ($output eq "-") { - # Write to STDOUT. - print $ts_body; - } else { - # Write to file. - open OUTPUT, ">", $output - or warn("$output: could not open output file: $!\n"), next REQUEST; - print OUTPUT $ts_body; - close OUTPUT - or warn("$output: could not close output file: $!\n"), next REQUEST; - } - STDERR->printflush(", $output written.\n") if $options{v}; -} -$curl->cleanup(); -WWW::Curl::Easy::global_cleanup(); diff --git a/cli/appdir/etc/ssl/openssl.cnf b/cli/appdir/etc/ssl/openssl.cnf deleted file mode 100644 index 290dd7b..0000000 --- a/cli/appdir/etc/ssl/openssl.cnf +++ /dev/null @@ -1,350 +0,0 @@ -# -# OpenSSL example configuration file. -# This is mostly being used for generation of certificate requests. -# - -# This definition stops the following lines choking if HOME isn't -# defined. -HOME = . -RANDFILE = $ENV::HOME/.rnd - -# Extra OBJECT IDENTIFIER info: -#oid_file = $ENV::HOME/.oid -oid_section = new_oids - -# To use this configuration file with the "-extfile" option of the -# "openssl x509" utility, name here the section containing the -# X.509v3 extensions to use: -# extensions = -# (Alternatively, use a configuration file that has only -# X.509v3 extensions in its main [= default] section.) - -[ new_oids ] - -# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. -# Add a simple OID like this: -# testoid1=1.2.3.4 -# Or use config file substitution like this: -# testoid2=${testoid1}.5.6 - -# Policies used by the TSA examples. -tsa_policy1 = 1.2.3.4.1 -tsa_policy2 = 1.2.3.4.5.6 -tsa_policy3 = 1.2.3.4.5.7 - -#################################################################### -[ ca ] -default_ca = CA_default # The default ca section - -#################################################################### -[ CA_default ] - -dir = /etc/ssl # Where everything is kept -certs = $dir/certs # Where the issued certs are kept -crl_dir = $dir/crl # Where the issued crl are kept -database = $dir/index.txt # database index file. -#unique_subject = no # Set to 'no' to allow creation of - # several ctificates with same subject. -new_certs_dir = $dir/newcerts # default place for new certs. - -certificate = $dir/cacert.pem # The CA certificate -serial = $dir/serial # The current serial number -crlnumber = $dir/crlnumber # the current crl number - # must be commented out to leave a V1 CRL -crl = $dir/crl.pem # The current CRL -private_key = $dir/private/cakey.pem# The private key -RANDFILE = $dir/private/.rand # private random number file - -x509_extensions = usr_cert # The extentions to add to the cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -# Extension copying option: use with caution. -# copy_extensions = copy - -# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs -# so this is commented out by default to leave a V1 CRL. -# crlnumber must also be commented out to leave a V1 CRL. -# crl_extensions = crl_ext - -default_days = 365 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = default # use public key default MD -preserve = no # keep passed DN ordering - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_match - -# For the CA policy -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 1024 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extentions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -# req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgits Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - - -# Extensions for a typical CA - - -# PKIX recommendation. - -subjectKeyIdentifier=hash - -authorityKeyIdentifier=keyid:always,issuer - -# This is what PKIX recommends but some broken software chokes on critical -# extensions. -#basicConstraints = critical,CA:true -# So we do this instead. -basicConstraints = CA:true - -# Key usage: this is typical for a CA certificate. However since it will -# prevent it being used as an test self-signed certificate it is best -# left out by default. -# keyUsage = cRLSign, keyCertSign - -# Some might want this also -# nsCertType = sslCA, emailCA - -# Include email address in subject alt name: another PKIX recommendation -# subjectAltName=email:copy -# Copy issuer details -# issuerAltName=issuer:copy - -# DER hex encoding of an extension: beware experts only! -# obj=DER:02:03 -# Where 'obj' is a standard or added object -# You can even override a supported extension: -# basicConstraints= critical, DER:30:03:01:01:FF - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ proxy_cert_ext ] -# These extensions should be added when creating a proxy certificate - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This really needs to be in place for it to be a proxy certificate. -proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo - -#################################################################### -[ tsa ] - -default_tsa = tsa_config1 # the default TSA section - -[ tsa_config1 ] - -# These are used by the TSA reply generation only. -dir = ./demoCA # TSA root directory -serial = $dir/tsaserial # The current serial number (mandatory) -crypto_device = builtin # OpenSSL engine to use for signing -signer_cert = $dir/tsacert.pem # The TSA signing certificate - # (optional) -certs = $dir/cacert.pem # Certificate chain to include in reply - # (optional) -signer_key = $dir/private/tsakey.pem # The TSA private key (optional) - -default_policy = tsa_policy1 # Policy if request did not specify it - # (optional) -other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) -digests = md5, sha1 # Acceptable message digests (mandatory) -accuracy = secs:1, millisecs:500, microsecs:100 # (optional) -clock_precision_digits = 0 # number of digits after dot. (optional) -ordering = yes # Is ordering defined for timestamps? - # (optional, default: no) -tsa_name = yes # Must the TSA name be included in the reply? - # (optional, default: no) -ess_cert_id_chain = no # Must the ESS cert id chain be included? - # (optional, default: no) diff --git a/cli/appdir/usr/lib/engines/lib4758cca.so b/cli/appdir/usr/lib/engines/lib4758cca.so deleted file mode 100755 index ab665c3..0000000 Binary files a/cli/appdir/usr/lib/engines/lib4758cca.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libaep.so b/cli/appdir/usr/lib/engines/libaep.so deleted file mode 100755 index f17cbdc..0000000 Binary files a/cli/appdir/usr/lib/engines/libaep.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libatalla.so b/cli/appdir/usr/lib/engines/libatalla.so deleted file mode 100755 index 8b56c8e..0000000 Binary files a/cli/appdir/usr/lib/engines/libatalla.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libcapi.so b/cli/appdir/usr/lib/engines/libcapi.so deleted file mode 100755 index 956d1ec..0000000 Binary files a/cli/appdir/usr/lib/engines/libcapi.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libchil.so b/cli/appdir/usr/lib/engines/libchil.so deleted file mode 100755 index 5e28b6f..0000000 Binary files a/cli/appdir/usr/lib/engines/libchil.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libcswift.so b/cli/appdir/usr/lib/engines/libcswift.so deleted file mode 100755 index 7a6622f..0000000 Binary files a/cli/appdir/usr/lib/engines/libcswift.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libgmp.so b/cli/appdir/usr/lib/engines/libgmp.so deleted file mode 100755 index a909210..0000000 Binary files a/cli/appdir/usr/lib/engines/libgmp.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libgost.so b/cli/appdir/usr/lib/engines/libgost.so deleted file mode 100755 index ec65faf..0000000 Binary files a/cli/appdir/usr/lib/engines/libgost.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libnuron.so b/cli/appdir/usr/lib/engines/libnuron.so deleted file mode 100755 index 68a7a5c..0000000 Binary files a/cli/appdir/usr/lib/engines/libnuron.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libpadlock.so b/cli/appdir/usr/lib/engines/libpadlock.so deleted file mode 100755 index f8af5c8..0000000 Binary files a/cli/appdir/usr/lib/engines/libpadlock.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libsureware.so b/cli/appdir/usr/lib/engines/libsureware.so deleted file mode 100755 index 6e1b23d..0000000 Binary files a/cli/appdir/usr/lib/engines/libsureware.so and /dev/null differ diff --git a/cli/appdir/usr/lib/engines/libubsec.so b/cli/appdir/usr/lib/engines/libubsec.so deleted file mode 100755 index 938e2f3..0000000 Binary files a/cli/appdir/usr/lib/engines/libubsec.so and /dev/null differ diff --git a/cli/appdir/usr/lib/libcrypto.so b/cli/appdir/usr/lib/libcrypto.so deleted file mode 120000 index c049566..0000000 --- a/cli/appdir/usr/lib/libcrypto.so +++ /dev/null @@ -1 +0,0 @@ -libcrypto.so.1.0.0 \ No newline at end of file diff --git a/cli/appdir/usr/lib/libcrypto.so.1.0.0 b/cli/appdir/usr/lib/libcrypto.so.1.0.0 deleted file mode 100755 index 4d8bcb1..0000000 Binary files a/cli/appdir/usr/lib/libcrypto.so.1.0.0 and /dev/null differ diff --git a/cli/appdir/usr/lib/libssl.so b/cli/appdir/usr/lib/libssl.so deleted file mode 120000 index 8a897d0..0000000 --- a/cli/appdir/usr/lib/libssl.so +++ /dev/null @@ -1 +0,0 @@ -libssl.so.1.0.0 \ No newline at end of file diff --git a/cli/appdir/usr/lib/libssl.so.1.0.0 b/cli/appdir/usr/lib/libssl.so.1.0.0 deleted file mode 100755 index d9657c7..0000000 Binary files a/cli/appdir/usr/lib/libssl.so.1.0.0 and /dev/null differ diff --git a/cli/appdir/usr/share/icons/hicolor/256x256/apps/AppImageUpdater.png b/cli/appdir/usr/share/icons/hicolor/256x256/apps/AppImageUpdater.png deleted file mode 100644 index 6c235b6..0000000 Binary files a/cli/appdir/usr/share/icons/hicolor/256x256/apps/AppImageUpdater.png and /dev/null differ diff --git a/cli/main.cc b/cli/main.cc index 4f0c340..13fa7ca 100644 --- a/cli/main.cc +++ b/cli/main.cc @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -10,14 +10,26 @@ #include #include +#ifndef APPIMAGE_UPDATER_VERSION +#define APPIMAGE_UPDATER_VERSION "2" +#endif +#ifndef APPIMAGE_UPDATER_COMMIT +#define APPIMAGE_UPDATER_COMMIT "none" +#endif +#ifndef APPIMAGE_UPDATER_BUILD_TIME +#define APPIMAGE_UPDATER_BUILD_TIME "Unknown" +#endif +#ifndef APPIMAGE_UPDATER_BUILD_NO +#define APPIMAGE_UPDATER_BUILD_NO "1" +#endif + static void signal_handler(int); void init_signals(void); struct sigaction sigact; bool updateRunning = false; -using AppImageUpdaterBridge::AppImageDeltaRevisioner; -AppImageDeltaRevisioner reviser; // This is made global for make it available for signal handlers +QAppImageUpdate *updater = nullptr; // This is made global for make it available for signal handlers static const char *star_emoji = "🌟"; static const char *heart_emoji = "💖"; @@ -29,10 +41,13 @@ int main(int ac, char **av){ int r = 0; init_signals(); QCoreApplication app(ac, av); + updater = new QAppImageUpdate(/*singleThreaded=*/false, &app); bool moveOldVersion = false, deleteOldVersion = false, debug = false, - avoidRunMsg = false; + avoidRunMsg = false, + checkForUpdateAndExit = false, + useTorrent = false; double megaBytesTotal = 0; QStringList appImagesToUpdate; QString currentOperatingAppImage; @@ -48,7 +63,13 @@ int main(int ac, char **av){ std::string progName(*av); ++av; // Go past program name. while(*av){ - if(!qstrcmp(*av, "-m") || + if(!qstrcmp(*av, "-t") || + !qstrcmp(*av, "--use-torrent")){ + useTorrent = true; + }else if(!qstrcmp(*av, "-j") || + !qstrcmp(*av, "--check-for-update")){ + checkForUpdateAndExit = true; + }else if(!qstrcmp(*av, "-m") || !qstrcmp(*av, "--move-old-version")){ moveOldVersion = true; }else if(!qstrcmp(*av, "-D") || @@ -77,8 +98,15 @@ int main(int ac, char **av){ return r; } - QObject::connect(&reviser, &AppImageDeltaRevisioner::started, [&](){ + QObject::connect(updater, &QAppImageUpdate::started, [&](short action){ // Put variable initialization here. + if(action == QAppImageUpdate::Action::CheckForUpdate) { + cutelog_mode(ctx,cutelog_multiline_mode); + cutelog_info(ctx, + "[Running on] %s" , + currentOperatingAppImage.toStdString().c_str()); + return; + } megaBytesTotal = 0; updateRunning = true; cutelog_mode(ctx, cutelog_multiline_mode); @@ -89,16 +117,23 @@ int main(int ac, char **av){ if(debug){ - QObject::connect(&reviser, &AppImageDeltaRevisioner::logger, + QObject::connect(updater, &QAppImageUpdate::logger, [&](QString msg, QString path) { - Q_UNUSED(path); + cutelog_mode(ctx, cutelog_multiline_mode); cutelog_debug(ctx, "%s", msg.toStdString().c_str() + 9); }); } - QObject::connect(&reviser, &AppImageDeltaRevisioner::progress, - [&](int percent, qint64 br, qint64 bt, double speed, QString unit) { + QObject::connect(updater, &QAppImageUpdate::progress, + [&](int percent, qint64 br, qint64 bt, double speed, QString unit, short action) { + if(action == QAppImageUpdate::Action::CheckForUpdate) { + cutelog_mode(ctx, cutelog_non_multiline_mode); + cutelog_progress(ctx, + "[%i%% Done] Downloading and parsing meta file.", + percent); + return; + } if(!br && !bt ){ return; } @@ -113,24 +148,10 @@ int main(int ac, char **av){ return; }); - QObject::connect(&reviser, &AppImageDeltaRevisioner::operatingAppImagePath, - [&](QString path){ - if(avoidRunMsg){ - avoidRunMsg = false; - return; - } - currentOperatingAppImage = QFileInfo(path).fileName(); - cutelog_mode(ctx,cutelog_multiline_mode); - cutelog_info(ctx, - "[Running on] %s" , - currentOperatingAppImage.toStdString().c_str()); - - }); - - QObject::connect(&reviser, &AppImageDeltaRevisioner::error, - [&](short errorCode){ + QObject::connect(updater, &QAppImageUpdate::error, + [&](short errorCode, short action){ updateRunning = false; - QString reason = AppImageUpdaterBridge::errorCodeToDescriptionString(errorCode); + QString reason = QAppImageUpdate::errorCodeToDescriptionString(errorCode); cutelog_mode(ctx,cutelog_multiline_mode); cutelog_fatal( ctx, @@ -141,35 +162,59 @@ int main(int ac, char **av){ if(appImagesToUpdate.isEmpty()){ QCoreApplication::quit(); }else{ - reviser.setAppImage(appImagesToUpdate.takeFirst()); - reviser.checkForUpdate(); + auto path = appImagesToUpdate.takeFirst(); + currentOperatingAppImage = QFileInfo(path).fileName(); + + updater->setAppImage(path); + updater->start(QAppImageUpdate::Action::CheckForUpdate); } }); - QObject::connect(&reviser, &AppImageDeltaRevisioner::updateAvailable, - [&](bool isAvailable, QJsonObject info){ - Q_UNUSED(info); - if(!isAvailable){ - cutelog_mode(ctx,cutelog_multiline_mode); - cutelog_success(ctx, - "[No Update Needed] %s" , + QObject::connect(updater, &QAppImageUpdate::finished, + [&](QJsonObject info, short action) { + if(action == QAppImageUpdate::Action::CheckForUpdate) { + if(!info["UpdateAvailable"].toBool()){ + cutelog_mode(ctx,cutelog_multiline_mode); + cutelog_success(ctx, + "[No Update Needed] %s" , + currentOperatingAppImage.toStdString().c_str()); + if(appImagesToUpdate.isEmpty()){ + QCoreApplication::quit(); + }else{ + auto path = appImagesToUpdate.takeFirst(); + currentOperatingAppImage = QFileInfo(path).fileName(); + + updater->setAppImage(path); + updater->start(QAppImageUpdate::Action::CheckForUpdate); + } + return; + } + cutelog_mode(ctx,cutelog_multiline_mode); + cutelog_info(ctx, + "[Update Needed] %s" , currentOperatingAppImage.toStdString().c_str()); - if(appImagesToUpdate.isEmpty()){ - QCoreApplication::quit(); + if(checkForUpdateAndExit) { + if(appImagesToUpdate.isEmpty()){ + QCoreApplication::quit(); + }else{ + auto path = appImagesToUpdate.takeFirst(); + currentOperatingAppImage = QFileInfo(path).fileName(); + + updater->setAppImage(path); + updater->start(QAppImageUpdate::Action::CheckForUpdate); + } + return; + } + if(useTorrent) { + updater->start(QAppImageUpdate::Action::UpdateWithTorrent); }else{ - reviser.setAppImage(appImagesToUpdate.takeFirst()); - reviser.checkForUpdate(); + updater->start(QAppImageUpdate::Action::Update); } return; } - avoidRunMsg = true; - reviser.start(); - }); - QObject::connect(&reviser, &AppImageDeltaRevisioner::finished, - [&](QJsonObject newVersion, QString oldAppImagePath) { - updateRunning = false; - QString newVer = newVersion.value("AbsolutePath").toString(); + updateRunning = false; + QString newVer = info.value("NewVersionPath").toString(); cutelog_mode(ctx, cutelog_multiline_mode); cutelog_success(ctx, "[Updated] %s", currentOperatingAppImage.toStdString().c_str()); cutelog_info(ctx, @@ -177,13 +222,13 @@ int main(int ac, char **av){ newVer.toStdString().c_str()); if(moveOldVersion){ - QString movePath = oldAppImagePath; + QString movePath = info.value("OldVersionPath").toString(); movePath.append(".old-version"); - QFile::rename(oldAppImagePath, movePath); + QFile::rename(info.value("OldVersionPath").toString(), movePath); cutelog_info(ctx, "[Moved] %s", movePath.toStdString().c_str()); }else if(deleteOldVersion){ - QFile::remove(oldAppImagePath); + QFile::remove(info.value("OldVersionPath").toString()); cutelog_info(ctx, "[Removed] %s", currentOperatingAppImage.toStdString().c_str()); } @@ -191,20 +236,28 @@ int main(int ac, char **av){ if(appImagesToUpdate.isEmpty()){ QCoreApplication::quit(); }else{ - reviser.setAppImage(appImagesToUpdate.takeFirst()); - reviser.checkForUpdate(); + auto path = appImagesToUpdate.takeFirst(); + currentOperatingAppImage = QFileInfo(path).fileName(); + + updater->setAppImage(path); + updater->start(QAppImageUpdate::Action::CheckForUpdate); } return; }); - QObject::connect(&reviser, &AppImageDeltaRevisioner::canceled, + QObject::connect(updater, &QAppImageUpdate::canceled, [&](){ QCoreApplication::quit(); }); - reviser.setAppImage(appImagesToUpdate.takeFirst()); - reviser.checkForUpdate(); - + { + auto path = appImagesToUpdate.takeFirst(); + currentOperatingAppImage = QFileInfo(path).fileName(); + + updater->setAppImage(path); + updater->start(QAppImageUpdate::Action::CheckForUpdate); + } + r = app.exec(); cutelog_safe_finish(ctx); cutelog_free(ctx); @@ -233,7 +286,7 @@ static void signal_handler(int sig){ if(!updateRunning){ QCoreApplication::quit(); }else{ - reviser.cancel(); + updater->cancel(); } } } @@ -254,7 +307,9 @@ static void print_usage(const char *prog) { printf( "Usage: %s [OPTIONS] [APPIMAGE(S)]\n\n" "OPTIONS:\n" - " -m,--move-old-version Move the old version with suffix .old-version\n" + " -t,--use-torrent Enable BitTorrent Usage for Decentralized Update.\n" + " -j,--check-for-update Check for update and exit.\n" + " -m,--move-old-version Move the old version with suffix .old-version\n" " -D,--delete-old-version Delete the old version after update.\n" " -d,--debug Debug mode.\n" " -v,--version Show version.\n" diff --git a/global.hpp b/global.hpp new file mode 100644 index 0000000..ef11887 --- /dev/null +++ b/global.hpp @@ -0,0 +1,5 @@ +#ifndef APPIMAGE_UPDATER_GLOBAL_HPP_INCLUDED +#define APPIMAGE_UPDATER_GLOBAL_HPP_INCLUDED +class AppImageImageProvider; +extern AppImageImageProvider *g_AppImageImageProvider; +#endif diff --git a/main.cc b/main.cc index a7e6cec..3a19fbf 100644 --- a/main.cc +++ b/main.cc @@ -1,24 +1,53 @@ +#include +#include +#include #include +#include +#include +#include #include -#include -#include -#include +#include -using AppImageUpdaterBridge::AppImageUpdaterDialog; +#include "AppImageUpdaterStandalone.hpp" + +#include "BuildConstants.hpp" +#include "SettingsManager.hpp" +#include "SystemTray.hpp" +#include "DropItemParser.hpp" +#include "Updater.hpp" +#include "SeedManager.hpp" +#include "Executer.hpp" +#include "Helpers.hpp" + +#include "global.hpp" +#include "AppImageImageProvider.hpp" + +#ifndef APPIMAGE_UPDATER_VERSION +#define APPIMAGE_UPDATER_VERSION "2" +#endif +#ifndef APPIMAGE_UPDATER_COMMIT +#define APPIMAGE_UPDATER_COMMIT "none" +#endif + + +AppImageImageProvider *g_AppImageImageProvider = nullptr; int main(int argc, char **argv) { - QApplication app(argc, argv); - int standaloneFlags = AppImageUpdaterDialog::Default ^ AppImageUpdaterDialog::ShowBeforeProgress; - - app.setQuitOnLastWindowClosed(false); - QApplication::setOrganizationName("AppImage"); - QApplication::setApplicationName("AppImageUpdater"); - QApplication::setApplicationVersion(APPIMAGE_UPDATER_VERSION); - + std::cout << "AppImage Updater v" << APPIMAGE_UPDATER_VERSION << "(" << APPIMAGE_UPDATER_COMMIT << "), " + << "AppImage Delta Updater for Humans.\n" + << "Copyright (C) Antony Jr.\n\n"; + QLockFile lockFile(QDir::homePath() + "/" + ".AppImageUpdater.lock"); + QApplication app(argc, argv); + QApplication::setOrganizationName("antony-jr"); + QApplication::setApplicationName("AppImage Updater"); + + int standaloneFlags = (QAppImageUpdate::GuiFlag::Default | QAppImageUpdate::GuiFlag::NoShowErrorDialogOnPermissionErrors) + ^ QAppImageUpdate::GuiFlag::ShowBeforeProgress; QCommandLineParser parser; - parser.setApplicationDescription(QString::fromUtf8("A Simple Program to Update the AppImage format using Zsync Algorithm.")); + + parser.setApplicationDescription(QString::fromUtf8("AppImage Updater for Humans.")); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption minimized(QString::fromUtf8("minimized"), QString::fromUtf8("Start the Application minimized.")); @@ -45,15 +74,15 @@ int main(int argc, char **argv) if(!parser.value(standalone).isEmpty()){ if(parser.isSet(noconfirm)){ - standaloneFlags ^= AppImageUpdaterDialog::ShowUpdateConfirmationDialog; + standaloneFlags ^= QAppImageUpdate::GuiFlag::ShowUpdateConfirmationDialog; } if(parser.isSet(beforeProgress)){ - standaloneFlags |= AppImageUpdaterDialog::ShowBeforeProgress; + standaloneFlags |= QAppImageUpdate::GuiFlag::ShowBeforeProgress; } if(parser.isSet(silent)){ - standaloneFlags ^= AppImageUpdaterDialog::ShowProgressDialog; + standaloneFlags ^= QAppImageUpdate::GuiFlag::ShowProgressDialog; } AppImageUpdaterStandalone standaloneDialogHandle(parser.value(standalone), standaloneFlags); @@ -63,7 +92,33 @@ int main(int argc, char **argv) return app.exec(); } - AppImageUpdater mainWidget(parser.isSet(minimized)); - QObject::connect(&mainWidget, &AppImageUpdater::quit, &app, &QApplication::quit, Qt::QueuedConnection); + if(!lockFile.tryLock()) { + if(!lockFile.removeStaleLockFile()) { + return 0; + } + + if(!lockFile.tryLock()) { + return 0; + } + } + + app.setQuitOnLastWindowClosed(false); + + qmlRegisterType("Core.BuildConstants", 1, 0, "BuildConstants"); + qmlRegisterType("Core.SettingsManager", 1, 0, "SettingsManager"); + qmlRegisterType("Core.SystemTray", 1, 0, "SystemTray"); + qmlRegisterType("Core.DropItemParser", 1, 0, "DropItemParser"); + qmlRegisterType("Core.Updater", 1, 0, "Updater"); + qmlRegisterType("Core.SeedManager", 1, 0, "SeedManager"); + qmlRegisterType("Core.Executer", 1, 0, "Executer"); + qmlRegisterType("Core.Helpers", 1, 0, "Helpers"); + + app.setWindowIcon(QIcon(QString::fromUtf8(":/logo.png"))); + QQuickStyle::setStyle("Material"); // Use Google Material Design + QQmlApplicationEngine engine; + + g_AppImageImageProvider = new AppImageImageProvider; + engine.addImageProvider("AIImage", g_AppImageImageProvider); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); } diff --git a/qml/Pages/AboutPage.qml b/qml/Pages/AboutPage.qml new file mode 100644 index 0000000..72e20c8 --- /dev/null +++ b/qml/Pages/AboutPage.qml @@ -0,0 +1,74 @@ +import Core.BuildConstants 1.0 +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.12 + +Page { + function getAboutContent() { + var r = qsTr("AppImage Updater v"); + r += bconstants.getVersion(); + r += qsTr(" (Commit "); + r += bconstants.getCommit(); + r += qsTr("), build "); + r += bconstants.getBuildNo(); + r += qsTr(" built on "); + r += bconstants.getBuildTime(); + r += qsTr(".
"); + r += qsTr("This program is licensed under Lesser GNU Public License.
"); + r += qsTr("Copyright \u00A9 Antony Jr.
"); + r += qsTr("Report issues at Project Page
"); + r += qsTr("
Uses QAppImageUpdate v"); + r += bconstants.getQAppImageUpdateVersion(); + r += qsTr(", Torrent Rasterbar v"); + r += bconstants.getTorrentVersion(); + r += qsTr(" and LibAppImage commit 5e8f9d7.
"); + return r; + } + + visible: true + title: qsTr("About") + + ColumnLayout { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 0 + + Image { + Layout.alignment: Qt.AlignCenter | Qt.AlignVCenter + cache: true + Layout.preferredHeight: parent.Layout.preferredHeight * 0.6 + Layout.preferredWidth: parent.Layout.preferredWidth * 0.6 + fillMode: Image.PreserveAspectFit + source: "qrc:/logo.png" + } + + Label { + Layout.alignment: Qt.AlignHCenter | Qt.AlignVTop + Layout.preferredWidth: parent.Layout.preferredWidth + horizontalAlignment: Qt.AlignHCenter + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = parent.Layout.preferredHeight * factor; + var calculatedWPxSize = parent.Layout.preferredWidth * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: getAboutContent() + wrapMode: Text.WordWrap + textFormat: Text.RichText + onLinkActivated: Qt.openUrlExternally(link) + } + + } + +} diff --git a/qml/Pages/CompletedPage.qml b/qml/Pages/CompletedPage.qml new file mode 100644 index 0000000..5cdb5bb --- /dev/null +++ b/qml/Pages/CompletedPage.qml @@ -0,0 +1,255 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.12 + +Page { + visible: true + title: qsTr("Completed Update(s)") + + Component.onCompleted: { + setCompletedCount(0); + } + + ColumnLayout { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 0 + + + ScrollView { + Layout.alignment: Qt.AlignCenter; + Layout.preferredWidth: parent.Layout.preferredWidth - 10 + Layout.preferredHeight: parent.Layout.preferredHeight - 60 + + //width: parent.Layout.preferredWidth - 10 + //height: parent.Layout.preferredHeight - 60 + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + ListView { + id: completedList + width: parent.width + height: parent.height + + model: completedUpdatesList + + delegate: Pane { + width: parent.width - 20 + height: contentCol.implicitHeight + 50 + Material.elevation: 4 + RowLayout { + Image { + Layout.alignment: Qt.AlignHLeft | Qt.AlignVCenter + cache: true + Layout.preferredHeight: 130 + Layout.preferredWidth: 130 + fillMode: Image.PreserveAspectFit + source: "image://AIImage/" + ImageId + } + + ColumnLayout { + id: contentCol + + RowLayout { + Label { + Layout.preferredWidth: root.width - 250 + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: qsTr("

") + Name + qsTr("


") + wrapMode: Text.WordWrap + textFormat: Text.RichText + onLinkActivated: Qt.openUrlExternally(link) + + } + } + Label { + Layout.preferredWidth: root.width - 250 + font.pixelSize: (function() { + var factor = 0.023; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: helpers.fileNameFromPath(NewAbsPath) + wrapMode: Text.WordWrap + } + + + + RowLayout { + id: alreadyNewRowTop + visible: !Updated + Rectangle { + id: alreadyNewLblRec + Layout.preferredWidth: 30 + alreadyNewLbl.implicitWidth + Layout.preferredHeight: alreadyNewRow.implicitHeight + color: "#8BC34A" + RowLayout { + /// Layout.preferredWidth: 30 + absPathLbl.implicitWidth + /// Layout.preferredHeight: oldAbsPathLbl.implicitHeight + id: alreadyNewRow + Rectangle { + Layout.preferredWidth: 10; + Layout.preferredHeight: alreadyNewLbl.implicitHeight + color: "#8BC34A" + } + Label { + id: alreadyNewLbl + Layout.preferredWidth: root.width - 185 + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: "ALREADY LATEST VERSION" + color: "white" + wrapMode: Text.WordWrap + } + } + } + } + RowLayout { + id: updatedRowTop + visible: Updated + Rectangle { + id: updatedLblRec + Layout.preferredWidth: 30 + updatedLbl.implicitWidth + Layout.preferredHeight: updatedRow.implicitHeight + color: "#2196F3" + RowLayout { + /// Layout.preferredWidth: 30 + absPathLbl.implicitWidth + /// Layout.preferredHeight: oldAbsPathLbl.implicitHeight + id: updatedRow + Rectangle { + Layout.preferredWidth: 10; + Layout.preferredHeight: updatedLbl.implicitHeight + color: "#2196F3" + } + Label { + id: updatedLbl + Layout.preferredWidth: root.width - 185 + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: "SYNCED TO LATEST VERSION" + color: "white" + wrapMode: Text.WordWrap + } + } + } + } + + + + Label { + Layout.preferredWidth: root.width - 250 + visible: Seeding + text: SeedingStatusText + wrapMode: Text.WordWrap + } + + + + + + Flow { + Layout.preferredWidth: root.width - 175 + spacing: 8 + + RowLayout { + width: (UsedTorrent ? 200 : 75) + Button { + text: qsTr("Open") + Material.background: Material.Teal + Material.foreground: "#ffffff" + onClicked: { + mainexecuter.exec(Hash, NewAbsPath); + root.hide(); + } + } + + Button { + visible: UsedTorrent + enabled: settings_manager.isDecentralizedUpdateEnabled + text: QueuedSeeding ? qsTr("Queued") : (Seeding ? qsTr("Stop Seeding") : (RemovingSeeding ? qsTr("Removing") : qsTr("Start Seeding"))) + Material.background: (QueuedSeeding || Seeding || RemovingSeeding) ? Material.Red : Material.Green + Material.foreground: "#ffffff" + onClicked: { + if(Seeding && !QueuedSeeding && !RemovingSeeding) { + seeder.stopSeeding(Hash); + return; + } + + if(!Seeding && !QueuedSeeding && !RemovingSeeding) { + seeder.startSeeding(Hash, NewAbsPath, TorrentFileUrl); + } + } + } + + } + + Button { + text: qsTr("Open Directory"); + onClicked: { + mainexecuter.openDirectory(NewAbsPath); + } + } + + Button { + text: OldVersionRemoved ? qsTr("Removed") : qsTr("Remove Old AppImage") + Material.background: Material.Red + Material.foreground: "#ffffff" + enabled: !OldVersionRemoved + visible: NewAbsPath != OldAbsPath + onClicked: { + if(NewAbsPath == OldAbsPath) { + return; + } + var removed = helpers.removeFile(OldAbsPath); + if(removed) { + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == Hash) { + obj["OldVersionRemoved"] = true; + break; + } + } + } + } + } + } + } + } + } + } // Close delegate property + } // Close ListView + } // Close ScrollView + } // Column Layout +} diff --git a/qml/Pages/FailedPage.qml b/qml/Pages/FailedPage.qml new file mode 100644 index 0000000..0fd4732 --- /dev/null +++ b/qml/Pages/FailedPage.qml @@ -0,0 +1,124 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.12 + +Page { + visible: true + title: qsTr("Failed Update(s)") + + Component.onCompleted: { + setFailedCount(0); + } + ColumnLayout { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 0 + + + ScrollView { + Layout.alignment: Qt.AlignCenter; + Layout.preferredWidth: parent.Layout.preferredWidth - 10 + Layout.preferredHeight: parent.Layout.preferredHeight - 60 + + //width: parent.Layout.preferredWidth - 10 + //height: parent.Layout.preferredHeight - 60 + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + ListView { + id: failedList + width: parent.width + height: parent.height + + model: failedUpdatesList + + delegate: Pane { + width: parent.width - 20 + height: contentCol.implicitHeight + 50 + Material.elevation: 4 + RowLayout { + Image { + Layout.alignment: Qt.AlignHLeft | Qt.AlignVCenter + cache: true + Layout.preferredHeight: 130 + Layout.preferredWidth: 130 + fillMode: Image.PreserveAspectFit + source: "image://AIImage/" + ImageId + } + + ColumnLayout { + id: contentCol + Label { + Layout.preferredWidth: root.width - 250 + font.pixelSize: (function() { + var factor = 0.023; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: qsTr("

") + Name + qsTr("


") + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + + Label { + Layout.preferredWidth: root.width - 250 + font.pixelSize: (function() { + var factor = 0.023; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: helpers.fileNameFromPath(AbsolutePath) + wrapMode: Text.WordWrap + } + + + Label { + Layout.preferredWidth: root.width - 250 + font.pixelSize: (function() { + var factor = 0.023; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: ErrorMsg + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + Button { + text: qsTr("Retry") + Material.background: Material.Teal + Material.foreground: "#ffffff" + onClicked: { + coreUpdater.retry({"AbsolutePath": AbsolutePath, + "Hash": Hash, + "ImageId": ImageId, + "Name": Name}); + } + } + + } + } + } // Close delegate property + } // Close ListView + } // Close ScrollView + } // Column Layout +} diff --git a/qml/Pages/HomePage.qml b/qml/Pages/HomePage.qml new file mode 100644 index 0000000..bfcb1d2 --- /dev/null +++ b/qml/Pages/HomePage.qml @@ -0,0 +1,374 @@ +import Core.DropItemParser 1.0 +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.12 + +Page { + visible: true + title: qsTr("Drag and Drop AppImage(s) to Update!") + + FileDialog { + id: browseFileDialog + + title: qsTr("Browse AppImage") + folder: shortcuts.home + selectFolder: false + onAccepted: { + dropParser.clearBuffer(); + for (var i = 0; i < browseFileDialog.fileUrls.length; ++i) { + dropParser.appendToBuffer(browseFileDialog.fileUrls[i]); + } + dropParser.start(); + } + } + + DropItemParser { + ///notify("

Queued Item to Updater

"); + + id: dropParser + + onLoading: { + defaultLayout.fetching = true; + } + onFailed: { + defaultLayout.fetching = false; + } + onEnqueue: { + coreUpdater.queue(absolutePath, appName, icon); + } + onFinished: { + defaultLayout.fetching = false; + } + + onHideApp: { + root.hide(); + } + } + + DropArea { + id: appimageDropArea + + anchors.fill: parent + onEntered: { + defaultLayout.visible = false; + dropLayout.visible = true; + } + onExited: { + dropLayout.visible = false; + defaultLayout.visible = true; + } + onDropped: { + dropLayout.visible = false; + defaultLayout.visible = true; + if (drop.hasUrls) { + dropParser.clearBuffer(); + for (var i = 0; i < drop.urls.length; ++i) { + dropParser.appendToBuffer(drop.urls[i]); + } + dropParser.start(); + } + } + } + + ColumnLayout { + id: dropLayout + + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 2 + visible: false + + Image { + cache: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignVTop + Layout.preferredHeight: (parent.Layout.preferredHeight) + Layout.preferredWidth: (parent.Layout.preferredWidth) + fillMode: Image.PreserveAspectFit + source: "qrc:/drop_image.png" + } + + } + + ColumnLayout { + id: defaultLayout + + property bool fetching: false + + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 2 + + Image { + visible: !root.updating && !defaultLayout.fetching && !root.updateLoading + cache: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignVTop + Layout.preferredHeight: (parent.Layout.preferredHeight) * 0.85 + Layout.preferredWidth: (parent.Layout.preferredWidth) * 0.85 + fillMode: Image.PreserveAspectFit + source: "qrc:/dotted_square.png" + } + + Button { + visible: !root.updating && !defaultLayout.fetching && !root.updateLoading + Layout.alignment: Qt.AlignHCenter | Qt.AlignVTop + Layout.preferredWidth: (parent.Layout.preferredWidth) * 0.3 + text: qsTr("Browse") + Material.theme: settings_manager.isDarkMode ? Material.Dark : Material.Light + highlighted: true + Material.background: Material.Green + onClicked: { + browseFileDialog.open(); + } + } + + ProgressBar { + visible: defaultLayout.fetching || root.updateLoading + indeterminate: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + } + + ScrollView { + id: mainPageScrollView + + visible: root.updating + contentWidth: root.width - 10 + Layout.preferredWidth: parent.Layout.preferredWidth - 10 + Layout.preferredHeight: parent.Layout.preferredHeight - 10 + Layout.alignment: Qt.AlignCenter + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + clip: true + ScrollBar.horizontal.interactive: false + + ColumnLayout { + + id: mainColumnLayout + + ///Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.preferredWidth: root.width - 10 + Layout.preferredHeight: root.height - 40 + spacing: 2 + + Image { + visible: root.updating + cache: true + Layout.alignment: Qt.AlignCenter + Layout.preferredHeight: 125 + Layout.preferredWidth: 125 + fillMode: Image.PreserveAspectFit + source: root.currentAppImageIconSrc + } + + Label { + visible: root.updating + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: parent.Layout.preferredWidth - 10 + horizontalAlignment: Qt.AlignHCenter + font.pixelSize: 20.0 + /* + font.pixelSize: (function() { + var factor = 0.04; + var calculatedHPxSize = parent.Layout.preferredHeight * factor; + var calculatedWPxSize = parent.Layout.preferredWidth * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })()*/ + text: "

" + root.currentAppImageName + "

" + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + ScrollView { + id: releaseScrollView + + visible: root.updating && root.currentAppImageReleaseNotes.length > 0 + contentWidth: root.width - 50 + Layout.preferredWidth: parent.Layout.preferredWidth - 50 + Layout.preferredHeight: parent.Layout.preferredHeight - ((root.loadingProgress || root.actualProgress) ? 312 : 240) + Layout.alignment: Qt.AlignCenter + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + clip: true + ScrollBar.horizontal.interactive: false + + TextEdit { + width: root.width - 80 + height: parent.height - 10 + visible: !root.loadingProgress && root.updating && root.currentAppImageReleaseNotes.length > 0 + readOnly: true + text: root.currentAppImageReleaseNotes + wrapMode: Text.WordWrap + color: settings_manager.isDarkMode ? "#fff" : "#212121" + textFormat: Text.RichText + selectByMouse: true + onLinkActivated: Qt.openUrlExternally(link) + } + + background: Rectangle { + width: releaseScrollView.implicitWidth + height: releaseScrollView.implicitHeight + color: settings_manager.isDarkMode ? "#212121" : "#fff" + } + + } + + RowLayout { + Layout.alignment: Qt.AlignCenter + visible: !root.loadingProgress && root.showUpdateChoice + + Button { + text: qsTr("Accept Update") + Material.theme: settings_manager.isDarkMode ? Material.Dark : Material.Light + highlighted: true + Material.background: Material.Green + onClicked: { + coreUpdater.continueCurrentUpdate(); + } + } + + Button { + text: qsTr("Skip Update") + Material.theme: settings_manager.isDarkMode ? Material.Dark : Material.Light + highlighted: true + Material.background: Material.Red + onClicked: { + coreUpdater.cancelCurrentUpdate(); + } + } + + Button { + text: qsTr("Accept All") + Material.theme: settings_manager.isDarkMode ? Material.Dark : Material.Light + highlighted: true + Material.background: Material.Teal + onClicked: { + coreUpdater.toggleNoConfirm(); + coreUpdater.continueCurrentUpdate(); + } + } + + } + + ProgressBar { + visible: root.loadingProgress + indeterminate: true + Layout.preferredWidth: root.width - 100 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + } + + ProgressBar { + visible: root.actualProgress + indeterminate: false + Layout.preferredWidth: root.width - 100 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + value: root.progressBarValue + } + + RowLayout { + id: alreadyNewRowTop + Layout.preferredWidth: root.width - 100 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + visible: root.actualProgress && root.usingTorrent + Rectangle { + id: alreadyNewLblRec + Layout.preferredWidth: 30 + alreadyNewLbl.implicitWidth + Layout.preferredHeight: alreadyNewRow.implicitHeight + color: "#8BC34A" + RowLayout { + /// Layout.preferredWidth: 30 + absPathLbl.implicitWidth + /// Layout.preferredHeight: oldAbsPathLbl.implicitHeight + id: alreadyNewRow + Rectangle { + Layout.preferredWidth: 10; + Layout.preferredHeight: alreadyNewLbl.implicitHeight + color: "#8BC34A" + } + Label { + id: alreadyNewLbl + //Layout.preferredWidth: root.width - 185 + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: "USING TORRENT" + color: "white" + wrapMode: Text.WordWrap + } + } + } + } + + + Label { + visible: root.actualProgress + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: root.width - 100 + Layout.preferredHeight: 30 + horizontalAlignment: Qt.AlignLeft | Qt.AlignVTop + text: root.progressText + root.torrentText + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + Flow { + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: root.width - 100 + visible: root.actualProgress + spacing: 8 + Button { + text: qsTr("Cancel Update") + Material.theme: settings_manager.isDarkMode ? Material.Dark : Material.Light + highlighted: true + Material.background: Material.Red + onClicked: { + coreUpdater.cancelCurrentUpdate(); + } + } + + Button { + text: qsTr("Cancel All") + Material.theme: settings_manager.isDarkMode ? Material.Dark : Material.Light + highlighted: true + Material.background: Material.Teal + onClicked: { + coreUpdater.cancelAll(); + } + } + + Switch { + checked: coreUpdater.isNoConfirmEnabled + text: qsTr("Accept All Updates") + onClicked: { + coreUpdater.toggleNoConfirm(); + } + } + } + + + } // main scroll view + } + + } + +} diff --git a/qml/Pages/QueuedPage.qml b/qml/Pages/QueuedPage.qml new file mode 100644 index 0000000..aeb146c --- /dev/null +++ b/qml/Pages/QueuedPage.qml @@ -0,0 +1,105 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.12 + +Page { + visible: true + title: qsTr("Queued AppImage(s)") + + Component.onCompleted: { + setQueuedCount(0); + } + + ColumnLayout { + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 0 + + + ScrollView { + Layout.alignment: Qt.AlignCenter; + Layout.preferredWidth: parent.Layout.preferredWidth - 10 + Layout.preferredHeight: parent.Layout.preferredHeight - 60 + + //width: parent.Layout.preferredWidth - 10 + //height: parent.Layout.preferredHeight - 60 + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + ListView { + id: queuedList + width: parent.width + height: parent.height + + model: queuedUpdatesList + + delegate: Pane { + width: parent.width - 20 + height: contentCol.implicitHeight + 50 + Material.elevation: 4 + RowLayout { + //width: parent.width - 100 + Image { + Layout.alignment: Qt.AlignHLeft | Qt.AlignVCenter + cache: true + Layout.preferredHeight: 130 + Layout.preferredWidth: 130 + fillMode: Image.PreserveAspectFit + source: "image://AIImage/" + ImageId + } + + ColumnLayout { + id: contentCol + Label { + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: qsTr("

") + Name + qsTr("


") + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + RowLayout { + id: pathRow + + Label { + Layout.preferredWidth: root.width - 250 + font.pixelSize: (function() { + var factor = 0.023; + var calculatedHPxSize = root.height * factor; + var calculatedWPxSize = root.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: helpers.fileNameFromPath(AbsolutePath) + wrapMode: Text.WordWrap + } + } + + Button { + text: qsTr("Remove") + onClicked: { + coreUpdater.removeFromQueue(Hash); + } + } + } + } + } // Close delegate property + } // Close ListView + } // Close ScrollView + } // Column Layout +} diff --git a/qml/Pages/SettingsPage.qml b/qml/Pages/SettingsPage.qml new file mode 100644 index 0000000..8809da7 --- /dev/null +++ b/qml/Pages/SettingsPage.qml @@ -0,0 +1,184 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.12 + +Page { + visible: true + title: qsTr("Settings") + + ColumnLayout { + id: settingsColLayout + + property real factor: 100 + + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + Layout.alignment: Qt.AlignHLeft | Qt.AlignVTop + spacing: 0 + + Switch { + Layout.preferredWidth: parent.Layout.preferredWidth - settingsColLayout.factor + Layout.alignment: Qt.AlignCenter + checked: settings_manager.isDarkMode + text: qsTr("Enable Dark Mode") + onClicked: { + if (checked) + root.Material.theme = Material.Dark; + else + root.Material.theme = Material.Light; + settings_manager.isDarkMode = checked; + } + } + + Switch { + Layout.preferredWidth: parent.Layout.preferredWidth - settingsColLayout.factor + Layout.alignment: Qt.AlignCenter + checked: settings_manager.isAllowSystemTrayNotification + text: qsTr("Allow System Tray Notification") + onClicked: { + settings_manager.isAllowSystemTrayNotification = checked; + } + } + + Switch { + Layout.preferredWidth: parent.Layout.preferredWidth - settingsColLayout.factor + Layout.alignment: Qt.AlignCenter + checked: settings_manager.isRunOnStartup + text: qsTr("Run at Startup") + onClicked: { + settings_manager.isRunOnStartup = checked; + } + } + + Switch { + Layout.preferredWidth: parent.Layout.preferredWidth - settingsColLayout.factor + Layout.alignment: Qt.AlignCenter + checked: settings_manager.isDecentralizedUpdateEnabled + text: qsTr("Use Decentralized Update") + onClicked: { + if (checked) + btWarningPopup.open(); + + settings_manager.isDecentralizedUpdateEnabled = checked; + } + } + + Switch { + Layout.preferredWidth: parent.Layout.preferredWidth - settingsColLayout.factor + Layout.alignment: Qt.AlignCenter + checked: settings_manager.isProxyEnabled + text: qsTr("Enable Proxy") + onClicked: { + settings_manager.isProxyEnabled = checked; + } + } + + ColumnLayout { + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: parent.Layout.preferredWidth - (110 + settingsColLayout.factor) + + Label { + font.pixelSize: (function() { + var factor = 0.03; + var calculatedHPxSize = parent.Layout.preferredHeight * factor; + var calculatedWPxSize = parent.Layout.preferredWidth * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: qsTr("

Proxy Settings

") + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + RowLayout { + TextField { + placeholderText: qsTr("Proxy Host") + text: settings_manager.ProxyHost + enabled: settings_manager.isProxyEnabled + onEditingFinished: { + settings_manager.ProxyHost = text; + } + } + + TextField { + placeholderText: qsTr("Proxy Port") + text: settings_manager.ProxyPort.toLocaleString() + enabled: settings_manager.isProxyEnabled + inputMask: "9999" + onEditingFinished: { + settings_manager.ProxyPort = Number(text); + } + + validator: RegExpValidator{regExp: /^[0-9/]+$/} + //validator: IntValidator { + // bottom: 0 + // top: 66666 + //} + + } + + } + + RowLayout { + Label { + text: qsTr("Proxy Type: ") + enabled: settings_manager.isProxyEnabled + } + + ComboBox { + model: ["Socks 5", "HTTP(S)"] + enabled: settings_manager.isProxyEnabled + Component.onCompleted: { + var x = find(settings_manager.ProxyType, Qt.MatchExactly); + if (x == -1) + settings_manager.ProxyType = model[0]; + else + currentIndex = x; + } + onActivated: { + settings_manager.ProxyType = model[index]; + } + onHighlighted: { + settings_manager.ProxyType = model[index]; + } + } + + } + + RowLayout { + TextField { + placeholderText: qsTr("Username") + text: settings_manager.ProxyUser + enabled: settings_manager.isProxyEnabled + onEditingFinished: { + settings_manager.ProxyUser = text; + } + } + + TextField { + placeholderText: qsTr("Password") + text: settings_manager.ProxyPass + echoMode: TextInput.Password + enabled: settings_manager.isProxyEnabled + onEditingFinished: { + settings_manager.ProxyPass = text; + } + } + + } + //col + + } + + } + +} diff --git a/qml/main.qml b/qml/main.qml new file mode 100644 index 0000000..f71e021 --- /dev/null +++ b/qml/main.qml @@ -0,0 +1,667 @@ +import Core.Updater 1.0 +import Core.BuildConstants 1.0 +import Core.SettingsManager 1.0 +import Core.SystemTray 1.0 +import Core.SeedManager 1.0 +import Core.Executer 1.0 +import Core.Helpers 1.0 +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.Material 2.12 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.12 +import QtQuick.Window 2.0 +import QtQuick.Shapes 1.11 + +ApplicationWindow { + id: root + property bool actualProgress: false; + property bool updating: false; + property bool updateLoading: false; + property bool showUpdateChoice: false; + property bool loadingProgress: false; + property bool usingTorrent: false; + property real progressBarValue: 0.0; + property string progressText: qsTr(""); + property string torrentText: qsTr(""); + property string currentAppImageIconSrc: qsTr(""); + property string currentAppImageName: qsTr(""); + property string currentAppImageReleaseNotes: qsTr(""); + + /// All Lists + ListModel { + id: completedUpdatesList + } + + ListModel { + id: failedUpdatesList + } + + ListModel { + id: queuedUpdatesList + } + + /// Info popup + function info(newPath, oldPath) { + infoPopup.newAbsPath = newPath; + infoPopup.oldAbsPath = oldPath; + infoPopup.open(); + } + + /// Notify routine + function notify(str) { + notificationPopup.notifyText = str; + notificationPopup.open(); + } + + function notificationGlow(glow) { + noti_glow.glow = glow; + } + + /// Count on each page + function totalCounts() { + return completedCount.count + queuedCount.count + failedCount.count; + } + + function setCompletedCount(count) { + completedCount.count = count; + } + + function setQueuedCount(count) { + queuedCount.count = count; + } + + function setFailedCount(count) { + failedCount.count = count; + } + + flags: Qt.WindowStaysOnTopHint + title: qsTr("AppImage Updater") + width: 650 + height: 500 + minimumWidth: 400 + minimumHeight: 500 + visible: helpers.isMinimized() ? false : true + Material.theme: Material.Light // Use google material design + Component.onCompleted: { + setX(Screen.width / 2 - width / 2); + setY(Screen.height / 2 - height / 2); + if (settings_manager.isDarkMode) + root.Material.theme = Material.Dark; + + } + + /// Popups used. + Timer { + id: notificationTimer + + interval: 800 + running: false + repeat: false + onTriggered: { + notificationTimer.stop(); + notificationPopup.close(); + } + } + + Popup { + id: notificationPopup + + property string notifyText: "" + + x: (root.width / 2) - (notificationPopup.width / 2) + y: (root.height / 2) - (notificationPopup.height / 2) + width: 250 + height: 70 + modal: true + focus: true + onOpened: { + notificationTimer.start(); + } + + ColumnLayout { + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Label { + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + ///font.pixelSize: 8 + text: notificationPopup.notifyText + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + } + + } + + Popup { + id: btWarningPopup + + x: (root.width / 2) - (btWarningPopup.width / 2) + y: (root.height / 4) - (btWarningPopup.height / 4) + width: root.width / 1.5 + height: root.height / 1.5 + modal: true + focus: true + + ColumnLayout { + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Label { + id: warningLbl + Layout.alignment: Qt.AlignHCenter | Qt.AlignVTop + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height + font.pixelSize: (function() { + ///var factor = 0.032; + var factor = 0.038; + var calculatedHPxSize = btWarningPopup.height * factor; + var calculatedWPxSize = btWarningPopup.width * factor; + if (calculatedHPxSize > calculatedWPxSize) + return calculatedWPxSize; + else + return calculatedHPxSize; + })() + text: qsTr("

Privacy Warning


") + (function() { + var r = qsTr(""); + r += qsTr("At the moment the so called 'Decentralized Update'"); + r += qsTr(" is achieved with the BitTorrent protocol. "); + r += qsTr(" In any P2P network your IP address will be shared publically with"); + r += qsTr(" peers and the entire world."); + r += qsTr(" This means that anyone can see your IP address downloading some software "); + r += qsTr("which is a serious privacy breach in our opinion, So if you really care about "); + r += qsTr("privacy please don't use this feature. "); + r += qsTr("This also consumes more bandwidth than normal download"); + r += qsTr(", So for mobile networks and metered networks please don't"); + r += qsTr(" enable this feature. "); + r += qsTr(" This is only suitable for users who update large number of"); + r += qsTr(" AppImages or a large one."); + r += qsTr("This is also very useful in a local network. "); + r += qsTr("

"); + r += qsTr("TL;DR Do not enable this if you don't know what you are doing!"); + r += qsTr(" This is only for brave hearts! "); + r += qsTr("No one is responsible if you do something wrong!

"); + return r; + })() + wrapMode: Text.WordWrap + textFormat: Text.RichText + onLinkActivated: Qt.openUrlExternally(link) + } + + } + + } + + // Backend Core + Helpers { + id: helpers + } + + BuildConstants { + id: bconstants + } + + SettingsManager { + id: settings_manager + } + + onClosing: { + root.hide(); + if(settings_manager.isAllowSystemTrayNotification) { + system_tray.notify("AppImage Updater is minimized to system tray."); + } + + } + + SystemTray { + id: system_tray + onShowOrHide: { + if(root.visible) { + root.hide(); + } else { + root.show(); + } + } + + onQuit: { + Qt.quit(); + } + + onForceHide: { + root.hide(); + } + + onRaiseApp: { + root.show(); + } + } + + Executer { + id: mainexecuter + onTerminalApp: { + notify("Terminal Application should be run from Terminal."); + } + } + + SeedManager { + id: seeder + + onErrorSeeding: { + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + obj["Seeding"] = obj["QueuedSeeding"] = false; + obj["RemovingSeeding"] = false; + break; + } + } + } + } + + onQueuedSeeding: { + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + obj["Seeding"] = false; + obj["QueuedSeeding"] = true; + obj["RemovingSeeding"] = false; + break; + } + } + } + + } + + onStartedSeeding: { + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + obj["Seeding"] = true; + obj["QueuedSeeding"] = false; + obj["RemovingSeeding"] = false; + break; + } + } + } + + } + + onRemovingSeeding: { + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + obj["Seeding"] = false; + obj["QueuedSeeding"] = false; + obj["RemovingSeeding"] = true; + break; + } + } + } + } + + onStoppedSeeding: { + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + obj["Seeding"] = obj["QueuedSeeding"] = false; + obj["RemovingSeeding"] = false; + break; + } + } + } + + } + + onTorrentStatus: { + /// hash, statusText + for(var i = 0; i < completedUpdatesList.count; ++i) { + var obj = completedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + obj["SeedingStatusText"] = statusText; + break; + } + } + } + } + } + + Updater { + id: coreUpdater + onTorrentStatus: { + /// seeders, peers + root.usingTorrent = true; + root.torrentText = "Connected to " + seeders + " Seeders of " + peers + " Peers." + } + + onLoading: { + root.loadingProgress = false; + root.progressBarValue = 0; + root.usingTorrent = false; + root.actualProgress = false; + root.progressText = ""; + root.torrentText = ""; + + root.updating = false; + root.showUpdateChoice = false; + root.updateLoading = true; + } + + onMetaInfo: { + console.log("Got Meta Data"); + root.currentAppImageIconSrc = "image://AIImage/" + info["ImageId"]; + root.currentAppImageName = info["Name"]; + root.currentAppImageReleaseNotes = info["ReleaseNotes"]; + root.updateLoading = false; + root.updating = true; + root.showUpdateChoice = true; + + if(system_tray.isHidden) { + if(settings_manager.isAllowSystemTrayNotification) { + system_tray.notify("Please select a option to continue update."); + } + system_tray.changeTrayIconToRed(); + } + } + + onRetrySent: { + for(var i = 0; i < failedUpdatesList.count; ++i) { + var obj = failedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + failedUpdatesList.remove(i); + break; + } + } + } + } + + onQueued: { + queuedUpdatesList.append(info); + notify("Item Queued"); + } + + onRemovedFromQueue: { + for(var i = 0; i < queuedUpdatesList.count; ++i) { + var obj = queuedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + queuedUpdatesList.remove(i); + break; + } + } + } + } + + onCanceled: { + for(var i = 0; i < queuedUpdatesList.count; ++i) { + var obj = queuedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == hash) { + queuedUpdatesList.remove(i); + break; + } + } + } + } + + onFailed: { + for(var i = 0; i < queuedUpdatesList.count; ++i) { + var obj = queuedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == info["Hash"]) { + queuedUpdatesList.remove(i); + break; + } + } + } + failedUpdatesList.append(info); + } + + onStarted: { + root.updateLoading = false; + root.showUpdateChoice = false; + + root.loadingProgress = true; + } + + onProgressText: { + root.loadingProgress = false; + root.progressBarValue = progressValue * 0.01; + root.actualProgress = true; + root.progressText = progressTextString + "."; + } + + onFinished: { + for(var i = 0; i < queuedUpdatesList.count; ++i) { + var obj = queuedUpdatesList.get(i); + if(obj) { + if(obj["Hash"] == info["Hash"]) { + queuedUpdatesList.remove(i); + break; + } + } + } + info["RemovingSeeding"] = false; + info["QueuedSeeding"] = false; + info["Seeding"] = false; + info["SeedingStatusText"] = qsTr(""); + info["OldVersionRemoved"] = false; + completedUpdatesList.append(info); + } + + onFinishedAll: { + queuedUpdatesList.clear(); + root.updateLoading = false; + root.showUpdateChoice = false; + root.updating = false; + notify("All Update(s) Finished"); + + if(settings_manager.isAllowSystemTrayNotification) { + system_tray.notify("Finished All Updates."); + } + + if(system_tray.isHidden) { + system_tray.changeTrayIconToBlue(); + } + + } + onCompletedCountChanged: { + if(stackView.currentItem.title != "Completed Update(s)"){ + setCompletedCount(n); + } + } + + onFailedCountChanged: { + if(stackView.currentItem.title != "Failed Update(s)") { + setFailedCount(n); + } + } + + onQueuedCountChanged: { + if(stackView.currentItem.title != "Queued Update(s)") { + setQueuedCount(n); + } + } + } + // --- + + Drawer { + id: drawer + + width: root.width * 0.66 + height: root.height + + Column { + anchors.fill: parent + + ItemDelegate { + text: qsTr("Completed Update(s)") + width: parent.width + onClicked: { + stackView.push("qrc:/Pages/CompletedPage.qml"); + drawer.close(); + } + + RoundButton { + property int count : 0; + id: completedCount + anchors.right: parent.right + text: String(completedCount.count); + visible: completedCount.count > 0 + flat: true; + Material.background: Material.Green; + Material.foreground: "#fff"; + } + } + + ItemDelegate { + text: qsTr("Queued Update(s)") + width: parent.width + onClicked: { + stackView.push("qrc:/Pages/QueuedPage.qml"); + drawer.close(); + } + + RoundButton { + property int count: 0; + id: queuedCount + anchors.right: parent.right + text: String(queuedCount.count); + flat: true; + visible: queuedCount.count > 0 + Material.background: Material.Blue; + Material.foreground: "#fff"; + } + + } + + ItemDelegate { + text: qsTr("Failed Update(s)") + width: parent.width + onClicked: { + stackView.push("qrc:/Pages/FailedPage.qml"); + drawer.close(); + } + + RoundButton { + property int count: 0; + id: failedCount + anchors.right: parent.right + text: String(failedCount.count); + flat: true + visible: failedCount.count > 0 + Material.background: Material.Red; + Material.foreground: "#fff"; + } + } + + ItemDelegate { + text: qsTr("Settings") + width: parent.width + onClicked: { + stackView.push("qrc:/Pages/SettingsPage.qml"); + drawer.close(); + } + } + + ItemDelegate { + text: qsTr("About") + width: parent.width + onClicked: { + stackView.push("qrc:/Pages/AboutPage.qml"); + drawer.close(); + } + } + + ItemDelegate { + text: qsTr("Quit") + width: parent.width + onClicked: { + drawer.close(); + Qt.quit(); + } + } + + } + + } + + StackView { + id: stackView + + initialItem: "qrc:/Pages/HomePage.qml" + anchors.fill: parent + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + } + + header: ToolBar { + contentHeight: toolButton.implicitHeight + + ToolButton { + id: toolButton + + text: stackView.depth > 1 ? "\u25C0" : "\u2630" + Material.foreground: "#fff"; + font.pixelSize: Qt.application.font.pixelSize * 1.6 + onClicked: { + if (stackView.depth > 1) + stackView.pop(); + else + drawer.open(); + } + } + + Shape { + property bool glow: false; + id: noti_glow + visible: stackView.depth > 1 ? false : totalCounts() == 0 ? glow : true; + width: 30 + height: 30 + layer.enabled: true + layer.samples: 4 + ShapePath { + fillColor: "#ef5350" + strokeColor: "#ef5350" + strokeWidth: 2 + capStyle: ShapePath.FlatCap + + PathAngleArc { + centerX: 15; centerY: 12 + radiusX: 9; radiusY: 9 + startAngle: 0 + sweepAngle: 360 + } + } + } + + Label { + text: stackView.currentItem.title + anchors.centerIn: parent + color: "#fff" + } + + } + +} diff --git a/qml/qml.qrc b/qml/qml.qrc new file mode 100644 index 0000000..656d842 --- /dev/null +++ b/qml/qml.qrc @@ -0,0 +1,11 @@ + + + main.qml + Pages/HomePage.qml + Pages/CompletedPage.qml + Pages/FailedPage.qml + Pages/QueuedPage.qml + Pages/SettingsPage.qml + Pages/AboutPage.qml + + diff --git a/release_notes/v2.0.0.md b/release_notes/v2.0.0.md new file mode 100644 index 0000000..cae049e --- /dev/null +++ b/release_notes/v2.0.0.md @@ -0,0 +1,29 @@ +# AppImage Updater 🐅 v2.0.0 🐅 ( Darker than Black ) + +This is the next major release of AppImage Updater, this release includes many changes and new features. +This release has a new UI/UX design built from ground up with *Google Material Design.* + +This release also has a new feature which is known as **"Decentralized Update"**. This feature allows the user to +update AppImages with the *BitTorrent Protocol* to avoid rate limiting and server slow downs. This feature is very helpful in local area networks and other intranets. + +# Change Log + + * Uses QAppImageUpdate **v2.0.2**. + * Uses LibAppImage. + * Uses Torrent Rasterbar. + * Changed UI/UX to Material Design. + * Add Proxy Support. + * Improved AppImage Updater Standalone Dialogs. + * Improved CLI. + * Add Decentralized Update feature. + * Add Dark Theme. + +# Bug Fixes + +Fixed Authorization Dialog in OpenSUSE and other distros which uses xdg-su exclusively. Memory leaks and other bugs. +Fix double application instance. + +# User Issue Fixes + +https://github.com/antony-jr/AppImageUpdater/issues/23 +https://github.com/antony-jr/AppImageUpdater/issues/20 diff --git a/scripts/check.py b/scripts/check.py new file mode 100644 index 0000000..0626bea --- /dev/null +++ b/scripts/check.py @@ -0,0 +1,34 @@ +import sys + +if len(sys.argv) < 2: + print("Usage: check.py [GIT COMMIT MESSAGE] [OUTPUT OF git tags]") + sys.exit(-1) + +message = sys.argv[1]; +tags = sys.argv[2]; +if "RELEASE" in message: + parts = message.split('[RELEASE ') + if len(parts) != 2: + print("false") + sys.exit(0) + + parts2 = parts[1].split(']') + if len(parts2) != 2: + print("false") + sys.exit(0) + + version = parts2[0] + version = version.lower() + + # Check if there is a existing tag. + with open(tags, 'r') as fp: + for i in fp: + s = i.strip() + if version == s: + print("false") + sys.exit(0) + print(version) +else: + print("false") + sys.exit(0) +sys.exit(0)
- + Download