Comparar commits
1 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 3708480952 |
@@ -1,7 +1,7 @@
|
||||
## Patches used
|
||||
|
||||
There are our patches on top of Qt 5.4.0, which we are currently
|
||||
using for our binary packages on Windows and Mac OS X. Most of them
|
||||
using for our binary packages on Windows and Mac OS. Most of them
|
||||
have been sent upstream and are part of newer Qt releases.
|
||||
|
||||
All changes are designed to up upstream, and all those that are
|
||||
@@ -28,26 +28,18 @@ purpose is outlined in each patches' front matter.
|
||||
* 0007-X-Network-Fix-up-previous-corruption-patch.patch
|
||||
* 0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch
|
||||
* 0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch
|
||||
* 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
|
||||
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
|
||||
|
||||
### Part of Qt v5.5.2 (UNRELEASED!)
|
||||
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
|
||||
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
|
||||
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
|
||||
|
||||
### Part of Qt v5.6 and later
|
||||
### Upstreamed but not in any release yet (as of 2015-11-16)
|
||||
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
|
||||
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
|
||||
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
|
||||
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
|
||||
* 0013-Make-UnknownAccessibility-not-block-requests.patch
|
||||
* 0015-Remove-legacy-platform-code-in-QSslSocket-for-OS-X-1.patch
|
||||
* 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
|
||||
* 0019-Ensure-system-tray-icon-is-prepared-even-when-menu-bar.patch
|
||||
|
||||
### Part of Qt 5.7 and later
|
||||
* 0015-Remove-legacy-platform-code-in-QSslSocket-for-OS-X-1.patch
|
||||
|
||||
### Not submitted upstream to be part of any release:
|
||||
### Not submitted to be part of any release:
|
||||
* 0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch
|
||||
This is only needed if you intent to harvest debugging symbols
|
||||
for breakpad.
|
||||
|
||||
+1
-1
Submodule binary updated: 230ed45cd0...ac42257499
@@ -620,6 +620,7 @@ Section Uninstall
|
||||
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCOverlays_x64.dll"
|
||||
!insertmacro UnInstallLib DLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCUtil_x64.dll"
|
||||
!undef LIBRARY_X64
|
||||
${Else}
|
||||
DetailPrint "Uninstalling x86 overlay DLLs"
|
||||
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCContextMenu_x86.dll"
|
||||
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_PROTECTED "$INSTDIR\shellext\OCOverlays_x86.dll"
|
||||
|
||||
@@ -59,10 +59,14 @@ int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
|
||||
#include <shlwapi.h>
|
||||
int csync_fnmatch(const char *pattern, const char *name, int flags) {
|
||||
BOOL match;
|
||||
mbchar_t *mbname = c_utf8_path_to_locale(name);
|
||||
mbchar_t *mbpatt = c_utf8_path_to_locale(pattern);
|
||||
|
||||
(void) flags;
|
||||
|
||||
match = PathMatchSpecA(name, pattern);
|
||||
match = PathMatchSpecW(mbname, mbpatt);
|
||||
c_free_locale_string(mbname);
|
||||
c_free_locale_string(mbpatt);
|
||||
|
||||
if(match)
|
||||
return 0;
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
* than fmmatch anyway, which does not care for flags.
|
||||
**/
|
||||
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
|
||||
#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
|
||||
#endif
|
||||
|
||||
int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags);
|
||||
|
||||
@@ -297,10 +297,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||
// zero size in statedb can happen during migration
|
||||
|| (tmp->size != 0 && fs->size != tmp->size))) {
|
||||
|
||||
// Checksum comparison at this stage is only enabled for .eml files,
|
||||
// check #4754 #4755
|
||||
bool isEmlFile = csync_fnmatch("*.eml", file, FNM_CASEFOLD) == 0;
|
||||
if (isEmlFile && fs->size == tmp->size && tmp->checksumTypeId) {
|
||||
if (fs->size == tmp->size && tmp->checksumTypeId) {
|
||||
if (ctx->callbacks.checksum_hook) {
|
||||
st->checksum = ctx->callbacks.checksum_hook(
|
||||
file, tmp->checksumTypeId,
|
||||
|
||||
@@ -230,10 +230,8 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
|
||||
ULARGE_INTEGER FileIndex;
|
||||
mbchar_t *wuri = c_utf8_path_to_locale( uri );
|
||||
|
||||
h = CreateFileW( wuri, 0, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL );
|
||||
h = CreateFileW( wuri, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL+FILE_FLAG_BACKUP_SEMANTICS+FILE_FLAG_OPEN_REPARSE_POINT, NULL );
|
||||
if( h == INVALID_HANDLE_VALUE ) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "CreateFileW failed on %s", uri );
|
||||
errno = GetLastError();
|
||||
|
||||
@@ -85,16 +85,6 @@ sub fromFileName($)
|
||||
}
|
||||
}
|
||||
|
||||
sub setCredentials
|
||||
{
|
||||
my ($dav, $user, $passwd) = @_;
|
||||
|
||||
$dav->credentials(-url=> $owncloud, -realm=>"sabre/dav",
|
||||
-user=> $user, -pass=> $passwd);
|
||||
$dav->credentials(-url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $user, -pass=> $passwd);
|
||||
}
|
||||
|
||||
|
||||
sub initTesting(;$)
|
||||
{
|
||||
@@ -137,7 +127,9 @@ sub initTesting(;$)
|
||||
my $ua = HTTP::DAV::UserAgent->new(keep_alive => 1 );
|
||||
$d = HTTP::DAV->new(-useragent => $ua);
|
||||
|
||||
setCredentials($d, $user, $passwd);
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $user,
|
||||
-pass=> $passwd );
|
||||
# $d->DebugLevel(3);
|
||||
$prefix = "t1" unless( defined $prefix );
|
||||
|
||||
@@ -201,7 +193,9 @@ sub removeRemoteDir($;$)
|
||||
|
||||
my $url = testDirUrl() . $dir;
|
||||
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
|
||||
setCredentials($d, $optionsRef->{user}, $optionsRef->{passwd});
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $optionsRef->{user},
|
||||
-pass=> $optionsRef->{passwd} );
|
||||
if( $optionsRef->{url} ) {
|
||||
$url = $optionsRef->{url} . $dir;
|
||||
}
|
||||
@@ -225,7 +219,9 @@ sub createRemoteDir(;$$)
|
||||
my $url = testDirUrl() . $dir;
|
||||
|
||||
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
|
||||
setCredentials($d, $optionsRef->{user}, $optionsRef->{passwd});
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $optionsRef->{user},
|
||||
-pass=> $optionsRef->{passwd} );
|
||||
if( $optionsRef->{url} ) {
|
||||
$url = $optionsRef->{url} . $dir;
|
||||
}
|
||||
@@ -400,7 +396,9 @@ sub traverse( $$;$ )
|
||||
my %seen;
|
||||
|
||||
|
||||
setCredentials($d, $user, $passwd);
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $user,
|
||||
-pass=> $passwd );
|
||||
$d->open( $owncloud );
|
||||
|
||||
if( my $r = $d->propfind( -url => $url, -depth => 1 ) ) {
|
||||
@@ -515,7 +513,9 @@ sub put_to_dir( $$;$ )
|
||||
my $targetUrl = testDirUrl();
|
||||
|
||||
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
|
||||
setCredentials($d, $optionsRef->{user}, $optionsRef->{passwd});
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $optionsRef->{user},
|
||||
-pass=> $optionsRef->{passwd} );
|
||||
if( $optionsRef->{url} ) {
|
||||
$targetUrl = $optionsRef->{url};
|
||||
}
|
||||
@@ -649,7 +649,9 @@ sub moveRemoteFile($$;$)
|
||||
{
|
||||
my ($from, $to, $no_testdir) = @_;
|
||||
|
||||
setCredentials($d, $user, $passwd);
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $user,
|
||||
-pass=> $passwd );
|
||||
|
||||
my $fromUrl = testDirUrl(). $from;
|
||||
my $toUrl = testDirUrl() . $to;
|
||||
@@ -723,7 +725,9 @@ sub createShare($$)
|
||||
|
||||
my $dd = HTTP::DAV->new();
|
||||
|
||||
setCredentials($dd, $share_user, $share_passwd);
|
||||
$dd->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $share_user,
|
||||
-pass=> $share_passwd );
|
||||
$dd->open( $owncloud);
|
||||
|
||||
# create a remote dir
|
||||
@@ -765,7 +769,9 @@ sub removeShare($$)
|
||||
|
||||
my $dd = HTTP::DAV->new();
|
||||
|
||||
setCredentials($dd, $share_user, $share_passwd);
|
||||
$dd->credentials( -url => $owncloud, -realm=>"ownCloud",
|
||||
-user => $share_user,
|
||||
-pass => $share_passwd );
|
||||
$dd->open( $owncloud);
|
||||
|
||||
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
|
||||
|
||||
@@ -146,9 +146,9 @@ assertLocalAndRemoteDir( '', 0);
|
||||
|
||||
# create a true conflict.
|
||||
printInfo( "Create a conflict." );
|
||||
system( "echo \"This is more server stuff\" >> /tmp/kernelcrash.txt" );
|
||||
system( "echo \"This is more stuff\" >> /tmp/kernelcrash.txt" );
|
||||
put_to_dir( '/tmp/kernelcrash.txt', 'remoteToLocal1' );
|
||||
system( "sleep 2 && echo \"This is more client stuff\" >> " . localDir() . "remoteToLocal1/kernelcrash.txt" );
|
||||
system( "sleep 2 && touch " . localDir() . "remoteToLocal1/kernelcrash.txt" );
|
||||
csync();
|
||||
assertLocalAndRemoteDir( '', 1);
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ assert($emlpropafter->get_property( "getlastmodified" ) eq
|
||||
$emlpropbefore->get_property( "getlastmodified" ));
|
||||
|
||||
printInfo( "Change content of eml file (but not size)");
|
||||
system( "sleep 1 && sed -i -e 's/in/IN/' $locDir/test.eml" );
|
||||
system( "sed -i -e 's/in/IN/' $locDir/test.eml" );
|
||||
|
||||
csync( );
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ endif()
|
||||
set(OWNCLOUDDOLPHINHELPER ${APPLICATION_EXECUTABLE}dolphinpluginhelper)
|
||||
add_library(${OWNCLOUDDOLPHINHELPER} SHARED ownclouddolphinpluginhelper.cpp)
|
||||
target_link_libraries(${OWNCLOUDDOLPHINHELPER} Qt5::Network)
|
||||
generate_export_header(${OWNCLOUDDOLPHINHELPER} BASE_NAME ownclouddolphinpluginhelper)
|
||||
generate_export_header(${OWNCLOUDDOLPHINHELPER})
|
||||
install(TARGETS ${OWNCLOUDDOLPHINHELPER} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
#---OVERLAY PLUGIN---
|
||||
|
||||
@@ -21,15 +21,11 @@ import socket
|
||||
|
||||
from gi.repository import GObject, Nautilus
|
||||
|
||||
# Please do not touch the following line.
|
||||
# The reason is that we use a script to adopt this file for branding
|
||||
# by replacing this line with the branding app name. If the following
|
||||
# line is changed, the script can not match the pattern and fails.
|
||||
print("Initializing owncloud-client-nautilus extension")
|
||||
|
||||
# Do not touch the following line.
|
||||
appname = 'ownCloud'
|
||||
|
||||
print("Initializing "+appname+"-client-nautilus extension")
|
||||
|
||||
|
||||
def get_local_path(url):
|
||||
if url[0:7] == 'file://':
|
||||
url = url[7:]
|
||||
@@ -185,10 +181,10 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||
if not syncedFile:
|
||||
return items
|
||||
|
||||
# Create a menu item
|
||||
# Create an menu item
|
||||
labelStr = "Share with " + appname + "..."
|
||||
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
|
||||
tip='Share file {} through {}'.format(file.get_name(), appname) )
|
||||
tip='Share file %s through ownCloud' % file.get_name())
|
||||
item.connect("activate", self.menu_share, file)
|
||||
items.append(item)
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ using namespace std;
|
||||
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||
pipename += L"ownCloud";
|
||||
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
@@ -71,7 +72,8 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
||||
|
||||
void OCClientInterface::ShareObject(const std::wstring &path)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||
pipename += L"ownCloud";
|
||||
|
||||
CommunicationSocket socket;
|
||||
if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) {
|
||||
|
||||
@@ -24,31 +24,11 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#define DEFAULT_BUFLEN 4096
|
||||
#define BUFSIZE 1024
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
std::wstring getUserName() {
|
||||
DWORD len = DEFAULT_BUFLEN;
|
||||
TCHAR buf[DEFAULT_BUFLEN];
|
||||
if (GetUserName(buf, &len)) {
|
||||
return std::wstring(&buf[0], len);
|
||||
} else {
|
||||
return std::wstring();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::wstring CommunicationSocket::DefaultPipePath()
|
||||
{
|
||||
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||
pipename += L"ownCloud\\";
|
||||
pipename += getUserName();
|
||||
return pipename;
|
||||
}
|
||||
#define DEFAULT_BUFLEN 4096
|
||||
|
||||
CommunicationSocket::CommunicationSocket()
|
||||
: _pipe(INVALID_HANDLE_VALUE)
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
class __declspec(dllexport) CommunicationSocket
|
||||
{
|
||||
public:
|
||||
static std::wstring DefaultPipePath();
|
||||
|
||||
CommunicationSocket();
|
||||
~CommunicationSocket();
|
||||
|
||||
@@ -45,4 +43,4 @@ private:
|
||||
bool _connected;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -31,10 +31,13 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
// This code is run in a thread
|
||||
void RemotePathChecker::workerThreadLoop()
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
auto pipename = std::wstring(L"\\\\.\\pipe\\");
|
||||
pipename += L"ownCloud";
|
||||
|
||||
bool connected = false;
|
||||
CommunicationSocket socket;
|
||||
std::unordered_set<std::wstring> asked;
|
||||
@@ -238,4 +241,4 @@ RemotePathChecker::FileState RemotePathChecker::_StrToFileState(const std::wstri
|
||||
}
|
||||
|
||||
return StateNone;
|
||||
}
|
||||
}
|
||||
externo
+31
-46
@@ -1,6 +1,6 @@
|
||||
/******************************************************************************
|
||||
** This file is an amalgamation of many separate C source files from SQLite
|
||||
** version 3.12.2. By combining all the individual C code files into this
|
||||
** version 3.12.1. By combining all the individual C code files into this
|
||||
** single large file, the entire code can be compiled as a single translation
|
||||
** unit. This allows many compilers to do optimizations that would not be
|
||||
** possible if the files were compiled separately. Performance improvements
|
||||
@@ -336,9 +336,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.12.2"
|
||||
#define SQLITE_VERSION_NUMBER 3012002
|
||||
#define SQLITE_SOURCE_ID "2016-04-18 17:30:31 92dc59fd5ad66f646666042eb04195e3a61a9e8e"
|
||||
#define SQLITE_VERSION "3.12.1"
|
||||
#define SQLITE_VERSION_NUMBER 3012001
|
||||
#define SQLITE_SOURCE_ID "2016-04-08 15:09:49 fe7d3b75fe1bde41511b323925af8ae1b910bc4d"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@@ -63941,28 +63941,6 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
pPage = pCur->apPage[iCellDepth];
|
||||
pCell = findCell(pPage, iCellIdx);
|
||||
|
||||
/* If the bPreserve flag is set to true, then the cursor position must
|
||||
** be preserved following this delete operation. If the current delete
|
||||
** will cause a b-tree rebalance, then this is done by saving the cursor
|
||||
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
|
||||
** returning.
|
||||
**
|
||||
** Or, if the current delete will not cause a rebalance, then the cursor
|
||||
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
|
||||
** before or after the deleted entry. In this case set bSkipnext to true. */
|
||||
if( bPreserve ){
|
||||
if( !pPage->leaf
|
||||
|| (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
|
||||
){
|
||||
/* A b-tree rebalance will be required after deleting this entry.
|
||||
** Save the cursor key. */
|
||||
rc = saveCursorKey(pCur);
|
||||
if( rc ) return rc;
|
||||
}else{
|
||||
bSkipnext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the page containing the entry to delete is not a leaf page, move
|
||||
** the cursor to the largest entry in the tree that is smaller than
|
||||
** the entry being deleted. This cell will replace the cell being deleted
|
||||
@@ -63989,6 +63967,28 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
|
||||
}
|
||||
|
||||
/* If the bPreserve flag is set to true, then the cursor position must
|
||||
** be preserved following this delete operation. If the current delete
|
||||
** will cause a b-tree rebalance, then this is done by saving the cursor
|
||||
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
|
||||
** returning.
|
||||
**
|
||||
** Or, if the current delete will not cause a rebalance, then the cursor
|
||||
** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
|
||||
** before or after the deleted entry. In this case set bSkipnext to true. */
|
||||
if( bPreserve ){
|
||||
if( !pPage->leaf
|
||||
|| (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
|
||||
){
|
||||
/* A b-tree rebalance will be required after deleting this entry.
|
||||
** Save the cursor key. */
|
||||
rc = saveCursorKey(pCur);
|
||||
if( rc ) return rc;
|
||||
}else{
|
||||
bSkipnext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the page containing the entry to be deleted writable. Then free any
|
||||
** overflow pages associated with the entry and finally remove the cell
|
||||
** itself from within the page. */
|
||||
@@ -82769,6 +82769,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
|
||||
){
|
||||
int pgsz; /* Page size of main database */
|
||||
int i; /* Used to iterate through aTask[] */
|
||||
int mxCache; /* Cache size */
|
||||
VdbeSorter *pSorter; /* The new sorter */
|
||||
KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */
|
||||
int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */
|
||||
@@ -82825,20 +82826,11 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
|
||||
}
|
||||
|
||||
if( !sqlite3TempInMemory(db) ){
|
||||
i64 mxCache; /* Cache size in bytes*/
|
||||
u32 szPma = sqlite3GlobalConfig.szPma;
|
||||
pSorter->mnPmaSize = szPma * pgsz;
|
||||
|
||||
mxCache = db->aDb[0].pSchema->cache_size;
|
||||
if( mxCache<0 ){
|
||||
/* A negative cache-size value C indicates that the cache is abs(C)
|
||||
** KiB in size. */
|
||||
mxCache = mxCache * -1024;
|
||||
}else{
|
||||
mxCache = mxCache * pgsz;
|
||||
}
|
||||
mxCache = MIN(mxCache, SQLITE_MAX_PMASZ);
|
||||
pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache);
|
||||
if( mxCache<(int)szPma ) mxCache = (int)szPma;
|
||||
pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ);
|
||||
|
||||
/* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
|
||||
** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
|
||||
@@ -95571,7 +95563,6 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
|
||||
zType = z + sqlite3Strlen30(z) + 1;
|
||||
memcpy(zType, pType->z, pType->n);
|
||||
zType[pType->n] = 0;
|
||||
sqlite3Dequote(zType);
|
||||
pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
|
||||
pCol->colFlags |= COLFLAG_HASTYPE;
|
||||
}
|
||||
@@ -121369,13 +121360,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
}
|
||||
}
|
||||
/* These registers need to be preserved in case there is an IN operator
|
||||
** loop. So we could deallocate the registers here (and potentially
|
||||
** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems
|
||||
** simpler and safer to simply not reuse the registers.
|
||||
**
|
||||
** sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
|
||||
*/
|
||||
sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
|
||||
sqlite3ExprCachePop(pParse);
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
@@ -185473,7 +185458,7 @@ static void fts5SourceIdFunc(
|
||||
){
|
||||
assert( nArg==0 );
|
||||
UNUSED_PARAM2(nArg, apUnused);
|
||||
sqlite3_result_text(pCtx, "fts5: 2016-04-18 17:30:31 92dc59fd5ad66f646666042eb04195e3a61a9e8e", -1, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(pCtx, "fts5: 2016-04-08 15:09:49 fe7d3b75fe1bde41511b323925af8ae1b910bc4d", -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
static int fts5Init(sqlite3 *db){
|
||||
|
||||
externo
+3
-3
@@ -111,9 +111,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.12.2"
|
||||
#define SQLITE_VERSION_NUMBER 3012002
|
||||
#define SQLITE_SOURCE_ID "2016-04-18 17:30:31 92dc59fd5ad66f646666042eb04195e3a61a9e8e"
|
||||
#define SQLITE_VERSION "3.12.1"
|
||||
#define SQLITE_VERSION_NUMBER 3012001
|
||||
#define SQLITE_SOURCE_ID "2016-04-08 15:09:49 fe7d3b75fe1bde41511b323925af8ae1b910bc4d"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
||||
@@ -54,7 +54,6 @@ set(client_SRCS
|
||||
folderwizard.cpp
|
||||
generalsettings.cpp
|
||||
ignorelisteditor.cpp
|
||||
lockwatcher.cpp
|
||||
logbrowser.cpp
|
||||
networksettings.cpp
|
||||
ocsjob.cpp
|
||||
|
||||
@@ -197,7 +197,8 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_model->classify(index) != FolderStatusModel::RootFolder) {
|
||||
QString alias = _model->data( index, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
if (alias.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,9 +95,6 @@ void AccountState::setState(State state)
|
||||
} else if (oldState == SignedOut && _state == Disconnected) {
|
||||
checkConnectivity();
|
||||
}
|
||||
if (oldState == Connected || _state == Connected) {
|
||||
emit isConnectedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// might not have changed but the underlying _connectionErrors might have
|
||||
@@ -152,6 +149,11 @@ bool AccountState::isConnectedOrTemporarilyUnavailable() const
|
||||
return isConnected() || _state == ServiceUnavailable;
|
||||
}
|
||||
|
||||
bool AccountState::canSync() const
|
||||
{
|
||||
return isConnected();
|
||||
}
|
||||
|
||||
void AccountState::tagLastSuccessfullETagRequest()
|
||||
{
|
||||
_timeSinceLastETagCheck.restart();
|
||||
|
||||
@@ -102,6 +102,9 @@ public:
|
||||
bool isConnected() const;
|
||||
bool isConnectedOrTemporarilyUnavailable() const;
|
||||
|
||||
/// Returns whether sync actions are allowed to run.
|
||||
bool canSync() const;
|
||||
|
||||
/// Triggers a ping to the server to update state and
|
||||
/// connection status and errors.
|
||||
void checkConnectivity();
|
||||
@@ -127,7 +130,6 @@ private:
|
||||
|
||||
signals:
|
||||
void stateChanged(int state);
|
||||
void isConnectedChanged();
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList& errors);
|
||||
|
||||
@@ -67,13 +67,8 @@ void ShibbolethCredentials::setAccount(Account* account)
|
||||
{
|
||||
AbstractCredentials::setAccount(account);
|
||||
|
||||
// This is for existing saved accounts.
|
||||
if (_user.isEmpty()) {
|
||||
_user = _account->credentialSetting(QLatin1String(userC)).toString();
|
||||
}
|
||||
|
||||
// When constructed with a cookie (by the wizard), we usually don't know the
|
||||
// user name yet. Request it now from the server.
|
||||
// user name yet. Request it now.
|
||||
if (_ready && _user.isEmpty()) {
|
||||
QTimer::singleShot(1234, this, SLOT(slotFetchUser()));
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@ Folder::Folder(const FolderDefinition& definition,
|
||||
if (!setIgnoredFiles())
|
||||
qWarning("Could not read system exclude file");
|
||||
|
||||
connect(_accountState.data(), SIGNAL(isConnectedChanged()), this, SIGNAL(canSyncChanged()));
|
||||
connect(_engine.data(), SIGNAL(rootEtag(QString)), this, SLOT(etagRetreivedFromSyncEngine(QString)));
|
||||
connect(_engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
|
||||
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
|
||||
@@ -116,7 +115,6 @@ Folder::Folder(const FolderDefinition& definition,
|
||||
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
|
||||
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
|
||||
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
|
||||
connect(_engine.data(), SIGNAL(seenLockedFile(QString)), FolderMan::instance(), SLOT(slotSyncOnceFileUnlocks(QString)));
|
||||
}
|
||||
|
||||
Folder::~Folder()
|
||||
@@ -231,7 +229,7 @@ bool Folder::syncPaused() const
|
||||
|
||||
bool Folder::canSync() const
|
||||
{
|
||||
return !syncPaused() && accountState()->isConnected();
|
||||
return !syncPaused() && accountState()->canSync();
|
||||
}
|
||||
|
||||
void Folder::setSyncPaused( bool paused )
|
||||
@@ -250,7 +248,6 @@ void Folder::setSyncPaused( bool paused )
|
||||
}
|
||||
emit syncPausedChanged(this, paused);
|
||||
emit syncStateChange();
|
||||
emit canSyncChanged();
|
||||
}
|
||||
|
||||
void Folder::setSyncState(SyncResult::Status state)
|
||||
@@ -581,7 +578,6 @@ void Folder::slotWatchedPathChanged(const QString& path)
|
||||
// When no sync is running or it's in the prepare phase, we can
|
||||
// always schedule a new sync.
|
||||
if (! _engine->isSyncRunning() || _syncResult.status() == SyncResult::SyncPrepare) {
|
||||
emit watchedFileChangedExternally(path);
|
||||
emit scheduleToSync(this);
|
||||
return;
|
||||
}
|
||||
@@ -605,7 +601,6 @@ void Folder::slotWatchedPathChanged(const QString& path)
|
||||
#endif
|
||||
|
||||
if (! ownChange) {
|
||||
emit watchedFileChangedExternally(path);
|
||||
emit scheduleToSync(this);
|
||||
}
|
||||
}
|
||||
@@ -688,8 +683,7 @@ void Folder::wipe()
|
||||
QFile::remove( stateDbFile + "-wal" );
|
||||
QFile::remove( stateDbFile + "-journal" );
|
||||
|
||||
if (canSync())
|
||||
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
|
||||
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
|
||||
}
|
||||
|
||||
bool Folder::setIgnoredFiles()
|
||||
|
||||
+2
-7
@@ -131,6 +131,8 @@ public:
|
||||
|
||||
/**
|
||||
* Returns true when the folder may sync.
|
||||
*
|
||||
* !syncPaused() && accountState->canSync().
|
||||
*/
|
||||
bool canSync() const;
|
||||
|
||||
@@ -201,13 +203,6 @@ signals:
|
||||
void progressInfo(const ProgressInfo& progress);
|
||||
void newBigFolderDiscovered(const QString &); // A new folder bigger than the threshold was discovered
|
||||
void syncPausedChanged(Folder*, bool paused);
|
||||
void canSyncChanged();
|
||||
|
||||
/**
|
||||
* Fires for each change inside this folder that wasn't caused
|
||||
* by sync activity.
|
||||
*/
|
||||
void watchedFileChangedExternally(const QString& path);
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
+2
-37
@@ -22,7 +22,6 @@
|
||||
#include "accountstate.h"
|
||||
#include "accountmanager.h"
|
||||
#include "filesystem.h"
|
||||
#include "lockwatcher.h"
|
||||
#include <syncengine.h>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
@@ -45,7 +44,6 @@ FolderMan::FolderMan(QObject *parent) :
|
||||
QObject(parent),
|
||||
_currentSyncFolder(0),
|
||||
_syncEnabled( true ),
|
||||
_lockWatcher(new LockWatcher),
|
||||
_appRestartRequired(false)
|
||||
{
|
||||
Q_ASSERT(!_instance);
|
||||
@@ -66,9 +64,6 @@ FolderMan::FolderMan(QObject *parent) :
|
||||
|
||||
connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState*)),
|
||||
SLOT(slotRemoveFoldersForAccount(AccountState*)));
|
||||
|
||||
connect(_lockWatcher.data(), SIGNAL(fileUnlocked(QString)),
|
||||
SLOT(slotScheduleFolderOwningFile(QString)));
|
||||
}
|
||||
|
||||
FolderMan *FolderMan::instance()
|
||||
@@ -112,8 +107,6 @@ void FolderMan::unloadFolder( Folder *f )
|
||||
this, SLOT(slotFolderSyncPaused(Folder*,bool)));
|
||||
disconnect(&f->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
|
||||
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
|
||||
disconnect(f, SIGNAL(watchedFileChangedExternally(QString)),
|
||||
&f->syncEngine().syncFileStatusTracker(), SLOT(slotPathTouched(QString)));
|
||||
}
|
||||
|
||||
int FolderMan::unloadAndDeleteAllFolders()
|
||||
@@ -152,13 +145,11 @@ void FolderMan::registerFolderMonitor( Folder *folder )
|
||||
// to the signal mapper which maps to the folder alias. The changed path
|
||||
// is lost this way, but we do not need it for the current implementation.
|
||||
connect(fw, SIGNAL(pathChanged(QString)), folder, SLOT(slotWatchedPathChanged(QString)));
|
||||
|
||||
_folderWatchers.insert(folder->alias(), fw);
|
||||
}
|
||||
|
||||
// register the folder with the socket API
|
||||
if (folder->canSync())
|
||||
_socketApi->slotRegisterPath(folder->alias());
|
||||
_socketApi->slotRegisterPath(folder->alias());
|
||||
}
|
||||
|
||||
void FolderMan::addMonitorPath( const QString& alias, const QString& path )
|
||||
@@ -428,17 +419,6 @@ void FolderMan::slotFolderSyncPaused( Folder *f, bool paused )
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::slotFolderCanSyncChanged()
|
||||
{
|
||||
Folder *f = qobject_cast<Folder*>(sender());
|
||||
Q_ASSERT(f);
|
||||
if (f->canSync()) {
|
||||
_socketApi->slotRegisterPath(f->alias());
|
||||
} else {
|
||||
_socketApi->slotUnregisterPath(f->alias());
|
||||
}
|
||||
}
|
||||
|
||||
// this really terminates the current sync process
|
||||
// ie. no questions, no prisoners
|
||||
// csync still remains in a stable state, regardless of that.
|
||||
@@ -477,11 +457,6 @@ void FolderMan::slotScheduleAppRestart()
|
||||
qDebug() << "## Application restart requested!";
|
||||
}
|
||||
|
||||
void FolderMan::slotSyncOnceFileUnlocks(const QString& path)
|
||||
{
|
||||
_lockWatcher->addFile(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* if a folder wants to be synced, it calls this slot and is added
|
||||
* to the queue. The slot to actually start a sync is called afterwards.
|
||||
@@ -561,7 +536,7 @@ void FolderMan::slotAccountStateChanged()
|
||||
}
|
||||
QString accountName = accountState->account()->displayName();
|
||||
|
||||
if (accountState->isConnected()) {
|
||||
if (accountState->canSync()) {
|
||||
qDebug() << "Account" << accountName << "connected, scheduling its folders";
|
||||
|
||||
foreach (Folder *f, _folderMap.values()) {
|
||||
@@ -766,13 +741,6 @@ void FolderMan::slotServerVersionChanged(Account *account)
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::slotScheduleFolderOwningFile(const QString& path)
|
||||
{
|
||||
if (Folder* f = folderForPath(path)) {
|
||||
slotScheduleSync(f);
|
||||
}
|
||||
}
|
||||
|
||||
void FolderMan::slotFolderSyncStarted( )
|
||||
{
|
||||
qDebug() << ">===================================== sync started for " << _currentSyncFolder->remoteUrl().toString();
|
||||
@@ -832,11 +800,8 @@ Folder* FolderMan::addFolderInternal(FolderDefinition folderDefinition, AccountS
|
||||
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
|
||||
connect(folder, SIGNAL(syncStateChange()), SLOT(slotForwardFolderSyncStateChange()));
|
||||
connect(folder, SIGNAL(syncPausedChanged(Folder*,bool)), SLOT(slotFolderSyncPaused(Folder*,bool)));
|
||||
connect(folder, SIGNAL(canSyncChanged()), SLOT(slotFolderCanSyncChanged()));
|
||||
connect(&folder->syncEngine().syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString &, SyncFileStatus)),
|
||||
_socketApi.data(), SLOT(slotFileStatusChanged(const QString &, SyncFileStatus)));
|
||||
connect(folder, SIGNAL(watchedFileChangedExternally(QString)),
|
||||
&folder->syncEngine().syncFileStatusTracker(), SLOT(slotPathTouched(QString)));
|
||||
|
||||
registerFolderMonitor(folder);
|
||||
return folder;
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace OCC {
|
||||
class Application;
|
||||
class SyncResult;
|
||||
class SocketApi;
|
||||
class LockWatcher;
|
||||
|
||||
/**
|
||||
* @brief The FolderMan class
|
||||
@@ -144,7 +143,6 @@ signals:
|
||||
public slots:
|
||||
void slotRemoveFolder( Folder* );
|
||||
void slotFolderSyncPaused(Folder *, bool paused);
|
||||
void slotFolderCanSyncChanged();
|
||||
|
||||
void slotFolderSyncStarted();
|
||||
void slotFolderSyncFinished( const SyncResult& );
|
||||
@@ -186,13 +184,6 @@ public slots:
|
||||
*/
|
||||
void slotScheduleAppRestart();
|
||||
|
||||
/**
|
||||
* Triggers a sync run once the lock on the given file is removed.
|
||||
*
|
||||
* Automatically detemines the folder that's responsible for the file.
|
||||
*/
|
||||
void slotSyncOnceFileUnlocks(const QString& path);
|
||||
|
||||
private slots:
|
||||
// slot to take the next folder from queue and start syncing.
|
||||
void slotStartScheduledFolderSync();
|
||||
@@ -206,12 +197,6 @@ private slots:
|
||||
|
||||
void slotServerVersionChanged(Account* account);
|
||||
|
||||
/**
|
||||
* Schedules the folder for synchronization that contains
|
||||
* the file with the given path.
|
||||
*/
|
||||
void slotScheduleFolderOwningFile(const QString& path);
|
||||
|
||||
private:
|
||||
/** Adds a new folder, does not add it to the account settings and
|
||||
* does not set an account on the new folder.
|
||||
@@ -242,7 +227,6 @@ private:
|
||||
QPointer<RequestEtagJob> _currentEtagJob; // alias of Folder running the current RequestEtagJob
|
||||
|
||||
QMap<QString, FolderWatcher*> _folderWatchers;
|
||||
QScopedPointer<LockWatcher> _lockWatcher;
|
||||
QScopedPointer<SocketApi> _socketApi;
|
||||
|
||||
/** The aliases of folders that shall be synced. */
|
||||
|
||||
@@ -574,6 +574,9 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||
return;
|
||||
}
|
||||
|
||||
QVarLengthArray<int, 10> undecidedIndexes;
|
||||
QVector<SubFolderInfo> newSubs;
|
||||
|
||||
std::set<QString> selectiveSyncUndecidedSet; // not QSet because it's not sorted
|
||||
foreach (const QString &str, selectiveSyncUndecidedList) {
|
||||
if (str.startsWith(parentInfo->_path) || parentInfo->_path == QLatin1String("/")) {
|
||||
@@ -581,16 +584,10 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
||||
}
|
||||
}
|
||||
|
||||
QStringList sortedSubfolders = list;
|
||||
// skip the parent item (first in the list)
|
||||
sortedSubfolders.erase(sortedSubfolders.begin());
|
||||
sortedSubfolders.sort();
|
||||
|
||||
QVarLengthArray<int, 10> undecidedIndexes;
|
||||
|
||||
QVector<SubFolderInfo> newSubs;
|
||||
newSubs.reserve(sortedSubfolders.size());
|
||||
foreach (const QString& path, sortedSubfolders) {
|
||||
newSubs.reserve(list.size() - 1);
|
||||
for (int i = 1; // skip the parent item (first in the list)
|
||||
i < list.size(); ++i) {
|
||||
const QString &path = list.at(i);
|
||||
auto relativePath = path.mid(pathToRemove.size());
|
||||
if (parentInfo->_folder->isFileExcludedRelative(relativePath)) {
|
||||
continue;
|
||||
|
||||
@@ -306,7 +306,6 @@ bool FolderWizardRemotePath::selectByPath(QString path)
|
||||
}
|
||||
|
||||
_ui.folderTreeWidget->setCurrentItem(it);
|
||||
_ui.folderTreeWidget->scrollToItem(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -322,9 +321,7 @@ void FolderWizardRemotePath::slotUpdateDirectories(const QStringList &list)
|
||||
root->setToolTip(0, tr("Choose this to sync the entire account"));
|
||||
root->setData(0, Qt::UserRole, "/");
|
||||
}
|
||||
QStringList sortedList = list;
|
||||
sortedList.sort();
|
||||
foreach (QString path, sortedList) {
|
||||
foreach (QString path, list) {
|
||||
path.remove(webdavFolder);
|
||||
QStringList paths = path.split('/');
|
||||
if (paths.last().isEmpty()) paths.removeLast();
|
||||
@@ -377,11 +374,8 @@ void FolderWizardRemotePath::slotLsColFolderEntry()
|
||||
path = path.mid(1);
|
||||
|
||||
LsColJob *job = runLsColJob(path);
|
||||
// No error handling, no updating, we do this manually
|
||||
// because of extra logic in the typed-path case.
|
||||
// no error handling, no updating, we do this manually
|
||||
disconnect(job, 0, this, 0);
|
||||
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
|
||||
SLOT(slotTypedPathError(QNetworkReply*)));
|
||||
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
|
||||
SLOT(slotTypedPathFound(QStringList)));
|
||||
}
|
||||
@@ -392,21 +386,6 @@ void FolderWizardRemotePath::slotTypedPathFound(const QStringList& subpaths)
|
||||
selectByPath(_ui.folderEntry->text());
|
||||
}
|
||||
|
||||
void FolderWizardRemotePath::slotTypedPathError(QNetworkReply* reply)
|
||||
{
|
||||
// Ignore 404s, otherwise users will get annoyed by error popups
|
||||
// when not typing fast enough. It's still clear that a given path
|
||||
// was not found, because the 'Next' button is disabled and no entry
|
||||
// is selected in the tree view.
|
||||
int httpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (httpCode == 404) {
|
||||
showWarn(""); // hides the warning pane
|
||||
return;
|
||||
}
|
||||
|
||||
slotHandleLsColNetworkError(reply);
|
||||
}
|
||||
|
||||
LsColJob* FolderWizardRemotePath::runLsColJob(const QString& path)
|
||||
{
|
||||
LsColJob *job = new LsColJob(_account, path, this);
|
||||
|
||||
@@ -98,7 +98,6 @@ protected slots:
|
||||
void slotFolderEntryEdited(const QString& text);
|
||||
void slotLsColFolderEntry();
|
||||
void slotTypedPathFound(const QStringList& subpaths);
|
||||
void slotTypedPathError(QNetworkReply* reply);
|
||||
private:
|
||||
LsColJob* runLsColJob(const QString& path);
|
||||
void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path);
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Christian Kamm <mail@ckamm.de>
|
||||
*
|
||||
* 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 "lockwatcher.h"
|
||||
#include "filesystem.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
static const int check_frequency = 20 * 1000; // ms
|
||||
|
||||
LockWatcher::LockWatcher(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
connect(&_timer, SIGNAL(timeout()),
|
||||
SLOT(checkFiles()));
|
||||
_timer.start(check_frequency);
|
||||
}
|
||||
|
||||
void LockWatcher::addFile(const QString& path)
|
||||
{
|
||||
_watchedPaths.insert(path);
|
||||
}
|
||||
|
||||
void LockWatcher::checkFiles()
|
||||
{
|
||||
QSet<QString> unlocked;
|
||||
|
||||
foreach (const QString& path, _watchedPaths) {
|
||||
if (!FileSystem::isFileLocked(path)) {
|
||||
emit fileUnlocked(path);
|
||||
unlocked.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Doing it this way instead of with a QMutableSetIterator
|
||||
// ensures that calling back into addFile from connected
|
||||
// slots isn't a problem.
|
||||
_watchedPaths.subtract(unlocked);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Christian Kamm <mail@ckamm.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* @brief Monitors files that are locked, signaling when they become unlocked
|
||||
*
|
||||
* Only relevant on Windows. Some high-profile applications like Microsoft
|
||||
* Word lock the document that is currently being edited. The synchronization
|
||||
* client will be unable to update them while they are locked.
|
||||
*
|
||||
* In this situation we do want to start a sync run as soon as the file
|
||||
* becomes available again. To do that, we need to regularly check whether
|
||||
* the file is still being locked.
|
||||
*
|
||||
* @ingroup gui
|
||||
*/
|
||||
|
||||
class LockWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LockWatcher(QObject* parent = 0);
|
||||
|
||||
/** Start watching a file.
|
||||
*
|
||||
* If the file is not locked later on, the fileUnlocked signal will be
|
||||
* emitted once.
|
||||
*/
|
||||
void addFile(const QString& path);
|
||||
|
||||
signals:
|
||||
/** Emitted when one of the watched files is no longer
|
||||
* being locked. */
|
||||
void fileUnlocked(const QString& path);
|
||||
|
||||
private slots:
|
||||
void checkFiles();
|
||||
|
||||
private:
|
||||
QSet<QString> _watchedPaths;
|
||||
QTimer _timer;
|
||||
};
|
||||
|
||||
}
|
||||
+1
-11
@@ -127,17 +127,8 @@ int main(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
|
||||
if (qgetenv("QT_QPA_PLATFORMTHEME") != "appmenu-qt5")
|
||||
// We can't call isSystemTrayAvailable with appmenu-qt5 begause it hides the systemtray
|
||||
// (issue #4693)
|
||||
#endif
|
||||
{
|
||||
} else {
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
// If the systemtray is not there, we will wait one second for it to maybe start
|
||||
// (eg boot time) then we show the settings dialog if there is still no systemtray.
|
||||
// On XFCE however, we show a message box with explainaition how to install a systemtray.
|
||||
Utility::sleep(1);
|
||||
auto desktopSession = qgetenv("XDG_CURRENT_DESKTOP").toLower();
|
||||
if (desktopSession.isEmpty()) {
|
||||
@@ -161,7 +152,6 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,9 +58,6 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) :
|
||||
header << tr("Size");
|
||||
|
||||
_ui->_treeWidget->setHeaderLabels( header );
|
||||
int timestampColumnWidth =
|
||||
_ui->_treeWidget->fontMetrics().width(timeString(QDateTime::currentDateTime()));
|
||||
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||
_ui->_treeWidget->setColumnCount(5);
|
||||
_ui->_treeWidget->setRootIsDecorated(false);
|
||||
@@ -82,10 +79,6 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) :
|
||||
_issueItemView = new QTreeWidget(this);
|
||||
header.removeLast();
|
||||
_issueItemView->setHeaderLabels( header );
|
||||
timestampColumnWidth =
|
||||
ActivityItemDelegate::rowHeight() // icon
|
||||
+ _issueItemView->fontMetrics().width(timeString(QDateTime::currentDateTime()));
|
||||
_issueItemView->setColumnWidth(0, timestampColumnWidth);
|
||||
_issueItemView->setColumnWidth(1, 180);
|
||||
_issueItemView->setColumnCount(4);
|
||||
_issueItemView->setRootIsDecorated(false);
|
||||
|
||||
@@ -222,7 +222,6 @@ void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
|
||||
}
|
||||
}
|
||||
|
||||
list.sort();
|
||||
foreach (QString path, list) {
|
||||
auto size = job ? job->_sizes.value(path) : 0;
|
||||
path.remove(pathToRemove);
|
||||
|
||||
@@ -98,6 +98,17 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
|
||||
return;
|
||||
}
|
||||
|
||||
// error label, red box and stuff
|
||||
_ui->errorLabel->setLineWidth(1);
|
||||
_ui->errorLabel->setFrameStyle(QFrame::Plain);
|
||||
|
||||
QPalette errPalette = _ui->errorLabel->palette();
|
||||
errPalette.setColor(QPalette::Active, QPalette::Base, QColor(0xaa, 0x4d, 0x4d));
|
||||
errPalette.setColor(QPalette::Active, QPalette::WindowText, QColor(0xaa, 0xaa, 0xaa));
|
||||
|
||||
_ui->errorLabel->setPalette(errPalette);
|
||||
_ui->errorLabel->setFrameShape(QFrame::Box);
|
||||
_ui->errorLabel->setContentsMargins(QMargins(12,12,12,12));
|
||||
_ui->errorLabel->hide();
|
||||
|
||||
|
||||
|
||||
@@ -36,43 +36,6 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>123</red>
|
||||
<green>121</green>
|
||||
<blue>134</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
|
||||
@@ -99,9 +99,6 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
|
||||
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
|
||||
_ui->errorLabel->hide();
|
||||
|
||||
// Setup the sharee search progress indicator
|
||||
_ui->shareeHorizontalLayout->addWidget(&_pi_sharee);
|
||||
}
|
||||
|
||||
ShareUserGroupWidget::~ShareUserGroupWidget()
|
||||
@@ -150,7 +147,6 @@ void ShareUserGroupWidget::slotLineEditReturn()
|
||||
void ShareUserGroupWidget::searchForSharees()
|
||||
{
|
||||
_completionTimer.stop();
|
||||
_pi_sharee.startAnimation();
|
||||
ShareeModel::ShareeSet blacklist;
|
||||
|
||||
// Add the current user to _sharees since we can't share with ourself
|
||||
@@ -162,6 +158,7 @@ void ShareUserGroupWidget::searchForSharees()
|
||||
}
|
||||
_ui->errorLabel->hide();
|
||||
_completerModel->fetch(_ui->shareeLineEdit->text(), blacklist);
|
||||
|
||||
}
|
||||
|
||||
void ShareUserGroupWidget::getShares()
|
||||
@@ -221,7 +218,6 @@ void ShareUserGroupWidget::slotAdjustScrollWidgetSize()
|
||||
|
||||
void ShareUserGroupWidget::slotShareesReady()
|
||||
{
|
||||
_pi_sharee.stopAnimation();
|
||||
if (_completerModel->rowCount() == 0) {
|
||||
displayError(0, tr("No results for '%1'").arg(_completerModel->currentSearch()));
|
||||
return;
|
||||
@@ -278,7 +274,6 @@ void ShareUserGroupWidget::slotCompleterHighlighted(const QModelIndex & index)
|
||||
|
||||
void ShareUserGroupWidget::displayError(int code, const QString& message)
|
||||
{
|
||||
_pi_sharee.stopAnimation();
|
||||
qDebug() << "Error from server" << code << message;
|
||||
_ui->errorLabel->setText(message);
|
||||
_ui->errorLabel->show();
|
||||
|
||||
@@ -129,8 +129,6 @@ private:
|
||||
bool _isFile;
|
||||
bool _disableCompleterActivated; // in order to avoid that we share the contents twice
|
||||
ShareManager *_manager;
|
||||
|
||||
QProgressIndicator _pi_sharee;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>457</width>
|
||||
<height>164</height>
|
||||
<height>188</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -15,55 +15,14 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="shareeHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="shareeLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Share with users or groups ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QLineEdit" name="shareeLineEdit">
|
||||
<property name="placeholderText">
|
||||
<string>Share with users or groups ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>0</green>
|
||||
<blue>0</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>123</red>
|
||||
<green>121</green>
|
||||
<blue>134</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">Placeholder for Error text</string>
|
||||
</property>
|
||||
@@ -91,8 +50,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>437</width>
|
||||
<height>94</height>
|
||||
<width>441</width>
|
||||
<height>98</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
||||
|
||||
+19
-37
@@ -56,13 +56,6 @@
|
||||
// The second number should be changed when there are new features.
|
||||
#define MIRALL_SOCKET_API_VERSION "1.0"
|
||||
|
||||
static inline QString removeTrailingSlash(QString path)
|
||||
{
|
||||
Q_ASSERT(path.endsWith(QLatin1Char('/')));
|
||||
path.truncate(path.length()-1);
|
||||
return path;
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
#define DEBUG qDebug() << "SocketApi: "
|
||||
@@ -74,8 +67,7 @@ SocketApi::SocketApi(QObject* parent)
|
||||
|
||||
if (Utility::isWindows()) {
|
||||
socketPath = QLatin1String("\\\\.\\pipe\\")
|
||||
+ QLatin1String("ownCloud") + '\\'
|
||||
+ QString::fromLocal8Bit(qgetenv("USERNAME"));
|
||||
+ QLatin1String("ownCloud");
|
||||
// TODO: once the windows extension supports multiple
|
||||
// client connections, switch back to the theme name
|
||||
// See issue #2388
|
||||
@@ -148,10 +140,8 @@ void SocketApi::slotNewConnection()
|
||||
_listeners.append(socket);
|
||||
|
||||
foreach( Folder *f, FolderMan::instance()->map() ) {
|
||||
if (f->canSync()) {
|
||||
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
sendMessage(socket, message);
|
||||
}
|
||||
QString message = buildRegisterPathMessage(f->path());
|
||||
sendMessage(socket, message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,31 +180,20 @@ void SocketApi::slotReadSocket()
|
||||
|
||||
void SocketApi::slotRegisterPath( const QString& alias )
|
||||
{
|
||||
// Make sure not to register twice to each connected client
|
||||
if (_registeredAliases.contains(alias))
|
||||
return;
|
||||
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f) {
|
||||
QString message = buildRegisterPathMessage(removeTrailingSlash(f->path()));
|
||||
QString message = buildRegisterPathMessage(f->path());
|
||||
foreach(QIODevice *socket, _listeners) {
|
||||
sendMessage(socket, message);
|
||||
}
|
||||
}
|
||||
|
||||
_registeredAliases.insert(alias);
|
||||
}
|
||||
|
||||
void SocketApi::slotUnregisterPath( const QString& alias )
|
||||
{
|
||||
if (!_registeredAliases.contains(alias))
|
||||
return;
|
||||
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f)
|
||||
broadcastMessage(QLatin1String("UNREGISTER_PATH"), removeTrailingSlash(f->path()), QString::null, true );
|
||||
|
||||
_registeredAliases.remove(alias);
|
||||
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true );
|
||||
}
|
||||
|
||||
void SocketApi::slotUpdateFolderView(Folder *f)
|
||||
@@ -232,11 +211,10 @@ void SocketApi::slotUpdateFolderView(Folder *f)
|
||||
f->syncResult().status() == SyncResult::Error ||
|
||||
f->syncResult().status() == SyncResult::SetupError ) {
|
||||
|
||||
QString rootPath = removeTrailingSlash(f->path());
|
||||
broadcastMessage(QLatin1String("STATUS"), rootPath,
|
||||
broadcastMessage(QLatin1String("STATUS"), f->path() ,
|
||||
f->syncEngine().syncFileStatusTracker().fileStatus("").toSocketAPIString());
|
||||
|
||||
broadcastMessage(QLatin1String("UPDATE_VIEW"), rootPath);
|
||||
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() );
|
||||
} else {
|
||||
qDebug() << "Not sending UPDATE_VIEW for" << f->alias() << "because status() is" << f->syncResult().status();
|
||||
}
|
||||
@@ -296,6 +274,8 @@ void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, QIODevic
|
||||
|
||||
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QIODevice* socket)
|
||||
{
|
||||
const QString nopString("NOP");
|
||||
|
||||
if( !socket ) {
|
||||
qDebug() << "No valid socket object.";
|
||||
return;
|
||||
@@ -308,16 +288,18 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QIODevice*
|
||||
Folder* syncFolder = FolderMan::instance()->folderForPath( argument );
|
||||
if (!syncFolder) {
|
||||
// this can happen in offline mode e.g.: nothing to worry about
|
||||
statusString = QLatin1String("NOP");
|
||||
statusString = nopString;
|
||||
} else {
|
||||
QString relativePath = QDir::cleanPath(argument).mid(syncFolder->cleanPath().length()+1);
|
||||
if( relativePath.endsWith(QLatin1Char('/')) ) {
|
||||
relativePath.truncate(relativePath.length()-1);
|
||||
qWarning() << "Removed trailing slash for directory: " << relativePath << "Status pushes won't have one.";
|
||||
}
|
||||
SyncFileStatus fileStatus = syncFolder->syncEngine().syncFileStatusTracker().fileStatus(relativePath);
|
||||
const QString file = QDir::cleanPath(argument).mid(syncFolder->cleanPath().length()+1);
|
||||
|
||||
statusString = fileStatus.toSocketAPIString();
|
||||
// future: Send more specific states for paused, disconnected etc.
|
||||
if( syncFolder->syncPaused() || !syncFolder->accountState()->isConnected() ) {
|
||||
statusString = nopString;
|
||||
} else {
|
||||
SyncFileStatus fileStatus = syncFolder->syncEngine().syncFileStatusTracker().fileStatus(file);
|
||||
|
||||
statusString = fileStatus.toSocketAPIString();
|
||||
}
|
||||
}
|
||||
|
||||
const QString message = QLatin1String("STATUS:") % statusString % QLatin1Char(':') % QDir::toNativeSeparators(argument);
|
||||
|
||||
@@ -77,7 +77,6 @@ private:
|
||||
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString& argument, QIODevice* socket);
|
||||
QString buildRegisterPathMessage(const QString& path);
|
||||
|
||||
QSet<QString> _registeredAliases;
|
||||
QList<QIODevice*> _listeners;
|
||||
SocketApiServer _localServer;
|
||||
};
|
||||
|
||||
@@ -219,7 +219,6 @@ void ConnectionValidator::slotAuthSuccess()
|
||||
void ConnectionValidator::checkServerCapabilities()
|
||||
{
|
||||
JsonApiJob *job = new JsonApiJob(_account, QLatin1String("ocs/v1.php/cloud/capabilities"), this);
|
||||
job->setTimeout(timeoutToUseMsec);
|
||||
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)), this, SLOT(slotCapabilitiesRecieved(QVariantMap)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ extern "C" {
|
||||
#include "csync.h"
|
||||
#include "vio/csync_vio_local.h"
|
||||
#include "std/c_path.h"
|
||||
#include "std/c_string.h"
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
@@ -590,38 +589,4 @@ bool FileSystem::remove(const QString &fileName, QString *errorString)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileSystem::isFileLocked(const QString& fileName)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
mbchar_t *wuri = c_utf8_path_to_locale(fileName.toUtf8());
|
||||
|
||||
// Check if file exists
|
||||
DWORD attr = GetFileAttributesW(wuri);
|
||||
if (attr != INVALID_FILE_ATTRIBUTES) {
|
||||
// Try to open the file with as much access as possible..
|
||||
HANDLE win_h = CreateFileW(
|
||||
wuri,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
c_free_locale_string(wuri);
|
||||
if (win_h == INVALID_HANDLE_VALUE) {
|
||||
/* could not be opened, so locked? */
|
||||
/* 32 == ERROR_SHARING_VIOLATION */
|
||||
return true;
|
||||
} else {
|
||||
CloseHandle(win_h);
|
||||
}
|
||||
} else {
|
||||
c_free_locale_string(wuri);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(fileName);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -182,11 +182,6 @@ QByteArray OWNCLOUDSYNC_EXPORT calcAdler32( const QString& fileName );
|
||||
*/
|
||||
QString OWNCLOUDSYNC_EXPORT makeConflictFileName(const QString &fn, const QDateTime &dt);
|
||||
|
||||
/**
|
||||
* Returns true when a file is locked. (Windows only)
|
||||
*/
|
||||
bool OWNCLOUDSYNC_EXPORT isFileLocked(const QString& fileName);
|
||||
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -369,9 +369,6 @@ signals:
|
||||
void progress(const SyncFileItem&, quint64 bytes);
|
||||
void finished();
|
||||
|
||||
/** Emitted when propagation has problems with a locked file. */
|
||||
void seenLockedFile(const QString &fileName);
|
||||
|
||||
private:
|
||||
|
||||
AccountPtr _account;
|
||||
@@ -379,11 +376,6 @@ private:
|
||||
/** Stores the time since a job touched a file. */
|
||||
QHash<QString, QElapsedTimer> _touchedFiles;
|
||||
mutable QMutex _touchedFilesMutex;
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
// access to signals which are protected in Qt4
|
||||
friend class PropagateDownloadFileQNAM;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -546,14 +546,6 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tmpFile.size() == 0 && _item->_size > 0) {
|
||||
FileSystem::remove(_tmpFile.fileName());
|
||||
done(SyncFileItem::NormalError,
|
||||
tr("The downloaded file is empty despite the server announced it should have been %1.")
|
||||
.arg(Utility::octetsToString(_item->_size)));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 transmissionChecksumValidated()
|
||||
// as this is (still) also correct.
|
||||
@@ -704,14 +696,7 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
QString renameError;
|
||||
QString conflictFileName = FileSystem::makeConflictFileName(fn, Utility::qDateTimeFromTime_t(_item->_modtime));
|
||||
if (!FileSystem::rename(fn, conflictFileName, &renameError)) {
|
||||
// If the rename fails, don't replace it.
|
||||
|
||||
// If the file is locked, we want to retry this sync when it
|
||||
// becomes available again.
|
||||
if (FileSystem::isFileLocked(fn)) {
|
||||
emit _propagator->seenLockedFile(fn);
|
||||
}
|
||||
|
||||
//If the rename fails, don't replace it.
|
||||
done(SyncFileItem::SoftError, renameError);
|
||||
return;
|
||||
}
|
||||
@@ -771,14 +756,7 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
_propagator->_journal->commit("download finished");
|
||||
}
|
||||
|
||||
// If the file is locked, we want to retry this sync when it
|
||||
// becomes available again, otherwise try again directly
|
||||
if (FileSystem::isFileLocked(fn)) {
|
||||
emit _propagator->seenLockedFile(fn);
|
||||
} else {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
}
|
||||
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -933,7 +933,6 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||
connect(_propagator.data(), SIGNAL(progress(const SyncFileItem &,quint64)),
|
||||
this, SLOT(slotProgress(const SyncFileItem &,quint64)));
|
||||
connect(_propagator.data(), SIGNAL(finished()), this, SLOT(slotFinished()), Qt::QueuedConnection);
|
||||
connect(_propagator.data(), SIGNAL(seenLockedFile(QString)), SIGNAL(seenLockedFile(QString)));
|
||||
|
||||
// apply the network limits to the propagator
|
||||
setNetworkLimits(_uploadLimit, _downloadLimit);
|
||||
@@ -1342,7 +1341,7 @@ SyncFileItem* SyncEngine::findSyncItem(const QString &fileName) const
|
||||
{
|
||||
Q_FOREACH(const SyncFileItemPtr &item, _syncedItems) {
|
||||
// Directories will appear in this list as well, and will get their status set once all children have been propagated
|
||||
if ((item->_file == fileName || (!item->_renameTarget.isEmpty() && item->_renameTarget == fileName)))
|
||||
if ((item->_file == fileName || item->_renameTarget == fileName))
|
||||
return item.data();
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -143,12 +143,6 @@ signals:
|
||||
// A new folder was discovered and was not synced because of the confirmation feature
|
||||
void newBigFolder(const QString &folder);
|
||||
|
||||
/** Emitted when propagation has problems with a locked file.
|
||||
*
|
||||
* Forwarded from OwncloudPropagator::seenLockedFile.
|
||||
*/
|
||||
void seenLockedFile(const QString &fileName);
|
||||
|
||||
private slots:
|
||||
void slotRootEtagReceived(const QString &);
|
||||
void slotItemCompleted(const SyncFileItem& item, const PropagatorJob & job);
|
||||
|
||||
@@ -28,15 +28,14 @@ static SyncFileStatus::SyncFileStatusTag lookupProblem(const QString &pathToMatc
|
||||
// qDebug() << Q_FUNC_INFO << pathToMatch << severity << problemPath;
|
||||
if (problemPath == pathToMatch) {
|
||||
return severity;
|
||||
} else if (severity == SyncFileStatus::StatusError
|
||||
&& problemPath.startsWith(pathToMatch)
|
||||
&& (pathToMatch.isEmpty() || problemPath.at(pathToMatch.size()) == '/')) {
|
||||
} else if (severity == SyncFileStatus::StatusError && problemPath.startsWith(pathToMatch) && problemPath.at(pathToMatch.size()) == '/') {
|
||||
Q_ASSERT(!pathToMatch.endsWith('/'));
|
||||
return SyncFileStatus::StatusWarning;
|
||||
} else if (!problemPath.startsWith(pathToMatch)) {
|
||||
// Starting at lower_bound we get the first path that is not smaller,
|
||||
// since: "a/" < "a/aa" < "a/aa/aaa" < "a/ab/aba"
|
||||
// If problemMap keys are ["a/aa/aaa", "a/ab/aba"] and pathToMatch == "a/aa",
|
||||
// lower_bound(pathToMatch) will point to "a/aa/aaa", and the moment that
|
||||
// since: "/a/" < "/a/aa" < "/a/aa/aaa" < "/a/ab/aba"
|
||||
// If problemMap keys are ["/a/aa/aaa", "/a/ab/aba"] and pathToMatch == "/a/aa",
|
||||
// lower_bound(pathToMatch) will point to "/a/aa/aaa", and the moment that
|
||||
// problemPath.startsWith(pathToMatch) == false, we know that we've looked
|
||||
// at everything that interest us.
|
||||
break;
|
||||
@@ -69,78 +68,95 @@ static inline bool showWarningInSocketApi(const SyncFileItem& item)
|
||||
|| status == SyncFileItem::Restoration;
|
||||
}
|
||||
|
||||
static inline bool showSyncInSocketApi( const SyncFileItem& item)
|
||||
{
|
||||
const auto inst = item._instruction;
|
||||
return inst == CSYNC_INSTRUCTION_NEW;
|
||||
}
|
||||
|
||||
SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine)
|
||||
: _syncEngine(syncEngine)
|
||||
{
|
||||
connect(syncEngine, SIGNAL(aboutToPropagate(SyncFileItemVector&)),
|
||||
SLOT(slotAboutToPropagate(SyncFileItemVector&)));
|
||||
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
|
||||
connect(syncEngine, SIGNAL(itemCompleted(const SyncFileItem&, const PropagatorJob&)),
|
||||
SLOT(slotItemCompleted(const SyncFileItem&)));
|
||||
connect(syncEngine, SIGNAL(started()), SLOT(slotClearDirtyPaths()));
|
||||
connect(syncEngine, SIGNAL(started()), SLOT(slotSyncEngineRunningChanged()));
|
||||
connect(syncEngine, SIGNAL(finished(bool)), SLOT(slotSyncEngineRunningChanged()));
|
||||
this, SLOT(slotItemCompleted(const SyncFileItem&)));
|
||||
}
|
||||
|
||||
SyncFileItem SyncFileStatusTracker::rootSyncFileItem()
|
||||
SyncFileStatus SyncFileStatusTracker::rootStatus()
|
||||
{
|
||||
SyncFileItem fakeRootItem;
|
||||
// It's is not entirely correct to use the sync's status as we'll show the root folder as
|
||||
// syncing even though no child might end up being propagated, but will give us something
|
||||
// better than always UpToDate for now.
|
||||
fakeRootItem._status = _syncEngine->isSyncRunning() ? SyncFileItem::NoStatus : SyncFileItem::Success;
|
||||
fakeRootItem._isDirectory = true;
|
||||
return fakeRootItem;
|
||||
/* Possible values for the status:
|
||||
enum SyncFileStatusTag {
|
||||
StatusNone,
|
||||
StatusSync,
|
||||
StatusWarning,
|
||||
StatusUpToDate,
|
||||
StatusError,
|
||||
};
|
||||
*/
|
||||
SyncFileStatus status = SyncFileStatus::StatusUpToDate;
|
||||
|
||||
if( !_syncEngine ) return SyncFileStatus::StatusNone;
|
||||
|
||||
if( _syncEngine->isSyncRunning() ) {
|
||||
status = SyncFileStatus::StatusSync;
|
||||
} else {
|
||||
// sync is not running. Check dirty list and _syncProblems
|
||||
int errs = 0;
|
||||
for (auto it = _syncProblems.begin(); it != _syncProblems.end(); ++it) {
|
||||
if( it->second == SyncFileStatus::StatusError ) {
|
||||
errs ++;
|
||||
break; // stop if an error found at all.
|
||||
}
|
||||
}
|
||||
if( errs ) {
|
||||
status = SyncFileStatus::StatusWarning; // some files underneath had errors
|
||||
}
|
||||
// Only warnings do not change the root emblem away from ok.
|
||||
}
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
SyncFileStatus SyncFileStatusTracker::fileStatus(const QString& relativePath)
|
||||
SyncFileStatus SyncFileStatusTracker::fileStatus(const QString& systemFileName)
|
||||
{
|
||||
Q_ASSERT(!relativePath.endsWith(QLatin1Char('/')));
|
||||
|
||||
if (relativePath.isEmpty()) {
|
||||
// This is the root sync folder, it doesn't have an entry in the database and won't be walked by csync, so create one manually.
|
||||
return syncFileItemStatus(rootSyncFileItem());
|
||||
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
|
||||
if( fileName.endsWith(QLatin1Char('/')) ) {
|
||||
fileName.truncate(fileName.length()-1);
|
||||
qDebug() << "Removed trailing slash: " << fileName;
|
||||
}
|
||||
|
||||
if( fileName.isEmpty() ) {
|
||||
// this is the root sync folder.
|
||||
return rootStatus();
|
||||
|
||||
}
|
||||
// The SyncEngine won't notify us at all for CSYNC_FILE_SILENTLY_EXCLUDED
|
||||
// and CSYNC_FILE_EXCLUDE_AND_REMOVE excludes. Even though it's possible
|
||||
// that the status of CSYNC_FILE_EXCLUDE_LIST excludes will change if the user
|
||||
// update the exclude list at runtime and doing it statically here removes
|
||||
// our ability to notify changes through the fileStatusChanged signal,
|
||||
// it's an acceptable compromize to treat all exclude types the same.
|
||||
if( _syncEngine->excludedFiles().isExcluded(_syncEngine->localPath() + relativePath,
|
||||
if( _syncEngine->excludedFiles().isExcluded(_syncEngine->localPath() + fileName,
|
||||
_syncEngine->localPath(),
|
||||
_syncEngine->ignoreHiddenFiles()) ) {
|
||||
return SyncFileStatus(SyncFileStatus::StatusWarning);
|
||||
}
|
||||
|
||||
if ( _dirtyPaths.contains(relativePath) )
|
||||
return SyncFileStatus::StatusSync;
|
||||
|
||||
SyncFileItem* item = _syncEngine->findSyncItem(relativePath);
|
||||
SyncFileItem* item = _syncEngine->findSyncItem(fileName);
|
||||
if (item) {
|
||||
return syncFileItemStatus(*item);
|
||||
return fileStatus(*item);
|
||||
}
|
||||
|
||||
// If we're not currently syncing that file, look it up in the database to know if it's shared
|
||||
SyncJournalFileRecord rec = _syncEngine->journal()->getFileRecord(relativePath);
|
||||
SyncJournalFileRecord rec = _syncEngine->journal()->getFileRecord(fileName);
|
||||
if (rec.isValid()) {
|
||||
return syncFileItemStatus(rec.toSyncFileItem());
|
||||
return fileStatus(rec.toSyncFileItem());
|
||||
}
|
||||
// Must be a new file, wait for the filesystem watcher to trigger a sync
|
||||
return SyncFileStatus();
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotPathTouched(const QString& fileName)
|
||||
{
|
||||
QString folderPath = _syncEngine->localPath();
|
||||
Q_ASSERT(fileName.startsWith(folderPath));
|
||||
|
||||
QString localPath = fileName.mid(folderPath.size());
|
||||
_dirtyPaths.insert(localPath);
|
||||
|
||||
emit fileStatusChanged(fileName, SyncFileStatus::StatusSync);
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
|
||||
{
|
||||
std::map<QString, SyncFileStatus::SyncFileStatusTag> oldProblems;
|
||||
@@ -153,8 +169,10 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusError;
|
||||
} else if (showWarningInSocketApi(*item)) {
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusWarning;
|
||||
} else if( showSyncInSocketApi(*item)) {
|
||||
_syncProblems[item->_file] = SyncFileStatus::StatusSync;
|
||||
}
|
||||
emit fileStatusChanged(getSystemDestination(item->destination()), syncFileItemStatus(*item));
|
||||
emit fileStatusChanged(getSystemDestination(*item), fileStatus(*item));
|
||||
}
|
||||
|
||||
// Make sure to push any status that might have been resolved indirectly since the last sync
|
||||
@@ -166,7 +184,7 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
|
||||
SyncFileStatus::SyncFileStatusTag severity = it->second;
|
||||
if (severity == SyncFileStatus::StatusError)
|
||||
invalidateParentPaths(path);
|
||||
emit fileStatusChanged(getSystemDestination(path), fileStatus(path));
|
||||
emit fileStatusChanged(_syncEngine->localPath() + path, fileStatus(path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,33 +197,22 @@ void SyncFileStatusTracker::slotItemCompleted(const SyncFileItem &item)
|
||||
invalidateParentPaths(item.destination());
|
||||
} else if (showWarningInSocketApi(item)) {
|
||||
_syncProblems[item._file] = SyncFileStatus::StatusWarning;
|
||||
} else if (showSyncInSocketApi(item)) {
|
||||
// new items that were in state sync can now be erased
|
||||
_syncProblems.erase(item._file);
|
||||
} else {
|
||||
// There is currently no situation where an error status set during discovery/update is fixed by propagation.
|
||||
Q_ASSERT(_syncProblems.find(item._file) == _syncProblems.end());
|
||||
}
|
||||
|
||||
emit fileStatusChanged(getSystemDestination(item.destination()), syncFileItemStatus(item));
|
||||
emit fileStatusChanged(getSystemDestination(item), fileStatus(item));
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotSyncEngineRunningChanged()
|
||||
{
|
||||
emit fileStatusChanged(_syncEngine->localPath(), syncFileItemStatus(rootSyncFileItem()));
|
||||
}
|
||||
|
||||
void SyncFileStatusTracker::slotClearDirtyPaths()
|
||||
{
|
||||
// We just assume that during a sync all dirty statuses will be resolved
|
||||
// one way or the other.
|
||||
_dirtyPaths.clear();
|
||||
}
|
||||
|
||||
SyncFileStatus SyncFileStatusTracker::syncFileItemStatus(const SyncFileItem& item)
|
||||
SyncFileStatus SyncFileStatusTracker::fileStatus(const SyncFileItem& item)
|
||||
{
|
||||
// Hack to know if the item was taken from the sync engine (Sync), or from the database (UpToDate)
|
||||
// Mark any directory in the SyncEngine's items as syncing, this is currently how we mark parent directories
|
||||
// of currently syncing items since the PropagateDirectory job will mark the directorie's SyncFileItem::_status as Success
|
||||
// once all child jobs have been completed.
|
||||
bool waitingForPropagation = (item._isDirectory || item._direction != SyncFileItem::None) && item._status == SyncFileItem::NoStatus;
|
||||
bool waitingForPropagation = item._direction != SyncFileItem::None && item._status == SyncFileItem::NoStatus;
|
||||
|
||||
SyncFileStatus status(SyncFileStatus::StatusUpToDate);
|
||||
if (waitingForPropagation) {
|
||||
status.set(SyncFileStatus::StatusSync);
|
||||
@@ -232,19 +239,20 @@ void SyncFileStatusTracker::invalidateParentPaths(const QString& path)
|
||||
QStringList splitPath = path.split('/', QString::SkipEmptyParts);
|
||||
for (int i = 0; i < splitPath.size(); ++i) {
|
||||
QString parentPath = QStringList(splitPath.mid(0, i)).join(QLatin1String("/"));
|
||||
emit fileStatusChanged(getSystemDestination(parentPath), fileStatus(parentPath));
|
||||
emit fileStatusChanged(_syncEngine->localPath() + parentPath, fileStatus(parentPath));
|
||||
}
|
||||
}
|
||||
|
||||
QString SyncFileStatusTracker::getSystemDestination(const QString& relativePath)
|
||||
QString SyncFileStatusTracker::getSystemDestination(const SyncFileItem& item)
|
||||
{
|
||||
QString systemPath = _syncEngine->localPath() + relativePath;
|
||||
// SyncEngine::localPath() has a trailing slash, make sure to remove it if the
|
||||
// destination is empty.
|
||||
if( systemPath.endsWith(QLatin1Char('/')) ) {
|
||||
systemPath.truncate(systemPath.length()-1);
|
||||
QString systemFileName = _syncEngine->localPath() + item.destination();
|
||||
// the trailing slash for directories must be appended as the filenames coming in
|
||||
// from the plugins have that too. Otherwise the matching entry item is not found
|
||||
// in the plugin.
|
||||
if( item._type == SyncFileItem::Type::Directory ) {
|
||||
systemFileName += QLatin1Char('/');
|
||||
}
|
||||
return systemPath;
|
||||
return systemFileName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "syncfileitem.h"
|
||||
#include "syncfilestatus.h"
|
||||
#include <map>
|
||||
#include <QSet>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -35,10 +34,7 @@ class OWNCLOUDSYNC_EXPORT SyncFileStatusTracker : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SyncFileStatusTracker(SyncEngine* syncEngine);
|
||||
SyncFileStatus fileStatus(const QString& relativePath);
|
||||
|
||||
public slots:
|
||||
void slotPathTouched(const QString& fileName);
|
||||
SyncFileStatus fileStatus(const QString& systemFileName);
|
||||
|
||||
signals:
|
||||
void fileStatusChanged(const QString& systemFileName, SyncFileStatus fileStatus);
|
||||
@@ -46,20 +42,17 @@ signals:
|
||||
private slots:
|
||||
void slotAboutToPropagate(SyncFileItemVector& items);
|
||||
void slotItemCompleted(const SyncFileItem& item);
|
||||
void slotSyncEngineRunningChanged();
|
||||
void slotClearDirtyPaths();
|
||||
|
||||
private:
|
||||
SyncFileStatus syncFileItemStatus(const SyncFileItem& item);
|
||||
SyncFileItem rootSyncFileItem();
|
||||
SyncFileStatus fileStatus(const SyncFileItem& item);
|
||||
SyncFileStatus rootStatus();
|
||||
|
||||
void invalidateParentPaths(const QString& path);
|
||||
QString getSystemDestination(const QString& relativePath);
|
||||
QString getSystemDestination(const SyncFileItem& syncEnginePath);
|
||||
|
||||
SyncEngine* _syncEngine;
|
||||
|
||||
std::map<QString, SyncFileStatus::SyncFileStatusTag> _syncProblems;
|
||||
QSet<QString> _dirtyPaths;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ list(APPEND FolderMan_SRC ../src/gui/folder.cpp )
|
||||
list(APPEND FolderMan_SRC ../src/gui/socketapi.cpp )
|
||||
list(APPEND FolderMan_SRC ../src/gui/accountstate.cpp )
|
||||
list(APPEND FolderMan_SRC ../src/gui/syncrunfilelog.cpp )
|
||||
list(APPEND FolderMan_SRC ../src/gui/lockwatcher.cpp )
|
||||
list(APPEND FolderMan_SRC ${FolderWatcher_SRC})
|
||||
list(APPEND FolderMan_SRC stub.cpp )
|
||||
#include_directories(${QTKEYCHAIN_INCLUDE_DIR})
|
||||
|
||||
+188
-193
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+173
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+182
-187
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+174
-179
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+194
-199
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+170
-175
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+171
-176
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+163
-168
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+161
-166
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+173
-178
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+169
-174
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+160
-165
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+163
-168
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Referência em uma Nova Issue
Bloquear um usuário