Merge remote-tracking branch 'upstream/master'

Conflicts:
	src/cmd/cmd.cpp
This commit is contained in:
Niels van Adrichem 2015-11-03 11:58:45 +01:00
commit 1ab44655e0
46 changed files with 1670 additions and 852 deletions

View file

@ -6,50 +6,50 @@ version 2.1 (release 2015-yy-zz)
Linux when building with Qt < 5.4 and having bandwidth limiting enabled.
So now you need Qt 5.4 on Linux if you want bandwidth limiting.
version 2.0.2 (release 2015-10-22)
version 2.0.2 (release 2015-10-21)
* csync_file_stat_s: Save a bit of memory
* Shibboleth: Add our base user agent to WebKit
* SelectiveSync: Increase folder list timeout to 60
* Propagation: Try another sync on 423 Locked #3387
* Propagation: Make 423 Locked a soft error #3387
* Propagation: Reset upload blacklist if a chunk suceeds
* Application: Fix crash on early shutdown #3898
* Linux: Don't show settings dialog always when launched twice #3273 #3771 #3485
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. #3813
* Propagation: Try another sync on 423 Locked (#3387)
* Propagation: Make 423 Locked a soft error (#3387)
* Propagation: Reset upload blacklist if a chunk succeeds
* Application: Fix crash on early shutdown (#3898)
* Linux: Don't show settings dialog always when launched twice (#3273, #3771, #3485)
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. (#3813)
* AccountSettings: only expand root elements on single click.
* AccountSettings: Do not allow to expand the folder list when disconnected.
* Use application SHORT name for the name of the MacOSX pkg file (ownBrander).
* FolderMan: Fix for removing a syncing folder #3843
* ConnectionMethodDialog: Don't be insecure on close #3863
* Updater: Ensure folders are not removed #3747
* Folder settings: Ensure path is cleaned #3811
* Propagator: Simplify sub job finished counting #3844
* Share dialog: Hide settings dialog before showing #3783
* UI: Only expand 1 level in folder list #3585
* UI: Allow folder expanding from button click #3585
* UI: Expand folder treeview on single click #3585
* GUI: Change tray menu order #3657
* FolderMan: Fix for removing a syncing folder (#3843)
* ConnectionMethodDialog: Don't be insecure on close (#3863)
* Updater: Ensure folders are not removed (#3747)
* Folder settings: Ensure path is cleaned (#3811)
* Propagator: Simplify sub job finished counting (#3844)
* Share dialog: Hide settings dialog before showing (#3783)
* UI: Only expand 1 level in folder list (#3585)
* UI: Allow folder expanding from button click (#3585)
* UI: Expand folder treeview on single click (#3585)
* GUI: Change tray menu order (#3657)
* GUI: Replace term "sign in" with "Log in" and friends.
* SetupPage: Fix crash caused by uninitialized Account object.
* Use a themable WebDAV path all over.
* Units: Back to the "usual" mix units (JEDEC standard).
* csync io: Full UNC path support on Win #3748
* Tray: Don't use the tray workaround with the KDE theme #3706, #3765
* ShareDialog: Fix folder display #3659
* AccountSettings: Restore from legacy only once #3565
* SSL Certificate Error Dialog: show account name #3729
* Tray notification: Don't show a message about modified folder #3613
* csync io: Full UNC path support on Win (#3748)
* Tray: Don't use the tray workaround with the KDE theme (#3706, #3765)
* ShareDialog: Fix folder display (#3659)
* AccountSettings: Restore from legacy only once (#3565)
* SSL Certificate Error Dialog: show account name (#3729)
* Tray notification: Don't show a message about modified folder (#3613)
* PropagateLocalRemove: remove entries from the DB even if there was an error.
* Settings UI improvements (eg. #3713, #3721, #3619 and others)
* Folder: Do not create the sync folder if it does not exist #3692
* Shell integratioon: don't show share menu item for top level folders
* Tray: Hide while modifying menus #3656 #3672
* AddFolder: Improve remote path selection error handling #3573
* csync_update: Use excluded_traversal() to improve performance #3638
* csync_excluded: Add fast _traversal() function #3638
* csync_exclude: Speed up siginificantly #3638
* AccountSettings: Adjust quota info design #3644 #3651
* Adjust buttons on remove folder/account questions #3654
* Folder: Do not create the sync folder if it does not exist (#3692)
* Shell integration: don't show share menu item for top level folders
* Tray: Hide while modifying menus (#3656, #3672)
* AddFolder: Improve remote path selection error handling (#3573)
* csync_update: Use excluded_traversal() to improve performance (#3638)
* csync_excluded: Add fast _traversal() function (#3638)
* csync_exclude: Speed up significantly (#3638)
* AccountSettings: Adjust quota info design (#3644, #3651)
* Adjust buttons on remove folder/account questions (#3654)
version 2.0.1 (release 2015-09-01)
* AccountWizard: fix when the theme specify a override URL (#3699)

View file

@ -741,15 +741,13 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
res = 0;
}
/* for non windows platforms, detect if the filename starts with a .
* and if so, it's a hidden file. For windows, the hidden state is
* discovered within the vio local stat function.
/* if the filename starts with a . we consider it a hidden file
* For windows, the hidden state is also discovered within the vio
* local stat function.
*/
#ifndef _WIN32
if( d_name[0] == '.' ) {
dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
#endif
if( res == 0) {
switch (dirent->type) {

View file

@ -2,6 +2,10 @@
#
# Copyright (C) by Klaas Freitag <freitag@owncloud.com>
#
# This program is the core of OwnCloud integration to Nautilus
# It will be installed on /usr/share/nautilus-python/extensions/ with the paquet owncloud-client-nautilus
# (https://github.com/owncloud/client/edit/master/shell_integration/nautilus/syncstate.py)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
@ -18,7 +22,9 @@ import socket
from gi.repository import GObject, Nautilus
# do not touch the following line.
print("Initializing owncloud-client-nautilus extension")
# Do not touch the following line.
appname = 'ownCloud'
def get_local_path(url):
@ -38,7 +44,6 @@ def get_runtime_dir():
return fallback
class SocketConnect(GObject.GObject):
def __init__(self):
GObject.GObject.__init__(self)
@ -77,38 +82,38 @@ class SocketConnect(GObject.GObject):
def _connectToSocketServer(self):
try:
self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
postfix = "/"+appname+"/socket"
sock_file = get_runtime_dir()+postfix
postfix = "/" + appname + "/socket" # Should use os.path.join instead
sock_file = get_runtime_dir() + postfix
print ("Socket: " + sock_file + " <=> " + postfix)
if sock_file != postfix:
try:
print("Socket File: "+sock_file)
print("Socket File: " + sock_file)
self._sock.connect(sock_file)
self.connected = True
print("Setting connected to %r" % self.connected )
print("Setting connected to %r." % self.connected )
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
print("Socket watch id: "+str(self._watch_id))
return False # don't run again
print("Socket watch id: " + str(self._watch_id))
return False # Don't run again
except Exception as e:
print("Could not connect to unix socket." + str(e))
print("Could not connect to unix socket. " + str(e))
else:
print("Sock-File not valid: "+sock_file)
except Exception as e:
print("Connect could not be established, try again later ")
print("Sock-File not valid: " + sock_file)
except Exception as e: # Bad habbit
print("Connect could not be established, try again later.")
self._sock.close()
return True # run again, if enabled via timeout_add()
return True # Run again, if enabled via timeout_add()
# notify is the raw answer from the socket
# Notify is the raw answer from the socket
def _handle_notify(self, source, condition):
data = source.recv(1024)
# prepend the remaining data from last call
# Prepend the remaining data from last call
if len(self._remainder) > 0:
data = self._remainder+data
data = self._remainder + data
self._remainder = ''
if len(data) > 0:
# remember the remainder for next round
# Remember the remainder for next round
lastNL = data.rfind('\n');
if lastNL > -1 and lastNL < len(data):
self._remainder = data[lastNL+1:]
@ -119,10 +124,10 @@ class SocketConnect(GObject.GObject):
else:
return False
return True # run again
return True # Run again
def _handle_server_response(self, line):
print("Server response: "+line)
print("Server response: " + line)
parts = line.split(':')
action = parts[0]
args = parts[1:]
@ -151,32 +156,33 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def get_file_items(self, window, files):
if len(files) != 1:
return
file=files[0]
items=[]
file = files[0]
items = []
# internal or external file?!
# Internal or external file?!
syncedFile = False
for reg_path in socketConnect.registered_paths:
topLevelFolder=False
topLevelFolder = False
filename = get_local_path(file.get_uri())
#check if its a folder (ends with an /), if yes add a "/" otherwise it will not find the entry in the table
if os.path.isdir(filename+"/"):
filename=filename+"/"
#check if toplevel folder, we need to ignore those as they cannot be shared
# Check if its a folder (ends with an /), if yes add a "/"
# otherwise it will not find the entry in the table
if os.path.isdir(filename + "/"):
filename += "/"
# Check if toplevel folder, we need to ignore those as they cannot be shared
if filename.count("/") < (reg_path.count("/")+2):
topLevelFolder=True
# only show the menu extension if the file is synced and the sync
# Only show the menu extension if the file is synced and the sync
# status is ok. Not for ignored files etc.
# ignore top level folders
if filename.startswith(reg_path) and topLevelFolder == False and socketConnect.nautilusVFSFile_table[filename]['state'] == 'OK':
syncedFile = True
# if it is neither in a synced folder or is a directory
if (not syncedFile):
# If it is neither in a synced folder or is a directory
if not syncedFile:
return items
# create an menu item
labelStr = "Share with "+appname+"..."
# Create an menu item
labelStr = "Share with " + appname + "..."
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
tip='Share file %s through ownCloud' % file.get_name())
item.connect("activate", self.menu_share, file)
@ -187,8 +193,8 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def menu_share(self, menu, file):
filename = get_local_path(file.get_uri())
print("Share file "+filename)
socketConnect.sendCommand("SHARE:"+filename+"\n")
print("Share file " + filename)
socketConnect.sendCommand("SHARE:" + filename + "\n")
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
@ -205,7 +211,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
return None
def askForOverlay(self, file):
# print("Asking for overlay for "+file)
# print("Asking for overlay for "+file) # For debug only
if os.path.isdir(file):
folderStatus = socketConnect.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
@ -240,8 +246,8 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
'NOP' : appname +'_error'
}
# file = args[0]
# print "Action for " + file + ": "+args[0]
# file = args[0] # For debug only
# print("Action for " + file + ": " + args[0]) # For debug only
if action == 'STATUS':
newState = args[0]
emblem = Emblems[newState]
@ -253,7 +259,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item']
item.add_emblem(emblem)
# print "Setting emblem on " + args[1]+ "<>"+emblem+"<>"
# print("Setting emblem on " + args[1] + "<>" + emblem + "<>") # For debug only
socketConnect.nautilusVFSFile_table[args[1]] = {'item': item, 'state':newState}
elif action == 'UPDATE_VIEW':
@ -278,9 +284,9 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if filename.startswith(reg_path):
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
# item.add_string_attribute('share_state', "share state")
# item.add_string_attribute('share_state', "share state") # ?
self.askForOverlay(filename)
break
else:
# print("Not in scope:"+filename)
# print("Not in scope:" + filename) # For debug only
pass

View file

@ -60,6 +60,7 @@ struct CmdOptions {
QString exclude;
QString unsyncedfolders;
QString davPath;
int restartTimes;
};
// we can't use csync_set_userdata because the SyncEngine sets it already.
@ -157,19 +158,20 @@ void help()
std::cout << " --password, -p [pass] Use [pass] as password" << std::endl;
std::cout << " -n Use netrc (5) for login" << std::endl;
std::cout << " --non-interactive Do not block execution with interaction" << std::endl;
std::cout << " --nonshib, -ns Use Non Shibboleth WebDAV authentication" << std::endl;
std::cout << " --davpath, -dp [path] Custom themed dav path, overrides --nonshib" << std::endl;
std::cout << " --nonshib Use Non Shibboleth WebDAV authentication" << std::endl;
std::cout << " --davpath [path] Custom themed dav path, overrides --nonshib" << std::endl;
std::cout << " --max-sync-retries [n] Retries maximum n times (default to 3)" << std::endl;
std::cout << " -h Sync hidden files,do not ignore them" << std::endl;
std::cout << " --version, -v Display version and exit" << std::endl;
std::cout << "" << std::endl;
exit(1);
exit(0);
}
void showVersion() {
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
std::cout << binaryName << " version " << qPrintable(Theme::instance()->version()) << std::endl;
exit(1);
exit(0);
}
void parseOptions( const QStringList& app_args, CmdOptions *options )
@ -226,10 +228,12 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
options->exclude = it.next();
} else if( option == "--unsyncedfolders" && !it.peekNext().startsWith("-") ) {
options->unsyncedfolders = it.next();
} else if( option == "--nonshib" || option == "-ns") {
} else if( option == "--nonshib" ) {
options->nonShib = true;
} else if( (option == "--davpath" || option == "-dp") && !it.peekNext().startsWith("-") ) {
} else if( option == "--davpath" && !it.peekNext().startsWith("-") ) {
options->davPath = it.next();
} else if( option == "--max-sync-retries" && !it.peekNext().startsWith("-") ) {
options->restartTimes = it.next().toInt();
} else {
help();
}
@ -277,6 +281,7 @@ int main(int argc, char **argv) {
options.interactive = true;
options.ignoreHiddenFiles = true;
options.nonShib = false;
options.restartTimes = 3;
ClientProxy clientProxy;
parseOptions( app.arguments(), &options );
@ -374,6 +379,7 @@ int main(int argc, char **argv) {
account->setCredentials(cred);
account->setSslErrorHandler(sslErrorHandler);
int restartCount = 0;
restart_sync:
CSYNC *_csync_ctx;
@ -465,7 +471,7 @@ restart_sync:
}
SyncEngine engine(account, _csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
QObject::connect(&engine, SIGNAL(finished()), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
// Have to be done async, else, an error before exec() does not terminate the event loop.
@ -476,9 +482,13 @@ restart_sync:
csync_destroy(_csync_ctx);
if (engine.isAnotherSyncNeeded()) {
qDebug() << "Restarting Sync, because another sync is needed";
if (restartCount < options.restartTimes) {
restartCount++;
qDebug() << "Restarting Sync, because another sync is needed" << restartCount;
goto restart_sync;
}
qWarning() << "Another sync is needed, but not done because restart count is exceeded" << restartCount;
}
return 0;
}

View file

@ -58,6 +58,7 @@ set(client_SRCS
protocolwidget.cpp
selectivesyncdialog.cpp
settingsdialog.cpp
share.cpp
sharedialog.cpp
socketapi.cpp
sslbutton.cpp

View file

@ -455,7 +455,15 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
ui->quotaProgressBar->setToolTip(toolTip);
} else {
ui->quotaProgressBar->setVisible(false);
ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
if (total == 0 || total == -1) {
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
} else {
QString usedStr = Utility::octetsToString(used);
ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
}
}
}

View file

@ -427,6 +427,7 @@ void Application::showVersion()
stream << _theme->appName().toLatin1().constData()
<< QLatin1String(" version ")
<< _theme->version().toLatin1().constData() << endl;
stream << "Using Qt " << qVersion() << endl;
displayHelpText(helpText);
}

View file

@ -887,7 +887,7 @@ void Folder::startSync(const QStringList &pathList)
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
@ -959,7 +959,7 @@ void Folder::slotCsyncUnavailable()
_csyncUnavail = true;
}
void Folder::slotSyncFinished()
void Folder::slotSyncFinished(bool success)
{
qDebug() << " - client version" << qPrintable(Theme::instance()->version())
<< " Qt" << qVersion()
@ -1017,7 +1017,7 @@ void Folder::slotSyncFinished()
qDebug() << "the last" << _consecutiveFailingSyncs << "syncs failed";
}
if (_syncResult.status() == SyncResult::Success) {
if (_syncResult.status() == SyncResult::Success && success) {
// Clear the white list as all the folders that should be on that list are sync-ed
journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList());
}

View file

@ -251,7 +251,7 @@ private slots:
void slotSyncStarted();
void slotSyncError(const QString& );
void slotCsyncUnavailable();
void slotSyncFinished();
void slotSyncFinished(bool);
void slotFolderDiscovered(bool local, QString folderName);
void slotTransmissionProgress(const ProgressInfo& pi);

View file

@ -22,6 +22,7 @@
#include <QFileIconProvider>
#include <QVarLengthArray>
#include <set>
Q_DECLARE_METATYPE(QPersistentModelIndex)
@ -546,10 +547,16 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
}
auto selectiveSyncUndecidedList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
QVarLengthArray<int, 10> undecidedIndexes;
QVector<SubFolderInfo> newSubs;
std::set<QString> selectiveSyncUndecidedSet; // not QSet because it's not sorted
foreach (const QString &str, selectiveSyncUndecidedList) {
if (str.startsWith(parentInfo->_path) || parentInfo->_path == QLatin1String("/")) {
selectiveSyncUndecidedSet.insert(str);
}
}
newSubs.reserve(list.size() - 1);
for (int i = 1; // skip the parent item (first in the list)
i < list.size(); ++i) {
@ -586,11 +593,19 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
}
}
foreach(const QString &str , selectiveSyncUndecidedList) {
if (str == relativePath) {
auto it = selectiveSyncUndecidedSet.lower_bound(relativePath);
if (it != selectiveSyncUndecidedSet.end()) {
if (*it == relativePath) {
newInfo._isUndecided = true;
} else if (str.startsWith(relativePath)) {
selectiveSyncUndecidedSet.erase(it);
} else if ((*it).startsWith(relativePath)) {
undecidedIndexes.append(newInfo._pathIdx.last());
// Remove all the items from the selectiveSyncUndecidedSet that starts with this path
QString relativePathNext = relativePath;
relativePathNext[relativePathNext.length()-1].unicode()++;
auto it2 = selectiveSyncUndecidedSet.lower_bound(relativePathNext);
selectiveSyncUndecidedSet.erase(it, it2);
}
}
newSubs.append(newInfo);
@ -603,6 +618,16 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
for (auto it = undecidedIndexes.begin(); it != undecidedIndexes.end(); ++it) {
suggestExpand(idx.child(*it, 0));
}
/* Try to remove the the undecided lists the items that are not on the server. */
auto it = std::remove_if(selectiveSyncUndecidedList.begin(), selectiveSyncUndecidedList.end(),
[&](const QString &s) { return selectiveSyncUndecidedSet.count(s); } );
if (it != selectiveSyncUndecidedList.end()) {
selectiveSyncUndecidedList.erase(it, selectiveSyncUndecidedList.end());
parentInfo->_folder->journalDb()->setSelectiveSyncList(
SyncJournalDb::SelectiveSyncUndecidedList, selectiveSyncUndecidedList);
emit dirtyChanged();
}
}
void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)

View file

@ -42,9 +42,9 @@ void OcsJob::addPassStatusCode(int code)
_passStatusCodes.append(code);
}
void OcsJob::appendPath(int id)
void OcsJob::appendPath(const QString &id)
{
setPath(path() + QString("/%1").arg(id));
setPath(path() + QLatin1Char('/') + id);
}
void OcsJob::start()
@ -105,9 +105,11 @@ bool OcsJob::finished()
<< Account::concatUrlPath(account()->url(), path())
<< _params
<< "has unexpected status code:" << statusCode << replyData;
}
emit ocsError(statusCode, message);
} else {
emit jobFinished(json);
}
deleteLater();
return true;
}

View file

@ -79,7 +79,7 @@ protected:
*
* This function appends the common id. so <PATH>/<ID>
*/
void appendPath(int id);
void appendPath(const QString &id);
public:
/**
@ -108,6 +108,15 @@ signals:
*/
void jobFinished(QVariantMap reply);
/**
* The status code was not one of the expected (passing)
* status code for this command
*
* @param statusCode The actual status code
* @param message The message provided by the server
*/
void ocsError(int statusCode, const QString &message);
private slots:
virtual bool finished() Q_DECL_OVERRIDE;

View file

@ -24,6 +24,7 @@ OcsShareJob::OcsShareJob(AccountPtr account, QObject* parent)
: OcsJob(account, parent)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/shares");
connect(this, SIGNAL(jobFinished(QVariantMap)), this, SLOT(jobDone(QVariantMap)));
}
void OcsShareJob::getShares(const QString &path)
@ -36,7 +37,7 @@ void OcsShareJob::getShares(const QString &path)
start();
}
void OcsShareJob::deleteShare(int shareId)
void OcsShareJob::deleteShare(const QString &shareId)
{
appendPath(shareId);
setVerb("DELETE");
@ -44,7 +45,7 @@ void OcsShareJob::deleteShare(int shareId)
start();
}
void OcsShareJob::setExpireDate(int shareId, const QDate &date)
void OcsShareJob::setExpireDate(const QString &shareId, const QDate &date)
{
appendPath(shareId);
setVerb("PUT");
@ -54,32 +55,35 @@ void OcsShareJob::setExpireDate(int shareId, const QDate &date)
} else {
addParam(QString::fromLatin1("expireDate"), QString());
}
_value = date;
start();
}
void OcsShareJob::setPassword(int shareId, const QString &password)
void OcsShareJob::setPassword(const QString &shareId, const QString &password)
{
appendPath(shareId);
setVerb("PUT");
addParam(QString::fromLatin1("password"), password);
_value = password;
start();
}
void OcsShareJob::setPublicUpload(int shareId, bool publicUpload)
void OcsShareJob::setPublicUpload(const QString &shareId, bool publicUpload)
{
appendPath(shareId);
setVerb("PUT");
const QString value = QString::fromLatin1(publicUpload ? "true" : "false");
addParam(QString::fromLatin1("publicUpload"), value);
_value = publicUpload;
start();
}
void OcsShareJob::createShare(const QString &path, ShareType shareType, const QString &password, const QDate &date)
void OcsShareJob::createShare(const QString &path, Share::ShareType shareType, const QString &password, const QDate &date)
{
setVerb("POST");
@ -87,7 +91,7 @@ void OcsShareJob::createShare(const QString &path, ShareType shareType, const QS
addParam(QString::fromLatin1("shareType"), QString::number(static_cast<int>(shareType)));
if (!password.isEmpty()) {
addParam(QString::fromLatin1("shareType"), password);
addParam(QString::fromLatin1("password"), password);
}
if (date.isValid()) {
@ -99,4 +103,9 @@ void OcsShareJob::createShare(const QString &path, ShareType shareType, const QS
start();
}
void OcsShareJob::jobDone(QVariantMap reply)
{
emit shareJobFinished(reply, _value);
}
}

View file

@ -15,6 +15,7 @@
#define OCSSHAREJOB_H
#include "ocsjob.h"
#include "share.h"
#include <QVector>
#include <QList>
#include <QPair>
@ -32,25 +33,6 @@ class OcsShareJob : public OcsJob {
Q_OBJECT
public:
/**
* Support sharetypes
*/
enum class ShareType : int {
Link = 3
};
/**
* Possible permissions
*/
enum class Permission : int {
Read = 1,
Update = 2,
Create = 4,
Delete = 8,
Share = 16,
All = 31
};
/**
* Constructor for new shares or listing of shares
*/
@ -66,7 +48,7 @@ public:
/**
* Delete the current Share
*/
void deleteShare(int shareId);
void deleteShare(const QString &shareId);
/**
* Set the expiration date of a share
@ -74,7 +56,7 @@ public:
* @param date The expire date, if this date is invalid the expire date
* will be removed
*/
void setExpireDate(int shareId, const QDate& date);
void setExpireDate(const QString &shareId, const QDate& date);
/**
* Set the password of a share
@ -82,14 +64,14 @@ public:
* @param password The password of the share, if the password is empty the
* share will be removed
*/
void setPassword(int shareId, const QString& password);
void setPassword(const QString &shareId, const QString& password);
/**
* Void set the share to be public upload
*
* @param publicUpload Set or remove public upload
*/
void setPublicUpload(int shareId, bool publicUpload);
void setPublicUpload(const QString &shareId, bool publicUpload);
/**
* Create a new share
@ -99,7 +81,25 @@ public:
* @param password Optionally a password for the share
* @param date Optionally an expire date for the share
*/
void createShare(const QString& path, ShareType shareType, const QString& password = "", const QDate& date = QDate());
void createShare(const QString& path, Share::ShareType shareType, const QString& password = "", const QDate& date = QDate());
signals:
/**
* Result of the OCS request
* The value parameter is only set if this was a put request.
* e.g. if we set the password to 'foo' the QVariant will hold a QString with 'foo'.
* This is needed so we can update the share objects properly
*
* @param reply The reply
* @param value To what did we set a variable (if we set any).
*/
void shareJobFinished(QVariantMap reply, QVariant value);
private slots:
void jobDone(QVariantMap reply);
private:
QVariant _value;
};
}

View file

@ -102,9 +102,10 @@ void QuotaInfo::slotUpdateLastQuota(const QVariantMap &result)
{
// The server can return fractional bytes (#1374)
// <d:quota-available-bytes>1374532061.2</d:quota-available-bytes>
quint64 avail = result["quota-available-bytes"].toDouble();
qint64 avail = result["quota-available-bytes"].toDouble();
_lastQuotaUsedBytes = result["quota-used-bytes"].toDouble();
_lastQuotaTotalBytes = _lastQuotaUsedBytes + avail;
// negative value of the available quota have special meaning (#3940)
_lastQuotaTotalBytes = avail >= 0 ? _lastQuotaUsedBytes + avail : avail;
emit quotaUpdated(_lastQuotaTotalBytes, _lastQuotaUsedBytes);
_jobRestartTimer.start(defaultIntervalT);
_lastQuotaRecieved = QDateTime::currentDateTime();

265
src/gui/share.cpp Normal file
View file

@ -0,0 +1,265 @@
/*
* Copyright (C) by Roeland Jago Douma <rullzer@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "share.h"
#include "ocssharejob.h"
#include "account.h"
#include <QUrl>
namespace OCC {
Share::Share(AccountPtr account,
const QString& id,
const QString& path,
ShareType shareType,
Permissions permissions)
: _account(account),
_id(id),
_path(path),
_shareType(shareType),
_permissions(permissions)
{
}
QString Share::getId() const
{
return _id;
}
Share::ShareType Share::getShareType() const
{
return _shareType;
}
Share::Permissions Share::getPermissions() const
{
return _permissions;
}
void Share::deleteShare()
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(shareJobFinished(QVariantMap, QVariant)), SLOT(slotDeleted()));
connect(job, SIGNAL(ocsError(int, const QString &)), SLOT(slotOcsError(int, const QString &)));
job->deleteShare(getId());
}
void Share::slotDeleted()
{
emit shareDeleted();
}
void Share::slotOcsError(int statusCode, const QString &message)
{
emit serverError(statusCode, message);
}
QUrl LinkShare::getLink() const
{
return _url;
}
QDate LinkShare::getExpireDate() const
{
return _expireDate;
}
bool LinkShare::isPasswordSet() const
{
return _passwordSet;
}
LinkShare::LinkShare(AccountPtr account,
const QString& id,
const QString& path,
Permissions permissions,
bool passwordSet,
const QUrl& url,
const QDate& expireDate)
: Share(account, id, path, Share::TypeLink, permissions),
_passwordSet(passwordSet),
_expireDate(expireDate),
_url(url)
{
}
bool LinkShare::getPublicUpload()
{
return ((_permissions & PermissionUpdate) &&
(_permissions & PermissionCreate));
}
void LinkShare::setPublicUpload(bool publicUpload)
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(shareJobFinished(QVariantMap, QVariant)), SLOT(slotPublicUploadSet(QVariantMap, QVariant)));
connect(job, SIGNAL(ocsError(int, QString)), SLOT(slotOcsError(int, QString)));
job->setPublicUpload(getId(), publicUpload);
}
void LinkShare::slotPublicUploadSet(const QVariantMap&, const QVariant &value)
{
//TODO FIX permission with names
if (value.toBool()) {
_permissions = PermissionRead | PermissionUpdate | PermissionCreate;
} else {
_permissions = PermissionRead;
}
emit publicUploadSet();
}
void LinkShare::setPassword(const QString &password)
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(shareJobFinished(QVariantMap, QVariant)), SLOT(slotPasswordSet(QVariantMap, QVariant)));
connect(job, SIGNAL(ocsError(int, QString)), SLOT(slotOcsError(int, QString)));
job->setPassword(getId(), password);
}
void LinkShare::slotPasswordSet(const QVariantMap&, const QVariant &value)
{
_passwordSet = value.toString() == "";
emit passwordSet();
}
void LinkShare::setExpireDate(const QDate &date)
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(shareJobFinished(QVariantMap, QVariant)), SLOT(slotExpireDateSet(QVariantMap, QVariant)));
connect(job, SIGNAL(ocsError(int, QString)), SLOT(slotOcsError(int, QString)));
job->setExpireDate(getId(), date);
}
void LinkShare::slotExpireDateSet(const QVariantMap&, const QVariant &value)
{
_expireDate = value.toDate();
emit expireDateSet();
}
ShareManager::ShareManager(AccountPtr account, QObject *parent)
: QObject(parent),
_account(account)
{
}
void ShareManager::createLinkShare(const QString &path,
const QString &password)
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(shareJobFinished(QVariantMap, QVariant)), SLOT(slotLinkShareCreated(QVariantMap)));
connect(job, SIGNAL(ocsError(int, QString)), SLOT(slotOcsError(int, QString)));
job->createShare(path, Share::TypeLink, password);
}
void ShareManager::slotLinkShareCreated(const QVariantMap &reply)
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
/*
* Before we had decent sharing capabilities on the server a 403 "generally"
* meant that a share was password protected
*/
if (code == 403) {
emit linkShareRequiresPassword();
return;
}
//Parse share
auto data = reply.value("ocs").toMap().value("data").toMap();
QSharedPointer<LinkShare> share(parseLinkShare(data));
emit linkShareCreated(share);
}
void ShareManager::fetchShares(const QString &path)
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(shareJobFinished(QVariantMap, QVariant)), SLOT(slotSharesFetched(QVariantMap)));
connect(job, SIGNAL(ocsError(int, QString)), SLOT(slotOcsError(int, QString)));
job->getShares(path);
}
void ShareManager::slotSharesFetched(const QVariantMap &reply)
{
auto tmpShares = reply.value("ocs").toMap().value("data").toList();
const QString versionString = _account->serverVersion();
qDebug() << Q_FUNC_INFO << versionString << "Fetched" << tmpShares.count() << "shares";
QList<QSharedPointer<Share>> shares;
foreach(const auto &share, tmpShares) {
auto data = share.toMap();
auto shareType = data.value("share_type").toInt();
QSharedPointer<Share> newShare;
if (shareType == Share::TypeLink) {
newShare = parseLinkShare(data);
} else {
newShare = QSharedPointer<Share>(new Share(_account,
data.value("id").toString(),
data.value("path").toString(),
(Share::ShareType)shareType,
(Share::Permissions)data.value("permissions").toInt()));
}
shares.append(QSharedPointer<Share>(newShare));
}
qDebug() << Q_FUNC_INFO << "Sending " << shares.count() << "shares";
emit sharesFetched(shares);
}
QSharedPointer<LinkShare> ShareManager::parseLinkShare(const QVariantMap &data) {
QUrl url;
// From ownCloud server 8.2 the url field is always set for public shares
if (data.contains("url")) {
url = QUrl(data.value("url").toString());
} else if (_account->serverVersionInt() >= (8 << 16)) {
// From ownCloud server version 8 on, a different share link scheme is used.
url = QUrl(Account::concatUrlPath(_account->url(), QString("index.php/s/%1").arg(data.value("token").toString())).toString());
} else {
QList<QPair<QString, QString>> queryArgs;
queryArgs.append(qMakePair(QString("service"), QString("files")));
queryArgs.append(qMakePair(QString("t"), data.value("token").toString()));
url = QUrl(Account::concatUrlPath(_account->url(), QLatin1String("public.php"), queryArgs).toString());
}
QDate expireDate;
if (data.value("expiration").isValid()) {
expireDate = QDate::fromString(data.value("expiration").toString(), "yyyy-MM-dd 00:00:00");
}
return QSharedPointer<LinkShare>(new LinkShare(_account,
data.value("id").toString(),
data.value("path").toString(),
(Share::Permissions)data.value("permissions").toInt(),
data.value("share_with").isValid(),
url,
expireDate));
}
void ShareManager::slotOcsError(int statusCode, const QString &message)
{
emit serverError(statusCode, message);
}
}

248
src/gui/share.h Normal file
View file

@ -0,0 +1,248 @@
/*
* Copyright (C) by Roeland Jago Douma <rullzer@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef SHARE_H
#define SHARE_H
#include "accountfwd.h"
#include <QObject>
#include <QDate>
#include <QString>
#include <QList>
#include <QSharedPointer>
#include <QUrl>
namespace OCC {
class Share : public QObject {
Q_OBJECT
public:
/**
* Possible share types
*/
enum ShareType {
TypeUser = 0,
TypeGroup = 1,
TypeLink = 3,
TypeRemote = 6,
};
Q_DECLARE_FLAGS(ShareTypes, ShareType)
/**
* Possible permissions
*/
enum Permission {
PermissionRead = 1,
PermissionUpdate = 2,
PermissionCreate = 4,
PermissionDelete = 8,
PermissionShare = 16
};
Q_DECLARE_FLAGS(Permissions, Permission)
/*
* Constructor for shares
*/
explicit Share(AccountPtr account,
const QString& id,
const QString& path,
ShareType shareType,
Permissions permissions);
/*
* Get the id
*/
QString getId() const;
/*
* Get the shareType
*/
ShareType getShareType() const;
/*
* Get permissions
*/
Permissions getPermissions() const;
/*
* Set the permissions of a share
*
* On success the permissionsSet signal is emitted
* In case of a server error the serverError signal is emitted.
*/
void setPermissions(int permissions);
/**
* Deletes a share
*
* On success the shareDeleted signal is emitted
* In case of a server error the serverError signal is emitted.
*/
void deleteShare();
signals:
void permissionsSet();
void shareDeleted();
void serverError(int code, const QString &message);
protected:
AccountPtr _account;
QString _id;
QString _path;
ShareType _shareType;
Permissions _permissions;
protected slots:
void slotOcsError(int statusCode, const QString &message);
private slots:
void slotDeleted();
};
/**
* A Link share is just like a regular share but then slightly different.
* There are several methods in the API that either work differently for
* link shares or are only available to link shares.
*/
class LinkShare : public Share {
Q_OBJECT
public:
explicit LinkShare(AccountPtr account,
const QString& id,
const QString& path,
Permissions permissions,
bool passwordSet,
const QUrl& url,
const QDate& expireDate);
/*
* Get the share link
*/
QUrl getLink() const;
/*
* Get the publicUpload status of this share
*/
bool getPublicUpload();
/*
* Set a share to be public upload
* This function can only be called on link shares
*
* On success the publicUploadSet signal is emitted
* In case of a server error the serverError signal is emitted.
*/
void setPublicUpload(bool publicUpload);
/*
* Set the password
*
* On success the passwordSet signal is emitted
* In case of a server error the serverError signal is emitted.
*/
void setPassword(const QString& password);
/*
* Is the password set?
*/
bool isPasswordSet() const;
/*
* Get the expiration date
*/
QDate getExpireDate() const;
/*
* Set the expiration date
*
* On success the expireDateSet signal is emitted
* In case of a server error the serverError signal is emitted.
*/
void setExpireDate(const QDate& expireDate);
signals:
void expireDateSet();
void publicUploadSet();
void passwordSet();
private slots:
void slotPasswordSet(const QVariantMap&, const QVariant &value);
void slotPublicUploadSet(const QVariantMap&, const QVariant &value);
void slotExpireDateSet(const QVariantMap&, const QVariant &value);
private:
bool _passwordSet;
QDate _expireDate;
QUrl _url;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Share::Permissions)
/**
* The share manager allows for creating, retrieving and deletion
* of shares. It abstracts away from the OCS Share API, all the usages
* shares should talk to this manager and not use OCS Share Job directly
*/
class ShareManager : public QObject {
Q_OBJECT
public:
explicit ShareManager(AccountPtr _account, QObject *parent = NULL);
/**
* Tell the manager to create a link share
*
* @param path The path of the linkshare relative to the user folder on the server
* @param password The password of the share
*
* On success the signal linkShareCreated is emitted
* For older server the linkShareRequiresPassword signal is emitted when it seems appropiate
* In case of a server error the serverError signal is emitted
*/
void createLinkShare(const QString& path,
const QString& password="");
/**
* Fetch all the shares for path
*
* @param path The path to get the shares for relative to the users folder on the server
*
* On success the sharesFetched signal is emitted
* In case of a server error the serverError signal is emitted
*/
void fetchShares(const QString& path);
signals:
void linkShareCreated(const QSharedPointer<LinkShare> &share);
void linkShareRequiresPassword();
void sharesFetched(const QList<QSharedPointer<Share>> &shares);
void serverError(int code, const QString &message);
private slots:
void slotSharesFetched(const QVariantMap &reply);
void slotLinkShareCreated(const QVariantMap &reply);
void slotOcsError(int statusCode, const QString &message);
private:
QSharedPointer<LinkShare> parseLinkShare(const QVariantMap &data);
AccountPtr _account;
};
}
#endif // SHARE_H

View file

@ -23,8 +23,8 @@
#include "configfile.h"
#include "capabilities.h"
#include "ocssharejob.h"
#include "thumbnailjob.h"
#include "share.h"
#include "QProgressIndicator.h"
#include <QBuffer>
@ -41,7 +41,8 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
_sharePath(sharePath),
_localPath(localPath),
_passwordJobRunning(false),
_public_share_id(0),
_manager(NULL),
_share(NULL),
_resharingAllowed(resharingAllowed)
{
setAttribute(Qt::WA_DeleteOnClose);
@ -90,7 +91,6 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
_ui->lineEdit_password->hide();
_ui->pushButton_setPassword->hide();
_ui->calendar->setDate(QDate::currentDate().addDays(1));
_ui->calendar->setEnabled(false);
QFileInfo f_info(_localPath);
@ -168,6 +168,16 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
_ui->checkBox_editing->setEnabled(false);
}
}
/*
* Create the share manager and connect it properly
*/
_manager = new ShareManager(_account, this);
connect(_manager, SIGNAL(sharesFetched(QList<QSharedPointer<Share>>)), SLOT(slotSharesFetched(QList<QSharedPointer<Share>>)));
connect(_manager, SIGNAL(linkShareCreated(QSharedPointer<LinkShare>)), SLOT(slotCreateShareFetched(const QSharedPointer<LinkShare>)));
connect(_manager, SIGNAL(linkShareRequiresPassword()), SLOT(slotCreateShareRequiresPassword()));
connect(_manager, SIGNAL(serverError(int, QString)), SLOT(displayError(int, QString)));
}
void ShareDialog::done( int r ) {
@ -178,25 +188,12 @@ void ShareDialog::done( int r ) {
void ShareDialog::setExpireDate(const QDate &date)
{
if( _public_share_id == 0 ) {
// no public share so far.
return;
}
_pi_date->startAnimation();
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotExpireSet(QVariantMap)));
job->setExpireDate(_public_share_id, date);
_share->setExpireDate(date);
}
void ShareDialog::slotExpireSet(const QVariantMap &reply)
void ShareDialog::slotExpireSet()
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
if (code != 100) {
displayError(code);
}
_pi_date->stopAnimation();
}
@ -226,40 +223,22 @@ void ShareDialog::slotPasswordChanged(const QString& newText)
void ShareDialog::setPassword(const QString &password)
{
if( _passwordJobRunning ) {
// This happens because the entry field and the button both trigger this slot.
return;
}
_pi_link->startAnimation();
_pi_password->startAnimation();
QString path;
if( _public_share_id > 0 ) {
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPasswordSet(QVariantMap)));
job->setPassword(_public_share_id, password);
_ui->checkBox_password->setEnabled(false);
_ui->lineEdit_password->setEnabled(false);
if( !_share.isNull() ) {
_share->setPassword(password);
} else {
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPasswordSet(QVariantMap)));
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotCreateShareFetched(QVariantMap)));
QDate date;
if( _ui->checkBox_expire->isChecked() ) {
date = _ui->calendar->date();
_ui->checkBox_shareLink->setEnabled(false);
_manager->createLinkShare(_sharePath, password);
}
job->createShare(_sharePath, OcsShareJob::ShareType::Link, password, date);
}
_passwordJobRunning = true;
}
void ShareDialog::slotPasswordSet(const QVariantMap &reply)
void ShareDialog::slotPasswordSet()
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
if (code != 100) {
displayError(code);
}
/*
* When setting/deleting a password from a share the old share is
* deleted and a new one is created. So we need to refetch the shares
@ -267,15 +246,12 @@ void ShareDialog::slotPasswordSet(const QVariantMap &reply)
*/
getShares();
_passwordJobRunning = false;
_pi_password->stopAnimation();
}
void ShareDialog::getShares()
{
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotSharesFetched(QVariantMap)));
job->getShares(_sharePath);
_manager->fetchShares(_sharePath);
if (QFileInfo(_localPath).isFile()) {
ThumbnailJob *job2 = new ThumbnailJob(_sharePath, _account, this);
@ -284,34 +260,27 @@ void ShareDialog::getShares()
}
}
void ShareDialog::slotSharesFetched(const QVariantMap &reply)
void ShareDialog::slotSharesFetched(const QList<QSharedPointer<Share>> &shares)
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
if (code != 100 && code != 404) {
displayError(code);
}
ShareDialog::_shares = reply.value("ocs").toMap().value("data").toList();
const QString versionString = _account->serverVersion();
qDebug() << Q_FUNC_INFO << versionString << "Fetched" << ShareDialog::_shares.count() << "shares";
qDebug() << Q_FUNC_INFO << versionString << "Fetched" << shares.count() << "shares";
//Show link checkbox now
_ui->checkBox_shareLink->setEnabled(true);
_pi_link->stopAnimation();
Q_FOREACH(auto share, ShareDialog::_shares) {
QVariantMap data = share.toMap();
Q_FOREACH(auto share, shares) {
if (data.value("share_type").toInt() == static_cast<int>(OcsShareJob::ShareType::Link)) {
_public_share_id = data.value("id").toULongLong();
if (share->getShareType() == Share::TypeLink) {
_share = qSharedPointerDynamicCast<LinkShare>(share);
_ui->pushButton_copy->show();
_ui->widget_shareLink->show();
_ui->checkBox_shareLink->setChecked(true);
if (data.value("share_with").isValid()) {
_ui->checkBox_password->setEnabled(true);
if (_share->isPasswordSet()) {
_ui->lineEdit_password->setEnabled(true);
_ui->checkBox_password->setChecked(true);
_ui->lineEdit_password->setPlaceholderText("********");
_ui->lineEdit_password->show();
@ -323,8 +292,9 @@ void ShareDialog::slotSharesFetched(const QVariantMap &reply)
_ui->pushButton_setPassword->hide();
}
if (data.value("expiration").isValid()) {
_ui->calendar->setDate(QDate::fromString(data.value("expiration").toString(), "yyyy-MM-dd 00:00:00"));
_ui->checkBox_expire->setEnabled(true);
if (_share->getExpireDate().isValid()) {
_ui->calendar->setDate(_share->getExpireDate());
_ui->calendar->setMinimumDate(QDate::currentDate().addDays(1));
_ui->calendar->setEnabled(true);
_ui->checkBox_expire->setChecked(true);
@ -333,38 +303,33 @@ void ShareDialog::slotSharesFetched(const QVariantMap &reply)
_ui->checkBox_expire->setChecked(false);
}
if (data.value("permissions").isValid()) {
int permissions = data.value("permissions").toInt();
/*
* Only directories can have public upload set
* For public links the server sets CREATE and UPDATE permissions.
*/
if (!_isFile &&
(permissions & static_cast<int>(OcsShareJob::Permission::Update)) &&
(permissions & static_cast<int>(OcsShareJob::Permission::Create))) {
if (!_isFile) {
_ui->checkBox_editing->setEnabled(true);
if (_share->getPublicUpload()) {
_ui->checkBox_editing->setChecked(true);
}
}
QString url;
// From ownCloud server 8.2 the url field is always set for public shares
if (data.contains("url")) {
url = data.value("url").toString();
} else if (versionString.contains('.') && versionString.split('.')[0].toInt() >= 8) {
// From ownCloud server version 8 on, a different share link scheme is used.
url = Account::concatUrlPath(_account->url(), QString("index.php/s/%1").arg(data.value("token").toString())).toString();
} else {
QList<QPair<QString, QString>> queryArgs;
queryArgs.append(qMakePair(QString("service"), QString("files")));
queryArgs.append(qMakePair(QString("t"), data.value("token").toString()));
url = Account::concatUrlPath(_account->url(), QLatin1String("public.php"), queryArgs).toString();
_ui->checkBox_editing->setChecked(false);
}
}
setShareLink(url);
setShareLink(_share->getLink().toString());
_ui->pushButton_copy->setEnabled(true);
// Connect all shares signals to gui slots
connect(_share.data(), SIGNAL(expireDateSet()), SLOT(slotExpireSet()));
connect(_share.data(), SIGNAL(publicUploadSet()), SLOT(slotPublicUploadSet()));
connect(_share.data(), SIGNAL(passwordSet()), SLOT(slotPasswordSet()));
connect(_share.data(), SIGNAL(shareDeleted()), SLOT(slotDeleteShareFetched()));
connect(_share.data(), SIGNAL(serverError(int, QString)), SLOT(displayError(int, QString)));
break;
}
}
if( _shares.count()>0 ) {
if( !_share.isNull() ) {
setShareCheckBoxTitle(true);
} else {
// If there are no shares yet, check the checkbox to create a link automatically.
@ -416,15 +381,9 @@ void ShareDialog::setShareLink( const QString& url )
}
void ShareDialog::slotDeleteShareFetched(const QVariantMap &reply)
void ShareDialog::slotDeleteShareFetched()
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
if (code != 100) {
displayError(code);
}
_public_share_id = 0;
_share.clear();
_pi_link->stopAnimation();
_ui->lineEdit_password->clear();
_ui->_labelShareLink->clear();
@ -440,7 +399,6 @@ void ShareDialog::slotDeleteShareFetched(const QVariantMap &reply)
_shareUrl.clear();
setShareCheckBoxTitle(false);
}
void ShareDialog::slotCheckBoxShareLinkClicked()
@ -458,6 +416,8 @@ void ShareDialog::slotCheckBoxShareLinkClicked()
_ui->checkBox_password->setChecked(true);
_ui->checkBox_password->setEnabled(false);
_ui->checkBox_password->setText(tr("Public sh&aring requires a password"));
_ui->checkBox_expire->setEnabled(false);
_ui->checkBox_editing->setEnabled(false);
_ui->lineEdit_password->setFocus();
_ui->pushButton_copy->hide();
_ui->widget_shareLink->show();
@ -466,24 +426,34 @@ void ShareDialog::slotCheckBoxShareLinkClicked()
return;
}
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotCreateShareFetched(QVariantMap)));
job->createShare(_sharePath, OcsShareJob::ShareType::Link);
_ui->checkBox_shareLink->setEnabled(false);
_manager->createLinkShare(_sharePath);
} else {
if (!_share.isNull()) {
// We have a share so delete it
_pi_link->startAnimation();
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotDeleteShareFetched(QVariantMap)));
job->deleteShare(_public_share_id);
_share->deleteShare();
} else {
// No share object so we are deleting while a password is required
_ui->widget_shareLink->hide();
}
}
}
void ShareDialog::slotCreateShareFetched(const QVariantMap &reply)
void ShareDialog::slotCreateShareFetched(const QSharedPointer<LinkShare> share)
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
_pi_link->stopAnimation();
_pi_password->stopAnimation();
if (code == 403) {
_share = share;
getShares();
}
void ShareDialog::slotCreateShareRequiresPassword()
{
// there needs to be a password
_ui->checkBox_password->setChecked(true);
_ui->checkBox_password->setEnabled(false);
@ -491,17 +461,10 @@ void ShareDialog::slotCreateShareFetched(const QVariantMap &reply)
_ui->lineEdit_password->setFocus();
_ui->pushButton_copy->hide();
_ui->widget_shareLink->show();
_ui->checkBox_expire->setEnabled(false);
_ui->checkBox_editing->setEnabled(false);
slotCheckBoxPasswordClicked();
return;
} else if (code != 100) {
displayError(code);
return;
}
_public_share_id = reply.value("ocs").toMap().values("data")[0].toMap().value("id").toULongLong();
_ui->pushButton_copy->show();
getShares();
}
void ShareDialog::slotCheckBoxPasswordClicked()
@ -512,7 +475,7 @@ void ShareDialog::slotCheckBoxPasswordClicked()
_ui->lineEdit_password->setPlaceholderText(tr("Please Set Password"));
_ui->lineEdit_password->setFocus();
} else {
ShareDialog::setPassword(QString());
setPassword(QString());
_ui->lineEdit_password->setPlaceholderText(QString());
_pi_password->startAnimation();
_ui->lineEdit_password->hide();
@ -532,7 +495,7 @@ void ShareDialog::slotCheckBoxExpireClicked()
}
else
{
ShareDialog::setExpireDate(QDate());
setExpireDate(QDate());
_ui->calendar->setEnabled(false);
}
}
@ -561,23 +524,13 @@ void ShareDialog::setPublicUpload(bool publicUpload)
_ui->checkBox_editing->setEnabled(false);
_pi_editing->startAnimation();
OcsShareJob *job = new OcsShareJob(_account, this);
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPublicUploadSet(QVariantMap)));
job->setPublicUpload(_public_share_id, publicUpload);
_share->setPublicUpload(publicUpload);
}
void ShareDialog::slotPublicUploadSet(const QVariantMap &reply)
void ShareDialog::slotPublicUploadSet()
{
QString message;
int code = OcsShareJob::getJsonReturnCode(reply, message);
if (code == 100) {
_ui->checkBox_editing->setEnabled(true);
} else {
qDebug() << Q_FUNC_INFO << reply;
displayError(code);
}
_pi_editing->stopAnimation();
_ui->checkBox_editing->setEnabled(true);
}
void ShareDialog::setShareCheckBoxTitle(bool haveShares)
@ -593,6 +546,13 @@ void ShareDialog::setShareCheckBoxTitle(bool haveShares)
}
void ShareDialog::displayError(int code, const QString &message)
{
const QString arg = QString("%1, %2").arg(code).arg(message);
const QString errMsg = tr("OCS API error code: %1").arg(arg);
displayError(errMsg);
}
void ShareDialog::displayError(const QString& errMsg)
{
_ui->errorLabel->setText( errMsg );
@ -605,120 +565,6 @@ void ShareDialog::displayError(int code)
displayError(errMsg);
}
#if 0
void ShareDialog::displayInfo( const QString& msg )
{
_ui->label_sharePath->setText(msg);
}
/*
* This code is disabled for now as we do not have answers for all the questions involved
* here, see https://github.com/owncloud/client/issues/2732
*/
bool ShareDialog::uploadExternalFile()
{
bool re = false;
const QString folderName = QString("ownCloud"); // FIXME: get a proper folder name
Folder *folder = 0;
Folder::Map folders = FolderMan::instance()->map();
if( folders.isEmpty() ) {
displayInfo(tr("There is no sync folder configured."));
return false;
}
if( folders.contains( Theme::instance()->appNameGUI()) ) {
folder = folders.value(Theme::instance()->appNameGUI());
}
if( !folder ) {
folder = folders.value( folders.keys().at(0));
}
FolderMan::instance()->folder(folderName);
if( ! folder ) {
qDebug() << "Folder not defined: " << folderName;
displayInfo(tr("Cannot find a folder to upload to."));
return false;
}
QFileInfo fi(_localPath);
if( fi.isDir() ) {
// we can not do this for directories yet.
displayInfo(tr("Sharing of external directories is not yet working."));
return false;
}
_sharePath = folder->remotePath()+QLatin1Char('/')+fi.fileName();
_folderAlias = folderName;
// connect the finish signal of the folder before the file to upload
// is copied to the sync folder.
connect( folder, SIGNAL(syncFinished(SyncResult)), this, SLOT(slotNextSyncFinished(SyncResult)) );
// copy the file
_expectedSyncFile = folder->path()+fi.fileName();
QFileInfo target(_expectedSyncFile);
if( target.exists() ) {
_ui->label_sharePath->setText(tr("A sync file with the same name exists. "
"The file cannot be registered to sync."));
// TODO: Add a file comparison here. If the existing file is still the same
// as the file-to-copy we can share it.
_sharePath.clear();
} else {
_uploadFails = 0;
_ui->pi_share->startAnimation();
QFile file( _localPath);
if( file.copy(_expectedSyncFile) ) {
// copying succeeded.
re = true;
displayInfo(tr("Waiting to upload..."));
} else {
displayInfo(tr("Unable to register in sync space."));
}
}
return re;
}
void ShareDialog::slotNextSyncFinished( const SyncResult& result )
{
// FIXME: Check for state!
SyncFileItemVector itemVector = result.syncFileItemVector();
SyncFileItem targetItem;
Folder *folder = FolderMan::instance()->folder(_folderAlias);
const QString folderPath = folder->path();
_ui->pi_share->stopAnimation();
foreach( SyncFileItem item, itemVector ) {
const QString fullSyncedFile = folderPath + item._file;
if( item._direction == SyncFileItem::Up &&
fullSyncedFile == _expectedSyncFile) {
// found the item!
targetItem = item;
continue;
}
}
if( targetItem.isEmpty() ) {
// The item was not in this sync run. Lets wait for the next one. FIXME
_uploadFails ++;
if( _uploadFails > 2 ) {
// stop the upload job
displayInfo(tr("The file cannot be synced."));
}
} else {
// it's there and the sync was successful.
// The server should be able to generate a share link now.
// Enable the sharing link
if( targetItem._status == SyncFileItem::Success ) {
_ui->checkBox_shareLink->setEnabled(true);
_ui->label_sharePath->setText(tr("%1 path: %2").arg(Theme::instance()->appNameGUI()).arg(_sharePath));
} else {
displayInfo(tr("Sync of registered file was not successful yet."));
}
}
_expectedSyncFile.clear();
}
#endif
void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &reply)
{
if (statusCode != 200) {

View file

@ -19,6 +19,8 @@
#include "QProgressIndicator.h"
#include <QDialog>
#include <QVariantMap>
#include <QSharedPointer>
#include <QList>
namespace OCC {
@ -29,6 +31,9 @@ class ShareDialog;
class AbstractCredentials;
class QuotaInfo;
class SyncResult;
class LinkShare;
class Share;
class ShareManager;
/**
* @brief The ShareDialog class
@ -45,11 +50,12 @@ public:
void getShares();
private slots:
void slotSharesFetched(const QVariantMap &reply);
void slotCreateShareFetched(const QVariantMap &reply);
void slotDeleteShareFetched(const QVariantMap &reply);
void slotPasswordSet(const QVariantMap &reply);
void slotExpireSet(const QVariantMap &reply);
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
void slotCreateShareFetched(const QSharedPointer<LinkShare> share);
void slotCreateShareRequiresPassword();
void slotDeleteShareFetched();
void slotPasswordSet();
void slotExpireSet();
void slotCalendarClicked(const QDate &date);
void slotCheckBoxShareLinkClicked();
void slotCheckBoxPasswordClicked();
@ -59,7 +65,9 @@ private slots:
void slotPushButtonCopyLinkPressed();
void slotThumbnailFetched(const int &statusCode, const QByteArray &reply);
void slotCheckBoxEditingClicked();
void slotPublicUploadSet(const QVariantMap &reply);
void slotPublicUploadSet();
void displayError(int code, const QString &message);
void done( int r );
private:
@ -83,8 +91,6 @@ private:
#endif
bool _passwordJobRunning;
QList<QVariant> _shares;
qulonglong _public_share_id;
void setPassword(const QString &password);
void setExpireDate(const QDate &date);
@ -93,6 +99,9 @@ private:
QProgressIndicator *_pi_date;
QProgressIndicator *_pi_editing;
ShareManager *_manager;
QSharedPointer<LinkShare> _share;
bool _resharingAllowed;
bool _isFile;
};

View file

@ -13,6 +13,8 @@
#include "capabilities.h"
#include "configfile.h"
#include <QVariantMap>
namespace OCC {
@ -63,9 +65,28 @@ bool Capabilities::shareResharing() const
return _capabilities["files_sharing"].toMap()["resharing"].toBool();
}
QStringList Capabilities::supportedChecksumTypes() const
QList<QByteArray> Capabilities::supportedChecksumTypesAdvertised() const
{
return QStringList();
return QList<QByteArray>();
}
QList<QByteArray> Capabilities::supportedChecksumTypes() const
{
auto list = supportedChecksumTypesAdvertised();
QByteArray cfgType = ConfigFile().transmissionChecksum().toLatin1();
if (!cfgType.isEmpty()) {
list.prepend(cfgType);
}
return list;
}
QByteArray Capabilities::preferredChecksumType() const
{
auto list = supportedChecksumTypes();
if (list.isEmpty()) {
return QByteArray();
}
return list.first();
}
}

View file

@ -39,7 +39,15 @@ public:
bool sharePublicLinkEnforceExpireDate() const;
int sharePublicLinkExpireDateDays() const;
bool shareResharing() const;
QStringList supportedChecksumTypes() const;
/// Returns the checksum types the server explicitly advertises
QList<QByteArray> supportedChecksumTypesAdvertised() const;
/// Like supportedChecksumTypesRaw(), but includes the type from the config
QList<QByteArray> supportedChecksumTypes() const;
/// Returns the checksum type that should be used for new uploads.
QByteArray preferredChecksumType() const;
private:
QVariantMap _capabilities;

View file

@ -588,11 +588,7 @@ void ConfigFile::setNewBigFolderSizeLimit(bool isChecked, quint64 mbytes)
bool ConfigFile::monoIcons() const
{
QSettings settings(configFile(), QSettings::IniFormat);
bool deflt = false;
if( Utility::isMac() ) {
deflt = true;
}
return settings.value(QLatin1String(monoIconsC), deflt).toBool();
return settings.value(QLatin1String(monoIconsC), false).toBool();
}
void ConfigFile::setMonoIcons(bool useMonoIcons)

View file

@ -207,7 +207,7 @@ int get_errno_from_http_errcode( int err, const QString & reason ) {
DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(AccountPtr account, const QString &path, QObject *parent)
DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, QObject *parent)
: QObject(parent), _subPath(path), _account(account), _ignoredFirst(false)
{
}
@ -237,7 +237,7 @@ void DiscoverySingleDirectoryJob::abort()
}
}
static csync_vio_file_stat_t* propertyMapToFileStat(QMap<QString,QString> map)
static csync_vio_file_stat_t* propertyMapToFileStat(const QMap<QString,QString> &map)
{
csync_vio_file_stat_t* file_stat = csync_vio_file_stat_new();
@ -289,7 +289,7 @@ static csync_vio_file_stat_t* propertyMapToFileStat(QMap<QString,QString> map)
return file_stat;
}
void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file,QMap<QString,QString> map)
void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, const QMap<QString,QString> &map)
{
//qDebug() << Q_FUNC_INFO << _subPath << file << map.count() << map.keys() << _account->davPath() << _lsColJob->reply()->request().url().path();
if (!_ignoredFirst) {
@ -392,7 +392,7 @@ void DiscoveryMainThread::setupHooks(DiscoveryJob *discoveryJob, const QString &
}
// Coming from owncloud_opendir -> DiscoveryJob::vio_opendir_hook -> doOpendirSignal
void DiscoveryMainThread::doOpendirSlot(QString subPath, DiscoveryDirectoryResult *r)
void DiscoveryMainThread::doOpendirSlot(const QString &subPath, DiscoveryDirectoryResult *r)
{
QString fullPath = _pathPrefix;
if (!_pathPrefix.endsWith('/')) {
@ -445,7 +445,7 @@ void DiscoveryMainThread::singleDirectoryJobResultSlot(const QList<FileStatPoint
_discoveryJob->_vioMutex.unlock();
}
void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg)
void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, const QString &msg)
{
if (!_currentDiscoveryDirectoryResult) {
return; // possibly aborted
@ -461,7 +461,7 @@ void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrno
_discoveryJob->_vioMutex.unlock();
}
void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(QString p)
void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(const QString &p)
{
// Should be thread safe since the sync thread is blocked
if (!_discoveryJob->_csync_ctx->remote.root_perms) {
@ -553,29 +553,29 @@ csync_vio_handle_t* DiscoveryJob::remote_vio_opendir_hook (const char *url,
{
DiscoveryJob *discoveryJob = static_cast<DiscoveryJob*>(userdata);
if (discoveryJob) {
qDebug() << Q_FUNC_INFO << discoveryJob << url << "Calling into main thread...";
qDebug() << discoveryJob << url << "Calling into main thread...";
DiscoveryDirectoryResult *directoryResult = new DiscoveryDirectoryResult();
QScopedPointer<DiscoveryDirectoryResult> directoryResult(new DiscoveryDirectoryResult());
directoryResult->code = EIO;
discoveryJob->_vioMutex.lock();
const QString qurl = QString::fromUtf8(url);
emit discoveryJob->doOpendirSignal(qurl, directoryResult);
emit discoveryJob->doOpendirSignal(qurl, directoryResult.data());
discoveryJob->_vioWaitCondition.wait(&discoveryJob->_vioMutex, ULONG_MAX); // FIXME timeout?
discoveryJob->_vioMutex.unlock();
qDebug() << Q_FUNC_INFO << discoveryJob << url << "...Returned from main thread";
qDebug() << discoveryJob << url << "...Returned from main thread";
// Upon awakening from the _vioWaitCondition, iterator should be a valid iterator.
if (directoryResult->code != 0) {
qDebug() << Q_FUNC_INFO << directoryResult->code << "when opening" << url << "msg=" << directoryResult->msg;
qDebug() << directoryResult->code << "when opening" << url << "msg=" << directoryResult->msg;
errno = directoryResult->code;
// save the error string to the context
discoveryJob->_csync_ctx->error_string = qstrdup( directoryResult->msg.toUtf8().constData() );
return NULL;
}
return (csync_vio_handle_t*) directoryResult;
return directoryResult.take();
}
return NULL;
}

View file

@ -80,7 +80,7 @@ struct DiscoveryDirectoryResult {
class DiscoverySingleDirectoryJob : public QObject {
Q_OBJECT
public:
explicit DiscoverySingleDirectoryJob(AccountPtr account, const QString &path, QObject *parent = 0);
explicit DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, QObject *parent = 0);
void start();
void abort();
// This is not actually a network job, it is just a job
@ -89,9 +89,9 @@ signals:
void etagConcatenation(const QString &);
void etag(const QString &);
void finishedWithResult(const QList<FileStatPointer> &);
void finishedWithError(int csyncErrnoCode, QString msg);
void finishedWithError(int csyncErrnoCode, const QString &msg);
private slots:
void directoryListingIteratedSlot(QString,QMap<QString,QString>);
void directoryListingIteratedSlot(QString, const QMap<QString,QString>&);
void lsJobFinishedWithoutErrorSlot();
void lsJobFinishedWithErrorSlot(QNetworkReply*);
private:
@ -125,13 +125,13 @@ public:
public slots:
// From DiscoveryJob:
void doOpendirSlot(QString url, DiscoveryDirectoryResult* );
void doOpendirSlot(const QString &url, DiscoveryDirectoryResult* );
void doGetSizeSlot(const QString &path ,qint64 *result);
// From Job:
void singleDirectoryJobResultSlot(const QList<FileStatPointer> &);
void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg);
void singleDirectoryJobFirstDirectoryPermissionsSlot(QString);
void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, const QString &msg);
void singleDirectoryJobFirstDirectoryPermissionsSlot(const QString&);
void slotGetSizeFinishedWithError();
void slotGetSizeResult(const QVariantMap&);

View file

@ -87,11 +87,11 @@ int OwncloudPropagator::maximumActiveJob()
return max;
}
/** Updates or creates a blacklist entry for the given item.
/** Updates, creates or removes a blacklist entry for the given item.
*
* Returns whether the file is in the blacklist now.
*/
static bool blacklist(SyncJournalDb* journal, const SyncFileItem& item)
static bool blacklistCheck(SyncJournalDb* journal, const SyncFileItem& item)
{
SyncJournalErrorBlacklistRecord oldEntry = journal->errorBlacklistEntry(item._file);
SyncJournalErrorBlacklistRecord newEntry = SyncJournalErrorBlacklistRecord::update(oldEntry, item);
@ -132,7 +132,7 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
// do not blacklist in case of soft error or fatal error.
break;
case SyncFileItem::NormalError:
if (blacklist(_propagator->_journal, *_item) && _item->_hasBlacklistEntry) {
if (blacklistCheck(_propagator->_journal, *_item) && _item->_hasBlacklistEntry) {
// do not error if the item was, and continues to be, blacklisted
status = SyncFileItem::FileIgnored;
_item->_errorString.prepend(tr("Continue blacklisting:") + " ");
@ -277,14 +277,14 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
/* Check and log the transmission checksum type */
ConfigFile cfg;
const QString checksumType = cfg.transmissionChecksum().toUpper();
const QString checksumType = cfg.transmissionChecksum();
/* if the checksum type is empty, it is not sent. No error */
if( !checksumType.isEmpty() ) {
if( checksumType == checkSumAdlerUpperC ||
if( checksumType == checkSumAdlerC ||
checksumType == checkSumMD5C ||
checksumType == checkSumSHA1C ) {
qDebug() << "Client sends and expects transmission checksum type" << checksumType;
qDebug() << "Client sends transmission checksum type" << checksumType;
} else {
qWarning() << "Unknown transmission checksum type from config" << checksumType;
}

View file

@ -138,6 +138,7 @@ void SqlDatabase::close()
SQLITE_DO(sqlite3_close(_db) );
if (_errId != SQLITE_OK) {
qWarning() << "ERROR When closing DB" << _error;
Q_ASSERT(!"SQLite Close Error");
}
_db = 0;
}
@ -211,6 +212,7 @@ int SqlQuery::prepare( const QString& sql)
if( _errId != SQLITE_OK ) {
_error = QString::fromUtf8(sqlite3_errmsg(_db));
qWarning() << "Sqlite prepare statement error:" << _error << "in" <<_sql;
Q_ASSERT(!"SQLITE Prepare error");
}
}
return _errId;
@ -316,6 +318,11 @@ void SqlQuery::bindValue(int pos, const QVariant& value)
Q_ASSERT( res == SQLITE_OK );
}
bool SqlQuery::nullValue(int index)
{
return sqlite3_column_type(_stmt, index) == SQLITE_NULL;
}
QString SqlQuery::stringValue(int index)
{
return QString::fromUtf16(static_cast<const ushort*>(sqlite3_column_text16(_stmt, index)));

View file

@ -66,6 +66,9 @@ public:
~SqlQuery();
QString error() const;
/// Checks whether the value at the given column index is NULL
bool nullValue(int index);
QString stringValue(int index);
int intValue(int index);
quint64 int64Value(int index);

View file

@ -351,7 +351,10 @@ void PropagateDownloadFileQNAM::start()
if (_resumeStart == _item->_size) {
qDebug() << "File is already complete, no need to download";
_tmpFile.close();
downloadFinished();
// Unfortunately we lost the checksum header, if any...
QByteArray noChecksumData;
downloadFinished(noChecksumData, noChecksumData);
return;
}
}
@ -359,6 +362,7 @@ void PropagateDownloadFileQNAM::start()
// If there's not enough space to fully download this file, stop.
const auto diskSpaceResult = _propagator->diskSpaceCheck();
if (diskSpaceResult == OwncloudPropagator::DiskSpaceFailure) {
_item->_errorMayBeBlacklisted = true;
done(SyncFileItem::NormalError,
tr("The download would reduce free disk space below %1").arg(
Utility::octetsToString(freeSpaceLimit())));
@ -531,11 +535,16 @@ void PropagateDownloadFileQNAM::slotGetFinished()
// Do checksum validation for the download. If there is no checksum header, the validator
// will also emit the validated() signal to continue the flow in slot downloadFinished()
// as this is (still) also correct.
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(_tmpFile.fileName(), this);
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(downloadFinished()));
connect(validator, SIGNAL(validationFailed(QString)), this, SLOT(slotChecksumFail(QString)));
validator->downloadValidation(job->reply()->rawHeader(checkSumHeaderC));
ValidateChecksumHeader *validator = new ValidateChecksumHeader(this);
connect(validator, SIGNAL(validated(QByteArray,QByteArray)),
SLOT(downloadFinished(QByteArray,QByteArray)));
connect(validator, SIGNAL(validationFailed(QString)),
SLOT(slotChecksumFail(QString)));
auto checksumHeader = job->reply()->rawHeader(checkSumHeaderC);
if (!downloadChecksumEnabled()) {
checksumHeader.clear();
}
validator->start(_tmpFile.fileName(), checksumHeader);
}
void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
@ -612,8 +621,13 @@ static void handleRecallFile(const QString &fn)
}
} // end namespace
void PropagateDownloadFileQNAM::downloadFinished()
void PropagateDownloadFileQNAM::downloadFinished(const QByteArray& checksumType, const QByteArray& checksum)
{
if (!checksumType.isEmpty()) {
_item->_transmissionChecksum = checksum;
_item->_transmissionChecksumType = checksumType;
}
QString fn = _propagator->getFilePath(_item->_file);
// In case of file name clash, report an error

View file

@ -117,7 +117,7 @@ public:
private slots:
void slotGetFinished();
void abort() Q_DECL_OVERRIDE;
void downloadFinished();
void downloadFinished(const QByteArray& checksumType, const QByteArray& checksum);
void slotDownloadProgress(qint64,qint64);
void slotChecksumFail( const QString& errMsg );

View file

@ -197,6 +197,16 @@ void PropagateUploadFileQNAM::start()
return;
}
if (_propagator->account()->serverVersionInt() < 0x080100) {
// Server version older than 8.1 don't support these character in filename.
static const QRegExp invalidCharRx("[\\\\:?*\"<>|]");
if (_item->_file.contains(invalidCharRx)) {
_item->_httpErrorCode = 400; // So the entry get blacklisted
done(SyncFileItem::NormalError, tr("File name contains at least one invalid character"));
return;
}
}
const QString filePath = _propagator->getFilePath(_item->_file);
// remember the modtime before checksumming to be able to detect a file
@ -205,30 +215,48 @@ void PropagateUploadFileQNAM::start()
_stopWatch.start();
// do whatever is needed to add a checksum to the http upload request.
// in any case, the validator will emit signal startUpload to let the flow
// continue in slotStartUpload here.
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(filePath, this);
auto supportedChecksumTypes = _propagator->account()->capabilities().supportedChecksumTypes();
// If the config file does not specify a checksum type but the
// server supports it choose a type based on that.
if (validator->checksumType().isEmpty()) {
QStringList checksumTypes = _propagator->account()->capabilities().supportedChecksumTypes();
if (!checksumTypes.isEmpty()) {
// TODO: We might want to prefer some types over others instead
// of choosing the first.
validator->setChecksumType(checksumTypes.first());
// If we already have a checksum header and the checksum type is supported
// by the server, we keep that - otherwise recompute.
//
// Note: Currently we *always* recompute because we usually only upload
// files that have changed and thus have a new checksum. But if an earlier
// phase computed a checksum, this is where we would make use of it.
if (!_item->_transmissionChecksumType.isEmpty()) {
if (supportedChecksumTypes.contains(_item->_transmissionChecksumType)) {
// TODO: We could validate the old checksum and thereby determine whether
// an upload is necessary or not.
slotStartUpload(_item->_transmissionChecksumType, _item->_transmissionChecksum);
return;
}
}
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(slotStartUpload(QByteArray)));
validator->uploadValidation();
// Compute a new checksum.
auto computeChecksum = new ComputeChecksum(this);
if (uploadChecksumEnabled()) {
computeChecksum->setChecksumType(_propagator->account()->capabilities().preferredChecksumType());
} else {
computeChecksum->setChecksumType(QByteArray());
}
connect(computeChecksum, SIGNAL(done(QByteArray,QByteArray)),
SLOT(slotStartUpload(QByteArray,QByteArray)));
computeChecksum->start(filePath);
}
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksum)
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksumType, const QByteArray& checksum)
{
// Store the computed checksum in the database, if different
if (checksumType != _item->_transmissionChecksumType
|| checksum != _item->_transmissionChecksum) {
_item->_transmissionChecksum = checksum;
_item->_transmissionChecksumType = checksumType;
_propagator->_journal->updateFileRecordChecksum(
_item->_file, checksum, checksumType);
}
const QString fullFilePath = _propagator->getFilePath(_item->_file);
_item->_checksum = checksum;
if (!FileSystem::fileExists(fullFilePath)) {
done(SyncFileItem::SoftError, tr("File Removed"));
@ -458,6 +486,7 @@ void PropagateUploadFileQNAM::startNextChunk()
UploadDevice *device = new UploadDevice(&_propagator->_bandwidthManager);
qint64 chunkStart = 0;
qint64 currentChunkSize = fileSize;
bool isFinalChunk = false;
if (_chunkCount > 1) {
int sendingChunk = (_currentChunk + _startChunk) % _chunkCount;
// XOR with chunk size to make sure everything goes well if chunk size changes between runs
@ -474,15 +503,16 @@ void PropagateUploadFileQNAM::startNextChunk()
if( currentChunkSize == 0 ) { // if the last chunk pretends to be 0, its actually the full chunk size.
currentChunkSize = chunkSize();
}
if( !_item->_checksum.isEmpty() ) {
headers[checkSumHeaderC] = _item->_checksum;
}
isFinalChunk = true;
}
} else {
// checksum if its only one chunk
if( !_item->_checksum.isEmpty() ) {
headers[checkSumHeaderC] = _item->_checksum;
// if there's only one chunk, it's the final one
isFinalChunk = true;
}
if (isFinalChunk && !_item->_transmissionChecksumType.isEmpty()) {
headers[checkSumHeaderC] = makeChecksumHeader(
_item->_transmissionChecksumType, _item->_transmissionChecksum);
}
if (! device->prepareAndOpen(_propagator->getFilePath(_item->_file), chunkStart, currentChunkSize)) {

View file

@ -195,7 +195,7 @@ private slots:
void startNextChunk();
void finalize(const SyncFileItem&);
void slotJobDestroyed(QObject *job);
void slotStartUpload(const QByteArray &checksum);
void slotStartUpload(const QByteArray& checksumType, const QByteArray& checksum);
private:
void startPollJob(const QString& path);

View file

@ -32,7 +32,6 @@ static const char checkSumHeaderC[] = "OC-Checksum";
static const char checkSumMD5C[] = "MD5";
static const char checkSumSHA1C[] = "SHA1";
static const char checkSumAdlerC[] = "Adler32";
static const char checkSumAdlerUpperC[] = "ADLER32";
/**
* @brief Declaration of the other propagation jobs

View file

@ -468,7 +468,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
// the file system in the DB, this is to avoid spurious upload on the next sync
item->_modtime = file->other.modtime;
_journal->setFileRecord(SyncJournalFileRecord(*item, _localPath + item->_file));
_journal->updateFileRecordMetadata(SyncJournalFileRecord(*item, _localPath + item->_file));
item->_should_update_metadata = false;
}
if (item->_isDirectory && file->should_update_metadata) {
@ -572,7 +572,7 @@ void SyncEngine::handleSyncError(CSYNC *ctx, const char *state) {
} else {
emit csyncError(errStr);
}
finalize();
finalize(false);
}
void SyncEngine::startSync()
@ -598,7 +598,7 @@ void SyncEngine::startSync()
if (!QDir(_localPath).exists()) {
// No _tr, it should only occur in non-mirall
emit csyncError("Unable to find local sync folder.");
finalize();
finalize(false);
return;
}
@ -612,7 +612,7 @@ void SyncEngine::startSync()
emit csyncError(tr("Only %1 are available, need at least %2 to start").arg(
Utility::octetsToString(freeBytes),
Utility::octetsToString(minFree)));
finalize();
finalize(false);
return;
}
} else {
@ -643,7 +643,7 @@ void SyncEngine::startSync()
if( fileRecordCount == -1 ) {
qDebug() << "No way to create a sync journal!";
emit csyncError(tr("Unable to initialize a sync journal."));
finalize();
finalize(false);
return;
// database creation error!
}
@ -666,7 +666,7 @@ void SyncEngine::startSync()
_discoveryMainThread = new DiscoveryMainThread(account());
_discoveryMainThread->setParent(this);
connect(this, SIGNAL(finished()), _discoveryMainThread, SLOT(deleteLater()));
connect(this, SIGNAL(finished(bool)), _discoveryMainThread, SLOT(deleteLater()));
qDebug() << "=====Server" << account()->serverVersion()
<< QString("rootEtagChangesNotOnlySubFolderEtags=%1").arg(account()->rootEtagChangesNotOnlySubFolderEtags());
if (account()->rootEtagChangesNotOnlySubFolderEtags()) {
@ -698,7 +698,7 @@ void SyncEngine::startSync()
QMetaObject::invokeMethod(discoveryJob, "start", Qt::QueuedConnection);
}
void SyncEngine::slotRootEtagReceived(QString e) {
void SyncEngine::slotRootEtagReceived(const QString &e) {
if (_remoteRootEtag.isEmpty()) {
qDebug() << Q_FUNC_INFO << e;
_remoteRootEtag = e;
@ -721,7 +721,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
if (!_journal->isConnected()) {
qDebug() << "Bailing out, DB failure";
emit csyncError(tr("Cannot open the sync journal"));
finalize();
finalize(false);
return;
} else {
// Commits a possibly existing (should not though) transaction and starts a new one for the propagate phase
@ -785,7 +785,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
emit aboutToRemoveAllFiles(_syncedItems.first()->_direction, &cancel);
if (cancel) {
qDebug() << Q_FUNC_INFO << "Abort sync";
finalize();
finalize(false);
return;
}
}
@ -833,7 +833,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
void SyncEngine::slotCleanPollsJobAborted(const QString &error)
{
csyncError(error);
finalize();
finalize(false);
}
void SyncEngine::setNetworkLimits(int upload, int download)
@ -888,10 +888,10 @@ void SyncEngine::slotFinished()
_journal->commit("All Finished.", false);
emit treeWalkResult(_syncedItems);
finalize();
finalize(true); // FIXME: should it be true if there was errors?
}
void SyncEngine::finalize()
void SyncEngine::finalize(bool success)
{
_thread.quit();
_thread.wait();
@ -902,7 +902,7 @@ void SyncEngine::finalize()
_stopWatch.stop();
_syncRunning = false;
emit finished();
emit finished(success);
// Delete the propagator only after emitting the signal.
_propagator.clear();

View file

@ -94,7 +94,7 @@ signals:
// During update, before reconcile
void rootEtag(QString);
void folderDiscovered(bool local, QString folderUrl);
void folderDiscovered(bool local, const QString &folderUrl);
// before actual syncing (after update+reconcile) for each item
void syncItemDiscovered(const SyncFileItem&);
@ -109,7 +109,7 @@ signals:
void transmissionProgress( const ProgressInfo& progress );
void finished();
void finished(bool success);
void started();
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
@ -118,7 +118,7 @@ signals:
void newBigFolder(const QString &folder);
private slots:
void slotRootEtagReceived(QString);
void slotRootEtagReceived(const QString &);
void slotItemCompleted(const SyncFileItem& item, const PropagatorJob & job);
void slotFinished();
void slotProgress(const SyncFileItem& item, quint64 curent);
@ -144,7 +144,7 @@ private:
void deleteStaleErrorBlacklistEntries();
// cleanup and emit the finished signal
void finalize();
void finalize(bool success);
static bool _syncRunning; //true when one sync is running somewhere (for debugging)

View file

@ -64,7 +64,8 @@ public:
};
SyncFileItem() : _type(UnknownType), _direction(None), _isDirectory(false),
_serverHasIgnoredFiles(false), _hasBlacklistEntry(false), _status(NoStatus),
_serverHasIgnoredFiles(false), _hasBlacklistEntry(false),
_errorMayBeBlacklisted(false), _status(NoStatus),
_isRestoration(false), _should_update_metadata(false),
_httpErrorCode(0), _requestDuration(0), _affectedItems(1),
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0), _size(0), _inode(0)
@ -137,6 +138,13 @@ public:
/// without the status being FileIgnored.
bool _hasBlacklistEntry BITFIELD(1);
/** If true and NormalError, this error may be blacklisted
*
* Note that non-local errors (httpErrorCode!=0) may also be
* blacklisted independently of this flag.
*/
bool _errorMayBeBlacklisted BITFIELD(1);
// Variables useful to report to the user
Status _status BITFIELD(4);
bool _isRestoration BITFIELD(1); // The original operation was forbidden, and this is a restoration
@ -157,7 +165,8 @@ public:
quint64 _inode;
QByteArray _fileId;
QByteArray _remotePerm;
QByteArray _checksum;
QByteArray _transmissionChecksum;
QByteArray _transmissionChecksumType;
QString _directDownloadUrl;
QString _directDownloadCookies;

View file

@ -206,6 +206,8 @@ bool SyncJournalDb::checkConnect()
"md5 VARCHAR(32)," /* This is the etag. Called md5 for compatibility */
// updateDatabaseStructure() will add a fileid column
// updateDatabaseStructure() will add a remotePerm column
// updateDatabaseStructure() will add a transmissionChecksum column
// updateDatabaseStructure() will add a transmissionChecksumTypeId column
"PRIMARY KEY(phash)"
");");
@ -271,6 +273,14 @@ bool SyncJournalDb::checkConnect()
return sqlFail("Create table selectivesync", createQuery);
}
// create the checksumtype table.
createQuery.prepare("CREATE TABLE IF NOT EXISTS checksumtype("
"id INTEGER PRIMARY KEY,"
"name TEXT UNIQUE"
");");
if (!createQuery.exec()) {
return sqlFail("Create table version", createQuery);
}
createQuery.prepare("CREATE TABLE IF NOT EXISTS version("
@ -346,13 +356,30 @@ bool SyncJournalDb::checkConnect()
}
_getFileRecordQuery.reset(new SqlQuery(_db));
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM "
"metadata WHERE phash=?1" );
_getFileRecordQuery->prepare(
"SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize,"
" ignoredChildrenRemote, transmissionChecksum, checksumtype.name"
" FROM metadata"
" LEFT JOIN checksumtype ON metadata.transmissionChecksumTypeId == checksumtype.id"
" WHERE phash=?1" );
_setFileRecordQuery.reset(new SqlQuery(_db) );
_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote) "
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14);" );
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, transmissionChecksum, transmissionChecksumTypeId) "
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12, ?13, ?14, ?15, ?16);" );
_setFileRecordChecksumQuery.reset(new SqlQuery(_db) );
_setFileRecordChecksumQuery->prepare(
"UPDATE metadata"
" SET transmissionChecksum = ?2, transmissionChecksumTypeId = ?3"
" WHERE phash == ?1;");
_setFileRecordMetadataQuery.reset(new SqlQuery(_db) );
_setFileRecordMetadataQuery->prepare(
"UPDATE metadata"
" SET inode=?2, mode=?3, modtime=?4, type=?5, md5=?6, fileid=?7,"
" remotePerm=?8, filesize=?9, ignoredChildrenRemote=?10"
" WHERE phash == ?1;");
_getDownloadInfoQuery.reset(new SqlQuery(_db) );
_getDownloadInfoQuery->prepare( "SELECT tmpfile, etag, errorcount FROM "
@ -403,6 +430,12 @@ bool SyncJournalDb::checkConnect()
_getSelectiveSyncListQuery.reset(new SqlQuery(_db));
_getSelectiveSyncListQuery->prepare("SELECT path FROM selectivesync WHERE type=?1");
_getChecksumTypeIdQuery.reset(new SqlQuery(_db));
_getChecksumTypeIdQuery->prepare("SELECT id FROM checksumtype WHERE name=?1");
_insertChecksumTypeQuery.reset(new SqlQuery(_db));
_insertChecksumTypeQuery->prepare("INSERT OR IGNORE INTO checksumtype (name) VALUES (?1)");
// don't start a new transaction now
commitInternal(QString("checkConnect End"), false);
@ -424,6 +457,8 @@ void SyncJournalDb::close()
_getFileRecordQuery.reset(0);
_setFileRecordQuery.reset(0);
_setFileRecordChecksumQuery.reset(0);
_setFileRecordMetadataQuery.reset(0);
_getDownloadInfoQuery.reset(0);
_setDownloadInfoQuery.reset(0);
_deleteDownloadInfoQuery.reset(0);
@ -435,6 +470,8 @@ void SyncJournalDb::close()
_getErrorBlacklistQuery.reset(0);
_setErrorBlacklistQuery.reset(0);
_getSelectiveSyncListQuery.reset(0);
_getChecksumTypeIdQuery.reset(0);
_insertChecksumTypeQuery.reset(0);
_db.close();
_avoidReadFromDbOnNextSyncFilter.clear();
@ -527,6 +564,27 @@ bool SyncJournalDb::updateMetadataTableStructure()
}
commitInternal("update database structure: add ignoredChildrenRemote col");
}
if( columns.indexOf(QLatin1String("transmissionChecksum")) == -1 ) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN transmissionChecksum TEXT;");
if( !query.exec()) {
sqlFail("updateMetadataTableStructure: add transmissionChecksum column", query);
re = false;
}
commitInternal("update database structure: add transmissionChecksum col");
}
if( columns.indexOf(QLatin1String("transmissionChecksumTypeId")) == -1 ) {
SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN transmissionChecksumTypeId INTEGER;");
if( !query.exec()) {
sqlFail("updateMetadataTableStructure: add transmissionChecksumTypeId column", query);
re = false;
}
commitInternal("update database structure: add transmissionChecksumTypeId col");
}
return re;
}
@ -627,6 +685,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
if( fileId.isEmpty() ) fileId = "";
QString remotePerm (record._remotePerm);
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
int checksumTypeId = mapChecksumType(record._transmissionChecksumType);
_setFileRecordQuery->reset();
_setFileRecordQuery->bindValue(1, QString::number(phash));
_setFileRecordQuery->bindValue(2, plen);
@ -642,6 +701,8 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
_setFileRecordQuery->bindValue(12, remotePerm );
_setFileRecordQuery->bindValue(13, record._fileSize );
_setFileRecordQuery->bindValue(14, record._serverHasIgnoredFiles ? 1:0);
_setFileRecordQuery->bindValue(15, record._transmissionChecksum );
_setFileRecordQuery->bindValue(16, checksumTypeId );
if( !_setFileRecordQuery->exec() ) {
qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :"
@ -652,7 +713,8 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
qDebug() << _setFileRecordQuery->lastQuery() << phash << plen << record._path << record._inode
<< record._mode
<< QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type)
<< record._etag << record._fileId << record._remotePerm << record._fileSize << (record._serverHasIgnoredFiles ? 1:0);
<< record._etag << record._fileId << record._remotePerm << record._fileSize << (record._serverHasIgnoredFiles ? 1:0)
<< record._transmissionChecksum << record._transmissionChecksumType << checksumTypeId;
_setFileRecordQuery->reset();
return true;
@ -732,6 +794,10 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
rec._remotePerm = _getFileRecordQuery->baValue(9);
rec._fileSize = _getFileRecordQuery->int64Value(10);
rec._serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
rec._transmissionChecksum = _getFileRecordQuery->baValue(12);
if( !_getFileRecordQuery->nullValue(13) ) {
rec._transmissionChecksumType = _getFileRecordQuery->baValue(13);
}
} else {
QString err = _getFileRecordQuery->error();
qDebug() << "No journal entry found for " << filename;
@ -820,6 +886,86 @@ int SyncJournalDb::getFileRecordCount()
return 0;
}
bool SyncJournalDb::updateFileRecordChecksum(const QString& filename,
const QByteArray& transmisisonChecksum,
const QByteArray& transmissionChecksumType)
{
QMutexLocker locker(&_mutex);
qlonglong phash = getPHash(filename);
if( !checkConnect() ) {
qDebug() << "Failed to connect database.";
return false;
}
int checksumTypeId = mapChecksumType(transmissionChecksumType);
auto & query = _setFileRecordChecksumQuery;
query->reset();
query->bindValue(1, QString::number(phash));
query->bindValue(2, transmisisonChecksum);
query->bindValue(3, checksumTypeId);
if( !query->exec() ) {
qWarning() << "Error SQL statement setFileRecordChecksumQuery: "
<< query->lastQuery() << " :"
<< query->error();
return false;
}
qDebug() << query->lastQuery() << phash << transmisisonChecksum
<< transmissionChecksumType << checksumTypeId;
query->reset();
return true;
}
bool SyncJournalDb::updateFileRecordMetadata(const SyncJournalFileRecord& record)
{
QMutexLocker locker(&_mutex);
qlonglong phash = getPHash(record._path);
QString etag( record._etag );
if( etag.isEmpty() ) etag = "";
QString fileId( record._fileId);
if( fileId.isEmpty() ) fileId = "";
QString remotePerm (record._remotePerm);
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
if( !checkConnect() ) {
qDebug() << "Failed to connect database.";
return false;
}
auto & query = _setFileRecordMetadataQuery;
query->reset();
query->bindValue(1, QString::number(phash));
query->bindValue(2, record._inode);
query->bindValue(3, record._mode);
query->bindValue(4, QString::number(Utility::qDateTimeToTime_t(record._modtime)));
query->bindValue(5, QString::number(record._type));
query->bindValue(6, etag);
query->bindValue(7, fileId);
query->bindValue(8, remotePerm);
query->bindValue(9, record._fileSize);
query->bindValue(10, record._serverHasIgnoredFiles ? 1 : 0);
if( !query->exec() ) {
qWarning() << "Error SQL statement setFileRecordMetadataQuery: "
<< query->lastQuery() << " :"
<< query->error();
return false;
}
qDebug() << query->lastQuery() << record._path << record._inode << record._mode << record._modtime
<< record._type << etag << fileId << remotePerm << record._fileSize
<< record._serverHasIgnoredFiles;
query->reset();
return true;
}
static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo * res)
{
bool ok = true;
@ -1389,6 +1535,39 @@ void SyncJournalDb::forceRemoteDiscoveryNextSyncLocked()
}
}
int SyncJournalDb::mapChecksumType(const QByteArray& checksumType)
{
if (checksumType.isEmpty()) {
return 0;
}
// Ensure the checksum type is in the db
_insertChecksumTypeQuery->reset();
_insertChecksumTypeQuery->bindValue(1, checksumType);
if( !_insertChecksumTypeQuery->exec() ) {
qWarning() << "Error SQL statement insertChecksumType: "
<< _insertChecksumTypeQuery->lastQuery() << " :"
<< _insertChecksumTypeQuery->error();
return 0;
}
// Retrieve the id
_getChecksumTypeIdQuery->reset();
_getChecksumTypeIdQuery->bindValue(1, checksumType);
if( !_getChecksumTypeIdQuery->exec() ) {
qWarning() << "Error SQL statement getChecksumTypeId: "
<< _getChecksumTypeIdQuery->lastQuery() << " :"
<< _getChecksumTypeIdQuery->error();
return 0;
}
if( !_getChecksumTypeIdQuery->next() ) {
qDebug() << "No checksum type mapping found for" << checksumType;
return 0;
}
return _getChecksumTypeIdQuery->intValue(0);
}
void SyncJournalDb::commit(const QString& context, bool startTrans)
{

View file

@ -42,6 +42,10 @@ public:
bool setFileRecord( const SyncJournalFileRecord& record );
bool deleteFileRecord( const QString& filename, bool recursively = false );
int getFileRecordCount();
bool updateFileRecordChecksum(const QString& filename,
const QByteArray& transmisisonChecksum,
const QByteArray& transmissionChecksumType);
bool updateFileRecordMetadata(const SyncJournalFileRecord& record);
bool exists();
void walCheckpoint();
@ -153,12 +157,21 @@ private:
// Same as forceRemoteDiscoveryNextSync but without acquiring the lock
void forceRemoteDiscoveryNextSyncLocked();
// Returns the integer id of the checksum type
//
// Returns 0 on failure and for empty checksum types.
int mapChecksumType(const QByteArray& checksumType);
SqlDatabase _db;
QString _dbFile;
QMutex _mutex; // Public functions are protected with the mutex.
int _transaction;
// NOTE! when adding a query, don't forget to reset it in SyncJournalDb::close
QScopedPointer<SqlQuery> _getFileRecordQuery;
QScopedPointer<SqlQuery> _setFileRecordQuery;
QScopedPointer<SqlQuery> _setFileRecordChecksumQuery;
QScopedPointer<SqlQuery> _setFileRecordMetadataQuery;
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
QScopedPointer<SqlQuery> _setDownloadInfoQuery;
QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
@ -170,6 +183,8 @@ private:
QScopedPointer<SqlQuery> _getErrorBlacklistQuery;
QScopedPointer<SqlQuery> _setErrorBlacklistQuery;
QScopedPointer<SqlQuery> _getSelectiveSyncListQuery;
QScopedPointer<SqlQuery> _getChecksumTypeIdQuery;
QScopedPointer<SqlQuery> _insertChecksumTypeQuery;
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
* It means that they should not be written to the DB in any case since doing

View file

@ -35,7 +35,9 @@ SyncJournalFileRecord::SyncJournalFileRecord()
SyncJournalFileRecord::SyncJournalFileRecord(const SyncFileItem &item, const QString &localFileName)
: _path(item._file), _modtime(Utility::qDateTimeFromTime_t(item._modtime)),
_type(item._type), _etag(item._etag), _fileId(item._fileId), _fileSize(item._size),
_remotePerm(item._remotePerm), _mode(0), _serverHasIgnoredFiles(item._serverHasIgnoredFiles)
_remotePerm(item._remotePerm), _mode(0), _serverHasIgnoredFiles(item._serverHasIgnoredFiles),
_transmissionChecksum(item._transmissionChecksum),
_transmissionChecksumType(item._transmissionChecksumType)
{
// use the "old" inode coming with the item for the case where the
// filesystem stat fails. That can happen if the the file was removed
@ -108,12 +110,16 @@ SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item)
{
SyncJournalErrorBlacklistRecord entry;
if (item._httpErrorCode == 0 // Do not blacklist local errors. (#1985)
bool mayBlacklist =
item._errorMayBeBlacklisted // explicitly flagged for blacklisting
|| (item._httpErrorCode != 0 // or non-local error
#ifdef OWNCLOUD_5XX_NO_BLACKLIST
|| item._httpErrorCode / 100 == 5 // In this configuration, never blacklist error 5xx
&& item._httpErrorCode / 100 != 5 // In this configuration, never blacklist error 5xx
#endif
) {
qDebug() << "This error is not blacklisted " << item._httpErrorCode;
);
if (!mayBlacklist) {
qDebug() << "This error is not blacklisted " << item._httpErrorCode << item._errorMayBeBlacklisted;
return entry;
}
@ -126,7 +132,7 @@ SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
entry._lastTryEtag = item._etag;
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
entry._ignoreDuration = qMin(qMax(minBlacklistTime, old._ignoreDuration * 5), maxBlacklistTime);
entry._ignoreDuration = qBound(minBlacklistTime, old._ignoreDuration * 5, maxBlacklistTime);
entry._file = item._file;
if( item._httpErrorCode == 403 || item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
@ -150,9 +156,12 @@ bool operator==(const SyncJournalFileRecord & lhs,
&& lhs._type == rhs._type
&& lhs._etag == rhs._etag
&& lhs._fileId == rhs._fileId
&& lhs._fileSize == rhs._fileSize
&& lhs._remotePerm == rhs._remotePerm
&& lhs._mode == rhs._mode
&& lhs._fileSize == rhs._fileSize;
&& lhs._serverHasIgnoredFiles == rhs._serverHasIgnoredFiles
&& lhs._transmissionChecksum == rhs._transmissionChecksum
&& lhs._transmissionChecksumType == rhs._transmissionChecksumType;
}
}

View file

@ -47,6 +47,8 @@ public:
QByteArray _remotePerm;
int _mode;
bool _serverHasIgnoredFiles;
QByteArray _transmissionChecksum;
QByteArray _transmissionChecksumType;
};
bool OWNCLOUDSYNC_EXPORT

View file

@ -16,133 +16,139 @@
#include "transmissionchecksumvalidator.h"
#include "syncfileitem.h"
#include "propagatorjobs.h"
#include "configfile.h"
#include "account.h"
#include <qtconcurrentrun.h>
namespace OCC {
TransmissionChecksumValidator::TransmissionChecksumValidator(const QString& filePath, QObject *parent)
: QObject(parent),
_filePath(filePath)
QByteArray makeChecksumHeader(const QByteArray& checksumType, const QByteArray& checksum)
{
// If the config file specifies a checksum type, use that.
ConfigFile cfg;
_checksumType = cfg.transmissionChecksum();
QByteArray header = checksumType;
header.append(':');
header.append(checksum);
return header;
}
void TransmissionChecksumValidator::setChecksumType(const QString& type)
bool parseChecksumHeader(const QByteArray& header, QByteArray* type, QByteArray* checksum)
{
if (header.isEmpty()) {
type->clear();
checksum->clear();
return true;
}
const auto idx = header.indexOf(':');
if (idx < 0) {
return false;
}
*type = header.left(idx);
*checksum = header.mid(idx + 1);
return true;
}
bool uploadChecksumEnabled()
{
static bool enabled = qgetenv("OWNCLOUD_DISABLE_CHECKSUM_UPLOAD").isEmpty();
return enabled;
}
bool downloadChecksumEnabled()
{
static bool enabled = qgetenv("OWNCLOUD_DISABLE_CHECKSUM_DOWNLOAD").isEmpty();
return enabled;
}
ComputeChecksum::ComputeChecksum(QObject* parent)
: QObject(parent)
{
}
void ComputeChecksum::setChecksumType(const QByteArray& type)
{
_checksumType = type;
}
QString TransmissionChecksumValidator::checksumType() const
QByteArray ComputeChecksum::checksumType() const
{
return _checksumType;
}
void TransmissionChecksumValidator::uploadValidation()
void ComputeChecksum::start(const QString& filePath)
{
const QString csType = checksumType();
if( csType.isEmpty() ) {
// if there is no checksum defined, continue to upload
emit validated(QByteArray());
} else {
// Calculate the checksum in a different thread first.
connect( &_watcher, SIGNAL(finished()),
this, SLOT(slotUploadChecksumCalculated()));
this, SLOT(slotCalculationDone()),
Qt::UniqueConnection );
if( csType == checkSumMD5C ) {
_checksumHeader = checkSumMD5C;
_checksumHeader += ":";
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, filePath));
} else if( csType == checkSumSHA1C ) {
_checksumHeader = checkSumSHA1C;
_checksumHeader += ":";
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, _filePath));
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, filePath));
}
#ifdef ZLIB_FOUND
else if( csType == checkSumAdlerC) {
_checksumHeader = checkSumAdlerC;
_checksumHeader += ":";
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, filePath));
}
#endif
else {
// for an unknown checksum, continue to upload
emit validated(QByteArray());
// for an unknown checksum or no checksum, we're done right now
if( !csType.isEmpty() ) {
qDebug() << "Unknown checksum type:" << csType;
}
emit done(QByteArray(), QByteArray());
}
}
void TransmissionChecksumValidator::slotUploadChecksumCalculated( )
void ComputeChecksum::slotCalculationDone()
{
QByteArray checksum = _watcher.future().result();
if( !checksum.isEmpty() ) {
checksum.prepend( _checksumHeader );
}
emit validated(checksum);
emit done(_checksumType, checksum);
}
void TransmissionChecksumValidator::downloadValidation( const QByteArray& checksumHeader )
ValidateChecksumHeader::ValidateChecksumHeader(QObject *parent)
: QObject(parent)
{
// if the incoming header is empty, there was no checksum header, and
// no validation can happen. Just continue.
const QString csType = checksumType();
}
// for empty checksum type, everything is valid.
if( csType.isEmpty() ) {
emit validated(QByteArray());
void ValidateChecksumHeader::start(const QString& filePath, const QByteArray& checksumHeader)
{
// If the incoming header is empty no validation can happen. Just continue.
if( checksumHeader.isEmpty() ) {
emit validated(QByteArray(), QByteArray());
return;
}
int indx = checksumHeader.indexOf(':');
if( indx < 0 ) {
if( !parseChecksumHeader(checksumHeader, &_expectedChecksumType, &_expectedChecksum) ) {
qDebug() << "Checksum header malformed:" << checksumHeader;
emit validationFailed(tr("The checksum header is malformed.")); // show must go on - even not validated.
return;
}
const QByteArray type = checksumHeader.left(indx).toUpper();
_expectedHash = checksumHeader.mid(indx+1);
connect( &_watcher, SIGNAL(finished()), this, SLOT(slotDownloadChecksumCalculated()) );
// start the calculation in different thread
if( type == checkSumMD5C ) {
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
} else if( type == checkSumSHA1C ) {
_watcher.setFuture(QtConcurrent::run(FileSystem::calcSha1, _filePath));
}
#ifdef ZLIB_FOUND
else if( type == checkSumAdlerUpperC ) {
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
}
#endif
else {
qDebug() << "Unknown checksum type" << type;
emit validationFailed(tr("The checksum header is malformed."));
return;
}
auto calculator = new ComputeChecksum(this);
calculator->setChecksumType(_expectedChecksumType);
connect(calculator, SIGNAL(done(QByteArray,QByteArray)),
SLOT(slotChecksumCalculated(QByteArray,QByteArray)));
calculator->start(filePath);
}
void TransmissionChecksumValidator::slotDownloadChecksumCalculated()
void ValidateChecksumHeader::slotChecksumCalculated(const QByteArray& checksumType,
const QByteArray& checksum)
{
const QByteArray hash = _watcher.future().result();
if( hash != _expectedHash ) {
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
} else {
// qDebug() << "Checksum checked and matching: " << _expectedHash;
emit validated(hash);
if( checksumType != _expectedChecksumType ) {
emit validationFailed(tr("The checksum header contained an unknown checksum type '%1'").arg(
QString::fromLatin1(_expectedChecksumType)));
return;
}
if( checksum != _expectedChecksum ) {
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
return;
}
emit validated(checksumType, checksum);
}
}

View file

@ -23,62 +23,84 @@
namespace OCC {
/// Creates a checksum header from type and value.
QByteArray makeChecksumHeader(const QByteArray& checksumType, const QByteArray& checksum);
/// Parses a checksum header
bool parseChecksumHeader(const QByteArray& header, QByteArray* type, QByteArray* checksum);
/// Checks OWNCLOUD_DISABLE_CHECKSUM_UPLOAD
bool uploadChecksumEnabled();
/// Checks OWNCLOUD_DISABLE_CHECKSUM_DOWNLOAD
bool downloadChecksumEnabled();
/**
* @brief The TransmissionChecksumValidator class
* @ingroup libsync
* Computes the checksum of a file.
* \ingroup libsync
*/
class OWNCLOUDSYNC_EXPORT TransmissionChecksumValidator : public QObject
class OWNCLOUDSYNC_EXPORT ComputeChecksum : public QObject
{
Q_OBJECT
public:
explicit TransmissionChecksumValidator(const QString& filePath, QObject *parent = 0);
explicit ComputeChecksum(QObject* parent = 0);
/**
* method to prepare a checksum for transmission and save it to the _checksum
* member of the SyncFileItem *item.
* The kind of requested checksum is taken from config. No need to set from outside.
* Sets the checksum type to be used. The default is empty.
*/
void setChecksumType(const QByteArray& type);
QByteArray checksumType() const;
/**
* Computes the checksum for the given file path.
*
* In any case of processing (checksum set, no checksum required and also unusual error)
* the object will emit the signal validated(). The item->_checksum is than either
* set to a proper value or empty.
* done() is emitted when the calculation finishes.
*/
void uploadValidation();
/**
* method to verify the checksum coming with requests in a checksum header. The required
* checksum method is read from config.
*
* If no checksum is there, or if a correct checksum is there, the signal validated()
* will be emitted. In case of any kind of error, the signal validationFailed() will
* be emitted.
*/
void downloadValidation( const QByteArray& checksumHeader );
/**
* By default the checksum type is read from the config file, but can be overridden
* with this method.
*/
void setChecksumType(const QString& type);
QString checksumType() const;
void start(const QString& filePath);
signals:
void validated(const QByteArray& checksum);
void validationFailed( const QString& errMsg );
void done(const QByteArray& checksumType, const QByteArray& checksum);
private slots:
void slotUploadChecksumCalculated();
void slotDownloadChecksumCalculated();
void slotCalculationDone();
private:
QString _checksumType;
QByteArray _expectedHash;
QByteArray _checksumHeader;
QString _filePath;
QByteArray _checksumType;
// watcher for the checksum calculation thread
QFutureWatcher<QByteArray> _watcher;
};
/**
* Checks whether a file's checksum matches the expected value.
* @ingroup libsync
*/
class OWNCLOUDSYNC_EXPORT ValidateChecksumHeader : public QObject
{
Q_OBJECT
public:
explicit ValidateChecksumHeader(QObject *parent = 0);
/**
* Check a file's actual checksum against the provided checksumHeader
*
* If no checksum is there, or if a correct checksum is there, the signal validated()
* will be emitted. In case of any kind of error, the signal validationFailed() will
* be emitted.
*/
void start(const QString& filePath, const QByteArray& checksumHeader);
signals:
void validated(const QByteArray& checksumType, const QByteArray& checksum);
void validationFailed( const QString& errMsg );
private slots:
void slotChecksumCalculated(const QByteArray& checksumType, const QByteArray& checksum);
private:
QByteArray _expectedChecksumType;
QByteArray _expectedChecksum;
};
}

View file

@ -60,16 +60,63 @@ private slots:
record._remotePerm = "744";
record._mode = -17;
record._fileSize = 213089055;
record._transmissionChecksum = "mychecksum";
record._transmissionChecksumType = "MD5";
QVERIFY(_db.setFileRecord(record));
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo");
QVERIFY(storedRecord == record);
// Update checksum
record._transmissionChecksum = "newchecksum";
record._transmissionChecksumType = "Adler32";
_db.updateFileRecordChecksum("foo", record._transmissionChecksum, record._transmissionChecksumType);
storedRecord = _db.getFileRecord("foo");
QVERIFY(storedRecord == record);
// Update metadata
record._inode = 12345;
record._modtime = dropMsecs(QDateTime::currentDateTime().addDays(1));
record._type = 7;
record._etag = "789FFF";
record._fileId = "efg";
record._remotePerm = "777";
record._mode = 12;
record._fileSize = 289055;
_db.updateFileRecordMetadata(record);
storedRecord = _db.getFileRecord("foo");
QVERIFY(storedRecord == record);
QVERIFY(_db.deleteFileRecord("foo"));
record = _db.getFileRecord("foo");
QVERIFY(!record.isValid());
}
void testFileRecordChecksum()
{
// Try with and without a checksum
{
SyncJournalFileRecord record;
record._path = "foo-checksum";
record._remotePerm = "744";
record._transmissionChecksum = "mychecksum";
record._transmissionChecksumType = "MD5";
QVERIFY(_db.setFileRecord(record));
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo-checksum");
QVERIFY(storedRecord == record);
}
{
SyncJournalFileRecord record;
record._path = "foo-nochecksum";
record._remotePerm = "744";
QVERIFY(_db.setFileRecord(record));
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo-nochecksum");
QVERIFY(storedRecord == record);
}
}
void testDownloadInfo()
{
typedef SyncJournalDb::DownloadInfo Info;

View file

@ -33,14 +33,16 @@ using namespace OCC;
QString _testfile;
QString _expectedError;
QByteArray _expected;
QByteArray _expectedType;
bool _successDown;
bool _errorSeen;
public slots:
void slotUpValidated(const QByteArray& checksum) {
void slotUpValidated(const QByteArray& type, const QByteArray& checksum) {
qDebug() << "Checksum: " << checksum;
QVERIFY(_expected == checksum );
QVERIFY(_expectedType == type );
}
void slotDownValidated() {
@ -62,23 +64,22 @@ using namespace OCC;
rootDir.mkpath(_root );
_testfile = _root+"/csFile";
Utility::writeRandomFile( _testfile);
}
void testUploadChecksummingAdler() {
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType("Adler32");
ComputeChecksum *vali = new ComputeChecksum(this);
_expectedType = "Adler32";
vali->setChecksumType(_expectedType);
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
connect(vali, SIGNAL(done(QByteArray,QByteArray)), SLOT(slotUpValidated(QByteArray,QByteArray)));
QString testfile = _testfile;
_expected = "Adler32:"+FileSystem::calcAdler32( testfile );
_expected = FileSystem::calcAdler32( _testfile );
qDebug() << "XX Expected Checksum: " << _expected;
vali->uploadValidation();
vali->start(_testfile);
QEventLoop loop;
connect(vali, SIGNAL(validated(QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
connect(vali, SIGNAL(done(QByteArray,QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
loop.exec();
delete vali;
@ -86,16 +87,16 @@ using namespace OCC;
void testUploadChecksummingMd5() {
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType( OCC::checkSumMD5C );
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
ComputeChecksum *vali = new ComputeChecksum(this);
_expectedType = OCC::checkSumMD5C;
vali->setChecksumType(_expectedType);
connect(vali, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(slotUpValidated(QByteArray,QByteArray)));
_expected = checkSumMD5C;
_expected.append(":"+FileSystem::calcMd5( _testfile ));
vali->uploadValidation();
_expected = FileSystem::calcMd5( _testfile );
vali->start(_testfile);
QEventLoop loop;
connect(vali, SIGNAL(validated(QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
connect(vali, SIGNAL(done(QByteArray,QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
loop.exec();
delete vali;
@ -103,17 +104,17 @@ using namespace OCC;
void testUploadChecksummingSha1() {
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType( OCC::checkSumSHA1C );
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
ComputeChecksum *vali = new ComputeChecksum(this);
_expectedType = OCC::checkSumSHA1C;
vali->setChecksumType(_expectedType);
connect(vali, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(slotUpValidated(QByteArray,QByteArray)));
_expected = checkSumSHA1C;
_expected.append(":"+FileSystem::calcSha1( _testfile ));
_expected = FileSystem::calcSha1( _testfile );
vali->uploadValidation();
vali->start(_testfile);
QEventLoop loop;
connect(vali, SIGNAL(validated(QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
connect(vali, SIGNAL(done(QByteArray,QByteArray)), &loop, SLOT(quit()), Qt::QueuedConnection);
loop.exec();
delete vali;
@ -126,22 +127,21 @@ using namespace OCC;
adler.append(FileSystem::calcAdler32( _testfile ));
_successDown = false;
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType("Adler32");
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotDownValidated()));
ValidateChecksumHeader *vali = new ValidateChecksumHeader(this);
connect(vali, SIGNAL(validated(QByteArray,QByteArray)), this, SLOT(slotDownValidated()));
connect(vali, SIGNAL(validationFailed(QString)), this, SLOT(slotDownError(QString)));
vali->downloadValidation(adler);
vali->start(_testfile, adler);
QTRY_VERIFY(_successDown);
_expectedError = QLatin1String("The downloaded file does not match the checksum, it will be resumed.");
_errorSeen = false;
vali->downloadValidation("Adler32:543345");
vali->start(_testfile, "Adler32:543345");
QTRY_VERIFY(_errorSeen);
_expectedError = QLatin1String("The checksum header is malformed.");
_expectedError = QLatin1String("The checksum header contained an unknown checksum type 'Klaas32'");
_errorSeen = false;
vali->downloadValidation("Klaas32:543345");
vali->start(_testfile, "Klaas32:543345");
QTRY_VERIFY(_errorSeen);
delete vali;

View file

@ -2155,7 +2155,7 @@ Il est déconseillé de l&apos;utiliser.</translation>
<message>
<location filename="../src/gui/sharedialog.ui" line="77"/>
<source>Share link</source>
<translation>Partage par lien</translation>
<translation>Partager par lien</translation>
</message>
<message>
<location filename="../src/gui/sharedialog.ui" line="125"/>
@ -2211,7 +2211,7 @@ Il est déconseillé de l&apos;utiliser.</translation>
<location filename="../src/gui/sharedialog.cpp" line="564"/>
<location filename="../src/gui/sharedialog.cpp" line="565"/>
<source>&amp;Share link</source>
<translation>&amp;Partage par lien</translation>
<translation>&amp;Partager par lien</translation>
</message>
<message>
<location filename="../src/gui/sharedialog.cpp" line="583"/>

View file

@ -83,7 +83,7 @@
<message>
<location filename="../src/gui/accountsettings.ui" line="97"/>
<source>Storage space: ...</source>
<translation type="unfinished"/>
<translation>ストレージ空き容量: ...</translation>
</message>
<message>
<location filename="../src/gui/accountsettings.ui" line="167"/>
@ -240,7 +240,7 @@
<message>
<location filename="../src/gui/accountsettings.cpp" line="448"/>
<source>%1 of %2 in use</source>
<translation type="unfinished"/>
<translation>%2 %1 使</translation>
</message>
<message>
<location filename="../src/gui/accountsettings.cpp" line="453"/>
@ -517,7 +517,9 @@ Please go in the settings to select it if you wish to download it.</source>
<source>This sync would remove all the files in the sync folder '%1'.
This might be because the folder was silently reconfigured, or that all the files were manually removed.
Are you sure you want to perform this operation?</source>
<translation type="unfinished"/>
<translation> &apos;%1&apos;
</translation>
</message>
<message>
<location filename="../src/gui/folder.cpp" line="1119"/>
@ -615,7 +617,7 @@ Are you sure you want to perform this operation?</source>
<message>
<location filename="../src/gui/folderman.cpp" line="1142"/>
<source>The selected path is not a folder!</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/gui/folderman.cpp" line="1146"/>
@ -625,27 +627,27 @@ Are you sure you want to perform this operation?</source>
<message>
<location filename="../src/gui/folderman.cpp" line="1161"/>
<source>The local folder %1 is already used in a folder sync connection. Please pick another one!</source>
<translation type="unfinished"/>
<translation> %1 </translation>
</message>
<message>
<location filename="../src/gui/folderman.cpp" line="1166"/>
<source>The local folder %1 already contains a folder used in a folder sync connection. Please pick another one!</source>
<translation type="unfinished"/>
<translation> %1 </translation>
</message>
<message>
<location filename="../src/gui/folderman.cpp" line="1173"/>
<source>The local folder %1 is a symbolic link. The link target already contains a folder used in a folder sync connection. Please pick another one!</source>
<translation type="unfinished"/>
<translation> %1 </translation>
</message>
<message>
<location filename="../src/gui/folderman.cpp" line="1180"/>
<source>The local folder %1 is already contained in a folder used in a folder sync connection. Please pick another one!</source>
<translation type="unfinished"/>
<translation> %1 </translation>
</message>
<message>
<location filename="../src/gui/folderman.cpp" line="1186"/>
<source>The local folder %1 is a symbolic link. The link target is already contained in a folder used in a folder sync connection. Please pick another one!</source>
<translation type="unfinished"/>
<translation> %1 </translation>
</message>
</context>
<context>
@ -653,7 +655,7 @@ Are you sure you want to perform this operation?</source>
<message>
<location filename="../src/gui/folderstatusdelegate.cpp" line="33"/>
<source>Add Folder Sync Connection</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/gui/folderstatusdelegate.cpp" line="85"/>
@ -766,7 +768,7 @@ Total time left %5</source>
<message numerus="yes">
<location filename="../src/gui/folderstatusmodel.cpp" line="872"/>
<source>Waiting for %n other folder(s)...</source>
<translation type="unfinished"><numerusform></numerusform></translation>
<translation><numerusform>%n ...</numerusform></translation>
</message>
<message>
<location filename="../src/gui/folderstatusmodel.cpp" line="878"/>
@ -798,7 +800,7 @@ Total time left %5</source>
<message>
<location filename="../src/gui/folderwizard.cpp" line="81"/>
<source>The folder alias is a descriptive name for this sync connection.</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/gui/folderwizard.cpp" line="115"/>
@ -846,7 +848,7 @@ Total time left %5</source>
<message>
<location filename="../src/gui/folderwizard.cpp" line="271"/>
<source>Failed to list a folder. Error: %1</source>
<translation type="unfinished"/>
<translation>: %1</translation>
</message>
<message>
<location filename="../src/gui/folderwizard.cpp" line="351"/>
@ -1068,7 +1070,9 @@ Account: %3
<source>Files or folders matching a pattern will not be synchronized.
Items where deletion is allowed will be deleted if they prevent a directory from being removed. This is useful for meta data.</source>
<translation type="unfinished"/>
<translation>
</translation>
</message>
<message>
<location filename="../src/gui/ignorelisteditor.cpp" line="109"/>
@ -1390,12 +1394,12 @@ for additional privileges during the process.</source>
<message>
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="142"/>
<source>Sync the folder &apos;%1&apos;</source>
<translation type="unfinished"/>
<translation>&apos;%1&apos; </translation>
</message>
<message>
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="147"/>
<source>&lt;p&gt;&lt;small&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The local folder is not empty. Pick a resolution!&lt;/small&gt;&lt;/p&gt;</source>
<translation type="unfinished"/>
<translation>&lt;p&gt;&lt;small&gt;&lt;strong&gt;:&lt;/strong&gt; &lt;/small&gt;&lt;/p&gt;</translation>
</message>
<message>
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="246"/>
@ -1742,7 +1746,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/owncloudpropagator.cpp" line="211"/>
<source>A file or folder was removed from a read only share, but restoring failed: %1</source>
<translation type="unfinished"/>
<translation>: %1</translation>
</message>
</context>
<context>
@ -1755,7 +1759,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/propagatorjobs.cpp" line="151"/>
<source>could not create folder %1</source>
<translation type="unfinished"/>
<translation> %1 </translation>
</message>
</context>
<context>
@ -1868,7 +1872,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/propagateupload.cpp" line="558"/>
<source>Forcing job abort on HTTP connection reset with Qt &lt; 5.4.2.</source>
<translation type="unfinished"/>
<translation>5.4.2 Qt HTTP </translation>
</message>
<message>
<location filename="../src/libsync/propagateupload.cpp" line="566"/>
@ -2402,7 +2406,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/gui/sslbutton.cpp" line="219"/>
<source>No support for SSL session tickets/identifiers</source>
<translation type="unfinished"/>
<translation>SSLセッションチケット/</translation>
</message>
<message>
<location filename="../src/gui/sslbutton.cpp" line="230"/>
@ -2575,27 +2579,27 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/syncengine.cpp" line="164"/>
<source>The mounted folder is temporarily not available on the server</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="167"/>
<source>An error occurred while opening a folder</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="170"/>
<source>Error while reading folder.</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="996"/>
<source>Not allowed because you don&apos;t have permission to add parent folder</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="1003"/>
<source>Not allowed because you don&apos;t have permission to add files in that folder</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="152"/>
@ -2625,12 +2629,12 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/syncengine.cpp" line="100"/>
<source>CSync failed to load or create the journal file. Make sure you have read and write permissions in the local sync folder.</source>
<translation type="unfinished"/>
<translation>CSyncはジャーナルファイルの読み込みや作成に失敗しました</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="149"/>
<source>CSync tried to create a folder that already exists.</source>
<translation type="unfinished"/>
<translation>CSyncはすでに存在するフォルダーを作成しようとしました</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="161"/>
@ -2701,7 +2705,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/syncengine.cpp" line="990"/>
<source>Not allowed because you don&apos;t have permission to add subfolders to that folder</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="1023"/>
@ -2812,7 +2816,7 @@ It is not advisable to use it.</source>
<location filename="../src/gui/owncloudgui.cpp" line="459"/>
<location filename="../src/gui/owncloudgui.cpp" line="526"/>
<source>Log in...</source>
<translation type="unfinished"/>
<translation>...</translation>
</message>
<message>
<location filename="../src/gui/owncloudgui.cpp" line="354"/>
@ -2849,12 +2853,12 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/gui/owncloudgui.cpp" line="449"/>
<source>Log out everywhere</source>
<translation type="unfinished"/>
<translation></translation>
</message>
<message>
<location filename="../src/gui/owncloudgui.cpp" line="457"/>
<source>Log in everywhere...</source>
<translation type="unfinished"/>
<translation>...</translation>
</message>
<message>
<location filename="../src/gui/owncloudgui.cpp" line="513"/>
@ -2967,7 +2971,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.ui" line="200"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If this box is checked, existing content in the local folder will be erased to start a clean sync from the server.&lt;/p&gt;&lt;p&gt;Do not check this if the local content should be uploaded to the servers folder.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"/>
<translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
</message>
<message>
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.ui" line="203"/>
@ -3161,7 +3165,7 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/utility.cpp" line="125"/>
<source>%L1 KB</source>
<translation type="unfinished"/>
<translation>%L1 KB</translation>
</message>
<message>
<location filename="../src/libsync/utility.cpp" line="128"/>

File diff suppressed because it is too large Load diff