aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/services/updatemanager.cpp
blob: 87df73b2b284ec87f4338f38b5c25b95fe6a1b48 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#ifdef PORTABLE_CONFIG

#include "updatemanager.h"
#include "services/notifymanager.h"
#include "common/unused.h"
#include <QDebug>
#include <QRegularExpression>
#include <QCoreApplication>
#include <QFileInfo>
#include <QtConcurrent/QtConcurrentRun>

UpdateManager::UpdateManager(QObject *parent) :
    QObject(parent)
{
    qRegisterMetaType<QList<UpdateManager::UpdateEntry>>();

    connect(this, SIGNAL(updatingError(QString)), NOTIFY_MANAGER, SLOT(error(QString)));

    QString updateBinary =
#if defined(Q_OS_WIN)
        "UpdateSQLiteStudio.exe";
#elif defined(Q_OS_LINUX)
        "UpdateSQLiteStudio";
#elif defined(Q_OS_OSX)
        "../../UpdateSQLiteStudio.app/Contents/MacOS/UpdateSQLiteStudio";
#else
        "";
#endif

    if (!updateBinary.isEmpty()) {
        updateBinaryAbsolutePath = QFileInfo(QCoreApplication::applicationDirPath() + "/" + updateBinary).absoluteFilePath();
    }
}

UpdateManager::~UpdateManager()
{

}

void UpdateManager::checkForUpdates()
{
    if (!CFG_CORE.General.CheckUpdatesOnStartup.get())
        return;

    if (updateBinaryAbsolutePath.isEmpty()) {
        qDebug() << "Updater binary not defined. Skipping updates checking.";
        return;
    }

    if (!QFileInfo(updateBinaryAbsolutePath).exists()) {
        QString errorDetails = tr("Updates installer executable is missing.");
        emit updatingError(tr("Unable to check for updates (%1)").arg(errorDetails.trimmed()));
        qWarning() << "Error while checking for updates: " << errorDetails;
        return;
    }

    QtConcurrent::run(this, &UpdateManager::checkForUpdatesAsync);
}

void UpdateManager::checkForUpdatesAsync()
{
    QProcess proc;
    proc.start(updateBinaryAbsolutePath, {"--checkupdates"});
    if (!waitForProcess(proc))
    {
        QString errorDetails = QString::fromLocal8Bit(proc.readAllStandardError());

        if (errorDetails.toLower().contains("no updates")) {
            emit noUpdatesAvailable();
            return;
        }

        if (errorDetails.isEmpty())
            errorDetails = tr("details are unknown");

        emit updatingError(tr("Unable to check for updates (%1)").arg(errorDetails.trimmed()));
        qWarning() << "Error while checking for updates: " << errorDetails;
        return;
    }

    processCheckResults(proc.readAllStandardOutput());
}

void UpdateManager::update()
{
    bool success = QProcess::startDetached(updateBinaryAbsolutePath, {"--updater"});
    if (!success)
    {
        emit updatingError(tr("Unable to run updater application (%1). Please report this.").arg(updateBinaryAbsolutePath));
        return;
    }
    qApp->exit(0);
}

bool UpdateManager::isPlatformEligibleForUpdate() const
{
    return getDistributionType() != DistributionType::OS_MANAGED;
}

bool UpdateManager::waitForProcess(QProcess& proc)
{
    if (!proc.waitForFinished(-1))
    {
        qDebug() << "Update QProcess timed out.";
        return false;
    }

    if (proc.exitStatus() == QProcess::CrashExit)
    {
        qDebug() << "Update QProcess finished by crashing.";
        return false;
    }

    if (proc.exitCode() != 0)
    {
        qDebug() << "Update QProcess finished with code:" << proc.exitCode();
        return false;
    }

    return true;
}

void UpdateManager::processCheckResults(const QByteArray &results)
{
    if (results.trimmed().isEmpty()) {
        emit noUpdatesAvailable();
        return;
    }

    QRegularExpression re(R"(\<update\s+([^\>]+)\>)");
    QRegularExpression versionRe(R"(version\=\"([\d\.]+)\")");
    QRegularExpression nameRe(R"(name\=\"([^\"]+)\")");

    QRegularExpressionMatchIterator reIter = re.globalMatch(results);
    QString updateNode;
    UpdateEntry theUpdate;
    QList<UpdateEntry> updates;
    while (reIter.hasNext())
    {
        updateNode = reIter.next().captured(1);
        theUpdate.version = versionRe.match(updateNode).captured(1);
        theUpdate.compontent = nameRe.match(updateNode).captured(1);
        updates << theUpdate;
    }

    if (updates.isEmpty())
        emit noUpdatesAvailable();
    else
        emit updatesAvailable(updates);
}

#endif // PORTABLE_CONFIG