Merge remote-tracking branch 'origin/2.5'

This commit is contained in:
Christian Kamm 2019-03-14 08:34:19 +01:00
commit db866afb60
15 changed files with 175 additions and 66 deletions

View File

@ -1,15 +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 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

View File

@ -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)

View File

@ -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();

View File

@ -521,7 +521,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;
@ -850,7 +850,7 @@ QVector<QByteArray> 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;
@ -1000,15 +1000,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;
@ -1037,7 +1037,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;
@ -1061,7 +1064,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);
@ -1113,7 +1122,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);
@ -1142,7 +1157,13 @@ bool SyncJournalDb::listFilesInPath(const QByteArray& path,
if (!_listFilesInPathQuery.exec())
return false;
while (_listFilesInPathQuery.next()) {
forever {
auto next = _listFilesInPathQuery.next();
if (!next.ok)
return false;
if (!next.hasData)
break;
SyncJournalFileRecord rec;
fillFileRecordFromGetQuery(rec, _listFilesInPathQuery);
if (!rec._path.startsWith(path) || rec._path.indexOf("/", path.size() + 1) > 0) {
@ -1166,7 +1187,7 @@ int SyncJournalDb::getFileRecordCount()
return -1;
}
if (query.next()) {
if (query.next().hasData) {
int count = query.intValue(0);
return count;
}
@ -1277,10 +1298,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;
@ -1334,7 +1353,7 @@ QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInf
QStringList superfluousPaths;
QVector<SyncJournalDb::DownloadInfo> deleted_entries;
while (query.next()) {
while (query.next().hasData) {
const QString file = query.stringValue(3); // path
if (!keep.contains(file)) {
superfluousPaths.append(file);
@ -1361,7 +1380,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);
}
}
@ -1386,7 +1405,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);
@ -1455,7 +1474,7 @@ QVector<uint> SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
QStringList superfluousPaths;
while (query.next()) {
while (query.next().hasData) {
const QString file = query.stringValue(0);
if (!keep.contains(file)) {
superfluousPaths.append(file);
@ -1479,7 +1498,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);
@ -1515,7 +1534,7 @@ bool SyncJournalDb::deleteStaleErrorBlacklistEntries(const QSet<QString> &keep)
QStringList superfluousPaths;
while (query.next()) {
while (query.next().hasData) {
const QString file = query.stringValue(0);
if (!keep.contains(file)) {
superfluousPaths.append(file);
@ -1538,7 +1557,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);
}
}
@ -1642,7 +1661,7 @@ QVector<SyncJournalDb::PollInfo> SyncJournalDb::getPollInfos()
return res;
}
while (query.next()) {
while (query.next().hasData) {
PollInfo info;
info._file = query.stringValue(0);
info._modtime = query.int64Value(1);
@ -1696,7 +1715,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('/'));
@ -1819,12 +1846,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);
}
@ -1855,7 +1882,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;
}
@ -1878,7 +1905,7 @@ QByteArray SyncJournalDb::dataFingerprint()
return QByteArray();
}
if (!_getDataFingerprintQuery.next()) {
if (!_getDataFingerprintQuery.next().hasData) {
return QByteArray();
}
return _getDataFingerprintQuery.baValue(0);
@ -1933,7 +1960,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;
@ -1966,7 +1993,7 @@ QByteArrayList SyncJournalDb::conflictRecordPaths()
ASSERT(query.exec());
QByteArrayList paths;
while (query.next())
while (query.next().hasData)
paths.append(query.baValue(0));
return paths;
@ -2031,8 +2058,11 @@ Optional<PinState> SyncJournalDb::PinStateInterface::rawForPath(const QByteArray
query.bindValue(1, path);
query.exec();
auto next = query.next();
if (!next.ok)
return {};
// no-entry means Inherited
if (!query.next())
if (!next.hasData)
return PinState::Inherited;
return static_cast<PinState>(query.intValue(0));
@ -2056,8 +2086,11 @@ Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPath(const QByt
query.bindValue(1, path);
query.exec();
auto next = query.next();
if (!next.ok)
return {};
// If the root path has no setting, assume AlwaysLocal
if (!query.next())
if (!next.hasData)
return PinState::AlwaysLocal;
return static_cast<PinState>(query.intValue(0));
@ -2110,7 +2143,12 @@ SyncJournalDb::PinStateInterface::rawList()
query.exec();
QVector<QPair<QByteArray, PinState>> result;
while (query.next()) {
forever {
auto next = query.next();
if (!next.ok)
return {};
if (!next.hasData)
break;
result.append({ query.baValue(0), static_cast<PinState>(query.intValue(1)) });
}
return result;

View File

@ -766,6 +766,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

View File

@ -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();

View File

@ -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";
}
}
}

View File

@ -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");

View File

@ -61,6 +61,8 @@ public:
/// Whether the tray menu is visible
bool contextMenuVisible() const;
void hideAndShowTray();
signals:
void setupProxy();

View File

@ -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;
}

View File

@ -487,6 +487,7 @@ public:
GetOrCreatePublicLinkShare(const AccountPtr &account, const QString &localFile,
std::function<void(const QString &link)> targetFun, QObject *parent)
: QObject(parent)
, _account(account)
, _shareManager(account)
, _localFile(localFile)
, _targetFun(targetFun)
@ -509,6 +510,12 @@ private slots:
void sharesFetched(const QList<QSharedPointer<Share>> &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<LinkShare>(share);
@ -551,6 +558,7 @@ private:
deleteLater();
}
AccountPtr _account;
ShareManager _shareManager;
QString _localFile;
std::function<void(const QString &url)> _targetFun;
@ -778,7 +786,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) {

View File

@ -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();

View File

@ -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;

View File

@ -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("пятницы") );

View File

@ -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);