From ad3ddee0ec86ad70be4c939d9bcc44773819ec2d Mon Sep 17 00:00:00 2001 From: abraunegg Date: Wed, 18 Oct 2023 17:32:24 +1100 Subject: [PATCH 1/3] Update PR * Add --create-directory * Add --remove-directory --- docs/application-config-options.md | 12 ++++++++---- src/config.d | 26 ++++++++++++++++++++++++- src/main.d | 24 +++++++++++++++++++++-- src/sync.d | 31 ++++++++++++++++++++---------- 4 files changed, 76 insertions(+), 17 deletions(-) diff --git a/docs/application-config-options.md b/docs/application-config-options.md index 3bc51c7a7e..4bfca26495 100644 --- a/docs/application-config-options.md +++ b/docs/application-config-options.md @@ -859,9 +859,11 @@ _**Usage Example:**_ `onedrive --confdir '~/.config/onedrive-business/'` _**Additional Usage Notes:**_ If using this option, it must be specified each and every time the application is used. If this is ommited, the application default configuration directory will be used. ### CLI Option: --create-directory -_**Description:**_ +_**Description:**_ This CLI option allows the user to create the specified directory path on Microsoft OneDrive without performing a sync. -_**Usage Example:**_ +_**Usage Example:**_ `onedrive --create-directory 'path/of/new/folder/structure/to/create/'` + +_**Additional Usage Notes:**_ The specified path to create is relative to your configured 'sync_dir'. ### CLI Option: --create-share-link _**Description:**_ This CLI option enables the creation of a shareable file link that can be provided to users to access the file that is stored on Microsoft OneDrive. By default, the permissions for the file will be 'read-only'. @@ -952,9 +954,11 @@ _**Description:**_ This CLI option controls the ability to re-authenticate your _**Usage Example:**_ `onedrive --reauth` ### CLI Option: --remove-directory -_**Description:**_ +_**Description:**_ This CLI option allows the user to remove the specified directory path on Microsoft OneDrive without performing a sync. -_**Usage Example:**_ +_**Usage Example:**_ `onedrive --remove-directory 'path/of/new/folder/structure/to/remove/'` + +_**Additional Usage Notes:**_ The specified path to remove is relative to your configured 'sync_dir'. ### CLI Option: --single-directory _**Description:**_ This CLI option controls the applications ability to sync a specific single directory. diff --git a/src/config.d b/src/config.d index cbc4ee8d51..1b7ebfc0fe 100644 --- a/src/config.d +++ b/src/config.d @@ -1993,7 +1993,7 @@ class ApplicationConfig { } // --monitor and --display-sync-status cannot be used together - if ( (getValueBool("monitor")) && (getValueBool("display_sync_status"))) { + if ((getValueBool("monitor")) && (getValueBool("display_sync_status"))) { log.error("ERROR: --monitor and --display-sync-status cannot be used together."); operationalConflictDetected = true; } @@ -2036,6 +2036,30 @@ class ApplicationConfig { operationalConflictDetected = true; } + // --monitor and --create-directory cannot be used together + if ((getValueBool("monitor")) && (!getValueString("create_directory").empty)) { + log.error("ERROR: --monitor and --create-directory cannot be used together."); + operationalConflictDetected = true; + } + + // --sync and --create-directory cannot be used together + if ((getValueBool("synchronize")) && (!getValueString("create_directory").empty)) { + log.error("ERROR: --sync and --create-directory cannot be used together."); + operationalConflictDetected = true; + } + + // --monitor and --remove-directory cannot be used together + if ((getValueBool("monitor")) && (!getValueString("remove_directory").empty)) { + log.error("ERROR: --monitor and --remove-directory cannot be used together."); + operationalConflictDetected = true; + } + + // --sync and --remove-directory cannot be used together + if ((getValueBool("synchronize")) && (!getValueString("remove_directory").empty)) { + log.error("ERROR: --sync and --remove-directory cannot be used together."); + operationalConflictDetected = true; + } + // Return bool value indicating if we have an operational conflict return operationalConflictDetected; } diff --git a/src/main.d b/src/main.d index b8fca99016..8597b0917a 100644 --- a/src/main.d +++ b/src/main.d @@ -398,6 +398,8 @@ int main(string[] cliArgs) { // - Are we getting the URL for a file online // - Are we listing who modified a file last online // - Are we createing a shareable link for an existing file on OneDrive? + // - Are we just creating a directory online, without any sync being performed? + // - Are we just deleting a directory online, without any sync being performed? // --get-sharepoint-drive-id - Get the SharePoint Library drive_id if (appConfig.getValueString("sharepoint_library_name") != "") { @@ -457,6 +459,24 @@ int main(string[] cliArgs) { return EXIT_SUCCESS; } + // --create-directory - Are we just creating a directory online, without any sync being performed? + if ((appConfig.getValueString("create_directory") != "")) { + // Handle the remote path creation and updating of the local database without performing a sync + syncEngineInstance.createDirectoryOnline(appConfig.getValueString("create_directory")); + // Exit application + // Use exit scopes to shutdown API + return EXIT_SUCCESS; + } + + // --remove-directory - Are we just deleting a directory online, without any sync being performed? + if ((appConfig.getValueString("remove_directory") != "")) { + // Handle the remote path deletion without performing a sync + syncEngineInstance.deleteByPath(appConfig.getValueString("remove_directory")); + // Exit application + // Use exit scopes to shutdown API + return EXIT_SUCCESS; + } + // If we get to this point, we have not performed a 'no-sync' task .. log.error("\nYour command line input is missing either the '--sync' or '--monitor' switches. Please include one (but not both) of these switches in your command line, or refer to 'onedrive --help' for additional guidance.\n"); log.error("It is important to note that you must include one of these two arguments in your command line for the application to perform a synchronisation with Microsoft OneDrive\n"); @@ -632,15 +652,15 @@ int main(string[] cliArgs) { // Delegated function for when inotify detects a delete event filesystemMonitor.onDelete = delegate(string path) { - log.log("Received inotify delete event from operating system .. attempting item deletion as requested"); log.vlog("[M] Local item deleted: ", path); try { + log.log("The operating system sent a deletion notification. Trying to delete the item as requested"); syncEngineInstance.deleteByPath(path); } catch (CurlException e) { log.vlog("Offline, cannot delete item!"); } catch(SyncException e) { if (e.msg == "The item to delete is not in the local database") { - log.vlog("Item cannot be deleted from OneDrive because it was not found in the local database"); + log.vlog("Item cannot be deleted from Microsoft OneDrive because it was not found in the local database"); } else { log.logAndNotify("Cannot delete remote item: ", e.msg); } diff --git a/src/sync.d b/src/sync.d index 15d56ea1c6..3ed60f38ae 100644 --- a/src/sync.d +++ b/src/sync.d @@ -4139,7 +4139,7 @@ class SyncEngine { // Step 2: Query for the path online if (!pathFoundInDB) { - + // path not found in database try { log.vdebug("Attempting to query OneDrive Online for this parent path as path not found in local database: ", parentPath); onlinePathData = createDirectoryOnlineOneDriveApiInstance.getPathDetails(parentPath); @@ -4147,13 +4147,14 @@ class SyncEngine { saveItem(onlinePathData); parentItem = makeItem(onlinePathData); } catch (OneDriveException exception) { - if (exception.httpStatusCode == 404) { // Parent does not exist ... need to create parent log.vdebug("Parent path does not exist online: ", parentPath); createDirectoryOnline(parentPath); + // no return here as we need to continue, but need to re-query the OneDrive API to get the right parental details now that they exist + onlinePathData = createDirectoryOnlineOneDriveApiInstance.getPathDetails(parentPath); + parentItem = makeItem(onlinePathData); } else { - string thisFunctionName = getFunctionName!({}); // HTTP request returned status code 408,429,503,504 if ((exception.httpStatusCode == 408) || (exception.httpStatusCode == 429) || (exception.httpStatusCode == 503) || (exception.httpStatusCode == 504)) { @@ -4186,12 +4187,10 @@ class SyncEngine { } } } - } else { // parent path found in database ... use those details ... parentItem = databaseItem; } - } // Make sure the full path does not exist online, this should generate a 404 response, to which then the folder will be created online @@ -4293,6 +4292,7 @@ class SyncEngine { } } else { // Simulate a successful 'directory create' & save it to the dryRun database copy + log.log("Successfully created the remote directory ", thisNewPathToCreate, " on OneDrive"); // The simulated response has to pass 'makeItem' as part of saveItem auto fakeResponse = createFakeResponse(thisNewPathToCreate); // Save item to the database @@ -4341,7 +4341,7 @@ class SyncEngine { // If we get to this point - onlinePathData = createDirectoryOnlineOneDriveApiInstance.getPathDetailsByDriveId(parentItem.driveId, thisNewPathToCreate) generated a 'valid' response .... // This means that the folder potentially exists online .. which is odd .. as it should not have existed - if (onlinePathData.type() == JSONType.object){ + if (onlinePathData.type() == JSONType.object) { // A valid object was responded with if (onlinePathData["name"].str == baseName(thisNewPathToCreate)) { // OneDrive 'name' matches local path name @@ -5179,7 +5179,6 @@ class SyncEngine { // If the item is a remote item, delete the reference in the local database itemDB.deleteById(itemToDelete.remoteDriveId, itemToDelete.remoteId); } - } else { // log that this is a dry-run activity log.log("dry run - no delete activity"); @@ -6227,8 +6226,8 @@ class SyncEngine { } // Delete an item by it's path - // This function is only used in --monitor mode - void deleteByPath(const(string) path) { + // This function is only used in --monitor mode and --remove-directory directive + void deleteByPath(string path) { // function variables Item dbItem; @@ -6242,10 +6241,22 @@ class SyncEngine { break; } } + + // Was the item found in the database? if (!itemInDB) { - throw new SyncException("The item to delete is not in the local database"); + // path to delete is not in the local database .. + // was this a --remove-directory attempt? + if ((appConfig.getValueString("remove_directory") != "")) { + // --remove-directory deletion attempt + log.error("The item to delete is not in the local database - unable to delete online"); + return; + } else { + // normal use .. --monitor being used + throw new SyncException("The item to delete is not in the local database"); + } } + // This needs to be enforced as we have to know the parent id of the object being deleted if (dbItem.parentId == null) { // the item is a remote folder, need to do the operation on the parent enforce(itemDB.selectByPathWithoutRemote(path, appConfig.defaultDriveId, dbItem)); From 1eff2d7d673a7e058ba9467f4da315237b47392e Mon Sep 17 00:00:00 2001 From: abraunegg Date: Wed, 18 Oct 2023 19:15:27 +1100 Subject: [PATCH 2/3] Update PR * Add --source-directory 'path/as/source/' --destination-directory 'path/as/destination' functionality --- docs/application-config-options.md | 12 ++++++++---- src/config.d | 28 ++++++++++++++++++++++++++-- src/main.d | 11 +++++++++++ src/sync.d | 25 +++++++++++++++++++++---- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/docs/application-config-options.md b/docs/application-config-options.md index 4bfca26495..00c30d47bf 100644 --- a/docs/application-config-options.md +++ b/docs/application-config-options.md @@ -873,9 +873,11 @@ _**Usage Example:**_ `onedrive --create-share-link 'relative/path/to/your/file.t _**Additional Usage Notes:**_ If writable access to the file is required, you must add `--with-editing-perms` to your command. See below for details. ### CLI Option: --destination-directory -_**Description:**_ +_**Description:**_ This CLI option specifies the 'destination' portion of moving a file or folder online, without performing a sync operation. -_**Usage Example:**_ +_**Usage Example:**_ `onedrive --source-directory 'path/as/source/' --destination-directory 'path/as/destination'` + +_**Additional Usage Notes:**_ All specified paths are relative to your configured 'sync_dir'. ### CLI Option: --display-config _**Description:**_ This CLI option will display the effective application configuration @@ -968,9 +970,11 @@ _**Usage Example:**_ `onedrive --sync --single-directory 'Data'` _**Additional Usage Notes:**_ The path specified is relative to your configured 'sync_dir' path. If the physical local path 'Folder' to sync is `~/OneDrive/Data/Folder` then the command would be `--single-directory 'Data/Folder'`. ### CLI Option: --source-directory -_**Description:**_ +_**Description:**_ This CLI option specifies the 'source' portion of moving a file or folder online, without performing a sync operation. + +_**Usage Example:**_ `onedrive --source-directory 'path/as/source/' --destination-directory 'path/as/destination'` -_**Usage Example:**_ +_**Additional Usage Notes:**_ All specified paths are relative to your configured 'sync_dir'. ### CLI Option: --sync | -s _**Description:**_ This CLI option controls the 'Standalone Mode' operational aspect of the client. When this option is used, the client will perform a one-time sync of data between Microsoft OneDrive and your local system. diff --git a/src/config.d b/src/config.d index 1b7ebfc0fe..dbb40bb11b 100644 --- a/src/config.d +++ b/src/config.d @@ -1986,9 +1986,9 @@ class ApplicationConfig { operationalConflictDetected = true; } - // --get-O365-drive-id cannot be used with --resync and/or --resync-auth + // --get-sharepoint-drive-id cannot be used with --resync and/or --resync-auth if ((!getValueString("sharepoint_library_name").empty) && ((getValueBool("resync")) || (getValueBool("resync_auth")))) { - log.error("ERROR: --get-O365-drive-id cannot be used with --resync or --resync-auth."); + log.error("ERROR: --get-sharepoint-drive-id cannot be used with --resync or --resync-auth."); operationalConflictDetected = true; } @@ -2060,6 +2060,30 @@ class ApplicationConfig { operationalConflictDetected = true; } + // --monitor and --source-directory cannot be used together + if ((getValueBool("monitor")) && (!getValueString("source_directory").empty)) { + log.error("ERROR: --monitor and --source-directory cannot be used together."); + operationalConflictDetected = true; + } + + // --sync and --source-directory cannot be used together + if ((getValueBool("synchronize")) && (!getValueString("source_directory").empty)) { + log.error("ERROR: --sync and --source-directory cannot be used together."); + operationalConflictDetected = true; + } + + // --monitor and --destination-directory cannot be used together + if ((getValueBool("monitor")) && (!getValueString("destination_directory").empty)) { + log.error("ERROR: --monitor and --destination-directory cannot be used together."); + operationalConflictDetected = true; + } + + // --sync and --destination-directory cannot be used together + if ((getValueBool("synchronize")) && (!getValueString("destination_directory").empty)) { + log.error("ERROR: --sync and --destination-directory cannot be used together."); + operationalConflictDetected = true; + } + // Return bool value indicating if we have an operational conflict return operationalConflictDetected; } diff --git a/src/main.d b/src/main.d index 8597b0917a..9281c835eb 100644 --- a/src/main.d +++ b/src/main.d @@ -400,6 +400,7 @@ int main(string[] cliArgs) { // - Are we createing a shareable link for an existing file on OneDrive? // - Are we just creating a directory online, without any sync being performed? // - Are we just deleting a directory online, without any sync being performed? + // - Are we renaming or moving a directory? // --get-sharepoint-drive-id - Get the SharePoint Library drive_id if (appConfig.getValueString("sharepoint_library_name") != "") { @@ -477,6 +478,16 @@ int main(string[] cliArgs) { return EXIT_SUCCESS; } + // Are we renaming or moving a directory? + // onedrive --source-directory 'path/as/source/' --destination-directory 'path/as/destination' + if ((appConfig.getValueString("source_directory") != "") && (appConfig.getValueString("destination_directory") != "")) { + // We are renaming or moving a directory + syncEngineInstance.uploadMoveItem(appConfig.getValueString("source_directory"), appConfig.getValueString("destination_directory")); + // Exit application + // Use exit scopes to shutdown API + return EXIT_SUCCESS; + } + // If we get to this point, we have not performed a 'no-sync' task .. log.error("\nYour command line input is missing either the '--sync' or '--monitor' switches. Please include one (but not both) of these switches in your command line, or refer to 'onedrive --help' for additional guidance.\n"); log.error("It is important to note that you must include one of these two arguments in your command line for the application to perform a synchronisation with Microsoft OneDrive\n"); diff --git a/src/sync.d b/src/sync.d index 3ed60f38ae..6a820f7d25 100644 --- a/src/sync.d +++ b/src/sync.d @@ -2947,6 +2947,12 @@ class SyncEngine { // Return a true|false response bool clientSideRuleExcludesPath = false; + + // does the path exist? + if (!exists(localFilePath)) { + // path does not exist - we cant review any client side rules on something that does not exist locally + return clientSideRuleExcludesPath; + } // - check_nosync if (!clientSideRuleExcludesPath) { @@ -6246,7 +6252,7 @@ class SyncEngine { if (!itemInDB) { // path to delete is not in the local database .. // was this a --remove-directory attempt? - if ((appConfig.getValueString("remove_directory") != "")) { + if (!appConfig.getValueBool("monitor")) { // --remove-directory deletion attempt log.error("The item to delete is not in the local database - unable to delete online"); return; @@ -6357,12 +6363,23 @@ class SyncEngine { } } else { if (!exists(newPath)) { - log.vlog("uploadMoveItem target has disappeared: ", newPath); - return; + // is this --monitor use? + if (appConfig.getValueBool("monitor")) { + log.vlog("uploadMoveItem target has disappeared: ", newPath); + return; + } } // Configure the modification JSON item - SysTime mtime = timeLastModified(newPath).toUTC(); + SysTime mtime; + if (appConfig.getValueBool("monitor")) { + // Use the newPath modified timestamp + mtime = timeLastModified(newPath).toUTC(); + } else { + // Use the current system time + mtime = Clock.currTime().toUTC(); + } + JSONValue data = [ "name": JSONValue(baseName(newPath)), "parentReference": JSONValue([ From 380e3167f2c2bce69fdd9fe5c78b616e9ebeae4d Mon Sep 17 00:00:00 2001 From: abraunegg Date: Thu, 19 Oct 2023 10:05:03 +1100 Subject: [PATCH 3/3] Update sync.d * Fix that the '.' were not being printed in --verbose mode for fetching the /delta response --- src/sync.d | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sync.d b/src/sync.d index 6a820f7d25..2cfae76f00 100644 --- a/src/sync.d +++ b/src/sync.d @@ -704,15 +704,15 @@ class SyncEngine { currentDeltaLink = null; } - // Dynamic output for a non-verbose run so that the user knows something is happening - if (log.verbose == 0) { + // Dynamic output for non-verbose and verbose run so that the user knows something is being retreived from the OneDrive API + if (log.verbose <= 1) { if (!appConfig.surpressLoggingOutput) { log.fileOnly("Fetching items from the OneDrive API for Drive ID: ", driveIdToQuery); // Use the dots to show the application is 'doing something' write("Fetching items from the OneDrive API for Drive ID: ", driveIdToQuery, " ."); } } else { - log.vlog("Fetching /delta response from the OneDrive API for Drive ID: ", driveIdToQuery); + log.vdebug("Fetching /delta response from the OneDrive API for Drive ID: ", driveIdToQuery); } // Create a new API Instance for querying /delta and initialise it @@ -738,7 +738,7 @@ class SyncEngine { ulong nrChanges = count(deltaChanges["value"].array); int changeCount = 0; - if (log.verbose == 0) { + if (log.verbose <= 1) { // Dynamic output for a non-verbose run so that the user knows something is happening if (!appConfig.surpressLoggingOutput) { write("."); @@ -791,7 +791,7 @@ class SyncEngine { object.destroy(getDeltaQueryOneDriveApiInstance); // Log that we have finished querying the /delta API - if (log.verbose == 0) { + if (log.verbose <= 1) { if (!appConfig.surpressLoggingOutput) { write("\n"); } @@ -2367,7 +2367,7 @@ class SyncEngine { // Display accountType, defaultDriveId, defaultRootId & remainingFreeSpace for verbose logging purposes //log.vlog("Application version: ", strip(import("version"))); - string tempVersion = "v2.5.0-alpha-2" ~ " GitHub version: " ~ strip(import("version")); + string tempVersion = "v2.5.0-alpha-3" ~ " GitHub version: " ~ strip(import("version")); log.vlog("Application version: ", tempVersion); log.vlog("Account Type: ", appConfig.accountType);