From 2553366a89df36048d79161072bf8c29b9cadb3c Mon Sep 17 00:00:00 2001 From: abraunegg Date: Fri, 28 Dec 2018 12:26:03 +1100 Subject: [PATCH] Implement Feature Request: Add status command or switch (Issue #112) (#307) * Implement Feature Request: Add status command or switch --- src/main.d | 19 ++++++- src/onedrive.d | 4 +- src/sync.d | 135 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 154 insertions(+), 4 deletions(-) diff --git a/src/main.d b/src/main.d index 35d23985..ce224b52 100644 --- a/src/main.d +++ b/src/main.d @@ -25,6 +25,8 @@ int main(string[] args) bool disableNotifications = false; // Display application configuration but do not sync bool displayConfiguration = false; + // Display sync status + bool displaySyncStatus = false; // only download remote changes bool downloadOnly; // Does the user want to disable upload validation - https://github.com/abraunegg/onedrive/issues/205 @@ -79,6 +81,7 @@ int main(string[] args) "debug-https", "Debug OneDrive HTTPS communication.", &debugHttp, "disable-notifications", "Do not use desktop notifications in monitor mode.", &disableNotifications, "display-config", "Display what options the client will use as currently configured - no sync will be performed.", &displayConfiguration, + "display-sync-status", "Display the sync status of the client - no sync will be performed.", &displaySyncStatus, "download-only|d", "Only download remote changes", &downloadOnly, "disable-upload-validation", "Disable upload validation when uploading to OneDrive", &disableUploadValidation, "enable-logging", "Enable client activity to a separate log file", &enableLogFile, @@ -334,7 +337,7 @@ int main(string[] args) // create-directory, remove-directory, source-directory, destination-directory // are activities that dont perform a sync no error message for these items either - if (((createDirectory != "") || (removeDirectory != "")) || ((sourceDirectory != "") && (destinationDirectory != "")) || (o365SharedLibraryName != "")) { + if (((createDirectory != "") || (removeDirectory != "")) || ((sourceDirectory != "") && (destinationDirectory != "")) || (o365SharedLibraryName != "") || (displaySyncStatus == true)) { performSyncOK = true; } @@ -419,6 +422,20 @@ int main(string[] args) sync.querySiteCollectionForDriveID(o365SharedLibraryName); } + // Are we displaying the sync status of the client? + if (displaySyncStatus) { + string remotePath = "/"; + string localPath = "."; + + // Are we doing a single directory check? + if (singleDirectory != ""){ + // Need two different path strings here + remotePath = singleDirectory; + localPath = singleDirectory; + } + sync.queryDriveForChanges(remotePath); + } + // Are we performing a sync, resync or monitor operation? if ((synchronize) || (resync) || (monitor)) { diff --git a/src/onedrive.d b/src/onedrive.d index b41fd0be..c3c70cd5 100644 --- a/src/onedrive.d +++ b/src/onedrive.d @@ -156,7 +156,7 @@ final class OneDriveApi const(char)[] url = deltaLink; if (url == null) { url = driveByIdUrl ~ driveId ~ "/items/" ~ id ~ "/delta"; - url ~= "?select=id,name,eTag,cTag,deleted,file,folder,root,fileSystemInfo,remoteItem,parentReference"; + url ~= "?select=id,name,eTag,cTag,deleted,file,folder,root,fileSystemInfo,remoteItem,parentReference,size"; } return get(url); } @@ -228,7 +228,7 @@ final class OneDriveApi checkAccessTokenExpired(); const(char)[] url; // string itemByPathUrl = "https://graph.microsoft.com/v1.0/me/drive/root:/"; - if (path == ".") url = driveUrl ~ "/root/"; + if ((path == ".")||(path == "/")) url = driveUrl ~ "/root/"; else url = itemByPathUrl ~ encodeComponent(path) ~ ":/"; url ~= "?select=id,name,eTag,cTag,deleted,file,folder,root,fileSystemInfo,remoteItem,parentReference"; return get(url); diff --git a/src/sync.d b/src/sync.d index a9cc18cd..3665e3e5 100644 --- a/src/sync.d +++ b/src/sync.d @@ -59,6 +59,11 @@ private bool isMalware(const ref JSONValue item) return ("malware" in item) != null; } +private bool hasFileSize(const ref JSONValue item) +{ + return ("size" in item) != null; +} + // construct an Item struct from a JSON driveItem private Item makeItem(const ref JSONValue driveItem) { @@ -495,7 +500,7 @@ final class SyncEngine } else { // The drive id does not match our users default drive id // Potentially the 'path id' we are requesting the details of is a Shared Folder (remote item) - // Use the 'id' that was passed in + // Use the 'id' that was passed in (folderId) idToQuery = id; } @@ -1795,4 +1800,132 @@ final class SyncEngine writeln("ERROR: This site could not be found. Please check it's name and your permissions to access the site."); } } + + // Query the OneDrive 'drive' to determine if we are 'in sync' or if there are pending changes + void queryDriveForChanges(string path) { + + // Function variables + int validChanges = 0; + long downloadSize = 0; + string driveId; + string folderId; + string deltaLink; + string thisItemId; + string thisItemPath; + string syncFolderName; + string syncFolderPath; + string syncFolderChildPath; + JSONValue changes; + JSONValue onedrivePathDetails; + + // Get the path details from OneDrive + try { + onedrivePathDetails = onedrive.getPathDetails(path); // Returns a JSON String for the OneDrive Path + } catch (OneDriveException e) { + if (e.httpStatusCode == 404) { + // Requested path could not be found + log.error("ERROR: The requested path to query was not found on OneDrive"); + return; + } + } + + if(isItemRemote(onedrivePathDetails)){ + // remote changes + driveId = onedrivePathDetails["remoteItem"]["parentReference"]["driveId"].str; // Should give something like 66d53be8a5056eca + folderId = onedrivePathDetails["remoteItem"]["id"].str; // Should give something like BC7D88EC1F539DCF!107 + syncFolderName = onedrivePathDetails["name"].str; + // A remote drive item will not have ["parentReference"]["path"] + syncFolderPath = ""; + syncFolderChildPath = ""; + } else { + driveId = defaultDriveId; + folderId = onedrivePathDetails["id"].str; // Should give something like 12345ABCDE1234A1!101 + syncFolderName = onedrivePathDetails["name"].str; + if (hasParentReferencePath(onedrivePathDetails)) { + syncFolderPath = onedrivePathDetails["parentReference"]["path"].str; + syncFolderChildPath = syncFolderPath ~ "/" ~ syncFolderName ~ "/"; + } else { + // root drive item will not have ["parentReference"]["path"] + syncFolderPath = ""; + syncFolderChildPath = ""; + } + } + + // Query Database for the deltaLink + deltaLink = itemdb.getDeltaLink(driveId, folderId); + + const(char)[] idToQuery; + if (driveId == defaultDriveId) { + // The drive id matches our users default drive id + idToQuery = defaultRootId.dup; + } else { + // The drive id does not match our users default drive id + // Potentially the 'path id' we are requesting the details of is a Shared Folder (remote item) + // Use folderId + idToQuery = folderId; + } + + // Query OneDrive changes + changes = onedrive.viewChangesById(driveId, idToQuery, deltaLink); + + // Are there any changes on OneDrive? + if (count(changes["value"].array) != 0) { + // Were we given a remote path to check if we are in sync for, or the root? + if (path != "/") { + // we were given a directory to check, we need to validate the list of changes against this path only + foreach (item; changes["value"].array) { + // Is this change valid for the 'path' we are checking? + if (hasParentReferencePath(item)) { + thisItemId = item["parentReference"]["id"].str; + thisItemPath = item["parentReference"]["path"].str; + } else { + thisItemId = item["id"].str; + // Is the defaultDriveId == driveId + if (driveId == defaultDriveId){ + // 'root' items will not have ["parentReference"]["path"] + if (isItemRoot(item)){ + thisItemPath = ""; + } else { + thisItemPath = item["parentReference"]["path"].str; + } + } else { + // A remote drive item will not have ["parentReference"]["path"] + thisItemPath = ""; + } + } + + if ( (thisItemId == folderId) || (canFind(thisItemPath, syncFolderChildPath)) || (canFind(thisItemPath, folderId)) ){ + // This is a change we want count + validChanges++; + if ((isItemFile(item)) && (hasFileSize(item))) { + downloadSize = downloadSize + item["size"].integer; + } + } + } + // Are there any valid changes? + if (validChanges != 0){ + writeln("Selected directory is out of sync with OneDrive"); + if (downloadSize > 0){ + downloadSize = downloadSize / 1000; + writeln("Approximate data to transfer: ", downloadSize, " KB"); + } + } else { + writeln("No pending remote changes - selected directory is in sync"); + } + } else { + writeln("Local directory is out of sync with OneDrive"); + foreach (item; changes["value"].array) { + if ((isItemFile(item)) && (hasFileSize(item))) { + downloadSize = downloadSize + item["size"].integer; + } + } + if (downloadSize > 0){ + downloadSize = downloadSize / 1000; + writeln("Approximate data to transfer: ", downloadSize, " KB"); + } + } + } else { + writeln("No pending remote changes - in sync"); + } + } }