mirror of
https://github.com/nextcloud/desktop.git
synced 2024-10-27 15:05:19 +03:00
Merge remote-tracking branch 'upstream/master'
Conflicts: src/cmd/cmd.cpp
This commit is contained in:
commit
1ab44655e0
46 changed files with 1670 additions and 852 deletions
64
ChangeLog
64
ChangeLog
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
@ -48,8 +53,8 @@ class SocketConnect(GObject.GObject):
|
|||
self._sock = None
|
||||
self._listeners = [self._update_registered_paths]
|
||||
self._remainder = ''
|
||||
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
||||
# all over the other objects.
|
||||
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
||||
# all over the other objects.
|
||||
|
||||
# returns true when one should try again!
|
||||
if self._connectToSocketServer():
|
||||
|
@ -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
|
||||
|
|
|
@ -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,8 +482,12 @@ restart_sync:
|
|||
csync_destroy(_csync_ctx);
|
||||
|
||||
if (engine.isAnotherSyncNeeded()) {
|
||||
qDebug() << "Restarting Sync, because another sync is needed";
|
||||
goto restart_sync;
|
||||
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;
|
||||
|
|
|
@ -58,6 +58,7 @@ set(client_SRCS
|
|||
protocolwidget.cpp
|
||||
selectivesyncdialog.cpp
|
||||
settingsdialog.cpp
|
||||
share.cpp
|
||||
sharedialog.cpp
|
||||
socketapi.cpp
|
||||
sslbutton.cpp
|
||||
|
|
|
@ -455,7 +455,15 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
|||
ui->quotaProgressBar->setToolTip(toolTip);
|
||||
} else {
|
||||
ui->quotaProgressBar->setVisible(false);
|
||||
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
emit jobFinished(json);
|
||||
deleteLater();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
265
src/gui/share.cpp
Normal 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
248
src/gui/share.h
Normal 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
|
|
@ -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,56 +223,35 @@ 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();
|
||||
}
|
||||
|
||||
job->createShare(_sharePath, OcsShareJob::ShareType::Link, password, date);
|
||||
_ui->checkBox_shareLink->setEnabled(false);
|
||||
_manager->createLinkShare(_sharePath, password);
|
||||
}
|
||||
_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
|
||||
* at this point.
|
||||
*/
|
||||
* 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
|
||||
* at this point.
|
||||
*/
|
||||
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))) {
|
||||
/*
|
||||
* Only directories can have public upload set
|
||||
* For public links the server sets CREATE and UPDATE permissions.
|
||||
*/
|
||||
if (!_isFile) {
|
||||
_ui->checkBox_editing->setEnabled(true);
|
||||
if (_share->getPublicUpload()) {
|
||||
_ui->checkBox_editing->setChecked(true);
|
||||
} else {
|
||||
_ui->checkBox_editing->setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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å 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,44 +426,47 @@ 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 {
|
||||
_pi_link->startAnimation();
|
||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotDeleteShareFetched(QVariantMap)));
|
||||
job->deleteShare(_public_share_id);
|
||||
|
||||
if (!_share.isNull()) {
|
||||
// We have a share so delete it
|
||||
_pi_link->startAnimation();
|
||||
_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) {
|
||||
// there needs to be a password
|
||||
_ui->checkBox_password->setChecked(true);
|
||||
_ui->checkBox_password->setEnabled(false);
|
||||
_ui->checkBox_password->setText(tr("Public shå requires a password"));
|
||||
_ui->lineEdit_password->setFocus();
|
||||
_ui->pushButton_copy->hide();
|
||||
_ui->widget_shareLink->show();
|
||||
|
||||
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();
|
||||
_share = share;
|
||||
getShares();
|
||||
}
|
||||
|
||||
void ShareDialog::slotCreateShareRequiresPassword()
|
||||
{
|
||||
// there needs to be a password
|
||||
_ui->checkBox_password->setChecked(true);
|
||||
_ui->checkBox_password->setEnabled(false);
|
||||
_ui->checkBox_password->setText(tr("Public shå requires a password"));
|
||||
_ui->lineEdit_password->setFocus();
|
||||
_ui->pushButton_copy->hide();
|
||||
_ui->widget_shareLink->show();
|
||||
_ui->checkBox_expire->setEnabled(false);
|
||||
_ui->checkBox_editing->setEnabled(false);
|
||||
|
||||
slotCheckBoxPasswordClicked();
|
||||
}
|
||||
|
||||
void ShareDialog::slotCheckBoxPasswordClicked()
|
||||
{
|
||||
if (_ui->checkBox_password->checkState() == Qt::Checked) {
|
||||
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
QByteArray _remotePerm;
|
||||
int _mode;
|
||||
bool _serverHasIgnoredFiles;
|
||||
QByteArray _transmissionChecksum;
|
||||
QByteArray _transmissionChecksumType;
|
||||
};
|
||||
|
||||
bool OWNCLOUDSYNC_EXPORT
|
||||
|
|
|
@ -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.
|
||||
// Calculate the checksum in a different thread first.
|
||||
connect( &_watcher, SIGNAL(finished()),
|
||||
this, SLOT(slotCalculationDone()),
|
||||
Qt::UniqueConnection );
|
||||
if( csType == checkSumMD5C ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, filePath));
|
||||
|
||||
connect( &_watcher, SIGNAL(finished()),
|
||||
this, SLOT(slotUploadChecksumCalculated()));
|
||||
if( csType == checkSumMD5C ) {
|
||||
_checksumHeader = checkSumMD5C;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
|
||||
|
||||
} else if( csType == checkSumSHA1C ) {
|
||||
_checksumHeader = checkSumSHA1C;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, _filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( csType == checkSumAdlerC) {
|
||||
_checksumHeader = checkSumAdlerC;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// for an unknown checksum, continue to upload
|
||||
emit validated(QByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::slotUploadChecksumCalculated( )
|
||||
{
|
||||
QByteArray checksum = _watcher.future().result();
|
||||
|
||||
if( !checksum.isEmpty() ) {
|
||||
checksum.prepend( _checksumHeader );
|
||||
}
|
||||
|
||||
emit validated(checksum);
|
||||
}
|
||||
|
||||
|
||||
void TransmissionChecksumValidator::downloadValidation( const QByteArray& checksumHeader )
|
||||
{
|
||||
// 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());
|
||||
return;
|
||||
}
|
||||
|
||||
int indx = checksumHeader.indexOf(':');
|
||||
if( indx < 0 ) {
|
||||
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));
|
||||
} else if( csType == checkSumSHA1C ) {
|
||||
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( type == checkSumAdlerUpperC ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
else if( csType == checkSumAdlerC) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
qDebug() << "Unknown checksum type" << type;
|
||||
// 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 ComputeChecksum::slotCalculationDone()
|
||||
{
|
||||
QByteArray checksum = _watcher.future().result();
|
||||
emit done(_checksumType, checksum);
|
||||
}
|
||||
|
||||
|
||||
ValidateChecksumHeader::ValidateChecksumHeader(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if( !parseChecksumHeader(checksumHeader, &_expectedChecksumType, &_expectedChecksum) ) {
|
||||
qDebug() << "Checksum header malformed:" << checksumHeader;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2155,7 +2155,7 @@ Il est déconseillé de l'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'utiliser.</translation>
|
|||
<location filename="../src/gui/sharedialog.cpp" line="564"/>
|
||||
<location filename="../src/gui/sharedialog.cpp" line="565"/>
|
||||
<source>&Share link</source>
|
||||
<translation>&Partage par lien</translation>
|
||||
<translation>&Partager par lien</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharedialog.cpp" line="583"/>
|
||||
|
|
|
@ -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>この同期により、ローカルの同期フォルダー '%1'にある全ファイルが削除されます。
|
||||
これはフォルダーが黙って再構成されたか、すべてのファイルが手動で削除されたことが原因である場合があります。
|
||||
本当にこの操作を実行しますか?</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 '%1'</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>'%1' フォルダーを同期</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="147"/>
|
||||
<source><p><small><strong>Warning:</strong> The local folder is not empty. Pick a resolution!</small></p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><p><small><strong>警告:</strong> ローカルフォルダーは空ではありません。解決方法を選択してください!</small></p></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 < 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'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'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'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><html><head/><body><p>If this box is checked, existing content in the local folder will be erased to start a clean sync from the server.</p><p>Do not check this if the local content should be uploaded to the servers folder.</p></body></html></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><html><head/><body><p>チェックした場合、ローカルフォルダー内に存在するコンテンツはサーバーからクリーンな同期を開始するために削除されます。</p><p>もしローカルのコンテンツをサーバーのフォルダーにアップロードするなら、チェックしないでください。</p></body></html></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
Loading…
Reference in a new issue