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.
|
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.
|
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
|
* csync_file_stat_s: Save a bit of memory
|
||||||
* Shibboleth: Add our base user agent to WebKit
|
* Shibboleth: Add our base user agent to WebKit
|
||||||
* SelectiveSync: Increase folder list timeout to 60
|
* SelectiveSync: Increase folder list timeout to 60
|
||||||
* Propagation: Try another sync on 423 Locked #3387
|
* Propagation: Try another sync on 423 Locked (#3387)
|
||||||
* Propagation: Make 423 Locked a soft error #3387
|
* Propagation: Make 423 Locked a soft error (#3387)
|
||||||
* Propagation: Reset upload blacklist if a chunk suceeds
|
* Propagation: Reset upload blacklist if a chunk succeeds
|
||||||
* Application: Fix crash on early shutdown #3898
|
* Application: Fix crash on early shutdown (#3898)
|
||||||
* Linux: Don't show settings dialog always when launched twice #3273 #3771 #3485
|
* 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
|
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. (#3813)
|
||||||
* AccountSettings: only expand root elements on single click.
|
* AccountSettings: only expand root elements on single click.
|
||||||
* AccountSettings: Do not allow to expand the folder list when disconnected.
|
* AccountSettings: Do not allow to expand the folder list when disconnected.
|
||||||
* Use application SHORT name for the name of the MacOSX pkg file (ownBrander).
|
* Use application SHORT name for the name of the MacOSX pkg file (ownBrander).
|
||||||
* FolderMan: Fix for removing a syncing folder #3843
|
* FolderMan: Fix for removing a syncing folder (#3843)
|
||||||
* ConnectionMethodDialog: Don't be insecure on close #3863
|
* ConnectionMethodDialog: Don't be insecure on close (#3863)
|
||||||
* Updater: Ensure folders are not removed #3747
|
* Updater: Ensure folders are not removed (#3747)
|
||||||
* Folder settings: Ensure path is cleaned #3811
|
* Folder settings: Ensure path is cleaned (#3811)
|
||||||
* Propagator: Simplify sub job finished counting #3844
|
* Propagator: Simplify sub job finished counting (#3844)
|
||||||
* Share dialog: Hide settings dialog before showing #3783
|
* Share dialog: Hide settings dialog before showing (#3783)
|
||||||
* UI: Only expand 1 level in folder list #3585
|
* UI: Only expand 1 level in folder list (#3585)
|
||||||
* UI: Allow folder expanding from button click #3585
|
* UI: Allow folder expanding from button click (#3585)
|
||||||
* UI: Expand folder treeview on single click #3585
|
* UI: Expand folder treeview on single click (#3585)
|
||||||
* GUI: Change tray menu order #3657
|
* GUI: Change tray menu order (#3657)
|
||||||
* GUI: Replace term "sign in" with "Log in" and friends.
|
* GUI: Replace term "sign in" with "Log in" and friends.
|
||||||
* SetupPage: Fix crash caused by uninitialized Account object.
|
* SetupPage: Fix crash caused by uninitialized Account object.
|
||||||
* Use a themable WebDAV path all over.
|
* Use a themable WebDAV path all over.
|
||||||
* Units: Back to the "usual" mix units (JEDEC standard).
|
* Units: Back to the "usual" mix units (JEDEC standard).
|
||||||
* csync io: Full UNC path support on Win #3748
|
* csync io: Full UNC path support on Win (#3748)
|
||||||
* Tray: Don't use the tray workaround with the KDE theme #3706, #3765
|
* Tray: Don't use the tray workaround with the KDE theme (#3706, #3765)
|
||||||
* ShareDialog: Fix folder display #3659
|
* ShareDialog: Fix folder display (#3659)
|
||||||
* AccountSettings: Restore from legacy only once #3565
|
* AccountSettings: Restore from legacy only once (#3565)
|
||||||
* SSL Certificate Error Dialog: show account name #3729
|
* SSL Certificate Error Dialog: show account name (#3729)
|
||||||
* Tray notification: Don't show a message about modified folder #3613
|
* Tray notification: Don't show a message about modified folder (#3613)
|
||||||
* PropagateLocalRemove: remove entries from the DB even if there was an error.
|
* PropagateLocalRemove: remove entries from the DB even if there was an error.
|
||||||
* Settings UI improvements (eg. #3713, #3721, #3619 and others)
|
* Settings UI improvements (eg. #3713, #3721, #3619 and others)
|
||||||
* Folder: Do not create the sync folder if it does not exist #3692
|
* 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
|
* Shell integration: don't show share menu item for top level folders
|
||||||
* Tray: Hide while modifying menus #3656 #3672
|
* Tray: Hide while modifying menus (#3656, #3672)
|
||||||
* AddFolder: Improve remote path selection error handling #3573
|
* AddFolder: Improve remote path selection error handling (#3573)
|
||||||
* csync_update: Use excluded_traversal() to improve performance #3638
|
* csync_update: Use excluded_traversal() to improve performance (#3638)
|
||||||
* csync_excluded: Add fast _traversal() function #3638
|
* csync_excluded: Add fast _traversal() function (#3638)
|
||||||
* csync_exclude: Speed up siginificantly #3638
|
* csync_exclude: Speed up significantly (#3638)
|
||||||
* AccountSettings: Adjust quota info design #3644 #3651
|
* AccountSettings: Adjust quota info design (#3644, #3651)
|
||||||
* Adjust buttons on remove folder/account questions #3654
|
* Adjust buttons on remove folder/account questions (#3654)
|
||||||
|
|
||||||
version 2.0.1 (release 2015-09-01)
|
version 2.0.1 (release 2015-09-01)
|
||||||
* AccountWizard: fix when the theme specify a override URL (#3699)
|
* 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;
|
res = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for non windows platforms, detect if the filename starts with a .
|
/* if the filename starts with a . we consider it a hidden file
|
||||||
* and if so, it's a hidden file. For windows, the hidden state is
|
* For windows, the hidden state is also discovered within the vio
|
||||||
* discovered within the vio local stat function.
|
* local stat function.
|
||||||
*/
|
*/
|
||||||
#ifndef _WIN32
|
|
||||||
if( d_name[0] == '.' ) {
|
if( d_name[0] == '.' ) {
|
||||||
dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
|
dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if( res == 0) {
|
if( res == 0) {
|
||||||
switch (dirent->type) {
|
switch (dirent->type) {
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
#
|
#
|
||||||
# Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
@ -18,7 +22,9 @@ import socket
|
||||||
|
|
||||||
from gi.repository import GObject, Nautilus
|
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'
|
appname = 'ownCloud'
|
||||||
|
|
||||||
def get_local_path(url):
|
def get_local_path(url):
|
||||||
|
@ -38,7 +44,6 @@ def get_runtime_dir():
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SocketConnect(GObject.GObject):
|
class SocketConnect(GObject.GObject):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
GObject.GObject.__init__(self)
|
GObject.GObject.__init__(self)
|
||||||
|
@ -48,8 +53,8 @@ class SocketConnect(GObject.GObject):
|
||||||
self._sock = None
|
self._sock = None
|
||||||
self._listeners = [self._update_registered_paths]
|
self._listeners = [self._update_registered_paths]
|
||||||
self._remainder = ''
|
self._remainder = ''
|
||||||
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
||||||
# all over the other objects.
|
# all over the other objects.
|
||||||
|
|
||||||
# returns true when one should try again!
|
# returns true when one should try again!
|
||||||
if self._connectToSocketServer():
|
if self._connectToSocketServer():
|
||||||
|
@ -77,38 +82,38 @@ class SocketConnect(GObject.GObject):
|
||||||
def _connectToSocketServer(self):
|
def _connectToSocketServer(self):
|
||||||
try:
|
try:
|
||||||
self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
postfix = "/"+appname+"/socket"
|
postfix = "/" + appname + "/socket" # Should use os.path.join instead
|
||||||
sock_file = get_runtime_dir()+postfix
|
sock_file = get_runtime_dir() + postfix
|
||||||
print ("Socket: " + sock_file + " <=> " + postfix)
|
print ("Socket: " + sock_file + " <=> " + postfix)
|
||||||
if sock_file != postfix:
|
if sock_file != postfix:
|
||||||
try:
|
try:
|
||||||
print("Socket File: "+sock_file)
|
print("Socket File: " + sock_file)
|
||||||
self._sock.connect(sock_file)
|
self._sock.connect(sock_file)
|
||||||
self.connected = True
|
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)
|
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
|
||||||
print("Socket watch id: "+str(self._watch_id))
|
print("Socket watch id: " + str(self._watch_id))
|
||||||
return False # don't run again
|
return False # Don't run again
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Could not connect to unix socket." + str(e))
|
print("Could not connect to unix socket. " + str(e))
|
||||||
else:
|
else:
|
||||||
print("Sock-File not valid: "+sock_file)
|
print("Sock-File not valid: " + sock_file)
|
||||||
except Exception as e:
|
except Exception as e: # Bad habbit
|
||||||
print("Connect could not be established, try again later ")
|
print("Connect could not be established, try again later.")
|
||||||
self._sock.close()
|
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):
|
def _handle_notify(self, source, condition):
|
||||||
data = source.recv(1024)
|
data = source.recv(1024)
|
||||||
# prepend the remaining data from last call
|
# Prepend the remaining data from last call
|
||||||
if len(self._remainder) > 0:
|
if len(self._remainder) > 0:
|
||||||
data = self._remainder+data
|
data = self._remainder + data
|
||||||
self._remainder = ''
|
self._remainder = ''
|
||||||
|
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
# remember the remainder for next round
|
# Remember the remainder for next round
|
||||||
lastNL = data.rfind('\n');
|
lastNL = data.rfind('\n');
|
||||||
if lastNL > -1 and lastNL < len(data):
|
if lastNL > -1 and lastNL < len(data):
|
||||||
self._remainder = data[lastNL+1:]
|
self._remainder = data[lastNL+1:]
|
||||||
|
@ -119,10 +124,10 @@ class SocketConnect(GObject.GObject):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True # run again
|
return True # Run again
|
||||||
|
|
||||||
def _handle_server_response(self, line):
|
def _handle_server_response(self, line):
|
||||||
print("Server response: "+line)
|
print("Server response: " + line)
|
||||||
parts = line.split(':')
|
parts = line.split(':')
|
||||||
action = parts[0]
|
action = parts[0]
|
||||||
args = parts[1:]
|
args = parts[1:]
|
||||||
|
@ -151,32 +156,33 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||||
def get_file_items(self, window, files):
|
def get_file_items(self, window, files):
|
||||||
if len(files) != 1:
|
if len(files) != 1:
|
||||||
return
|
return
|
||||||
file=files[0]
|
file = files[0]
|
||||||
items=[]
|
items = []
|
||||||
|
|
||||||
# internal or external file?!
|
# Internal or external file?!
|
||||||
syncedFile = False
|
syncedFile = False
|
||||||
for reg_path in socketConnect.registered_paths:
|
for reg_path in socketConnect.registered_paths:
|
||||||
topLevelFolder=False
|
topLevelFolder = False
|
||||||
filename = get_local_path(file.get_uri())
|
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
|
# Check if its a folder (ends with an /), if yes add a "/"
|
||||||
if os.path.isdir(filename+"/"):
|
# otherwise it will not find the entry in the table
|
||||||
filename=filename+"/"
|
if os.path.isdir(filename + "/"):
|
||||||
#check if toplevel folder, we need to ignore those as they cannot be shared
|
filename += "/"
|
||||||
|
# Check if toplevel folder, we need to ignore those as they cannot be shared
|
||||||
if filename.count("/") < (reg_path.count("/")+2):
|
if filename.count("/") < (reg_path.count("/")+2):
|
||||||
topLevelFolder=True
|
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.
|
# status is ok. Not for ignored files etc.
|
||||||
# ignore top level folders
|
# ignore top level folders
|
||||||
if filename.startswith(reg_path) and topLevelFolder == False and socketConnect.nautilusVFSFile_table[filename]['state'] == 'OK':
|
if filename.startswith(reg_path) and topLevelFolder == False and socketConnect.nautilusVFSFile_table[filename]['state'] == 'OK':
|
||||||
syncedFile = True
|
syncedFile = True
|
||||||
|
|
||||||
# if it is neither in a synced folder or is a directory
|
# If it is neither in a synced folder or is a directory
|
||||||
if (not syncedFile):
|
if not syncedFile:
|
||||||
return items
|
return items
|
||||||
|
|
||||||
# create an menu item
|
# Create an menu item
|
||||||
labelStr = "Share with "+appname+"..."
|
labelStr = "Share with " + appname + "..."
|
||||||
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
|
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
|
||||||
tip='Share file %s through ownCloud' % file.get_name())
|
tip='Share file %s through ownCloud' % file.get_name())
|
||||||
item.connect("activate", self.menu_share, file)
|
item.connect("activate", self.menu_share, file)
|
||||||
|
@ -187,8 +193,8 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||||
|
|
||||||
def menu_share(self, menu, file):
|
def menu_share(self, menu, file):
|
||||||
filename = get_local_path(file.get_uri())
|
filename = get_local_path(file.get_uri())
|
||||||
print("Share file "+filename)
|
print("Share file " + filename)
|
||||||
socketConnect.sendCommand("SHARE:"+filename+"\n")
|
socketConnect.sendCommand("SHARE:" + filename + "\n")
|
||||||
|
|
||||||
|
|
||||||
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
|
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
|
||||||
|
@ -205,7 +211,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def askForOverlay(self, file):
|
def askForOverlay(self, file):
|
||||||
# print("Asking for overlay for "+file)
|
# print("Asking for overlay for "+file) # For debug only
|
||||||
if os.path.isdir(file):
|
if os.path.isdir(file):
|
||||||
folderStatus = socketConnect.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
|
folderStatus = socketConnect.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
|
||||||
|
|
||||||
|
@ -240,8 +246,8 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
||||||
'NOP' : appname +'_error'
|
'NOP' : appname +'_error'
|
||||||
}
|
}
|
||||||
|
|
||||||
# file = args[0]
|
# file = args[0] # For debug only
|
||||||
# print "Action for " + file + ": "+args[0]
|
# print("Action for " + file + ": " + args[0]) # For debug only
|
||||||
if action == 'STATUS':
|
if action == 'STATUS':
|
||||||
newState = args[0]
|
newState = args[0]
|
||||||
emblem = Emblems[newState]
|
emblem = Emblems[newState]
|
||||||
|
@ -253,7 +259,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
||||||
if( not itemStore['state'] or newState != itemStore['state'] ):
|
if( not itemStore['state'] or newState != itemStore['state'] ):
|
||||||
item = itemStore['item']
|
item = itemStore['item']
|
||||||
item.add_emblem(emblem)
|
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}
|
socketConnect.nautilusVFSFile_table[args[1]] = {'item': item, 'state':newState}
|
||||||
|
|
||||||
elif action == 'UPDATE_VIEW':
|
elif action == 'UPDATE_VIEW':
|
||||||
|
@ -278,9 +284,9 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
||||||
if filename.startswith(reg_path):
|
if filename.startswith(reg_path):
|
||||||
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
|
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)
|
self.askForOverlay(filename)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# print("Not in scope:"+filename)
|
# print("Not in scope:" + filename) # For debug only
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -60,6 +60,7 @@ struct CmdOptions {
|
||||||
QString exclude;
|
QString exclude;
|
||||||
QString unsyncedfolders;
|
QString unsyncedfolders;
|
||||||
QString davPath;
|
QString davPath;
|
||||||
|
int restartTimes;
|
||||||
};
|
};
|
||||||
|
|
||||||
// we can't use csync_set_userdata because the SyncEngine sets it already.
|
// 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 << " --password, -p [pass] Use [pass] as password" << std::endl;
|
||||||
std::cout << " -n Use netrc (5) for login" << 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 << " --non-interactive Do not block execution with interaction" << std::endl;
|
||||||
std::cout << " --nonshib, -ns Use Non Shibboleth WebDAV authentication" << std::endl;
|
std::cout << " --nonshib Use Non Shibboleth WebDAV authentication" << std::endl;
|
||||||
std::cout << " --davpath, -dp [path] Custom themed dav path, overrides --nonshib" << 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 << " -h Sync hidden files,do not ignore them" << std::endl;
|
||||||
std::cout << " --version, -v Display version and exit" << std::endl;
|
std::cout << " --version, -v Display version and exit" << std::endl;
|
||||||
std::cout << "" << std::endl;
|
std::cout << "" << std::endl;
|
||||||
exit(1);
|
exit(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void showVersion() {
|
void showVersion() {
|
||||||
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
|
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
|
||||||
std::cout << binaryName << " version " << qPrintable(Theme::instance()->version()) << std::endl;
|
std::cout << binaryName << " version " << qPrintable(Theme::instance()->version()) << std::endl;
|
||||||
exit(1);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseOptions( const QStringList& app_args, CmdOptions *options )
|
void parseOptions( const QStringList& app_args, CmdOptions *options )
|
||||||
|
@ -226,10 +228,12 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
|
||||||
options->exclude = it.next();
|
options->exclude = it.next();
|
||||||
} else if( option == "--unsyncedfolders" && !it.peekNext().startsWith("-") ) {
|
} else if( option == "--unsyncedfolders" && !it.peekNext().startsWith("-") ) {
|
||||||
options->unsyncedfolders = it.next();
|
options->unsyncedfolders = it.next();
|
||||||
} else if( option == "--nonshib" || option == "-ns") {
|
} else if( option == "--nonshib" ) {
|
||||||
options->nonShib = true;
|
options->nonShib = true;
|
||||||
} else if( (option == "--davpath" || option == "-dp") && !it.peekNext().startsWith("-") ) {
|
} else if( option == "--davpath" && !it.peekNext().startsWith("-") ) {
|
||||||
options->davPath = it.next();
|
options->davPath = it.next();
|
||||||
|
} else if( option == "--max-sync-retries" && !it.peekNext().startsWith("-") ) {
|
||||||
|
options->restartTimes = it.next().toInt();
|
||||||
} else {
|
} else {
|
||||||
help();
|
help();
|
||||||
}
|
}
|
||||||
|
@ -277,6 +281,7 @@ int main(int argc, char **argv) {
|
||||||
options.interactive = true;
|
options.interactive = true;
|
||||||
options.ignoreHiddenFiles = true;
|
options.ignoreHiddenFiles = true;
|
||||||
options.nonShib = false;
|
options.nonShib = false;
|
||||||
|
options.restartTimes = 3;
|
||||||
ClientProxy clientProxy;
|
ClientProxy clientProxy;
|
||||||
|
|
||||||
parseOptions( app.arguments(), &options );
|
parseOptions( app.arguments(), &options );
|
||||||
|
@ -374,6 +379,7 @@ int main(int argc, char **argv) {
|
||||||
account->setCredentials(cred);
|
account->setCredentials(cred);
|
||||||
account->setSslErrorHandler(sslErrorHandler);
|
account->setSslErrorHandler(sslErrorHandler);
|
||||||
|
|
||||||
|
int restartCount = 0;
|
||||||
restart_sync:
|
restart_sync:
|
||||||
|
|
||||||
CSYNC *_csync_ctx;
|
CSYNC *_csync_ctx;
|
||||||
|
@ -465,7 +471,7 @@ restart_sync:
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncEngine engine(account, _csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
|
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()));
|
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.
|
// 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);
|
csync_destroy(_csync_ctx);
|
||||||
|
|
||||||
if (engine.isAnotherSyncNeeded()) {
|
if (engine.isAnotherSyncNeeded()) {
|
||||||
qDebug() << "Restarting Sync, because another sync is needed";
|
if (restartCount < options.restartTimes) {
|
||||||
goto restart_sync;
|
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;
|
return 0;
|
||||||
|
|
|
@ -58,6 +58,7 @@ set(client_SRCS
|
||||||
protocolwidget.cpp
|
protocolwidget.cpp
|
||||||
selectivesyncdialog.cpp
|
selectivesyncdialog.cpp
|
||||||
settingsdialog.cpp
|
settingsdialog.cpp
|
||||||
|
share.cpp
|
||||||
sharedialog.cpp
|
sharedialog.cpp
|
||||||
socketapi.cpp
|
socketapi.cpp
|
||||||
sslbutton.cpp
|
sslbutton.cpp
|
||||||
|
|
|
@ -455,7 +455,15 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
||||||
ui->quotaProgressBar->setToolTip(toolTip);
|
ui->quotaProgressBar->setToolTip(toolTip);
|
||||||
} else {
|
} else {
|
||||||
ui->quotaProgressBar->setVisible(false);
|
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()
|
stream << _theme->appName().toLatin1().constData()
|
||||||
<< QLatin1String(" version ")
|
<< QLatin1String(" version ")
|
||||||
<< _theme->version().toLatin1().constData() << endl;
|
<< _theme->version().toLatin1().constData() << endl;
|
||||||
|
stream << "Using Qt " << qVersion() << endl;
|
||||||
|
|
||||||
displayHelpText(helpText);
|
displayHelpText(helpText);
|
||||||
}
|
}
|
||||||
|
|
|
@ -887,7 +887,7 @@ void Folder::startSync(const QStringList &pathList)
|
||||||
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
|
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
|
||||||
|
|
||||||
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
|
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(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
|
||||||
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
|
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
|
||||||
|
|
||||||
|
@ -959,7 +959,7 @@ void Folder::slotCsyncUnavailable()
|
||||||
_csyncUnavail = true;
|
_csyncUnavail = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Folder::slotSyncFinished()
|
void Folder::slotSyncFinished(bool success)
|
||||||
{
|
{
|
||||||
qDebug() << " - client version" << qPrintable(Theme::instance()->version())
|
qDebug() << " - client version" << qPrintable(Theme::instance()->version())
|
||||||
<< " Qt" << qVersion()
|
<< " Qt" << qVersion()
|
||||||
|
@ -1017,7 +1017,7 @@ void Folder::slotSyncFinished()
|
||||||
qDebug() << "the last" << _consecutiveFailingSyncs << "syncs failed";
|
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
|
// Clear the white list as all the folders that should be on that list are sync-ed
|
||||||
journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList());
|
journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,7 +251,7 @@ private slots:
|
||||||
void slotSyncStarted();
|
void slotSyncStarted();
|
||||||
void slotSyncError(const QString& );
|
void slotSyncError(const QString& );
|
||||||
void slotCsyncUnavailable();
|
void slotCsyncUnavailable();
|
||||||
void slotSyncFinished();
|
void slotSyncFinished(bool);
|
||||||
|
|
||||||
void slotFolderDiscovered(bool local, QString folderName);
|
void slotFolderDiscovered(bool local, QString folderName);
|
||||||
void slotTransmissionProgress(const ProgressInfo& pi);
|
void slotTransmissionProgress(const ProgressInfo& pi);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include <QFileIconProvider>
|
#include <QFileIconProvider>
|
||||||
#include <QVarLengthArray>
|
#include <QVarLengthArray>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QPersistentModelIndex)
|
Q_DECLARE_METATYPE(QPersistentModelIndex)
|
||||||
|
|
||||||
|
@ -546,10 +547,16 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||||
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
|
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
|
||||||
}
|
}
|
||||||
auto selectiveSyncUndecidedList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
|
auto selectiveSyncUndecidedList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
|
||||||
|
|
||||||
QVarLengthArray<int, 10> undecidedIndexes;
|
QVarLengthArray<int, 10> undecidedIndexes;
|
||||||
|
|
||||||
QVector<SubFolderInfo> newSubs;
|
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);
|
newSubs.reserve(list.size() - 1);
|
||||||
for (int i = 1; // skip the parent item (first in the list)
|
for (int i = 1; // skip the parent item (first in the list)
|
||||||
i < list.size(); ++i) {
|
i < list.size(); ++i) {
|
||||||
|
@ -586,11 +593,19 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(const QString &str , selectiveSyncUndecidedList) {
|
auto it = selectiveSyncUndecidedSet.lower_bound(relativePath);
|
||||||
if (str == relativePath) {
|
if (it != selectiveSyncUndecidedSet.end()) {
|
||||||
|
if (*it == relativePath) {
|
||||||
newInfo._isUndecided = true;
|
newInfo._isUndecided = true;
|
||||||
} else if (str.startsWith(relativePath)) {
|
selectiveSyncUndecidedSet.erase(it);
|
||||||
|
} else if ((*it).startsWith(relativePath)) {
|
||||||
undecidedIndexes.append(newInfo._pathIdx.last());
|
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);
|
newSubs.append(newInfo);
|
||||||
|
@ -603,6 +618,16 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||||
for (auto it = undecidedIndexes.begin(); it != undecidedIndexes.end(); ++it) {
|
for (auto it = undecidedIndexes.begin(); it != undecidedIndexes.end(); ++it) {
|
||||||
suggestExpand(idx.child(*it, 0));
|
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)
|
void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
|
||||||
|
|
|
@ -42,9 +42,9 @@ void OcsJob::addPassStatusCode(int code)
|
||||||
_passStatusCodes.append(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()
|
void OcsJob::start()
|
||||||
|
@ -105,9 +105,11 @@ bool OcsJob::finished()
|
||||||
<< Account::concatUrlPath(account()->url(), path())
|
<< Account::concatUrlPath(account()->url(), path())
|
||||||
<< _params
|
<< _params
|
||||||
<< "has unexpected status code:" << statusCode << replyData;
|
<< "has unexpected status code:" << statusCode << replyData;
|
||||||
|
emit ocsError(statusCode, message);
|
||||||
|
} else {
|
||||||
|
emit jobFinished(json);
|
||||||
}
|
}
|
||||||
|
deleteLater();
|
||||||
emit jobFinished(json);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ protected:
|
||||||
*
|
*
|
||||||
* This function appends the common id. so <PATH>/<ID>
|
* This function appends the common id. so <PATH>/<ID>
|
||||||
*/
|
*/
|
||||||
void appendPath(int id);
|
void appendPath(const QString &id);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -108,6 +108,15 @@ signals:
|
||||||
*/
|
*/
|
||||||
void jobFinished(QVariantMap reply);
|
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:
|
private slots:
|
||||||
virtual bool finished() Q_DECL_OVERRIDE;
|
virtual bool finished() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ OcsShareJob::OcsShareJob(AccountPtr account, QObject* parent)
|
||||||
: OcsJob(account, parent)
|
: OcsJob(account, parent)
|
||||||
{
|
{
|
||||||
setPath("ocs/v1.php/apps/files_sharing/api/v1/shares");
|
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)
|
void OcsShareJob::getShares(const QString &path)
|
||||||
|
@ -36,7 +37,7 @@ void OcsShareJob::getShares(const QString &path)
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OcsShareJob::deleteShare(int shareId)
|
void OcsShareJob::deleteShare(const QString &shareId)
|
||||||
{
|
{
|
||||||
appendPath(shareId);
|
appendPath(shareId);
|
||||||
setVerb("DELETE");
|
setVerb("DELETE");
|
||||||
|
@ -44,7 +45,7 @@ void OcsShareJob::deleteShare(int shareId)
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OcsShareJob::setExpireDate(int shareId, const QDate &date)
|
void OcsShareJob::setExpireDate(const QString &shareId, const QDate &date)
|
||||||
{
|
{
|
||||||
appendPath(shareId);
|
appendPath(shareId);
|
||||||
setVerb("PUT");
|
setVerb("PUT");
|
||||||
|
@ -54,32 +55,35 @@ void OcsShareJob::setExpireDate(int shareId, const QDate &date)
|
||||||
} else {
|
} else {
|
||||||
addParam(QString::fromLatin1("expireDate"), QString());
|
addParam(QString::fromLatin1("expireDate"), QString());
|
||||||
}
|
}
|
||||||
|
_value = date;
|
||||||
|
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OcsShareJob::setPassword(int shareId, const QString &password)
|
void OcsShareJob::setPassword(const QString &shareId, const QString &password)
|
||||||
{
|
{
|
||||||
appendPath(shareId);
|
appendPath(shareId);
|
||||||
setVerb("PUT");
|
setVerb("PUT");
|
||||||
|
|
||||||
addParam(QString::fromLatin1("password"), password);
|
addParam(QString::fromLatin1("password"), password);
|
||||||
|
_value = password;
|
||||||
|
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OcsShareJob::setPublicUpload(int shareId, bool publicUpload)
|
void OcsShareJob::setPublicUpload(const QString &shareId, bool publicUpload)
|
||||||
{
|
{
|
||||||
appendPath(shareId);
|
appendPath(shareId);
|
||||||
setVerb("PUT");
|
setVerb("PUT");
|
||||||
|
|
||||||
const QString value = QString::fromLatin1(publicUpload ? "true" : "false");
|
const QString value = QString::fromLatin1(publicUpload ? "true" : "false");
|
||||||
addParam(QString::fromLatin1("publicUpload"), value);
|
addParam(QString::fromLatin1("publicUpload"), value);
|
||||||
|
_value = publicUpload;
|
||||||
|
|
||||||
start();
|
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");
|
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)));
|
addParam(QString::fromLatin1("shareType"), QString::number(static_cast<int>(shareType)));
|
||||||
|
|
||||||
if (!password.isEmpty()) {
|
if (!password.isEmpty()) {
|
||||||
addParam(QString::fromLatin1("shareType"), password);
|
addParam(QString::fromLatin1("password"), password);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (date.isValid()) {
|
if (date.isValid()) {
|
||||||
|
@ -99,4 +103,9 @@ void OcsShareJob::createShare(const QString &path, ShareType shareType, const QS
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OcsShareJob::jobDone(QVariantMap reply)
|
||||||
|
{
|
||||||
|
emit shareJobFinished(reply, _value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#define OCSSHAREJOB_H
|
#define OCSSHAREJOB_H
|
||||||
|
|
||||||
#include "ocsjob.h"
|
#include "ocsjob.h"
|
||||||
|
#include "share.h"
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
@ -32,25 +33,6 @@ class OcsShareJob : public OcsJob {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
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
|
* Constructor for new shares or listing of shares
|
||||||
*/
|
*/
|
||||||
|
@ -66,7 +48,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Delete the current Share
|
* Delete the current Share
|
||||||
*/
|
*/
|
||||||
void deleteShare(int shareId);
|
void deleteShare(const QString &shareId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the expiration date of a share
|
* 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
|
* @param date The expire date, if this date is invalid the expire date
|
||||||
* will be removed
|
* will be removed
|
||||||
*/
|
*/
|
||||||
void setExpireDate(int shareId, const QDate& date);
|
void setExpireDate(const QString &shareId, const QDate& date);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the password of a share
|
* Set the password of a share
|
||||||
|
@ -82,14 +64,14 @@ public:
|
||||||
* @param password The password of the share, if the password is empty the
|
* @param password The password of the share, if the password is empty the
|
||||||
* share will be removed
|
* 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
|
* Void set the share to be public upload
|
||||||
*
|
*
|
||||||
* @param publicUpload Set or remove 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
|
* Create a new share
|
||||||
|
@ -99,7 +81,25 @@ public:
|
||||||
* @param password Optionally a password for the share
|
* @param password Optionally a password for the share
|
||||||
* @param date Optionally an expire date 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)
|
// The server can return fractional bytes (#1374)
|
||||||
// <d:quota-available-bytes>1374532061.2</d:quota-available-bytes>
|
// <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();
|
_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);
|
emit quotaUpdated(_lastQuotaTotalBytes, _lastQuotaUsedBytes);
|
||||||
_jobRestartTimer.start(defaultIntervalT);
|
_jobRestartTimer.start(defaultIntervalT);
|
||||||
_lastQuotaRecieved = QDateTime::currentDateTime();
|
_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 "configfile.h"
|
||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
|
|
||||||
#include "ocssharejob.h"
|
|
||||||
#include "thumbnailjob.h"
|
#include "thumbnailjob.h"
|
||||||
|
#include "share.h"
|
||||||
|
|
||||||
#include "QProgressIndicator.h"
|
#include "QProgressIndicator.h"
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
|
@ -41,7 +41,8 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
|
||||||
_sharePath(sharePath),
|
_sharePath(sharePath),
|
||||||
_localPath(localPath),
|
_localPath(localPath),
|
||||||
_passwordJobRunning(false),
|
_passwordJobRunning(false),
|
||||||
_public_share_id(0),
|
_manager(NULL),
|
||||||
|
_share(NULL),
|
||||||
_resharingAllowed(resharingAllowed)
|
_resharingAllowed(resharingAllowed)
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
@ -90,7 +91,6 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
|
||||||
_ui->lineEdit_password->hide();
|
_ui->lineEdit_password->hide();
|
||||||
_ui->pushButton_setPassword->hide();
|
_ui->pushButton_setPassword->hide();
|
||||||
|
|
||||||
_ui->calendar->setDate(QDate::currentDate().addDays(1));
|
|
||||||
_ui->calendar->setEnabled(false);
|
_ui->calendar->setEnabled(false);
|
||||||
|
|
||||||
QFileInfo f_info(_localPath);
|
QFileInfo f_info(_localPath);
|
||||||
|
@ -168,6 +168,16 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
|
||||||
_ui->checkBox_editing->setEnabled(false);
|
_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 ) {
|
void ShareDialog::done( int r ) {
|
||||||
|
@ -178,25 +188,12 @@ void ShareDialog::done( int r ) {
|
||||||
|
|
||||||
void ShareDialog::setExpireDate(const QDate &date)
|
void ShareDialog::setExpireDate(const QDate &date)
|
||||||
{
|
{
|
||||||
if( _public_share_id == 0 ) {
|
|
||||||
// no public share so far.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_pi_date->startAnimation();
|
_pi_date->startAnimation();
|
||||||
|
_share->setExpireDate(date);
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotExpireSet(QVariantMap)));
|
|
||||||
job->setExpireDate(_public_share_id, 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();
|
_pi_date->stopAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,56 +223,35 @@ void ShareDialog::slotPasswordChanged(const QString& newText)
|
||||||
|
|
||||||
void ShareDialog::setPassword(const QString &password)
|
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_link->startAnimation();
|
||||||
_pi_password->startAnimation();
|
_pi_password->startAnimation();
|
||||||
QString path;
|
|
||||||
|
|
||||||
if( _public_share_id > 0 ) {
|
_ui->checkBox_password->setEnabled(false);
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
_ui->lineEdit_password->setEnabled(false);
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPasswordSet(QVariantMap)));
|
|
||||||
job->setPassword(_public_share_id, password);
|
if( !_share.isNull() ) {
|
||||||
|
_share->setPassword(password);
|
||||||
} else {
|
} else {
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
_ui->checkBox_shareLink->setEnabled(false);
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPasswordSet(QVariantMap)));
|
_manager->createLinkShare(_sharePath, password);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
_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
|
* 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
|
* deleted and a new one is created. So we need to refetch the shares
|
||||||
* at this point.
|
* at this point.
|
||||||
*/
|
*/
|
||||||
getShares();
|
getShares();
|
||||||
|
|
||||||
_passwordJobRunning = false;
|
|
||||||
_pi_password->stopAnimation();
|
_pi_password->stopAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareDialog::getShares()
|
void ShareDialog::getShares()
|
||||||
{
|
{
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
_manager->fetchShares(_sharePath);
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotSharesFetched(QVariantMap)));
|
|
||||||
job->getShares(_sharePath);
|
|
||||||
|
|
||||||
if (QFileInfo(_localPath).isFile()) {
|
if (QFileInfo(_localPath).isFile()) {
|
||||||
ThumbnailJob *job2 = new ThumbnailJob(_sharePath, _account, this);
|
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();
|
const QString versionString = _account->serverVersion();
|
||||||
|
qDebug() << Q_FUNC_INFO << versionString << "Fetched" << shares.count() << "shares";
|
||||||
qDebug() << Q_FUNC_INFO << versionString << "Fetched" << ShareDialog::_shares.count() << "shares";
|
|
||||||
|
|
||||||
//Show link checkbox now
|
//Show link checkbox now
|
||||||
_ui->checkBox_shareLink->setEnabled(true);
|
_ui->checkBox_shareLink->setEnabled(true);
|
||||||
_pi_link->stopAnimation();
|
_pi_link->stopAnimation();
|
||||||
|
|
||||||
Q_FOREACH(auto share, ShareDialog::_shares) {
|
Q_FOREACH(auto share, shares) {
|
||||||
QVariantMap data = share.toMap();
|
|
||||||
|
|
||||||
if (data.value("share_type").toInt() == static_cast<int>(OcsShareJob::ShareType::Link)) {
|
if (share->getShareType() == Share::TypeLink) {
|
||||||
_public_share_id = data.value("id").toULongLong();
|
_share = qSharedPointerDynamicCast<LinkShare>(share);
|
||||||
_ui->pushButton_copy->show();
|
_ui->pushButton_copy->show();
|
||||||
|
|
||||||
_ui->widget_shareLink->show();
|
_ui->widget_shareLink->show();
|
||||||
_ui->checkBox_shareLink->setChecked(true);
|
_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->checkBox_password->setChecked(true);
|
||||||
_ui->lineEdit_password->setPlaceholderText("********");
|
_ui->lineEdit_password->setPlaceholderText("********");
|
||||||
_ui->lineEdit_password->show();
|
_ui->lineEdit_password->show();
|
||||||
|
@ -323,8 +292,9 @@ void ShareDialog::slotSharesFetched(const QVariantMap &reply)
|
||||||
_ui->pushButton_setPassword->hide();
|
_ui->pushButton_setPassword->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.value("expiration").isValid()) {
|
_ui->checkBox_expire->setEnabled(true);
|
||||||
_ui->calendar->setDate(QDate::fromString(data.value("expiration").toString(), "yyyy-MM-dd 00:00:00"));
|
if (_share->getExpireDate().isValid()) {
|
||||||
|
_ui->calendar->setDate(_share->getExpireDate());
|
||||||
_ui->calendar->setMinimumDate(QDate::currentDate().addDays(1));
|
_ui->calendar->setMinimumDate(QDate::currentDate().addDays(1));
|
||||||
_ui->calendar->setEnabled(true);
|
_ui->calendar->setEnabled(true);
|
||||||
_ui->checkBox_expire->setChecked(true);
|
_ui->checkBox_expire->setChecked(true);
|
||||||
|
@ -333,38 +303,33 @@ void ShareDialog::slotSharesFetched(const QVariantMap &reply)
|
||||||
_ui->checkBox_expire->setChecked(false);
|
_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.
|
||||||
* 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 (!_isFile &&
|
if (_share->getPublicUpload()) {
|
||||||
(permissions & static_cast<int>(OcsShareJob::Permission::Update)) &&
|
|
||||||
(permissions & static_cast<int>(OcsShareJob::Permission::Create))) {
|
|
||||||
_ui->checkBox_editing->setChecked(true);
|
_ui->checkBox_editing->setChecked(true);
|
||||||
|
} else {
|
||||||
|
_ui->checkBox_editing->setChecked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString url;
|
setShareLink(_share->getLink().toString());
|
||||||
// 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);
|
|
||||||
|
|
||||||
_ui->pushButton_copy->setEnabled(true);
|
_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);
|
setShareCheckBoxTitle(true);
|
||||||
} else {
|
} else {
|
||||||
// If there are no shares yet, check the checkbox to create a link automatically.
|
// 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;
|
_share.clear();
|
||||||
int code = OcsShareJob::getJsonReturnCode(reply, message);
|
|
||||||
if (code != 100) {
|
|
||||||
displayError(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
_public_share_id = 0;
|
|
||||||
_pi_link->stopAnimation();
|
_pi_link->stopAnimation();
|
||||||
_ui->lineEdit_password->clear();
|
_ui->lineEdit_password->clear();
|
||||||
_ui->_labelShareLink->clear();
|
_ui->_labelShareLink->clear();
|
||||||
|
@ -440,7 +399,6 @@ void ShareDialog::slotDeleteShareFetched(const QVariantMap &reply)
|
||||||
_shareUrl.clear();
|
_shareUrl.clear();
|
||||||
|
|
||||||
setShareCheckBoxTitle(false);
|
setShareCheckBoxTitle(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareDialog::slotCheckBoxShareLinkClicked()
|
void ShareDialog::slotCheckBoxShareLinkClicked()
|
||||||
|
@ -458,6 +416,8 @@ void ShareDialog::slotCheckBoxShareLinkClicked()
|
||||||
_ui->checkBox_password->setChecked(true);
|
_ui->checkBox_password->setChecked(true);
|
||||||
_ui->checkBox_password->setEnabled(false);
|
_ui->checkBox_password->setEnabled(false);
|
||||||
_ui->checkBox_password->setText(tr("Public shå requires a password"));
|
_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->lineEdit_password->setFocus();
|
||||||
_ui->pushButton_copy->hide();
|
_ui->pushButton_copy->hide();
|
||||||
_ui->widget_shareLink->show();
|
_ui->widget_shareLink->show();
|
||||||
|
@ -466,44 +426,47 @@ void ShareDialog::slotCheckBoxShareLinkClicked()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
_ui->checkBox_shareLink->setEnabled(false);
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotCreateShareFetched(QVariantMap)));
|
_manager->createLinkShare(_sharePath);
|
||||||
job->createShare(_sharePath, OcsShareJob::ShareType::Link);
|
|
||||||
} else {
|
} else {
|
||||||
_pi_link->startAnimation();
|
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
if (!_share.isNull()) {
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotDeleteShareFetched(QVariantMap)));
|
// We have a share so delete it
|
||||||
job->deleteShare(_public_share_id);
|
_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_link->stopAnimation();
|
||||||
|
_pi_password->stopAnimation();
|
||||||
|
|
||||||
if (code == 403) {
|
_share = share;
|
||||||
// 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();
|
|
||||||
getShares();
|
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()
|
void ShareDialog::slotCheckBoxPasswordClicked()
|
||||||
{
|
{
|
||||||
if (_ui->checkBox_password->checkState() == Qt::Checked) {
|
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->setPlaceholderText(tr("Please Set Password"));
|
||||||
_ui->lineEdit_password->setFocus();
|
_ui->lineEdit_password->setFocus();
|
||||||
} else {
|
} else {
|
||||||
ShareDialog::setPassword(QString());
|
setPassword(QString());
|
||||||
_ui->lineEdit_password->setPlaceholderText(QString());
|
_ui->lineEdit_password->setPlaceholderText(QString());
|
||||||
_pi_password->startAnimation();
|
_pi_password->startAnimation();
|
||||||
_ui->lineEdit_password->hide();
|
_ui->lineEdit_password->hide();
|
||||||
|
@ -532,7 +495,7 @@ void ShareDialog::slotCheckBoxExpireClicked()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ShareDialog::setExpireDate(QDate());
|
setExpireDate(QDate());
|
||||||
_ui->calendar->setEnabled(false);
|
_ui->calendar->setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,23 +524,13 @@ void ShareDialog::setPublicUpload(bool publicUpload)
|
||||||
_ui->checkBox_editing->setEnabled(false);
|
_ui->checkBox_editing->setEnabled(false);
|
||||||
_pi_editing->startAnimation();
|
_pi_editing->startAnimation();
|
||||||
|
|
||||||
OcsShareJob *job = new OcsShareJob(_account, this);
|
_share->setPublicUpload(publicUpload);
|
||||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPublicUploadSet(QVariantMap)));
|
|
||||||
job->setPublicUpload(_public_share_id, 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();
|
_pi_editing->stopAnimation();
|
||||||
|
_ui->checkBox_editing->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareDialog::setShareCheckBoxTitle(bool haveShares)
|
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)
|
void ShareDialog::displayError(const QString& errMsg)
|
||||||
{
|
{
|
||||||
_ui->errorLabel->setText( errMsg );
|
_ui->errorLabel->setText( errMsg );
|
||||||
|
@ -605,120 +565,6 @@ void ShareDialog::displayError(int code)
|
||||||
displayError(errMsg);
|
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)
|
void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &reply)
|
||||||
{
|
{
|
||||||
if (statusCode != 200) {
|
if (statusCode != 200) {
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include "QProgressIndicator.h"
|
#include "QProgressIndicator.h"
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -29,6 +31,9 @@ class ShareDialog;
|
||||||
class AbstractCredentials;
|
class AbstractCredentials;
|
||||||
class QuotaInfo;
|
class QuotaInfo;
|
||||||
class SyncResult;
|
class SyncResult;
|
||||||
|
class LinkShare;
|
||||||
|
class Share;
|
||||||
|
class ShareManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The ShareDialog class
|
* @brief The ShareDialog class
|
||||||
|
@ -45,11 +50,12 @@ public:
|
||||||
void getShares();
|
void getShares();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotSharesFetched(const QVariantMap &reply);
|
void slotSharesFetched(const QList<QSharedPointer<Share>> &shares);
|
||||||
void slotCreateShareFetched(const QVariantMap &reply);
|
void slotCreateShareFetched(const QSharedPointer<LinkShare> share);
|
||||||
void slotDeleteShareFetched(const QVariantMap &reply);
|
void slotCreateShareRequiresPassword();
|
||||||
void slotPasswordSet(const QVariantMap &reply);
|
void slotDeleteShareFetched();
|
||||||
void slotExpireSet(const QVariantMap &reply);
|
void slotPasswordSet();
|
||||||
|
void slotExpireSet();
|
||||||
void slotCalendarClicked(const QDate &date);
|
void slotCalendarClicked(const QDate &date);
|
||||||
void slotCheckBoxShareLinkClicked();
|
void slotCheckBoxShareLinkClicked();
|
||||||
void slotCheckBoxPasswordClicked();
|
void slotCheckBoxPasswordClicked();
|
||||||
|
@ -59,7 +65,9 @@ private slots:
|
||||||
void slotPushButtonCopyLinkPressed();
|
void slotPushButtonCopyLinkPressed();
|
||||||
void slotThumbnailFetched(const int &statusCode, const QByteArray &reply);
|
void slotThumbnailFetched(const int &statusCode, const QByteArray &reply);
|
||||||
void slotCheckBoxEditingClicked();
|
void slotCheckBoxEditingClicked();
|
||||||
void slotPublicUploadSet(const QVariantMap &reply);
|
void slotPublicUploadSet();
|
||||||
|
|
||||||
|
void displayError(int code, const QString &message);
|
||||||
|
|
||||||
void done( int r );
|
void done( int r );
|
||||||
private:
|
private:
|
||||||
|
@ -83,8 +91,6 @@ private:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool _passwordJobRunning;
|
bool _passwordJobRunning;
|
||||||
QList<QVariant> _shares;
|
|
||||||
qulonglong _public_share_id;
|
|
||||||
void setPassword(const QString &password);
|
void setPassword(const QString &password);
|
||||||
void setExpireDate(const QDate &date);
|
void setExpireDate(const QDate &date);
|
||||||
|
|
||||||
|
@ -93,6 +99,9 @@ private:
|
||||||
QProgressIndicator *_pi_date;
|
QProgressIndicator *_pi_date;
|
||||||
QProgressIndicator *_pi_editing;
|
QProgressIndicator *_pi_editing;
|
||||||
|
|
||||||
|
ShareManager *_manager;
|
||||||
|
QSharedPointer<LinkShare> _share;
|
||||||
|
|
||||||
bool _resharingAllowed;
|
bool _resharingAllowed;
|
||||||
bool _isFile;
|
bool _isFile;
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
|
|
||||||
|
#include "configfile.h"
|
||||||
|
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
@ -63,9 +65,28 @@ bool Capabilities::shareResharing() const
|
||||||
return _capabilities["files_sharing"].toMap()["resharing"].toBool();
|
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;
|
bool sharePublicLinkEnforceExpireDate() const;
|
||||||
int sharePublicLinkExpireDateDays() const;
|
int sharePublicLinkExpireDateDays() const;
|
||||||
bool shareResharing() 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:
|
private:
|
||||||
QVariantMap _capabilities;
|
QVariantMap _capabilities;
|
||||||
|
|
|
@ -588,11 +588,7 @@ void ConfigFile::setNewBigFolderSizeLimit(bool isChecked, quint64 mbytes)
|
||||||
bool ConfigFile::monoIcons() const
|
bool ConfigFile::monoIcons() const
|
||||||
{
|
{
|
||||||
QSettings settings(configFile(), QSettings::IniFormat);
|
QSettings settings(configFile(), QSettings::IniFormat);
|
||||||
bool deflt = false;
|
return settings.value(QLatin1String(monoIconsC), false).toBool();
|
||||||
if( Utility::isMac() ) {
|
|
||||||
deflt = true;
|
|
||||||
}
|
|
||||||
return settings.value(QLatin1String(monoIconsC), deflt).toBool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigFile::setMonoIcons(bool useMonoIcons)
|
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)
|
: 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();
|
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;
|
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();
|
//qDebug() << Q_FUNC_INFO << _subPath << file << map.count() << map.keys() << _account->davPath() << _lsColJob->reply()->request().url().path();
|
||||||
if (!_ignoredFirst) {
|
if (!_ignoredFirst) {
|
||||||
|
@ -392,7 +392,7 @@ void DiscoveryMainThread::setupHooks(DiscoveryJob *discoveryJob, const QString &
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coming from owncloud_opendir -> DiscoveryJob::vio_opendir_hook -> doOpendirSignal
|
// 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;
|
QString fullPath = _pathPrefix;
|
||||||
if (!_pathPrefix.endsWith('/')) {
|
if (!_pathPrefix.endsWith('/')) {
|
||||||
|
@ -445,7 +445,7 @@ void DiscoveryMainThread::singleDirectoryJobResultSlot(const QList<FileStatPoint
|
||||||
_discoveryJob->_vioMutex.unlock();
|
_discoveryJob->_vioMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg)
|
void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, const QString &msg)
|
||||||
{
|
{
|
||||||
if (!_currentDiscoveryDirectoryResult) {
|
if (!_currentDiscoveryDirectoryResult) {
|
||||||
return; // possibly aborted
|
return; // possibly aborted
|
||||||
|
@ -461,7 +461,7 @@ void DiscoveryMainThread::singleDirectoryJobFinishedWithErrorSlot(int csyncErrno
|
||||||
_discoveryJob->_vioMutex.unlock();
|
_discoveryJob->_vioMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(QString p)
|
void DiscoveryMainThread::singleDirectoryJobFirstDirectoryPermissionsSlot(const QString &p)
|
||||||
{
|
{
|
||||||
// Should be thread safe since the sync thread is blocked
|
// Should be thread safe since the sync thread is blocked
|
||||||
if (!_discoveryJob->_csync_ctx->remote.root_perms) {
|
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);
|
DiscoveryJob *discoveryJob = static_cast<DiscoveryJob*>(userdata);
|
||||||
if (discoveryJob) {
|
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;
|
directoryResult->code = EIO;
|
||||||
|
|
||||||
discoveryJob->_vioMutex.lock();
|
discoveryJob->_vioMutex.lock();
|
||||||
const QString qurl = QString::fromUtf8(url);
|
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->_vioWaitCondition.wait(&discoveryJob->_vioMutex, ULONG_MAX); // FIXME timeout?
|
||||||
discoveryJob->_vioMutex.unlock();
|
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.
|
// Upon awakening from the _vioWaitCondition, iterator should be a valid iterator.
|
||||||
if (directoryResult->code != 0) {
|
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;
|
errno = directoryResult->code;
|
||||||
// save the error string to the context
|
// save the error string to the context
|
||||||
discoveryJob->_csync_ctx->error_string = qstrdup( directoryResult->msg.toUtf8().constData() );
|
discoveryJob->_csync_ctx->error_string = qstrdup( directoryResult->msg.toUtf8().constData() );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (csync_vio_handle_t*) directoryResult;
|
return directoryResult.take();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ struct DiscoveryDirectoryResult {
|
||||||
class DiscoverySingleDirectoryJob : public QObject {
|
class DiscoverySingleDirectoryJob : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
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 start();
|
||||||
void abort();
|
void abort();
|
||||||
// This is not actually a network job, it is just a job
|
// This is not actually a network job, it is just a job
|
||||||
|
@ -89,9 +89,9 @@ signals:
|
||||||
void etagConcatenation(const QString &);
|
void etagConcatenation(const QString &);
|
||||||
void etag(const QString &);
|
void etag(const QString &);
|
||||||
void finishedWithResult(const QList<FileStatPointer> &);
|
void finishedWithResult(const QList<FileStatPointer> &);
|
||||||
void finishedWithError(int csyncErrnoCode, QString msg);
|
void finishedWithError(int csyncErrnoCode, const QString &msg);
|
||||||
private slots:
|
private slots:
|
||||||
void directoryListingIteratedSlot(QString,QMap<QString,QString>);
|
void directoryListingIteratedSlot(QString, const QMap<QString,QString>&);
|
||||||
void lsJobFinishedWithoutErrorSlot();
|
void lsJobFinishedWithoutErrorSlot();
|
||||||
void lsJobFinishedWithErrorSlot(QNetworkReply*);
|
void lsJobFinishedWithErrorSlot(QNetworkReply*);
|
||||||
private:
|
private:
|
||||||
|
@ -125,13 +125,13 @@ public:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// From DiscoveryJob:
|
// From DiscoveryJob:
|
||||||
void doOpendirSlot(QString url, DiscoveryDirectoryResult* );
|
void doOpendirSlot(const QString &url, DiscoveryDirectoryResult* );
|
||||||
void doGetSizeSlot(const QString &path ,qint64 *result);
|
void doGetSizeSlot(const QString &path ,qint64 *result);
|
||||||
|
|
||||||
// From Job:
|
// From Job:
|
||||||
void singleDirectoryJobResultSlot(const QList<FileStatPointer> &);
|
void singleDirectoryJobResultSlot(const QList<FileStatPointer> &);
|
||||||
void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg);
|
void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, const QString &msg);
|
||||||
void singleDirectoryJobFirstDirectoryPermissionsSlot(QString);
|
void singleDirectoryJobFirstDirectoryPermissionsSlot(const QString&);
|
||||||
|
|
||||||
void slotGetSizeFinishedWithError();
|
void slotGetSizeFinishedWithError();
|
||||||
void slotGetSizeResult(const QVariantMap&);
|
void slotGetSizeResult(const QVariantMap&);
|
||||||
|
|
|
@ -87,11 +87,11 @@ int OwncloudPropagator::maximumActiveJob()
|
||||||
return max;
|
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.
|
* 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 oldEntry = journal->errorBlacklistEntry(item._file);
|
||||||
SyncJournalErrorBlacklistRecord newEntry = SyncJournalErrorBlacklistRecord::update(oldEntry, item);
|
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.
|
// do not blacklist in case of soft error or fatal error.
|
||||||
break;
|
break;
|
||||||
case SyncFileItem::NormalError:
|
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
|
// do not error if the item was, and continues to be, blacklisted
|
||||||
status = SyncFileItem::FileIgnored;
|
status = SyncFileItem::FileIgnored;
|
||||||
_item->_errorString.prepend(tr("Continue blacklisting:") + " ");
|
_item->_errorString.prepend(tr("Continue blacklisting:") + " ");
|
||||||
|
@ -277,14 +277,14 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
|
||||||
|
|
||||||
/* Check and log the transmission checksum type */
|
/* Check and log the transmission checksum type */
|
||||||
ConfigFile cfg;
|
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 the checksum type is empty, it is not sent. No error */
|
||||||
if( !checksumType.isEmpty() ) {
|
if( !checksumType.isEmpty() ) {
|
||||||
if( checksumType == checkSumAdlerUpperC ||
|
if( checksumType == checkSumAdlerC ||
|
||||||
checksumType == checkSumMD5C ||
|
checksumType == checkSumMD5C ||
|
||||||
checksumType == checkSumSHA1C ) {
|
checksumType == checkSumSHA1C ) {
|
||||||
qDebug() << "Client sends and expects transmission checksum type" << checksumType;
|
qDebug() << "Client sends transmission checksum type" << checksumType;
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Unknown transmission checksum type from config" << checksumType;
|
qWarning() << "Unknown transmission checksum type from config" << checksumType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,6 +138,7 @@ void SqlDatabase::close()
|
||||||
SQLITE_DO(sqlite3_close(_db) );
|
SQLITE_DO(sqlite3_close(_db) );
|
||||||
if (_errId != SQLITE_OK) {
|
if (_errId != SQLITE_OK) {
|
||||||
qWarning() << "ERROR When closing DB" << _error;
|
qWarning() << "ERROR When closing DB" << _error;
|
||||||
|
Q_ASSERT(!"SQLite Close Error");
|
||||||
}
|
}
|
||||||
_db = 0;
|
_db = 0;
|
||||||
}
|
}
|
||||||
|
@ -211,6 +212,7 @@ int SqlQuery::prepare( const QString& sql)
|
||||||
if( _errId != SQLITE_OK ) {
|
if( _errId != SQLITE_OK ) {
|
||||||
_error = QString::fromUtf8(sqlite3_errmsg(_db));
|
_error = QString::fromUtf8(sqlite3_errmsg(_db));
|
||||||
qWarning() << "Sqlite prepare statement error:" << _error << "in" <<_sql;
|
qWarning() << "Sqlite prepare statement error:" << _error << "in" <<_sql;
|
||||||
|
Q_ASSERT(!"SQLITE Prepare error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _errId;
|
return _errId;
|
||||||
|
@ -316,6 +318,11 @@ void SqlQuery::bindValue(int pos, const QVariant& value)
|
||||||
Q_ASSERT( res == SQLITE_OK );
|
Q_ASSERT( res == SQLITE_OK );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SqlQuery::nullValue(int index)
|
||||||
|
{
|
||||||
|
return sqlite3_column_type(_stmt, index) == SQLITE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
QString SqlQuery::stringValue(int index)
|
QString SqlQuery::stringValue(int index)
|
||||||
{
|
{
|
||||||
return QString::fromUtf16(static_cast<const ushort*>(sqlite3_column_text16(_stmt, index)));
|
return QString::fromUtf16(static_cast<const ushort*>(sqlite3_column_text16(_stmt, index)));
|
||||||
|
|
|
@ -66,6 +66,9 @@ public:
|
||||||
~SqlQuery();
|
~SqlQuery();
|
||||||
QString error() const;
|
QString error() const;
|
||||||
|
|
||||||
|
/// Checks whether the value at the given column index is NULL
|
||||||
|
bool nullValue(int index);
|
||||||
|
|
||||||
QString stringValue(int index);
|
QString stringValue(int index);
|
||||||
int intValue(int index);
|
int intValue(int index);
|
||||||
quint64 int64Value(int index);
|
quint64 int64Value(int index);
|
||||||
|
|
|
@ -351,7 +351,10 @@ void PropagateDownloadFileQNAM::start()
|
||||||
if (_resumeStart == _item->_size) {
|
if (_resumeStart == _item->_size) {
|
||||||
qDebug() << "File is already complete, no need to download";
|
qDebug() << "File is already complete, no need to download";
|
||||||
_tmpFile.close();
|
_tmpFile.close();
|
||||||
downloadFinished();
|
|
||||||
|
// Unfortunately we lost the checksum header, if any...
|
||||||
|
QByteArray noChecksumData;
|
||||||
|
downloadFinished(noChecksumData, noChecksumData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,6 +362,7 @@ void PropagateDownloadFileQNAM::start()
|
||||||
// If there's not enough space to fully download this file, stop.
|
// If there's not enough space to fully download this file, stop.
|
||||||
const auto diskSpaceResult = _propagator->diskSpaceCheck();
|
const auto diskSpaceResult = _propagator->diskSpaceCheck();
|
||||||
if (diskSpaceResult == OwncloudPropagator::DiskSpaceFailure) {
|
if (diskSpaceResult == OwncloudPropagator::DiskSpaceFailure) {
|
||||||
|
_item->_errorMayBeBlacklisted = true;
|
||||||
done(SyncFileItem::NormalError,
|
done(SyncFileItem::NormalError,
|
||||||
tr("The download would reduce free disk space below %1").arg(
|
tr("The download would reduce free disk space below %1").arg(
|
||||||
Utility::octetsToString(freeSpaceLimit())));
|
Utility::octetsToString(freeSpaceLimit())));
|
||||||
|
@ -531,11 +535,16 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
||||||
// Do checksum validation for the download. If there is no checksum header, the validator
|
// 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()
|
// will also emit the validated() signal to continue the flow in slot downloadFinished()
|
||||||
// as this is (still) also correct.
|
// as this is (still) also correct.
|
||||||
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(_tmpFile.fileName(), this);
|
ValidateChecksumHeader *validator = new ValidateChecksumHeader(this);
|
||||||
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(downloadFinished()));
|
connect(validator, SIGNAL(validated(QByteArray,QByteArray)),
|
||||||
connect(validator, SIGNAL(validationFailed(QString)), this, SLOT(slotChecksumFail(QString)));
|
SLOT(downloadFinished(QByteArray,QByteArray)));
|
||||||
validator->downloadValidation(job->reply()->rawHeader(checkSumHeaderC));
|
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 )
|
void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
|
||||||
|
@ -612,8 +621,13 @@ static void handleRecallFile(const QString &fn)
|
||||||
}
|
}
|
||||||
} // end namespace
|
} // 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);
|
QString fn = _propagator->getFilePath(_item->_file);
|
||||||
|
|
||||||
// In case of file name clash, report an error
|
// In case of file name clash, report an error
|
||||||
|
|
|
@ -117,7 +117,7 @@ public:
|
||||||
private slots:
|
private slots:
|
||||||
void slotGetFinished();
|
void slotGetFinished();
|
||||||
void abort() Q_DECL_OVERRIDE;
|
void abort() Q_DECL_OVERRIDE;
|
||||||
void downloadFinished();
|
void downloadFinished(const QByteArray& checksumType, const QByteArray& checksum);
|
||||||
void slotDownloadProgress(qint64,qint64);
|
void slotDownloadProgress(qint64,qint64);
|
||||||
void slotChecksumFail( const QString& errMsg );
|
void slotChecksumFail( const QString& errMsg );
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,16 @@ void PropagateUploadFileQNAM::start()
|
||||||
return;
|
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);
|
const QString filePath = _propagator->getFilePath(_item->_file);
|
||||||
|
|
||||||
// remember the modtime before checksumming to be able to detect a file
|
// remember the modtime before checksumming to be able to detect a file
|
||||||
|
@ -205,30 +215,48 @@ void PropagateUploadFileQNAM::start()
|
||||||
|
|
||||||
_stopWatch.start();
|
_stopWatch.start();
|
||||||
|
|
||||||
// do whatever is needed to add a checksum to the http upload request.
|
auto supportedChecksumTypes = _propagator->account()->capabilities().supportedChecksumTypes();
|
||||||
// in any case, the validator will emit signal startUpload to let the flow
|
|
||||||
// continue in slotStartUpload here.
|
|
||||||
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(filePath, this);
|
|
||||||
|
|
||||||
// If the config file does not specify a checksum type but the
|
// If we already have a checksum header and the checksum type is supported
|
||||||
// server supports it choose a type based on that.
|
// by the server, we keep that - otherwise recompute.
|
||||||
if (validator->checksumType().isEmpty()) {
|
//
|
||||||
QStringList checksumTypes = _propagator->account()->capabilities().supportedChecksumTypes();
|
// Note: Currently we *always* recompute because we usually only upload
|
||||||
if (!checksumTypes.isEmpty()) {
|
// files that have changed and thus have a new checksum. But if an earlier
|
||||||
// TODO: We might want to prefer some types over others instead
|
// phase computed a checksum, this is where we would make use of it.
|
||||||
// of choosing the first.
|
if (!_item->_transmissionChecksumType.isEmpty()) {
|
||||||
validator->setChecksumType(checksumTypes.first());
|
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)));
|
// Compute a new checksum.
|
||||||
validator->uploadValidation();
|
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);
|
const QString fullFilePath = _propagator->getFilePath(_item->_file);
|
||||||
_item->_checksum = checksum;
|
|
||||||
|
|
||||||
if (!FileSystem::fileExists(fullFilePath)) {
|
if (!FileSystem::fileExists(fullFilePath)) {
|
||||||
done(SyncFileItem::SoftError, tr("File Removed"));
|
done(SyncFileItem::SoftError, tr("File Removed"));
|
||||||
|
@ -458,6 +486,7 @@ void PropagateUploadFileQNAM::startNextChunk()
|
||||||
UploadDevice *device = new UploadDevice(&_propagator->_bandwidthManager);
|
UploadDevice *device = new UploadDevice(&_propagator->_bandwidthManager);
|
||||||
qint64 chunkStart = 0;
|
qint64 chunkStart = 0;
|
||||||
qint64 currentChunkSize = fileSize;
|
qint64 currentChunkSize = fileSize;
|
||||||
|
bool isFinalChunk = false;
|
||||||
if (_chunkCount > 1) {
|
if (_chunkCount > 1) {
|
||||||
int sendingChunk = (_currentChunk + _startChunk) % _chunkCount;
|
int sendingChunk = (_currentChunk + _startChunk) % _chunkCount;
|
||||||
// XOR with chunk size to make sure everything goes well if chunk size changes between runs
|
// 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.
|
if( currentChunkSize == 0 ) { // if the last chunk pretends to be 0, its actually the full chunk size.
|
||||||
currentChunkSize = chunkSize();
|
currentChunkSize = chunkSize();
|
||||||
}
|
}
|
||||||
if( !_item->_checksum.isEmpty() ) {
|
isFinalChunk = true;
|
||||||
headers[checkSumHeaderC] = _item->_checksum;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// checksum if its only one chunk
|
// if there's only one chunk, it's the final one
|
||||||
if( !_item->_checksum.isEmpty() ) {
|
isFinalChunk = true;
|
||||||
headers[checkSumHeaderC] = _item->_checksum;
|
}
|
||||||
}
|
|
||||||
|
if (isFinalChunk && !_item->_transmissionChecksumType.isEmpty()) {
|
||||||
|
headers[checkSumHeaderC] = makeChecksumHeader(
|
||||||
|
_item->_transmissionChecksumType, _item->_transmissionChecksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! device->prepareAndOpen(_propagator->getFilePath(_item->_file), chunkStart, currentChunkSize)) {
|
if (! device->prepareAndOpen(_propagator->getFilePath(_item->_file), chunkStart, currentChunkSize)) {
|
||||||
|
|
|
@ -195,7 +195,7 @@ private slots:
|
||||||
void startNextChunk();
|
void startNextChunk();
|
||||||
void finalize(const SyncFileItem&);
|
void finalize(const SyncFileItem&);
|
||||||
void slotJobDestroyed(QObject *job);
|
void slotJobDestroyed(QObject *job);
|
||||||
void slotStartUpload(const QByteArray &checksum);
|
void slotStartUpload(const QByteArray& checksumType, const QByteArray& checksum);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startPollJob(const QString& path);
|
void startPollJob(const QString& path);
|
||||||
|
|
|
@ -32,7 +32,6 @@ static const char checkSumHeaderC[] = "OC-Checksum";
|
||||||
static const char checkSumMD5C[] = "MD5";
|
static const char checkSumMD5C[] = "MD5";
|
||||||
static const char checkSumSHA1C[] = "SHA1";
|
static const char checkSumSHA1C[] = "SHA1";
|
||||||
static const char checkSumAdlerC[] = "Adler32";
|
static const char checkSumAdlerC[] = "Adler32";
|
||||||
static const char checkSumAdlerUpperC[] = "ADLER32";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Declaration of the other propagation jobs
|
* @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
|
// the file system in the DB, this is to avoid spurious upload on the next sync
|
||||||
item->_modtime = file->other.modtime;
|
item->_modtime = file->other.modtime;
|
||||||
|
|
||||||
_journal->setFileRecord(SyncJournalFileRecord(*item, _localPath + item->_file));
|
_journal->updateFileRecordMetadata(SyncJournalFileRecord(*item, _localPath + item->_file));
|
||||||
item->_should_update_metadata = false;
|
item->_should_update_metadata = false;
|
||||||
}
|
}
|
||||||
if (item->_isDirectory && file->should_update_metadata) {
|
if (item->_isDirectory && file->should_update_metadata) {
|
||||||
|
@ -572,7 +572,7 @@ void SyncEngine::handleSyncError(CSYNC *ctx, const char *state) {
|
||||||
} else {
|
} else {
|
||||||
emit csyncError(errStr);
|
emit csyncError(errStr);
|
||||||
}
|
}
|
||||||
finalize();
|
finalize(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncEngine::startSync()
|
void SyncEngine::startSync()
|
||||||
|
@ -598,7 +598,7 @@ void SyncEngine::startSync()
|
||||||
if (!QDir(_localPath).exists()) {
|
if (!QDir(_localPath).exists()) {
|
||||||
// No _tr, it should only occur in non-mirall
|
// No _tr, it should only occur in non-mirall
|
||||||
emit csyncError("Unable to find local sync folder.");
|
emit csyncError("Unable to find local sync folder.");
|
||||||
finalize();
|
finalize(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +612,7 @@ void SyncEngine::startSync()
|
||||||
emit csyncError(tr("Only %1 are available, need at least %2 to start").arg(
|
emit csyncError(tr("Only %1 are available, need at least %2 to start").arg(
|
||||||
Utility::octetsToString(freeBytes),
|
Utility::octetsToString(freeBytes),
|
||||||
Utility::octetsToString(minFree)));
|
Utility::octetsToString(minFree)));
|
||||||
finalize();
|
finalize(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -643,7 +643,7 @@ void SyncEngine::startSync()
|
||||||
if( fileRecordCount == -1 ) {
|
if( fileRecordCount == -1 ) {
|
||||||
qDebug() << "No way to create a sync journal!";
|
qDebug() << "No way to create a sync journal!";
|
||||||
emit csyncError(tr("Unable to initialize a sync journal."));
|
emit csyncError(tr("Unable to initialize a sync journal."));
|
||||||
finalize();
|
finalize(false);
|
||||||
return;
|
return;
|
||||||
// database creation error!
|
// database creation error!
|
||||||
}
|
}
|
||||||
|
@ -666,7 +666,7 @@ void SyncEngine::startSync()
|
||||||
|
|
||||||
_discoveryMainThread = new DiscoveryMainThread(account());
|
_discoveryMainThread = new DiscoveryMainThread(account());
|
||||||
_discoveryMainThread->setParent(this);
|
_discoveryMainThread->setParent(this);
|
||||||
connect(this, SIGNAL(finished()), _discoveryMainThread, SLOT(deleteLater()));
|
connect(this, SIGNAL(finished(bool)), _discoveryMainThread, SLOT(deleteLater()));
|
||||||
qDebug() << "=====Server" << account()->serverVersion()
|
qDebug() << "=====Server" << account()->serverVersion()
|
||||||
<< QString("rootEtagChangesNotOnlySubFolderEtags=%1").arg(account()->rootEtagChangesNotOnlySubFolderEtags());
|
<< QString("rootEtagChangesNotOnlySubFolderEtags=%1").arg(account()->rootEtagChangesNotOnlySubFolderEtags());
|
||||||
if (account()->rootEtagChangesNotOnlySubFolderEtags()) {
|
if (account()->rootEtagChangesNotOnlySubFolderEtags()) {
|
||||||
|
@ -698,7 +698,7 @@ void SyncEngine::startSync()
|
||||||
QMetaObject::invokeMethod(discoveryJob, "start", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(discoveryJob, "start", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncEngine::slotRootEtagReceived(QString e) {
|
void SyncEngine::slotRootEtagReceived(const QString &e) {
|
||||||
if (_remoteRootEtag.isEmpty()) {
|
if (_remoteRootEtag.isEmpty()) {
|
||||||
qDebug() << Q_FUNC_INFO << e;
|
qDebug() << Q_FUNC_INFO << e;
|
||||||
_remoteRootEtag = e;
|
_remoteRootEtag = e;
|
||||||
|
@ -721,7 +721,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||||
if (!_journal->isConnected()) {
|
if (!_journal->isConnected()) {
|
||||||
qDebug() << "Bailing out, DB failure";
|
qDebug() << "Bailing out, DB failure";
|
||||||
emit csyncError(tr("Cannot open the sync journal"));
|
emit csyncError(tr("Cannot open the sync journal"));
|
||||||
finalize();
|
finalize(false);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Commits a possibly existing (should not though) transaction and starts a new one for the propagate phase
|
// 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);
|
emit aboutToRemoveAllFiles(_syncedItems.first()->_direction, &cancel);
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
qDebug() << Q_FUNC_INFO << "Abort sync";
|
qDebug() << Q_FUNC_INFO << "Abort sync";
|
||||||
finalize();
|
finalize(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -833,7 +833,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||||
void SyncEngine::slotCleanPollsJobAborted(const QString &error)
|
void SyncEngine::slotCleanPollsJobAborted(const QString &error)
|
||||||
{
|
{
|
||||||
csyncError(error);
|
csyncError(error);
|
||||||
finalize();
|
finalize(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncEngine::setNetworkLimits(int upload, int download)
|
void SyncEngine::setNetworkLimits(int upload, int download)
|
||||||
|
@ -888,10 +888,10 @@ void SyncEngine::slotFinished()
|
||||||
|
|
||||||
_journal->commit("All Finished.", false);
|
_journal->commit("All Finished.", false);
|
||||||
emit treeWalkResult(_syncedItems);
|
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.quit();
|
||||||
_thread.wait();
|
_thread.wait();
|
||||||
|
@ -902,7 +902,7 @@ void SyncEngine::finalize()
|
||||||
_stopWatch.stop();
|
_stopWatch.stop();
|
||||||
|
|
||||||
_syncRunning = false;
|
_syncRunning = false;
|
||||||
emit finished();
|
emit finished(success);
|
||||||
|
|
||||||
// Delete the propagator only after emitting the signal.
|
// Delete the propagator only after emitting the signal.
|
||||||
_propagator.clear();
|
_propagator.clear();
|
||||||
|
|
|
@ -94,7 +94,7 @@ signals:
|
||||||
|
|
||||||
// During update, before reconcile
|
// During update, before reconcile
|
||||||
void rootEtag(QString);
|
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
|
// before actual syncing (after update+reconcile) for each item
|
||||||
void syncItemDiscovered(const SyncFileItem&);
|
void syncItemDiscovered(const SyncFileItem&);
|
||||||
|
@ -109,7 +109,7 @@ signals:
|
||||||
|
|
||||||
void transmissionProgress( const ProgressInfo& progress );
|
void transmissionProgress( const ProgressInfo& progress );
|
||||||
|
|
||||||
void finished();
|
void finished(bool success);
|
||||||
void started();
|
void started();
|
||||||
|
|
||||||
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
|
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
|
||||||
|
@ -118,7 +118,7 @@ signals:
|
||||||
void newBigFolder(const QString &folder);
|
void newBigFolder(const QString &folder);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotRootEtagReceived(QString);
|
void slotRootEtagReceived(const QString &);
|
||||||
void slotItemCompleted(const SyncFileItem& item, const PropagatorJob & job);
|
void slotItemCompleted(const SyncFileItem& item, const PropagatorJob & job);
|
||||||
void slotFinished();
|
void slotFinished();
|
||||||
void slotProgress(const SyncFileItem& item, quint64 curent);
|
void slotProgress(const SyncFileItem& item, quint64 curent);
|
||||||
|
@ -144,7 +144,7 @@ private:
|
||||||
void deleteStaleErrorBlacklistEntries();
|
void deleteStaleErrorBlacklistEntries();
|
||||||
|
|
||||||
// cleanup and emit the finished signal
|
// cleanup and emit the finished signal
|
||||||
void finalize();
|
void finalize(bool success);
|
||||||
|
|
||||||
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
|
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
SyncFileItem() : _type(UnknownType), _direction(None), _isDirectory(false),
|
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),
|
_isRestoration(false), _should_update_metadata(false),
|
||||||
_httpErrorCode(0), _requestDuration(0), _affectedItems(1),
|
_httpErrorCode(0), _requestDuration(0), _affectedItems(1),
|
||||||
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0), _size(0), _inode(0)
|
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0), _size(0), _inode(0)
|
||||||
|
@ -137,6 +138,13 @@ public:
|
||||||
/// without the status being FileIgnored.
|
/// without the status being FileIgnored.
|
||||||
bool _hasBlacklistEntry BITFIELD(1);
|
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
|
// Variables useful to report to the user
|
||||||
Status _status BITFIELD(4);
|
Status _status BITFIELD(4);
|
||||||
bool _isRestoration BITFIELD(1); // The original operation was forbidden, and this is a restoration
|
bool _isRestoration BITFIELD(1); // The original operation was forbidden, and this is a restoration
|
||||||
|
@ -157,7 +165,8 @@ public:
|
||||||
quint64 _inode;
|
quint64 _inode;
|
||||||
QByteArray _fileId;
|
QByteArray _fileId;
|
||||||
QByteArray _remotePerm;
|
QByteArray _remotePerm;
|
||||||
QByteArray _checksum;
|
QByteArray _transmissionChecksum;
|
||||||
|
QByteArray _transmissionChecksumType;
|
||||||
QString _directDownloadUrl;
|
QString _directDownloadUrl;
|
||||||
QString _directDownloadCookies;
|
QString _directDownloadCookies;
|
||||||
|
|
||||||
|
|
|
@ -206,6 +206,8 @@ bool SyncJournalDb::checkConnect()
|
||||||
"md5 VARCHAR(32)," /* This is the etag. Called md5 for compatibility */
|
"md5 VARCHAR(32)," /* This is the etag. Called md5 for compatibility */
|
||||||
// updateDatabaseStructure() will add a fileid column
|
// updateDatabaseStructure() will add a fileid column
|
||||||
// updateDatabaseStructure() will add a remotePerm column
|
// updateDatabaseStructure() will add a remotePerm column
|
||||||
|
// updateDatabaseStructure() will add a transmissionChecksum column
|
||||||
|
// updateDatabaseStructure() will add a transmissionChecksumTypeId column
|
||||||
"PRIMARY KEY(phash)"
|
"PRIMARY KEY(phash)"
|
||||||
");");
|
");");
|
||||||
|
|
||||||
|
@ -271,6 +273,14 @@ bool SyncJournalDb::checkConnect()
|
||||||
return sqlFail("Create table selectivesync", createQuery);
|
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("
|
createQuery.prepare("CREATE TABLE IF NOT EXISTS version("
|
||||||
|
@ -346,13 +356,30 @@ bool SyncJournalDb::checkConnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFileRecordQuery.reset(new SqlQuery(_db));
|
_getFileRecordQuery.reset(new SqlQuery(_db));
|
||||||
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM "
|
_getFileRecordQuery->prepare(
|
||||||
"metadata WHERE phash=?1" );
|
"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.reset(new SqlQuery(_db) );
|
||||||
_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
|
_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
|
||||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote) "
|
"(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);" );
|
"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.reset(new SqlQuery(_db) );
|
||||||
_getDownloadInfoQuery->prepare( "SELECT tmpfile, etag, errorcount FROM "
|
_getDownloadInfoQuery->prepare( "SELECT tmpfile, etag, errorcount FROM "
|
||||||
|
@ -403,6 +430,12 @@ bool SyncJournalDb::checkConnect()
|
||||||
_getSelectiveSyncListQuery.reset(new SqlQuery(_db));
|
_getSelectiveSyncListQuery.reset(new SqlQuery(_db));
|
||||||
_getSelectiveSyncListQuery->prepare("SELECT path FROM selectivesync WHERE type=?1");
|
_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
|
// don't start a new transaction now
|
||||||
commitInternal(QString("checkConnect End"), false);
|
commitInternal(QString("checkConnect End"), false);
|
||||||
|
|
||||||
|
@ -424,6 +457,8 @@ void SyncJournalDb::close()
|
||||||
|
|
||||||
_getFileRecordQuery.reset(0);
|
_getFileRecordQuery.reset(0);
|
||||||
_setFileRecordQuery.reset(0);
|
_setFileRecordQuery.reset(0);
|
||||||
|
_setFileRecordChecksumQuery.reset(0);
|
||||||
|
_setFileRecordMetadataQuery.reset(0);
|
||||||
_getDownloadInfoQuery.reset(0);
|
_getDownloadInfoQuery.reset(0);
|
||||||
_setDownloadInfoQuery.reset(0);
|
_setDownloadInfoQuery.reset(0);
|
||||||
_deleteDownloadInfoQuery.reset(0);
|
_deleteDownloadInfoQuery.reset(0);
|
||||||
|
@ -435,6 +470,8 @@ void SyncJournalDb::close()
|
||||||
_getErrorBlacklistQuery.reset(0);
|
_getErrorBlacklistQuery.reset(0);
|
||||||
_setErrorBlacklistQuery.reset(0);
|
_setErrorBlacklistQuery.reset(0);
|
||||||
_getSelectiveSyncListQuery.reset(0);
|
_getSelectiveSyncListQuery.reset(0);
|
||||||
|
_getChecksumTypeIdQuery.reset(0);
|
||||||
|
_insertChecksumTypeQuery.reset(0);
|
||||||
|
|
||||||
_db.close();
|
_db.close();
|
||||||
_avoidReadFromDbOnNextSyncFilter.clear();
|
_avoidReadFromDbOnNextSyncFilter.clear();
|
||||||
|
@ -527,6 +564,27 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
||||||
}
|
}
|
||||||
commitInternal("update database structure: add ignoredChildrenRemote col");
|
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;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,6 +685,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
||||||
if( fileId.isEmpty() ) fileId = "";
|
if( fileId.isEmpty() ) fileId = "";
|
||||||
QString remotePerm (record._remotePerm);
|
QString remotePerm (record._remotePerm);
|
||||||
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
|
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
|
||||||
|
int checksumTypeId = mapChecksumType(record._transmissionChecksumType);
|
||||||
_setFileRecordQuery->reset();
|
_setFileRecordQuery->reset();
|
||||||
_setFileRecordQuery->bindValue(1, QString::number(phash));
|
_setFileRecordQuery->bindValue(1, QString::number(phash));
|
||||||
_setFileRecordQuery->bindValue(2, plen);
|
_setFileRecordQuery->bindValue(2, plen);
|
||||||
|
@ -642,6 +701,8 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
||||||
_setFileRecordQuery->bindValue(12, remotePerm );
|
_setFileRecordQuery->bindValue(12, remotePerm );
|
||||||
_setFileRecordQuery->bindValue(13, record._fileSize );
|
_setFileRecordQuery->bindValue(13, record._fileSize );
|
||||||
_setFileRecordQuery->bindValue(14, record._serverHasIgnoredFiles ? 1:0);
|
_setFileRecordQuery->bindValue(14, record._serverHasIgnoredFiles ? 1:0);
|
||||||
|
_setFileRecordQuery->bindValue(15, record._transmissionChecksum );
|
||||||
|
_setFileRecordQuery->bindValue(16, checksumTypeId );
|
||||||
|
|
||||||
if( !_setFileRecordQuery->exec() ) {
|
if( !_setFileRecordQuery->exec() ) {
|
||||||
qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :"
|
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
|
qDebug() << _setFileRecordQuery->lastQuery() << phash << plen << record._path << record._inode
|
||||||
<< record._mode
|
<< record._mode
|
||||||
<< QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type)
|
<< 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();
|
_setFileRecordQuery->reset();
|
||||||
return true;
|
return true;
|
||||||
|
@ -732,6 +794,10 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
|
||||||
rec._remotePerm = _getFileRecordQuery->baValue(9);
|
rec._remotePerm = _getFileRecordQuery->baValue(9);
|
||||||
rec._fileSize = _getFileRecordQuery->int64Value(10);
|
rec._fileSize = _getFileRecordQuery->int64Value(10);
|
||||||
rec._serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
|
rec._serverHasIgnoredFiles = (_getFileRecordQuery->intValue(11) > 0);
|
||||||
|
rec._transmissionChecksum = _getFileRecordQuery->baValue(12);
|
||||||
|
if( !_getFileRecordQuery->nullValue(13) ) {
|
||||||
|
rec._transmissionChecksumType = _getFileRecordQuery->baValue(13);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
QString err = _getFileRecordQuery->error();
|
QString err = _getFileRecordQuery->error();
|
||||||
qDebug() << "No journal entry found for " << filename;
|
qDebug() << "No journal entry found for " << filename;
|
||||||
|
@ -820,6 +886,86 @@ int SyncJournalDb::getFileRecordCount()
|
||||||
return 0;
|
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)
|
static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo * res)
|
||||||
{
|
{
|
||||||
bool ok = true;
|
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)
|
void SyncJournalDb::commit(const QString& context, bool startTrans)
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,6 +42,10 @@ public:
|
||||||
bool setFileRecord( const SyncJournalFileRecord& record );
|
bool setFileRecord( const SyncJournalFileRecord& record );
|
||||||
bool deleteFileRecord( const QString& filename, bool recursively = false );
|
bool deleteFileRecord( const QString& filename, bool recursively = false );
|
||||||
int getFileRecordCount();
|
int getFileRecordCount();
|
||||||
|
bool updateFileRecordChecksum(const QString& filename,
|
||||||
|
const QByteArray& transmisisonChecksum,
|
||||||
|
const QByteArray& transmissionChecksumType);
|
||||||
|
bool updateFileRecordMetadata(const SyncJournalFileRecord& record);
|
||||||
bool exists();
|
bool exists();
|
||||||
void walCheckpoint();
|
void walCheckpoint();
|
||||||
|
|
||||||
|
@ -153,12 +157,21 @@ private:
|
||||||
// Same as forceRemoteDiscoveryNextSync but without acquiring the lock
|
// Same as forceRemoteDiscoveryNextSync but without acquiring the lock
|
||||||
void forceRemoteDiscoveryNextSyncLocked();
|
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;
|
SqlDatabase _db;
|
||||||
QString _dbFile;
|
QString _dbFile;
|
||||||
QMutex _mutex; // Public functions are protected with the mutex.
|
QMutex _mutex; // Public functions are protected with the mutex.
|
||||||
int _transaction;
|
int _transaction;
|
||||||
|
|
||||||
|
// NOTE! when adding a query, don't forget to reset it in SyncJournalDb::close
|
||||||
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
||||||
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
||||||
|
QScopedPointer<SqlQuery> _setFileRecordChecksumQuery;
|
||||||
|
QScopedPointer<SqlQuery> _setFileRecordMetadataQuery;
|
||||||
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
||||||
QScopedPointer<SqlQuery> _setDownloadInfoQuery;
|
QScopedPointer<SqlQuery> _setDownloadInfoQuery;
|
||||||
QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
|
QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
|
||||||
|
@ -170,6 +183,8 @@ private:
|
||||||
QScopedPointer<SqlQuery> _getErrorBlacklistQuery;
|
QScopedPointer<SqlQuery> _getErrorBlacklistQuery;
|
||||||
QScopedPointer<SqlQuery> _setErrorBlacklistQuery;
|
QScopedPointer<SqlQuery> _setErrorBlacklistQuery;
|
||||||
QScopedPointer<SqlQuery> _getSelectiveSyncListQuery;
|
QScopedPointer<SqlQuery> _getSelectiveSyncListQuery;
|
||||||
|
QScopedPointer<SqlQuery> _getChecksumTypeIdQuery;
|
||||||
|
QScopedPointer<SqlQuery> _insertChecksumTypeQuery;
|
||||||
|
|
||||||
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
|
/* 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
|
* 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)
|
SyncJournalFileRecord::SyncJournalFileRecord(const SyncFileItem &item, const QString &localFileName)
|
||||||
: _path(item._file), _modtime(Utility::qDateTimeFromTime_t(item._modtime)),
|
: _path(item._file), _modtime(Utility::qDateTimeFromTime_t(item._modtime)),
|
||||||
_type(item._type), _etag(item._etag), _fileId(item._fileId), _fileSize(item._size),
|
_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
|
// 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
|
// 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)
|
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item)
|
||||||
{
|
{
|
||||||
SyncJournalErrorBlacklistRecord entry;
|
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
|
#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
|
#endif
|
||||||
) {
|
);
|
||||||
qDebug() << "This error is not blacklisted " << item._httpErrorCode;
|
|
||||||
|
if (!mayBlacklist) {
|
||||||
|
qDebug() << "This error is not blacklisted " << item._httpErrorCode << item._errorMayBeBlacklisted;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +132,7 @@ SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
|
||||||
entry._lastTryEtag = item._etag;
|
entry._lastTryEtag = item._etag;
|
||||||
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
||||||
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
|
// 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;
|
entry._file = item._file;
|
||||||
|
|
||||||
if( item._httpErrorCode == 403 || item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
|
if( item._httpErrorCode == 403 || item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
|
||||||
|
@ -150,9 +156,12 @@ bool operator==(const SyncJournalFileRecord & lhs,
|
||||||
&& lhs._type == rhs._type
|
&& lhs._type == rhs._type
|
||||||
&& lhs._etag == rhs._etag
|
&& lhs._etag == rhs._etag
|
||||||
&& lhs._fileId == rhs._fileId
|
&& lhs._fileId == rhs._fileId
|
||||||
|
&& lhs._fileSize == rhs._fileSize
|
||||||
&& lhs._remotePerm == rhs._remotePerm
|
&& lhs._remotePerm == rhs._remotePerm
|
||||||
&& lhs._mode == rhs._mode
|
&& 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;
|
QByteArray _remotePerm;
|
||||||
int _mode;
|
int _mode;
|
||||||
bool _serverHasIgnoredFiles;
|
bool _serverHasIgnoredFiles;
|
||||||
|
QByteArray _transmissionChecksum;
|
||||||
|
QByteArray _transmissionChecksumType;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool OWNCLOUDSYNC_EXPORT
|
bool OWNCLOUDSYNC_EXPORT
|
||||||
|
|
|
@ -16,133 +16,139 @@
|
||||||
#include "transmissionchecksumvalidator.h"
|
#include "transmissionchecksumvalidator.h"
|
||||||
#include "syncfileitem.h"
|
#include "syncfileitem.h"
|
||||||
#include "propagatorjobs.h"
|
#include "propagatorjobs.h"
|
||||||
#include "configfile.h"
|
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
|
|
||||||
#include <qtconcurrentrun.h>
|
#include <qtconcurrentrun.h>
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
TransmissionChecksumValidator::TransmissionChecksumValidator(const QString& filePath, QObject *parent)
|
QByteArray makeChecksumHeader(const QByteArray& checksumType, const QByteArray& checksum)
|
||||||
: QObject(parent),
|
|
||||||
_filePath(filePath)
|
|
||||||
{
|
{
|
||||||
// If the config file specifies a checksum type, use that.
|
QByteArray header = checksumType;
|
||||||
ConfigFile cfg;
|
header.append(':');
|
||||||
_checksumType = cfg.transmissionChecksum();
|
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;
|
_checksumType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TransmissionChecksumValidator::checksumType() const
|
QByteArray ComputeChecksum::checksumType() const
|
||||||
{
|
{
|
||||||
return _checksumType;
|
return _checksumType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransmissionChecksumValidator::uploadValidation()
|
void ComputeChecksum::start(const QString& filePath)
|
||||||
{
|
{
|
||||||
const QString csType = checksumType();
|
const QString csType = checksumType();
|
||||||
|
|
||||||
if( csType.isEmpty() ) {
|
// Calculate the checksum in a different thread first.
|
||||||
// if there is no checksum defined, continue to upload
|
connect( &_watcher, SIGNAL(finished()),
|
||||||
emit validated(QByteArray());
|
this, SLOT(slotCalculationDone()),
|
||||||
} else {
|
Qt::UniqueConnection );
|
||||||
// Calculate the checksum in a different thread first.
|
if( csType == checkSumMD5C ) {
|
||||||
|
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, filePath));
|
||||||
|
|
||||||
connect( &_watcher, SIGNAL(finished()),
|
} else if( csType == checkSumSHA1C ) {
|
||||||
this, SLOT(slotUploadChecksumCalculated()));
|
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, filePath));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
#ifdef ZLIB_FOUND
|
#ifdef ZLIB_FOUND
|
||||||
else if( type == checkSumAdlerUpperC ) {
|
else if( csType == checkSumAdlerC) {
|
||||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, filePath));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
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."));
|
emit validationFailed(tr("The checksum header is malformed."));
|
||||||
return;
|
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( checksumType != _expectedChecksumType ) {
|
||||||
|
emit validationFailed(tr("The checksum header contained an unknown checksum type '%1'").arg(
|
||||||
if( hash != _expectedHash ) {
|
QString::fromLatin1(_expectedChecksumType)));
|
||||||
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
|
return;
|
||||||
} else {
|
|
||||||
// qDebug() << "Checksum checked and matching: " << _expectedHash;
|
|
||||||
emit validated(hash);
|
|
||||||
}
|
}
|
||||||
|
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 {
|
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
|
* Computes the checksum of a file.
|
||||||
* @ingroup libsync
|
* \ingroup libsync
|
||||||
*/
|
*/
|
||||||
class OWNCLOUDSYNC_EXPORT TransmissionChecksumValidator : public QObject
|
class OWNCLOUDSYNC_EXPORT ComputeChecksum : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
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
|
* Sets the checksum type to be used. The default is empty.
|
||||||
* member of the SyncFileItem *item.
|
*/
|
||||||
* The kind of requested checksum is taken from config. No need to set from outside.
|
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)
|
* done() is emitted when the calculation finishes.
|
||||||
* the object will emit the signal validated(). The item->_checksum is than either
|
|
||||||
* set to a proper value or empty.
|
|
||||||
*/
|
*/
|
||||||
void uploadValidation();
|
void start(const QString& filePath);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void validated(const QByteArray& checksum);
|
void done(const QByteArray& checksumType, const QByteArray& checksum);
|
||||||
void validationFailed( const QString& errMsg );
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotUploadChecksumCalculated();
|
void slotCalculationDone();
|
||||||
void slotDownloadChecksumCalculated();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString _checksumType;
|
QByteArray _checksumType;
|
||||||
QByteArray _expectedHash;
|
|
||||||
QByteArray _checksumHeader;
|
|
||||||
|
|
||||||
QString _filePath;
|
|
||||||
|
|
||||||
// watcher for the checksum calculation thread
|
// watcher for the checksum calculation thread
|
||||||
QFutureWatcher<QByteArray> _watcher;
|
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._remotePerm = "744";
|
||||||
record._mode = -17;
|
record._mode = -17;
|
||||||
record._fileSize = 213089055;
|
record._fileSize = 213089055;
|
||||||
|
record._transmissionChecksum = "mychecksum";
|
||||||
|
record._transmissionChecksumType = "MD5";
|
||||||
QVERIFY(_db.setFileRecord(record));
|
QVERIFY(_db.setFileRecord(record));
|
||||||
|
|
||||||
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo");
|
SyncJournalFileRecord storedRecord = _db.getFileRecord("foo");
|
||||||
QVERIFY(storedRecord == record);
|
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"));
|
QVERIFY(_db.deleteFileRecord("foo"));
|
||||||
record = _db.getFileRecord("foo");
|
record = _db.getFileRecord("foo");
|
||||||
QVERIFY(!record.isValid());
|
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()
|
void testDownloadInfo()
|
||||||
{
|
{
|
||||||
typedef SyncJournalDb::DownloadInfo Info;
|
typedef SyncJournalDb::DownloadInfo Info;
|
||||||
|
|
|
@ -33,14 +33,16 @@ using namespace OCC;
|
||||||
QString _testfile;
|
QString _testfile;
|
||||||
QString _expectedError;
|
QString _expectedError;
|
||||||
QByteArray _expected;
|
QByteArray _expected;
|
||||||
|
QByteArray _expectedType;
|
||||||
bool _successDown;
|
bool _successDown;
|
||||||
bool _errorSeen;
|
bool _errorSeen;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void slotUpValidated(const QByteArray& checksum) {
|
void slotUpValidated(const QByteArray& type, const QByteArray& checksum) {
|
||||||
qDebug() << "Checksum: " << checksum;
|
qDebug() << "Checksum: " << checksum;
|
||||||
QVERIFY(_expected == checksum );
|
QVERIFY(_expected == checksum );
|
||||||
|
QVERIFY(_expectedType == type );
|
||||||
}
|
}
|
||||||
|
|
||||||
void slotDownValidated() {
|
void slotDownValidated() {
|
||||||
|
@ -62,23 +64,22 @@ using namespace OCC;
|
||||||
rootDir.mkpath(_root );
|
rootDir.mkpath(_root );
|
||||||
_testfile = _root+"/csFile";
|
_testfile = _root+"/csFile";
|
||||||
Utility::writeRandomFile( _testfile);
|
Utility::writeRandomFile( _testfile);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testUploadChecksummingAdler() {
|
void testUploadChecksummingAdler() {
|
||||||
|
|
||||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
ComputeChecksum *vali = new ComputeChecksum(this);
|
||||||
vali->setChecksumType("Adler32");
|
_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 = FileSystem::calcAdler32( _testfile );
|
||||||
_expected = "Adler32:"+FileSystem::calcAdler32( testfile );
|
|
||||||
qDebug() << "XX Expected Checksum: " << _expected;
|
qDebug() << "XX Expected Checksum: " << _expected;
|
||||||
vali->uploadValidation();
|
vali->start(_testfile);
|
||||||
|
|
||||||
QEventLoop loop;
|
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();
|
loop.exec();
|
||||||
|
|
||||||
delete vali;
|
delete vali;
|
||||||
|
@ -86,16 +87,16 @@ using namespace OCC;
|
||||||
|
|
||||||
void testUploadChecksummingMd5() {
|
void testUploadChecksummingMd5() {
|
||||||
|
|
||||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
ComputeChecksum *vali = new ComputeChecksum(this);
|
||||||
vali->setChecksumType( OCC::checkSumMD5C );
|
_expectedType = OCC::checkSumMD5C;
|
||||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
vali->setChecksumType(_expectedType);
|
||||||
|
connect(vali, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(slotUpValidated(QByteArray,QByteArray)));
|
||||||
|
|
||||||
_expected = checkSumMD5C;
|
_expected = FileSystem::calcMd5( _testfile );
|
||||||
_expected.append(":"+FileSystem::calcMd5( _testfile ));
|
vali->start(_testfile);
|
||||||
vali->uploadValidation();
|
|
||||||
|
|
||||||
QEventLoop loop;
|
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();
|
loop.exec();
|
||||||
|
|
||||||
delete vali;
|
delete vali;
|
||||||
|
@ -103,17 +104,17 @@ using namespace OCC;
|
||||||
|
|
||||||
void testUploadChecksummingSha1() {
|
void testUploadChecksummingSha1() {
|
||||||
|
|
||||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
ComputeChecksum *vali = new ComputeChecksum(this);
|
||||||
vali->setChecksumType( OCC::checkSumSHA1C );
|
_expectedType = OCC::checkSumSHA1C;
|
||||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
|
vali->setChecksumType(_expectedType);
|
||||||
|
connect(vali, SIGNAL(done(QByteArray,QByteArray)), this, SLOT(slotUpValidated(QByteArray,QByteArray)));
|
||||||
|
|
||||||
_expected = checkSumSHA1C;
|
_expected = FileSystem::calcSha1( _testfile );
|
||||||
_expected.append(":"+FileSystem::calcSha1( _testfile ));
|
|
||||||
|
|
||||||
vali->uploadValidation();
|
vali->start(_testfile);
|
||||||
|
|
||||||
QEventLoop loop;
|
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();
|
loop.exec();
|
||||||
|
|
||||||
delete vali;
|
delete vali;
|
||||||
|
@ -126,22 +127,21 @@ using namespace OCC;
|
||||||
adler.append(FileSystem::calcAdler32( _testfile ));
|
adler.append(FileSystem::calcAdler32( _testfile ));
|
||||||
_successDown = false;
|
_successDown = false;
|
||||||
|
|
||||||
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
|
ValidateChecksumHeader *vali = new ValidateChecksumHeader(this);
|
||||||
vali->setChecksumType("Adler32");
|
connect(vali, SIGNAL(validated(QByteArray,QByteArray)), this, SLOT(slotDownValidated()));
|
||||||
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotDownValidated()));
|
|
||||||
connect(vali, SIGNAL(validationFailed(QString)), this, SLOT(slotDownError(QString)));
|
connect(vali, SIGNAL(validationFailed(QString)), this, SLOT(slotDownError(QString)));
|
||||||
vali->downloadValidation(adler);
|
vali->start(_testfile, adler);
|
||||||
|
|
||||||
QTRY_VERIFY(_successDown);
|
QTRY_VERIFY(_successDown);
|
||||||
|
|
||||||
_expectedError = QLatin1String("The downloaded file does not match the checksum, it will be resumed.");
|
_expectedError = QLatin1String("The downloaded file does not match the checksum, it will be resumed.");
|
||||||
_errorSeen = false;
|
_errorSeen = false;
|
||||||
vali->downloadValidation("Adler32:543345");
|
vali->start(_testfile, "Adler32:543345");
|
||||||
QTRY_VERIFY(_errorSeen);
|
QTRY_VERIFY(_errorSeen);
|
||||||
|
|
||||||
_expectedError = QLatin1String("The checksum header is malformed.");
|
_expectedError = QLatin1String("The checksum header contained an unknown checksum type 'Klaas32'");
|
||||||
_errorSeen = false;
|
_errorSeen = false;
|
||||||
vali->downloadValidation("Klaas32:543345");
|
vali->start(_testfile, "Klaas32:543345");
|
||||||
QTRY_VERIFY(_errorSeen);
|
QTRY_VERIFY(_errorSeen);
|
||||||
|
|
||||||
delete vali;
|
delete vali;
|
||||||
|
|
|
@ -2155,7 +2155,7 @@ Il est déconseillé de l'utiliser.</translation>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/sharedialog.ui" line="77"/>
|
<location filename="../src/gui/sharedialog.ui" line="77"/>
|
||||||
<source>Share link</source>
|
<source>Share link</source>
|
||||||
<translation>Partage par lien</translation>
|
<translation>Partager par lien</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/sharedialog.ui" line="125"/>
|
<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="564"/>
|
||||||
<location filename="../src/gui/sharedialog.cpp" line="565"/>
|
<location filename="../src/gui/sharedialog.cpp" line="565"/>
|
||||||
<source>&Share link</source>
|
<source>&Share link</source>
|
||||||
<translation>&Partage par lien</translation>
|
<translation>&Partager par lien</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/sharedialog.cpp" line="583"/>
|
<location filename="../src/gui/sharedialog.cpp" line="583"/>
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/accountsettings.ui" line="97"/>
|
<location filename="../src/gui/accountsettings.ui" line="97"/>
|
||||||
<source>Storage space: ...</source>
|
<source>Storage space: ...</source>
|
||||||
<translation type="unfinished"/>
|
<translation>ストレージ空き容量: ...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/accountsettings.ui" line="167"/>
|
<location filename="../src/gui/accountsettings.ui" line="167"/>
|
||||||
|
@ -240,7 +240,7 @@
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/accountsettings.cpp" line="448"/>
|
<location filename="../src/gui/accountsettings.cpp" line="448"/>
|
||||||
<source>%1 of %2 in use</source>
|
<source>%1 of %2 in use</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%2 の %1 を使用</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/accountsettings.cpp" line="453"/>
|
<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'.
|
<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.
|
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>
|
Are you sure you want to perform this operation?</source>
|
||||||
<translation type="unfinished"/>
|
<translation>この同期により、ローカルの同期フォルダー '%1'にある全ファイルが削除されます。
|
||||||
|
これはフォルダーが黙って再構成されたか、すべてのファイルが手動で削除されたことが原因である場合があります。
|
||||||
|
本当にこの操作を実行しますか?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folder.cpp" line="1119"/>
|
<location filename="../src/gui/folder.cpp" line="1119"/>
|
||||||
|
@ -615,7 +617,7 @@ Are you sure you want to perform this operation?</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1142"/>
|
<location filename="../src/gui/folderman.cpp" line="1142"/>
|
||||||
<source>The selected path is not a folder!</source>
|
<source>The selected path is not a folder!</source>
|
||||||
<translation type="unfinished"/>
|
<translation>指定のパスは、フォルダーではありません!</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1146"/>
|
<location filename="../src/gui/folderman.cpp" line="1146"/>
|
||||||
|
@ -625,27 +627,27 @@ Are you sure you want to perform this operation?</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1161"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1166"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1173"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1180"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderman.cpp" line="1186"/>
|
<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>
|
<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>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -653,7 +655,7 @@ Are you sure you want to perform this operation?</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderstatusdelegate.cpp" line="33"/>
|
<location filename="../src/gui/folderstatusdelegate.cpp" line="33"/>
|
||||||
<source>Add Folder Sync Connection</source>
|
<source>Add Folder Sync Connection</source>
|
||||||
<translation type="unfinished"/>
|
<translation>同期フォルダーを追加</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderstatusdelegate.cpp" line="85"/>
|
<location filename="../src/gui/folderstatusdelegate.cpp" line="85"/>
|
||||||
|
@ -766,7 +768,7 @@ Total time left %5</source>
|
||||||
<message numerus="yes">
|
<message numerus="yes">
|
||||||
<location filename="../src/gui/folderstatusmodel.cpp" line="872"/>
|
<location filename="../src/gui/folderstatusmodel.cpp" line="872"/>
|
||||||
<source>Waiting for %n other folder(s)...</source>
|
<source>Waiting for %n other folder(s)...</source>
|
||||||
<translation type="unfinished"><numerusform></numerusform></translation>
|
<translation><numerusform>%n 他のフォルダーの完了待ち...</numerusform></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderstatusmodel.cpp" line="878"/>
|
<location filename="../src/gui/folderstatusmodel.cpp" line="878"/>
|
||||||
|
@ -798,7 +800,7 @@ Total time left %5</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderwizard.cpp" line="81"/>
|
<location filename="../src/gui/folderwizard.cpp" line="81"/>
|
||||||
<source>The folder alias is a descriptive name for this sync connection.</source>
|
<source>The folder alias is a descriptive name for this sync connection.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>このフォルダーのエイリアスは、この同期接続用の記述名です。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderwizard.cpp" line="115"/>
|
<location filename="../src/gui/folderwizard.cpp" line="115"/>
|
||||||
|
@ -846,7 +848,7 @@ Total time left %5</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderwizard.cpp" line="271"/>
|
<location filename="../src/gui/folderwizard.cpp" line="271"/>
|
||||||
<source>Failed to list a folder. Error: %1</source>
|
<source>Failed to list a folder. Error: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>フォルダーをリストアップできません。エラー: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/folderwizard.cpp" line="351"/>
|
<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.
|
<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>
|
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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/ignorelisteditor.cpp" line="109"/>
|
<location filename="../src/gui/ignorelisteditor.cpp" line="109"/>
|
||||||
|
@ -1390,12 +1394,12 @@ for additional privileges during the process.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="142"/>
|
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="142"/>
|
||||||
<source>Sync the folder '%1'</source>
|
<source>Sync the folder '%1'</source>
|
||||||
<translation type="unfinished"/>
|
<translation>'%1' フォルダーを同期</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="147"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="246"/>
|
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.cpp" line="246"/>
|
||||||
|
@ -1742,7 +1746,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/owncloudpropagator.cpp" line="211"/>
|
<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>
|
<source>A file or folder was removed from a read only share, but restoring failed: %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>ファイルまたはフォルダーが読み込み専用の共有から削除されましたが、復元に失敗しました: %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1755,7 +1759,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/propagatorjobs.cpp" line="151"/>
|
<location filename="../src/libsync/propagatorjobs.cpp" line="151"/>
|
||||||
<source>could not create folder %1</source>
|
<source>could not create folder %1</source>
|
||||||
<translation type="unfinished"/>
|
<translation>フォルダー %1 を作成できませんでした</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
|
@ -1868,7 +1872,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/propagateupload.cpp" line="558"/>
|
<location filename="../src/libsync/propagateupload.cpp" line="558"/>
|
||||||
<source>Forcing job abort on HTTP connection reset with Qt < 5.4.2.</source>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/propagateupload.cpp" line="566"/>
|
<location filename="../src/libsync/propagateupload.cpp" line="566"/>
|
||||||
|
@ -2402,7 +2406,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/sslbutton.cpp" line="219"/>
|
<location filename="../src/gui/sslbutton.cpp" line="219"/>
|
||||||
<source>No support for SSL session tickets/identifiers</source>
|
<source>No support for SSL session tickets/identifiers</source>
|
||||||
<translation type="unfinished"/>
|
<translation>SSLセッションチケット/識別子はサポートされていません</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/sslbutton.cpp" line="230"/>
|
<location filename="../src/gui/sslbutton.cpp" line="230"/>
|
||||||
|
@ -2575,27 +2579,27 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="164"/>
|
<location filename="../src/libsync/syncengine.cpp" line="164"/>
|
||||||
<source>The mounted folder is temporarily not available on the server</source>
|
<source>The mounted folder is temporarily not available on the server</source>
|
||||||
<translation type="unfinished"/>
|
<translation>サーバー上のマウント済フォルダーが一時的に利用できません。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="167"/>
|
<location filename="../src/libsync/syncengine.cpp" line="167"/>
|
||||||
<source>An error occurred while opening a folder</source>
|
<source>An error occurred while opening a folder</source>
|
||||||
<translation type="unfinished"/>
|
<translation>フォルダーを開く際にエラーが発生しました</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="170"/>
|
<location filename="../src/libsync/syncengine.cpp" line="170"/>
|
||||||
<source>Error while reading folder.</source>
|
<source>Error while reading folder.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>フォルダーの読み込みエラー</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="996"/>
|
<location filename="../src/libsync/syncengine.cpp" line="996"/>
|
||||||
<source>Not allowed because you don't have permission to add parent folder</source>
|
<source>Not allowed because you don't have permission to add parent folder</source>
|
||||||
<translation type="unfinished"/>
|
<translation>親フォルダーを追加する権限がありません</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="1003"/>
|
<location filename="../src/libsync/syncengine.cpp" line="1003"/>
|
||||||
<source>Not allowed because you don't have permission to add files in that folder</source>
|
<source>Not allowed because you don't have permission to add files in that folder</source>
|
||||||
<translation type="unfinished"/>
|
<translation>そのフォルダーにファイルを追加する権限がありません</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="152"/>
|
<location filename="../src/libsync/syncengine.cpp" line="152"/>
|
||||||
|
@ -2625,12 +2629,12 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="100"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="149"/>
|
<location filename="../src/libsync/syncengine.cpp" line="149"/>
|
||||||
<source>CSync tried to create a folder that already exists.</source>
|
<source>CSync tried to create a folder that already exists.</source>
|
||||||
<translation type="unfinished"/>
|
<translation>CSyncはすでに存在するフォルダーを作成しようとしました。</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="161"/>
|
<location filename="../src/libsync/syncengine.cpp" line="161"/>
|
||||||
|
@ -2701,7 +2705,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="990"/>
|
<location filename="../src/libsync/syncengine.cpp" line="990"/>
|
||||||
<source>Not allowed because you don't have permission to add subfolders to that folder</source>
|
<source>Not allowed because you don't have permission to add subfolders to that folder</source>
|
||||||
<translation type="unfinished"/>
|
<translation>そのフォルダーにサブフォルダーを追加する権限がありません</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/syncengine.cpp" line="1023"/>
|
<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="459"/>
|
||||||
<location filename="../src/gui/owncloudgui.cpp" line="526"/>
|
<location filename="../src/gui/owncloudgui.cpp" line="526"/>
|
||||||
<source>Log in...</source>
|
<source>Log in...</source>
|
||||||
<translation type="unfinished"/>
|
<translation>ログイン...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/owncloudgui.cpp" line="354"/>
|
<location filename="../src/gui/owncloudgui.cpp" line="354"/>
|
||||||
|
@ -2849,12 +2853,12 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/owncloudgui.cpp" line="449"/>
|
<location filename="../src/gui/owncloudgui.cpp" line="449"/>
|
||||||
<source>Log out everywhere</source>
|
<source>Log out everywhere</source>
|
||||||
<translation type="unfinished"/>
|
<translation>全サーバーからログアウト</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/owncloudgui.cpp" line="457"/>
|
<location filename="../src/gui/owncloudgui.cpp" line="457"/>
|
||||||
<source>Log in everywhere...</source>
|
<source>Log in everywhere...</source>
|
||||||
<translation type="unfinished"/>
|
<translation>全サーバーにログイン...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/owncloudgui.cpp" line="513"/>
|
<location filename="../src/gui/owncloudgui.cpp" line="513"/>
|
||||||
|
@ -2967,7 +2971,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.ui" line="200"/>
|
<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>
|
<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>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.ui" line="203"/>
|
<location filename="../src/gui/wizard/owncloudadvancedsetuppage.ui" line="203"/>
|
||||||
|
@ -3161,7 +3165,7 @@ It is not advisable to use it.</source>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/utility.cpp" line="125"/>
|
<location filename="../src/libsync/utility.cpp" line="125"/>
|
||||||
<source>%L1 KB</source>
|
<source>%L1 KB</source>
|
||||||
<translation type="unfinished"/>
|
<translation>%L1 KB</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/libsync/utility.cpp" line="128"/>
|
<location filename="../src/libsync/utility.cpp" line="128"/>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue