From a39f4a532f102e9cc60039108124ce1f572f15cb Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Mon, 4 Mar 2019 13:58:38 +0100 Subject: [PATCH 1/6] OwnSql: Distinguish no-data from error #6677 This could fix a problem where the client incorrectly decides to delete local data. Previously any sqlite3_step() return value that wasn't SQLITE_ROW would be interpreted as "there's no more data here". Thus an sqlite error at a bad time could cause the remote discovery to fail to read an unchanged subtree from the database. These files would then be deleted locally. With this change sqlite errors from sqlite3_step are detected and logged. For the particular case of SyncJournalDb::getFilesBelowPath() the error will now be propagated and the sync run will fail instead of performing spurious deletes. Note that many other database functions still don't distinguish not-found from error cases. Most of them won't have as severe effects on affected sync runs though. --- ChangeLog | 1 + src/common/ownsql.cpp | 27 ++++++++-- src/common/ownsql.h | 9 +++- src/common/syncjournaldb.cpp | 95 +++++++++++++++++++++++------------- test/testownsql.cpp | 8 +-- test/testpermissions.cpp | 2 +- 6 files changed, 99 insertions(+), 43 deletions(-) diff --git a/ChangeLog b/ChangeLog index 594c0a82c4..f12b8c85e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ ChangeLog version 2.5.4 (unreleased) * Crash fix: Infinite recursion for bad paths on Windows (#7041) * Crash fix: SocketApi mustn't send if readyRead happens after disconnected (#7044) +* Fix rare sync error causing spurious local deletes (#6677) * Disable HTTP2 support due to bugs in Qt 5.12.1 (#7020, QTBUG-73947) * Fix loading of persisted cookies when loading accounts (#7054) * Windows: Fix breaking of unrelated explorer actions (#7004, #7023) diff --git a/src/common/ownsql.cpp b/src/common/ownsql.cpp index ff22eff52a..cd7e452950 100644 --- a/src/common/ownsql.cpp +++ b/src/common/ownsql.cpp @@ -338,10 +338,31 @@ bool SqlQuery::exec() return true; } -bool SqlQuery::next() +auto SqlQuery::next() -> NextResult { - SQLITE_DO(sqlite3_step(_stmt)); - return _errId == SQLITE_ROW; + const bool firstStep = !sqlite3_stmt_busy(_stmt); + + int n = 0; + forever { + _errId = sqlite3_step(_stmt); + if (n < SQLITE_REPEAT_COUNT && firstStep && (_errId == SQLITE_LOCKED || _errId == SQLITE_BUSY)) { + sqlite3_reset(_stmt); // not necessary after sqlite version 3.6.23.1 + n++; + OCC::Utility::usleep(SQLITE_SLEEP_TIME_USEC); + } else { + break; + } + } + + NextResult result; + result.ok = _errId == SQLITE_ROW || _errId == SQLITE_DONE; + result.hasData = _errId == SQLITE_ROW; + if (!result.ok) { + _error = QString::fromUtf8(sqlite3_errmsg(_db)); + qCWarning(lcSql) << "Sqlite step statement error:" << _errId << _error << "in" << _sql; + } + + return result; } void SqlQuery::bindValue(int pos, const QVariant &value) diff --git a/src/common/ownsql.h b/src/common/ownsql.h index a5e53e8d0d..5ca2d7442c 100644 --- a/src/common/ownsql.h +++ b/src/common/ownsql.h @@ -129,7 +129,14 @@ public: bool isSelect(); bool isPragma(); bool exec(); - bool next(); + + struct NextResult + { + bool ok = false; + bool hasData = false; + }; + NextResult next(); + void bindValue(int pos, const QVariant &value); QString lastQuery() const; int numRowsAffected(); diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp index 5002a9e2b9..16bee0d81d 100644 --- a/src/common/syncjournaldb.cpp +++ b/src/common/syncjournaldb.cpp @@ -515,7 +515,7 @@ bool SyncJournalDb::checkConnect() bool forceRemoteDiscovery = false; SqlQuery versionQuery("SELECT major, minor, patch FROM version;", _db); - if (!versionQuery.next()) { + if (!versionQuery.next().hasData) { // If there was no entry in the table, it means we are likely upgrading from 1.5 qCInfo(lcDb) << "possibleUpgradeFromMirall_1_5 detected!"; forceRemoteDiscovery = true; @@ -829,7 +829,7 @@ QVector SyncJournalDb::tableColumns(const QByteArray &table) if (!query.exec()) { return columns; } - while (query.next()) { + while (query.next().hasData) { columns.append(query.baValue(1)); } qCDebug(lcDb) << "Columns in the current journal: " << columns; @@ -984,15 +984,15 @@ bool SyncJournalDb::getFileRecord(const QByteArray &filename, SyncJournalFileRec return false; } - if (_getFileRecordQuery.next()) { + auto next = _getFileRecordQuery.next(); + if (!next.ok) { + QString err = _getFileRecordQuery.error(); + qCWarning(lcDb) << "No journal entry found for " << filename << "Error: " << err; + close(); + return false; + } + if (next.hasData) { fillFileRecordFromGetQuery(*rec, _getFileRecordQuery); - } else { - int errId = _getFileRecordQuery.errorId(); - if (errId != SQLITE_DONE) { // only do this if the problem is different from SQLITE_DONE - QString err = _getFileRecordQuery.error(); - qCWarning(lcDb) << "No journal entry found for " << filename << "Error: " << err; - close(); - } } } return true; @@ -1021,7 +1021,10 @@ bool SyncJournalDb::getFileRecordByInode(quint64 inode, SyncJournalFileRecord *r if (!_getFileRecordQueryByInode.exec()) return false; - if (_getFileRecordQueryByInode.next()) + auto next = _getFileRecordQueryByInode.next(); + if (!next.ok) + return false; + if (next.hasData) fillFileRecordFromGetQuery(*rec, _getFileRecordQueryByInode); return true; @@ -1045,7 +1048,13 @@ bool SyncJournalDb::getFileRecordsByFileId(const QByteArray &fileId, const std:: if (!_getFileRecordQueryByFileId.exec()) return false; - while (_getFileRecordQueryByFileId.next()) { + forever { + auto next = _getFileRecordQueryByFileId.next(); + if (!next.ok) + return false; + if (!next.hasData) + break; + SyncJournalFileRecord rec; fillFileRecordFromGetQuery(rec, _getFileRecordQueryByFileId); rowCallback(rec); @@ -1097,7 +1106,13 @@ bool SyncJournalDb::getFilesBelowPath(const QByteArray &path, const std::functio return false; } - while (query->next()) { + forever { + auto next = query->next(); + if (!next.ok) + return false; + if (!next.hasData) + break; + SyncJournalFileRecord rec; fillFileRecordFromGetQuery(rec, *query); rowCallback(rec); @@ -1124,7 +1139,13 @@ bool SyncJournalDb::postSyncCleanup(const QSet &filepathsToKeep, QByteArrayList superfluousItems; - while (query.next()) { + forever { + auto next = query.next(); + if (!next.ok) + return false; + if (!next.hasData) + break; + const QString file = query.baValue(1); bool keep = filepathsToKeep.contains(file); if (!keep) { @@ -1167,7 +1188,7 @@ int SyncJournalDb::getFileRecordCount() return -1; } - if (query.next()) { + if (query.next().hasData) { int count = query.intValue(0); return count; } @@ -1301,10 +1322,8 @@ SyncJournalDb::DownloadInfo SyncJournalDb::getDownloadInfo(const QString &file) return res; } - if (_getDownloadInfoQuery.next()) { + if (_getDownloadInfoQuery.next().hasData) { toDownloadInfo(_getDownloadInfoQuery, &res); - } else { - res._valid = false; } } return res; @@ -1358,7 +1377,7 @@ QVector SyncJournalDb::getAndDeleteStaleDownloadInf QStringList superfluousPaths; QVector deleted_entries; - while (query.next()) { + while (query.next().hasData) { const QString file = query.stringValue(3); // path if (!keep.contains(file)) { superfluousPaths.append(file); @@ -1385,7 +1404,7 @@ int SyncJournalDb::downloadInfoCount() if (!query.exec()) { sqlFail("Count number of downloadinfo entries failed", query); } - if (query.next()) { + if (query.next().hasData) { re = query.intValue(0); } } @@ -1410,7 +1429,7 @@ SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString &file) return res; } - if (_getUploadInfoQuery.next()) { + if (_getUploadInfoQuery.next().hasData) { bool ok = true; res._chunk = _getUploadInfoQuery.intValue(0); res._transferid = _getUploadInfoQuery.intValue(1); @@ -1479,7 +1498,7 @@ QVector SyncJournalDb::deleteStaleUploadInfos(const QSet &keep) QStringList superfluousPaths; - while (query.next()) { + while (query.next().hasData) { const QString file = query.stringValue(0); if (!keep.contains(file)) { superfluousPaths.append(file); @@ -1503,7 +1522,7 @@ SyncJournalErrorBlacklistRecord SyncJournalDb::errorBlacklistEntry(const QString _getErrorBlacklistQuery.reset_and_clear_bindings(); _getErrorBlacklistQuery.bindValue(1, file); if (_getErrorBlacklistQuery.exec()) { - if (_getErrorBlacklistQuery.next()) { + if (_getErrorBlacklistQuery.next().hasData) { entry._lastTryEtag = _getErrorBlacklistQuery.baValue(0); entry._lastTryModtime = _getErrorBlacklistQuery.int64Value(1); entry._retryCount = _getErrorBlacklistQuery.intValue(2); @@ -1539,7 +1558,7 @@ bool SyncJournalDb::deleteStaleErrorBlacklistEntries(const QSet &keep) QStringList superfluousPaths; - while (query.next()) { + while (query.next().hasData) { const QString file = query.stringValue(0); if (!keep.contains(file)) { superfluousPaths.append(file); @@ -1562,7 +1581,7 @@ int SyncJournalDb::errorBlackListEntryCount() if (!query.exec()) { sqlFail("Count number of blacklist entries failed", query); } - if (query.next()) { + if (query.next().hasData) { re = query.intValue(0); } } @@ -1666,7 +1685,7 @@ QVector SyncJournalDb::getPollInfos() return res; } - while (query.next()) { + while (query.next().hasData) { PollInfo info; info._file = query.stringValue(0); info._modtime = query.int64Value(1); @@ -1720,7 +1739,15 @@ QStringList SyncJournalDb::getSelectiveSyncList(SyncJournalDb::SelectiveSyncList *ok = false; return result; } - while (_getSelectiveSyncListQuery.next()) { + forever { + auto next = _getSelectiveSyncListQuery.next(); + if (!next.ok) { + *ok = false; + return result; + } + if (!next.hasData) + break; + auto entry = _getSelectiveSyncListQuery.stringValue(0); if (!entry.endsWith(QLatin1Char('/'))) { entry.append(QLatin1Char('/')); @@ -1843,12 +1870,12 @@ QByteArray SyncJournalDb::getChecksumType(int checksumTypeId) return {}; query.bindValue(1, checksumTypeId); if (!query.exec()) { - return 0; + return QByteArray(); } - if (!query.next()) { + if (!query.next().hasData) { qCWarning(lcDb) << "No checksum type mapping found for" << checksumTypeId; - return 0; + return QByteArray(); } return query.baValue(0); } @@ -1875,7 +1902,7 @@ int SyncJournalDb::mapChecksumType(const QByteArray &checksumType) return 0; } - if (!_getChecksumTypeIdQuery.next()) { + if (!_getChecksumTypeIdQuery.next().hasData) { qCWarning(lcDb) << "No checksum type mapping found for" << checksumType; return 0; } @@ -1896,7 +1923,7 @@ QByteArray SyncJournalDb::dataFingerprint() return QByteArray(); } - if (!_getDataFingerprintQuery.next()) { + if (!_getDataFingerprintQuery.next().hasData) { return QByteArray(); } return _getDataFingerprintQuery.baValue(0); @@ -1951,7 +1978,7 @@ ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path) ASSERT(query.initOrReset(QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag, basePath FROM conflicts WHERE path=?1;"), _db)); query.bindValue(1, path); ASSERT(query.exec()); - if (!query.next()) + if (!query.next().hasData) return entry; entry.path = path; @@ -1984,7 +2011,7 @@ QByteArrayList SyncJournalDb::conflictRecordPaths() ASSERT(query.exec()); QByteArrayList paths; - while (query.next()) + while (query.next().hasData) paths.append(query.baValue(0)); return paths; diff --git a/test/testownsql.cpp b/test/testownsql.cpp index 86ec77d28d..58e9bc5b6a 100644 --- a/test/testownsql.cpp +++ b/test/testownsql.cpp @@ -71,7 +71,7 @@ private slots: q.prepare(sql); q.exec(); - while( q.next() ) { + while( q.next().hasData ) { qDebug() << "Name: " << q.stringValue(1); qDebug() << "Address: " << q.stringValue(2); } @@ -83,7 +83,7 @@ private slots: q.prepare(sql); q.bindValue(1, 2); q.exec(); - if( q.next() ) { + if( q.next().hasData ) { qDebug() << "Name:" << q.stringValue(1); qDebug() << "Address:" << q.stringValue(2); } @@ -96,7 +96,7 @@ private slots: int rc = q.prepare(sql); qDebug() << "Pragma:" << rc; q.exec(); - if( q.next() ) { + if( q.next().hasData ) { qDebug() << "P:" << q.stringValue(1); } } @@ -118,7 +118,7 @@ private slots: SqlQuery q(_db); q.prepare(sql); - if(q.next()) { + if(q.next().hasData) { QString name = q.stringValue(1); QString address = q.stringValue(2); QVERIFY( name == QString::fromUtf8("пятницы") ); diff --git a/test/testpermissions.cpp b/test/testpermissions.cpp index 57aac6eafe..a23e84f71a 100644 --- a/test/testpermissions.cpp +++ b/test/testpermissions.cpp @@ -31,7 +31,7 @@ static void assertCsyncJournalOk(SyncJournalDb &journal) QVERIFY(db.openReadOnly(journal.databaseFilePath())); SqlQuery q("SELECT count(*) from metadata where length(fileId) == 0", db); QVERIFY(q.exec()); - QVERIFY(q.next()); + QVERIFY(q.next().hasData); QCOMPARE(q.intValue(0), 0); #if defined(Q_OS_WIN) // Make sure the file does not appear in the FileInfo FileSystem::setFileHidden(journal.databaseFilePath() + "-shm", true); From f3ab5843041ef3a15ddfb7582a242ab305c0b38a Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 6 Mar 2019 12:59:27 +0100 Subject: [PATCH 2/6] Shares: "copy link" action can create shares with expiry #7061 Previously it gave up if "expiry required" was enabled. Now it'll create a link share per day with the default expiry for these setups. --- src/gui/sharelinkwidget.cpp | 2 +- src/gui/socketapi.cpp | 9 ++++++++- src/libsync/capabilities.cpp | 15 ++++++++++----- src/libsync/capabilities.h | 3 ++- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/gui/sharelinkwidget.cpp b/src/gui/sharelinkwidget.cpp index 219f4270c6..c56a81c67d 100644 --- a/src/gui/sharelinkwidget.cpp +++ b/src/gui/sharelinkwidget.cpp @@ -145,7 +145,7 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account, if (_account->capabilities().sharePublicLinkEnforceExpireDate()) { _ui->checkBox_expire->setEnabled(false); _ui->calendar->setMaximumDate(QDate::currentDate().addDays( - _account->capabilities().sharePublicLinkExpireDateDays())); + _account->capabilities().sharePublicLinkDefaultExpireDateDays())); _expiryRequired = true; } diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp index 6ee993fc5d..bc8ac7e9f7 100644 --- a/src/gui/socketapi.cpp +++ b/src/gui/socketapi.cpp @@ -487,6 +487,7 @@ public: GetOrCreatePublicLinkShare(const AccountPtr &account, const QString &localFile, std::function targetFun, QObject *parent) : QObject(parent) + , _account(account) , _shareManager(account) , _localFile(localFile) , _targetFun(targetFun) @@ -509,6 +510,12 @@ private slots: void sharesFetched(const QList> &shares) { auto shareName = SocketApi::tr("Context menu share"); + + // If shares will expire, create a new one every day. + if (_account->capabilities().sharePublicLinkDefaultExpire()) { + shareName = SocketApi::tr("Context menu share %1").arg(QDate::currentDate().toString(Qt::ISODate)); + } + // If there already is a context menu share, reuse it for (const auto &share : shares) { const auto linkShare = qSharedPointerDynamicCast(share); @@ -551,6 +558,7 @@ private: deleteLater(); } + AccountPtr _account; ShareManager _shareManager; QString _localFile; std::function _targetFun; @@ -731,7 +739,6 @@ void SocketApi::sendSharingContextMenuOptions(const FileData &fileData, SocketLi // Is is possible to create a public link without user choices? bool canCreateDefaultPublicLink = publicLinksEnabled - && !capabilities.sharePublicLinkEnforceExpireDate() && !capabilities.sharePublicLinkEnforcePassword(); if (canCreateDefaultPublicLink) { diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp index 08c1e45ff7..10d46573c6 100644 --- a/src/libsync/capabilities.cpp +++ b/src/libsync/capabilities.cpp @@ -60,16 +60,21 @@ bool Capabilities::sharePublicLinkEnforcePassword() const return _capabilities["files_sharing"].toMap()["public"].toMap()["password"].toMap()["enforced"].toBool(); } +bool Capabilities::sharePublicLinkDefaultExpire() const +{ + return _capabilities["files_sharing"].toMap()["public"].toMap()["expire_date"].toMap()["enabled"].toBool(); +} + +int Capabilities::sharePublicLinkDefaultExpireDateDays() const +{ + return _capabilities["files_sharing"].toMap()["public"].toMap()["expire_date"].toMap()["days"].toInt(); +} + bool Capabilities::sharePublicLinkEnforceExpireDate() const { return _capabilities["files_sharing"].toMap()["public"].toMap()["expire_date"].toMap()["enforced"].toBool(); } -int Capabilities::sharePublicLinkExpireDateDays() const -{ - return _capabilities["files_sharing"].toMap()["public"].toMap()["expire_date"].toMap()["days"].toInt(); -} - bool Capabilities::sharePublicLinkMultiple() const { return _capabilities["files_sharing"].toMap()["public"].toMap()["multiple"].toBool(); diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h index 63a59d6c00..3a073e1805 100644 --- a/src/libsync/capabilities.h +++ b/src/libsync/capabilities.h @@ -38,8 +38,9 @@ public: bool sharePublicLinkAllowUpload() const; bool sharePublicLinkSupportsUploadOnly() const; bool sharePublicLinkEnforcePassword() const; + bool sharePublicLinkDefaultExpire() const; + int sharePublicLinkDefaultExpireDateDays() const; bool sharePublicLinkEnforceExpireDate() const; - int sharePublicLinkExpireDateDays() const; bool sharePublicLinkMultiple() const; bool shareResharing() const; bool chunkingNg() const; From 69bde24c30a9d6b5f385b8200731811c6700e965 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 5 Mar 2019 10:35:11 +0100 Subject: [PATCH 3/6] FolderWatcher: Work around missing notifications on linux #7068 There was a period of time between local discovery of a new folder and the folder receiving a filesystem watcher. Any changes to its contents during that time were missing, leading to misbehaviors. --- src/gui/folder.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 1d15164ce5..093cf12238 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -925,15 +925,30 @@ void Folder::slotItemCompleted(const SyncFileItemPtr &item) return; } - // add new directories or remove gone away dirs to the watcher +#ifdef Q_OS_LINUX + // The linux folderwatcher isn't recursive: it can just watch individual folders. + // That means new and removed directories need to be announced to the watcher. + // TODO: The watcher wrapper should take care of that itself using the notifications + // it gets! It already updates itself on interior moves and there's no + // reason it couldn't notice new and removed directories too. if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_NEW) { - if (_folderWatcher) + if (_folderWatcher) { _folderWatcher->addPath(path() + item->_file); + + // Since there's a delay between folder creation and folder watching, new + // local folders need to be discovered again during the next sync because + // notifications might have been missed. + if (item->_direction == SyncFileItem::Up) { + _localDiscoveryTracker->addTouchedPath(item->_file.toUtf8()); + scheduleThisFolderSoon(); + } + } } if (item->isDirectory() && item->_instruction == CSYNC_INSTRUCTION_REMOVE) { if (_folderWatcher) _folderWatcher->removePath(path() + item->_file); } +#endif _syncResult.processCompletedItem(item); From e24c0f2f14a2b46d4099b4a34d52bb722d333c71 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 6 Mar 2019 10:55:45 +0100 Subject: [PATCH 4/6] Tray: Try to establish tray after 10s if failed initially #6518 When owncloud is started during desktop startup the tray may not yet be running when the client starts. This will make the client attempt to create a tray icon again after 10 seconds if there's no tray during initial startup. --- src/gui/application.cpp | 7 +++++++ src/gui/application.h | 3 +++ src/gui/main.cpp | 26 +++++++++++++++++--------- src/gui/owncloudgui.cpp | 6 ++++++ src/gui/owncloudgui.h | 2 ++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 747f7aef6d..33265af35f 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -741,6 +741,13 @@ void Application::openVirtualFile(const QString &filename) }); } +void Application::tryTrayAgain() +{ + qCInfo(lcApplication) << "Trying tray icon, tray available:" << QSystemTrayIcon::isSystemTrayAvailable(); + if (!_gui->contextMenuVisible()) + _gui->hideAndShowTray(); +} + bool Application::event(QEvent *event) { #ifdef Q_OS_MAC diff --git a/src/gui/application.h b/src/gui/application.h index c29ca8b41d..f4d46b04a8 100644 --- a/src/gui/application.h +++ b/src/gui/application.h @@ -80,6 +80,9 @@ public slots: */ void openVirtualFile(const QString &filename); + /// Attempt to show() the tray icon again. Used if no systray was available initially. + void tryTrayAgain(); + protected: void parseOptions(const QStringList &); void setupTranslations(); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index efe87a9d6b..e1f7b4f406 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -127,6 +127,7 @@ int main(int argc, char **argv) } return 0; } + // We can't call isSystemTrayAvailable with appmenu-qt5 begause it hides the systemtray // (issue #4693) if (qgetenv("QT_QPA_PLATFORMTHEME") != "appmenu-qt5") @@ -135,27 +136,34 @@ int main(int argc, char **argv) // If the systemtray is not there, we will wait one second for it to maybe start // (eg boot time) then we show the settings dialog if there is still no systemtray. // On XFCE however, we show a message box with explainaition how to install a systemtray. + qCInfo(lcApplication) << "System tray is not available, waiting..."; Utility::sleep(1); + auto desktopSession = qgetenv("XDG_CURRENT_DESKTOP").toLower(); if (desktopSession.isEmpty()) { desktopSession = qgetenv("DESKTOP_SESSION").toLower(); } if (desktopSession == "xfce") { int attempts = 0; - forever { - if (!QSystemTrayIcon::isSystemTrayAvailable()) { - Utility::sleep(1); - attempts++; - if (attempts < 30) - continue; - } else { + while (!QSystemTrayIcon::isSystemTrayAvailable()) { + attempts++; + if (attempts >= 30) { + qCWarning(lcApplication) << "System tray unavailable (xfce)"; + warnSystray(); break; } - warnSystray(); + Utility::sleep(1); } } - if (!QSystemTrayIcon::isSystemTrayAvailable() && desktopSession != "ubuntu") { + + if (QSystemTrayIcon::isSystemTrayAvailable()) { + app.tryTrayAgain(); + } else if (desktopSession != "ubuntu") { + qCInfo(lcApplication) << "System tray still not available, showing window and trying again later"; app.showSettingsDialog(); + QTimer::singleShot(10000, &app, &Application::tryTrayAgain); + } else { + qCInfo(lcApplication) << "System tray still not available, but assuming it's fine on 'ubuntu' desktop"; } } } diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 8ba0ed3e2e..9133e63923 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -426,6 +426,12 @@ bool ownCloudGui::contextMenuVisible() const return _contextMenu->isVisible(); } +void ownCloudGui::hideAndShowTray() +{ + _tray->hide(); + _tray->show(); +} + static bool minimalTrayMenu() { static QByteArray var = qgetenv("OWNCLOUD_MINIMAL_TRAY_MENU"); diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h index b12fdf7b55..b76e4e06a8 100644 --- a/src/gui/owncloudgui.h +++ b/src/gui/owncloudgui.h @@ -61,6 +61,8 @@ public: /// Whether the tray menu is visible bool contextMenuVisible() const; + void hideAndShowTray(); + signals: void setupProxy(); From 3d6614c71169a6e65fb014371838953daa13cd6f Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Sun, 10 Mar 2019 17:30:44 +0100 Subject: [PATCH 5/6] ChangeLog: 2.5.4 --- ChangeLog | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index f12b8c85e0..45c776b105 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,16 +1,19 @@ ChangeLog ========= -version 2.5.4 (unreleased) +version 2.5.4 (2019-03-xx) * Crash fix: Infinite recursion for bad paths on Windows (#7041) * Crash fix: SocketApi mustn't send if readyRead happens after disconnected (#7044) -* Fix rare sync error causing spurious local deletes (#6677) +* Fix rare error causing spurious local deletes (#6677) * Disable HTTP2 support due to bugs in Qt 5.12.1 (#7020, QTBUG-73947) * Fix loading of persisted cookies when loading accounts (#7054) * Windows: Fix breaking of unrelated explorer actions (#7004, #7023) * Windows: Forbid syncing of files with bytes 0x00 to 0x1F in filenames (#6987) -* OSX: Opt out of dark mode until problems can be addressed (#7043) -* OSX: Fix folder dehydration requests (#6977) +* macOS: Opt out of dark mode until problems can be addressed (#7043) +* macOS: Fix folder dehydration requests (#6977) +* Linux: Tray: Try to establish tray after 10s if failed initially (#6518) +* Linux: FolderWatcher: Work around missing notifications (#7068) +* Shares: "copy link" action can create shares with expiry (#7061) * Selective sync: Don't collapse folder tree when changing selection (#7055) * Client cert dialog: Avoid incorrect behavior due to multiple signal connections From 94230e57b0a72e42462a8eac1da51c3d62a3678b Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Mon, 11 Mar 2019 11:17:16 +0100 Subject: [PATCH 6/6] VERSION.cmake: This branch is 2.5.5 now --- VERSION.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.cmake b/VERSION.cmake index e519036e0f..a1173e0f7c 100644 --- a/VERSION.cmake +++ b/VERSION.cmake @@ -1,6 +1,6 @@ set( MIRALL_VERSION_MAJOR 2 ) set( MIRALL_VERSION_MINOR 5 ) -set( MIRALL_VERSION_PATCH 4 ) +set( MIRALL_VERSION_PATCH 5 ) set( MIRALL_VERSION_YEAR 2019 ) set( MIRALL_SOVERSION 0 )