From 6634284efacaeffc607df2ec6d8c1fad1f4d083b Mon Sep 17 00:00:00 2001 From: abraunegg Date: Mon, 9 Dec 2024 14:24:33 +1100 Subject: [PATCH] Fix regression in handling uploading new and modified content to OneDrive Business and SharePoint (#3032) * Fix regression from v2.4.x in handling uploading new and modified content to OneDrive Business and SharePoint to not create new versions of files post upload which adds to user quota * Ad configuration option 'create_new_file_version' to create new versions if that is the desire --- docs/application-config-options.md | 22 ++- src/config.d | 5 + src/onedrive.d | 13 +- src/sync.d | 210 +++++++++++++++++++++++------ 4 files changed, 201 insertions(+), 49 deletions(-) diff --git a/docs/application-config-options.md b/docs/application-config-options.md index f5d209b39..a185d1a90 100644 --- a/docs/application-config-options.md +++ b/docs/application-config-options.md @@ -14,6 +14,7 @@ Before reading this document, please ensure you are running application version - [classify_as_big_delete](#classify_as_big_delete) - [cleanup_local_files](#cleanup_local_files) - [connect_timeout](#connect_timeout) + - [create_new_file_version](#create_new_file_version) - [data_timeout](#data_timeout) - [debug_https](#debug_https) - [disable_download_validation](#disable_download_validation) @@ -204,6 +205,23 @@ _**Default Value:**_ 10 _**Config Example:**_ `connect_timeout = "15"` +### create_new_file_version +_**Description:**_ This setting controls how the application handles the Microsoft SharePoint *feature* which modifies all PDF, MS Office & HTML files post upload, effectively breaking the integrity of your data online. By default, when the application determines that this *feature* has modified your file post upload, the now online modified file will be downloaded. When this option is enabled, rather than downloading the file, a new online file version is created which negates the download of the modified file. + +_**Value Type:**_ Boolean + +_**Default Value:**_ False + +_**Config Example:**_ `create_new_file_version = "false"` or `create_new_file_version = "true"` + +_**CLI Option Use:**_ *None - this is a config file option only* + +> [!IMPORTANT] +> If you enable 'disable_upload_validation' via `disable_upload_validation = "true"` there is zero facility to determine if a file was modified post upload. As such, the application will default to the state that the upload integrity check has failed. When `create_new_file_version = "false"` your uploaded file will be downloaded *regardless* of the online modification state. + +> [!WARNING] +> When this option is set to 'true', new file versions will be created online which will count towards your Microsoft OneDrive Quota. + ### data_timeout _**Description:**_ This setting controls the timeout duration, in seconds, for when data is not received on an active connection to Microsoft OneDrive over HTTPS when using the curl library, before that connection is timeout out. @@ -515,6 +533,8 @@ _**Default Value:**_ False _**Config Example:**_ `permanent_delete = "true"` +_**CLI Option Use:**_ *None - this is a config file option only* + > [!IMPORTANT] > The Microsoft OneDrive API for this capability is also very narrow: > | Account Type | Config Option is Supported | @@ -772,7 +792,7 @@ _**Default Value:**_ False _**Config Example:**_ `sync_business_shared_items = "false"` or `sync_business_shared_items = "true"` -_**CLI Option Use:**_ *none* - this is a config file option only +_**CLI Option Use:**_ *None - this is a config file option only* > [!NOTE] > This option is considered a 'Client Side Filtering Rule' and if configured, is utilised for all sync operations. After changing this option, you will be required to perform a resync. diff --git a/src/config.d b/src/config.d index 0a5fc1c2b..5266366eb 100644 --- a/src/config.d +++ b/src/config.d @@ -334,6 +334,11 @@ class ApplicationConfig { boolValues["cleanup_local_files"] = false; // - Perform a permanentDelete on deletion activities boolValues["permanent_delete"] = false; + // - Controls how the application handles the Microsoft SharePoint 'feature' of modifying all PDF, MS Office & HTML files with added XML content post upload + // There are 2 ways to solve this: + // 1. Download the modified file immediately after upload as per v2.4.x (default) + // 2. Create a new online version of the file, which then contributes to the users 'quota' + boolValues["create_new_file_version"] = false; // Webhook Feature Options boolValues["webhook_enabled"] = false; diff --git a/src/onedrive.d b/src/onedrive.d index 3477aa3cf..46b493080 100644 --- a/src/onedrive.d +++ b/src/onedrive.d @@ -663,14 +663,15 @@ class OneDriveApi { // https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createuploadsession //JSONValue createUploadSession(string parentDriveId, string parentId, string filename, string eTag = null, JSONValue item = null) { JSONValue createUploadSession(string parentDriveId, string parentId, string filename, const(char)[] eTag = null, JSONValue item = null) { - // string[string] requestHeaders; + string[string] requestHeaders; string url = driveByIdUrl ~ parentDriveId ~ "/items/" ~ parentId ~ ":/" ~ encodeComponent(filename) ~ ":/createUploadSession"; // eTag If-Match header addition commented out for the moment // At some point, post the creation of this upload session the eTag is being 'updated' by OneDrive, thus when uploadFragment() is used // this generates a 412 Precondition Failed and then a 416 Requested Range Not Satisfiable // This needs to be investigated further as to why this occurs - // if (eTag) requestHeaders["If-Match"] = eTag; - return post(url, item.toString()); + + if (eTag) requestHeaders["If-Match"] = to!string(eTag); + return post(url, item.toString(), requestHeaders); } // https://dev.onedrive.com/items/upload_large_files.htm @@ -823,7 +824,7 @@ class OneDriveApi { JSONValue response; try { - response = post(tokenUrl, postData, true, "application/x-www-form-urlencoded"); + response = post(tokenUrl, postData, null, true, "application/x-www-form-urlencoded"); } catch (OneDriveException exception) { // an error was generated if ((exception.httpStatusCode == 400) || (exception.httpStatusCode == 401)) { @@ -1141,10 +1142,10 @@ class OneDriveApi { }, validateJSONResponse, callingFunction, lineno); } - private JSONValue post(const(char)[] url, const(char)[] postData, bool skipToken = false, const(char)[] contentType = "application/json", string callingFunction=__FUNCTION__, int lineno=__LINE__) { + private JSONValue post(const(char)[] url, const(char)[] postData, string[string] requestHeaders=null, bool skipToken = false, const(char)[] contentType = "application/json", string callingFunction=__FUNCTION__, int lineno=__LINE__) { bool validateJSONResponse = true; return oneDriveErrorHandlerWrapper((CurlResponse response) { - connect(HTTP.Method.post, url, skipToken, response); + connect(HTTP.Method.post, url, skipToken, response, requestHeaders); curlEngine.setContent(contentType, postData); return curlEngine.execute(); }, validateJSONResponse, callingFunction, lineno); diff --git a/src/sync.d b/src/sync.d index c9e34f95d..34c05e024 100644 --- a/src/sync.d +++ b/src/sync.d @@ -2544,7 +2544,7 @@ class SyncEngine { } // Perform the actual download of an object from OneDrive - void downloadFileItem(JSONValue onedriveJSONItem) { + void downloadFileItem(JSONValue onedriveJSONItem, bool ignoreDataPreservationCheck = false) { bool downloadFailed = false; string OneDriveFileXORHash; @@ -2603,25 +2603,29 @@ class SyncEngine { // Does the file already exist in the path locally? if (exists(newItemPath)) { - // file exists locally already - foreach (driveId; onlineDriveDetails.keys) { - if (itemDB.selectByPath(newItemPath, driveId, databaseItem)) { - fileFoundInDB = true; - break; + // To accommodate forcing the download of a file, post upload to Microsoft OneDrive, we need to ignore the checking of hashes and making a safe backup + if (!ignoreDataPreservationCheck) { + + // file exists locally already + foreach (driveId; onlineDriveDetails.keys) { + if (itemDB.selectByPath(newItemPath, driveId, databaseItem)) { + fileFoundInDB = true; + break; + } + } + + // Log the DB details + if (debugLogging) {addLogEntry("File to download exists locally and this is the DB record: " ~ to!string(databaseItem), ["debug"]);} + + // Does the DB (what we think is in sync) hash match the existing local file hash? + if (!testFileHash(newItemPath, databaseItem)) { + // local file is different to what we know to be true + addLogEntry("The local file to replace (" ~ newItemPath ~ ") has been modified locally since the last download. Renaming it to avoid potential local data loss."); + // If local data protection is configured (bypassDataPreservation = false), safeBackup the local file, passing in if we are performing a --dry-run or not + // In case the renamed path is needed + string renamedPath; + safeBackup(newItemPath, dryRun, bypassDataPreservation, renamedPath); } - } - - // Log the DB details - if (debugLogging) {addLogEntry("File to download exists locally and this is the DB record: " ~ to!string(databaseItem), ["debug"]);} - - // Does the DB (what we think is in sync) hash match the existing local file hash? - if (!testFileHash(newItemPath, databaseItem)) { - // local file is different to what we know to be true - addLogEntry("The local file to replace (" ~ newItemPath ~ ") has been modified locally since the last download. Renaming it to avoid potential local data loss."); - // If local data protection is configured (bypassDataPreservation = false), safeBackup the local file, passing in if we are performing a --dry-run or not - // In case the renamed path is needed - string renamedPath; - safeBackup(newItemPath, dryRun, bypassDataPreservation, renamedPath); } } @@ -4614,8 +4618,9 @@ class SyncEngine { // Check the integrity of the uploaded modified file if not in a --dry-run scenario if (!dryRun) { - // Perform the integrity of the uploaded modified file - performUploadIntegrityValidationChecks(uploadResponse, localFilePath, thisFileSizeLocal); + bool uploadIntegrityPassed; + // Check the integrity of the uploaded modified file, if the local file still exists + uploadIntegrityPassed = performUploadIntegrityValidationChecks(uploadResponse, localFilePath, thisFileSizeLocal); // Update the date / time of the file online to match the local item // Get the local file last modified time @@ -4624,7 +4629,53 @@ class SyncEngine { // Get the latest eTag, and use that string etagFromUploadResponse = uploadResponse["eTag"].str; // Attempt to update the online date time stamp based on our local data - uploadLastModifiedTime(dbItem, targetDriveId, targetItemId, localModifiedTime, etagFromUploadResponse); + if (appConfig.accountType == "personal") { + // Business | SharePoint we used a session to upload the data, thus, local timestamps are given when the session is created + uploadLastModifiedTime(dbItem, targetDriveId, targetItemId, localModifiedTime, etagFromUploadResponse); + } else { + // Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint. + // This means that the file which was uploaded, is potentially no longer the file we have locally + // There are 2 ways to solve this: + // 1. Download the modified file immediately after upload as per v2.4.x (default) + // 2. Create a new online version of the file, which then contributes to the users 'quota' + if (!uploadIntegrityPassed) { + // upload integrity check failed + if (!appConfig.getValueBool("create_new_file_version")) { + // are we in an --upload-only scenario + if(!uploadOnly){ + // Download the now online modified file + addLogEntry("WARNING: Microsoft OneDrive modified your uploaded file via its SharePoint 'enrichment' feature. To keep your local and online versions consistent, the altered file will now be downloaded."); + addLogEntry("WARNING: Please refer to https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details."); + // Download the file directly using the prior upload JSON response + downloadFileItem(uploadResponse, true); + } else { + // --upload-only being used + // we are not downloading a file, warn that file differences will exist + addLogEntry("WARNING: The file uploaded to Microsoft OneDrive has been modified through its SharePoint 'enrichment' process and no longer matches your local version."); + addLogEntry("WARNING: Please refer to https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details."); + } + } else { + // Create a new online version of the file by updating the metadata, which negates the need to download the file + uploadLastModifiedTime(dbItem, targetDriveId, targetItemId, localModifiedTime, etagFromUploadResponse); + } + } else { + // Upload of the modified file passed integrity checks + // We need to make sure that the local file on disk has this timestamp from this JSON, otherwise on the next application run: + // The last modified timestamp has changed however the file content has not changed + // The local item has the same hash value as the item online - correcting timestamp online + // This then creates another version online which we do not want to do .. unless configured to do so + if (!appConfig.getValueBool("create_new_file_version")) { + // create an applicable item + Item onlineItem; + onlineItem = makeItem(uploadResponse); + // correct the local file timestamp to avoid creating a new version online + setTimes(localFilePath, onlineItem.mtime, onlineItem.mtime); + } else { + // Create a new online version of the file by updating the metadata, which negates the need to download the file + uploadLastModifiedTime(dbItem, targetDriveId, targetItemId, localModifiedTime, etagFromUploadResponse); + } + } + } } } } @@ -4640,7 +4691,8 @@ class SyncEngine { uploadFileOneDriveApiInstance.initialise(); // Configure JSONValue variables we use for a session upload - JSONValue currentOnlineData; + JSONValue currentOnlineJSONData; + Item currentOnlineItemData; JSONValue uploadSessionData; string currentETag; @@ -4669,7 +4721,7 @@ class SyncEngine { // Try and get the absolute latest object details from online, so we get the latest eTag to try and avoid a 412 eTag error try { - currentOnlineData = uploadFileOneDriveApiInstance.getPathDetailsById(targetDriveId, targetItemId); + currentOnlineJSONData = uploadFileOneDriveApiInstance.getPathDetailsById(targetDriveId, targetItemId); } catch (OneDriveException exception) { // Display what the error is // - 408,429,503,504 errors are handled as a retry within uploadFileOneDriveApiInstance @@ -4677,16 +4729,20 @@ class SyncEngine { } // Was a valid JSON response provided? - if (currentOnlineData.type() == JSONType.object) { + if (currentOnlineJSONData.type() == JSONType.object) { // Does the response contain an eTag? - if (hasETag(currentOnlineData)) { + if (hasETag(currentOnlineJSONData)) { // Use the value returned from online as this will attempt to avoid a 412 response if we are creating a session upload - currentETag = currentOnlineData["eTag"].str; + currentETag = currentOnlineJSONData["eTag"].str; } else { // Use the database value - greater potential for a 412 error to occur if we are creating a session upload if (debugLogging) {addLogEntry("Online data for file returned zero eTag - using database eTag value", ["debug"]);} currentETag = dbItem.eTag; } + + // Make a reusable item from this online JSON data + currentOnlineItemData = makeItem(currentOnlineJSONData); + } else { // no valid JSON response - greater potential for a 412 error to occur if we are creating a session upload if (debugLogging) {addLogEntry("Online data returned was invalid - using database eTag value", ["debug"]);} @@ -4699,10 +4755,10 @@ class SyncEngine { } // If the filesize is greater than zero , and we have valid 'latest' online data is the online file matching what we think is in the database? - if ((thisFileSizeLocal > 0) && (currentOnlineData.type() == JSONType.object)) { + if ((thisFileSizeLocal > 0) && (currentOnlineJSONData.type() == JSONType.object)) { // Issue #2626 | Case 2-1 // If the 'online' file is newer, this will be overwritten with the file from the local filesystem - potentially constituting online data loss - Item onlineFile = makeItem(currentOnlineData); + Item onlineFile = makeItem(currentOnlineJSONData); // Which file is technically newer? The local file or the remote file? SysTime localModifiedTime = timeLastModified(localFilePath).toUTC(); @@ -4716,7 +4772,7 @@ class SyncEngine { if (localModifiedTime < onlineModifiedTime) { // Online File is actually newer than the locally modified file if (debugLogging) { - addLogEntry("currentOnlineData: " ~ to!string(currentOnlineData), ["debug"]); + addLogEntry("currentOnlineJSONData: " ~ to!string(currentOnlineJSONData), ["debug"]); addLogEntry("onlineFile: " ~ to!string(onlineFile), ["debug"]); addLogEntry("database item: " ~ to!string(dbItem), ["debug"]); } @@ -4775,11 +4831,12 @@ class SyncEngine { // The best way to do this is generate a 10 digit alphanumeric string, and use this as the file extension string threadUploadSessionFilePath = appConfig.uploadSessionFilePath ~ "." ~ generateAlphanumericString(); - // Create the upload session + // Create the upload session using the latest online data 'currentOnlineData' etag try { - uploadSessionData = createSessionFileUpload(uploadFileOneDriveApiInstance, localFilePath, targetDriveId, targetParentId, baseName(localFilePath), currentETag, threadUploadSessionFilePath); + // create the session + uploadSessionData = createSessionForFileUpload(uploadFileOneDriveApiInstance, localFilePath, targetDriveId, targetParentId, baseName(localFilePath), currentOnlineItemData.eTag, threadUploadSessionFilePath); } catch (OneDriveException exception) { - + // an exception was generated string thisFunctionName = getFunctionName!({}); // HTTP request returned status code 403 @@ -4788,6 +4845,7 @@ class SyncEngine { addLogEntry("Unable to upload this modified file as this was shared as read-only: " ~ localFilePath); return uploadResponse; } + // HTTP request returned status code 423 // Resolve https://github.com/abraunegg/onedrive/issues/36 if (exception.httpStatusCode == 423) { @@ -4801,7 +4859,6 @@ class SyncEngine { // Display what the error is displayOneDriveErrorMessage(exception.msg, thisFunctionName); } - } catch (FileException e) { addLogEntry("DEBUG TO REMOVE: Modified file upload FileException Handling (Create the Upload Session)"); displayFileSystemErrorMessage(e.msg, getFunctionName!({})); @@ -4812,6 +4869,13 @@ class SyncEngine { // This is a valid JSON object // Perform the upload using the session that has been created try { + // so that we have this data available if we need to re-create the session + // - targetDriveId, targetParentId, baseName(localFilePath), currentOnlineItemData.eTag, threadUploadSessionFilePath + uploadSessionData["targetDriveId"] = targetDriveId; + uploadSessionData["targetParentId"] = targetParentId; + uploadSessionData["currentETag"] = currentOnlineItemData.eTag; + + // attempt the session upload using the session data provided uploadResponse = performSessionFileUpload(uploadFileOneDriveApiInstance, thisFileSizeLocal, uploadSessionData, threadUploadSessionFilePath); } catch (OneDriveException exception) { // Function name @@ -6269,7 +6333,7 @@ class SyncEngine { // Attempt to upload the > 4MB file using an upload session for all account types try { // Create the Upload Session - uploadSessionData = createSessionFileUpload(uploadFileOneDriveApiInstance, fileToUpload, parentItem.driveId, parentItem.id, baseName(fileToUpload), null, threadUploadSessionFilePath); + uploadSessionData = createSessionForFileUpload(uploadFileOneDriveApiInstance, fileToUpload, parentItem.driveId, parentItem.id, baseName(fileToUpload), null, threadUploadSessionFilePath); } catch (OneDriveException exception) { // An error was responded with - what was it @@ -6372,8 +6436,9 @@ class SyncEngine { if (exists(fileToUpload)) { // Are we in a --dry-run scenario if (!dryRun) { + bool uploadIntegrityPassed; // Check the integrity of the uploaded file, if the local file still exists - performUploadIntegrityValidationChecks(uploadResponse, fileToUpload, thisFileSize); + uploadIntegrityPassed = performUploadIntegrityValidationChecks(uploadResponse, fileToUpload, thisFileSize); // Update the file modified time on OneDrive and save item details to database // Update the item's metadata on OneDrive @@ -6382,7 +6447,38 @@ class SyncEngine { string newFileId = uploadResponse["id"].str; string newFileETag = uploadResponse["eTag"].str; // Attempt to update the online date time stamp based on our local data - uploadLastModifiedTime(parentItem, parentItem.driveId, newFileId, mtime, newFileETag); + if (appConfig.accountType == "personal") { + // Business | SharePoint we used a session to upload the data, thus, local timestamps are given when the session is created + uploadLastModifiedTime(parentItem, parentItem.driveId, newFileId, mtime, newFileETag); + } else { + // Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint. + // This means that the file which was uploaded, is potentially no longer the file we have locally + // There are 2 ways to solve this: + // 1. Download the modified file immediately after upload as per v2.4.x (default) + // 2. Create a new online version of the file, which then contributes to the users 'quota' + if (!uploadIntegrityPassed) { + // upload integrity check failed + // We do not want to create a new online file version .. unless configured to do so + if (!appConfig.getValueBool("create_new_file_version")) { + // are we in an --upload-only scenario + if(!uploadOnly){ + // Download the now online modified file + addLogEntry("WARNING: Microsoft OneDrive modified your uploaded file via its SharePoint 'enrichment' feature. To keep your local and online versions consistent, the altered file will now be downloaded."); + addLogEntry("WARNING: Please refer to https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details."); + // Download the file directly using the prior upload JSON response + downloadFileItem(uploadResponse, true); + } else { + // --upload-only being used + // we are not downloading a file, warn that file differences will exist + addLogEntry("WARNING: The file uploaded to Microsoft OneDrive has been modified through its SharePoint 'enrichment' process and no longer matches your local version."); + addLogEntry("WARNING: Please refer to https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details."); + } + } else { + // Create a new online version of the file by updating the metadata, which negates the need to download the file + uploadLastModifiedTime(parentItem, parentItem.driveId, newFileId, mtime, newFileETag); + } + } + } } // Are we in an --upload-only & --remove-source-files scenario? @@ -6412,7 +6508,7 @@ class SyncEngine { } // Create the OneDrive Upload Session - JSONValue createSessionFileUpload(OneDriveApi activeOneDriveApiInstance, string fileToUpload, string parentDriveId, string parentId, string filename, string eTag, string threadUploadSessionFilePath) { + JSONValue createSessionForFileUpload(OneDriveApi activeOneDriveApiInstance, string fileToUpload, string parentDriveId, string parentId, string filename, string eTag, string threadUploadSessionFilePath) { // Upload file via a OneDrive API session JSONValue uploadSession; @@ -6480,6 +6576,9 @@ class SyncEngine { int h, m, s; string etaString; string uploadLogEntry = "Uploading: " ~ uploadSessionData["localPath"].str ~ " ... "; + + // If we get a 404, create a new upload session and store it here + JSONValue newUploadSession; // Start the session upload using the active API instance for this thread while (true) { @@ -6537,6 +6636,11 @@ class SyncEngine { } // There was an error uploadResponse from OneDrive when uploading the file fragment + if (exception.httpStatusCode == 404) { + // The upload session was not found .. ?? we just created it .. maybe the backend is still creating it ? + if (debugLogging) {addLogEntry("The upload session was not found .... re-create session");} + newUploadSession = createSessionForFileUpload(activeOneDriveApiInstance, uploadSessionData["localPath"].str, uploadSessionData["targetDriveId"].str, uploadSessionData["targetParentId"].str, baseName(uploadSessionData["localPath"].str), null, threadUploadSessionFilePath); + } // Issue https://github.com/abraunegg/onedrive/issues/2747 // if a 416 uploadResponse is generated, continue @@ -6552,15 +6656,35 @@ class SyncEngine { // Insert a new line as well, so that the below error is inserted on the console in the right location if (verboseLogging) {addLogEntry("Fragment upload failed - received an exception response from OneDrive API", ["verbose"]);} + // display what the error is - displayOneDriveErrorMessage(exception.msg, getFunctionName!({})); + if (exception.httpStatusCode != 404) { + displayOneDriveErrorMessage(exception.msg, getFunctionName!({})); + } + // retry fragment upload in case error is transient if (verboseLogging) {addLogEntry("Retrying fragment upload", ["verbose"]);} try { + // retry + string effectiveRetryUploadURL; + string effectiveLocalPath; + + // If we re-created the session, use the new data on re-try + if ("uploadUrl" in newUploadSession) { + // get this from 'newUploadSession' + effectiveRetryUploadURL = newUploadSession["uploadUrl"].str; + effectiveLocalPath = newUploadSession["localPath"].str; + } else { + // get this from the original input + effectiveRetryUploadURL = uploadSessionData["uploadUrl"].str; + effectiveLocalPath = uploadSessionData["localPath"].str; + } + + // retry the fragment upload uploadResponse = activeOneDriveApiInstance.uploadFragment( - uploadSessionData["uploadUrl"].str, - uploadSessionData["localPath"].str, + effectiveRetryUploadURL, + effectiveLocalPath, offset, fragSize, thisFileSize @@ -8019,7 +8143,9 @@ class SyncEngine { if ((localFileSize == uploadFileSize) && (localFileHash == uploadFileHash)) { // Uploaded file integrity intact if (debugLogging) {addLogEntry("Uploaded local file matches reported online size and hash values", ["debug"]);} + // set to true and return integrityValid = true; + return integrityValid; } else { // Upload integrity failure .. what failed? // There are 2 scenarios where this happens: @@ -8043,7 +8169,7 @@ class SyncEngine { if (verboseLogging) { addLogEntry("CAUTION: When you upload files to Microsoft OneDrive that uses SharePoint as its backend, Microsoft OneDrive will alter your files post upload.", ["verbose"]); addLogEntry("CAUTION: This will lead to technical differences between the version stored online and your local original file, potentially causing issues with the accuracy or consistency of your data.", ["verbose"]); - addLogEntry("CAUTION: Please read https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details.", ["verbose"]); + addLogEntry("CAUTION: Please refer to https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details.", ["verbose"]); } } // How can this be disabled?