diff --git a/docs/USAGE.md b/docs/USAGE.md index 76734657..fcf11bb5 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -689,6 +689,8 @@ Options: Set the directory used to store the configuration files --create-directory ARG Create a directory on OneDrive - no sync will be performed. + --create-share-link ARG + Create a shareable link for an existing file on OneDrive --debug-https Debug OneDrive HTTPS communication. --destination-directory ARG diff --git a/onedrive.1.in b/onedrive.1.in index ff048c0d..e5e9c633 100644 --- a/onedrive.1.in +++ b/onedrive.1.in @@ -44,6 +44,9 @@ Set the directory used to store the configuration files \fB\-\-create\-directory\fP ARG Create a directory on OneDrive \- no sync will be performed. .TP +\fB\-\-create\-share\-link\fP ARG +Create a shareable link for an existing file on OneDrive +.TP \fB\-\-debug\-https\fP Debug OneDrive HTTPS communication. .br diff --git a/src/config.d b/src/config.d index c665d852..2134703b 100644 --- a/src/config.d +++ b/src/config.d @@ -249,6 +249,7 @@ final class Config { // Add additional options that are NOT configurable via config file stringValues["create_directory"] = ""; + stringValues["create_share_link"] = ""; stringValues["destination_directory"] = ""; stringValues["get_file_link"] = ""; stringValues["get_o365_drive_id"] = ""; @@ -293,6 +294,9 @@ final class Config "create-directory", "Create a directory on OneDrive - no sync will be performed.", &stringValues["create_directory"], + "create-share-link", + "Create a shareable link for an existing file on OneDrive", + &stringValues["create_share_link"], "debug-https", "Debug OneDrive HTTPS communication.", &boolValues["debug_https"], @@ -591,7 +595,9 @@ void outputLongHelp(Option[] opt) auto argsNeedingOptions = [ "--confdir", "--create-directory", + "--create-share-link", "--destination-directory", + "--get-file-link", "--get-O365-drive-id", "--log-dir", "--min-notify-changes", diff --git a/src/main.d b/src/main.d index 88f5a3cf..d8d148e5 100644 --- a/src/main.d +++ b/src/main.d @@ -565,7 +565,7 @@ int main(string[] args) // create-directory, remove-directory, source-directory, destination-directory // these are activities that dont perform a sync, so to not generate an error message for these items either - if (((cfg.getValueString("create_directory") != "") || (cfg.getValueString("remove_directory") != "")) || ((cfg.getValueString("source_directory") != "") && (cfg.getValueString("destination_directory") != "")) || (cfg.getValueString("get_file_link") != "") || (cfg.getValueString("get_o365_drive_id") != "") || cfg.getValueBool("display_sync_status") || cfg.getValueBool("list_business_shared_folders")) { + if (((cfg.getValueString("create_directory") != "") || (cfg.getValueString("remove_directory") != "")) || ((cfg.getValueString("source_directory") != "") && (cfg.getValueString("destination_directory") != "")) || (cfg.getValueString("get_file_link") != "") || (cfg.getValueString("create_share_link") != "") || (cfg.getValueString("get_o365_drive_id") != "") || cfg.getValueBool("display_sync_status") || cfg.getValueBool("list_business_shared_folders")) { performSyncOK = true; } @@ -700,8 +700,8 @@ int main(string[] args) // Use exit scopes to shutdown API return EXIT_FAILURE; } else { - if (cfg.getValueString("get_file_link") == "") { - // Print out that we are initializing the engine only if we are not grabbing the file link + if ((cfg.getValueString("get_file_link") == "") && (cfg.getValueString("create_share_link") == "")) { + // Print out that we are initializing the engine only if we are not grabbing the file link or creating a shareable link log.logAndNotify("Initializing the Synchronization Engine ..."); } } @@ -790,8 +790,18 @@ int main(string[] args) return EXIT_SUCCESS; } + // Are we createing an anonymous read-only shareable link for an existing file on OneDrive? + if (cfg.getValueString("create_share_link") != "") { + // Query OneDrive for the file, and if valid, create a shareable link for the file + sync.createShareableLinkForFile(cfg.getValueString("create_share_link")); + // Exit application + // Use exit scopes to shutdown API + return EXIT_SUCCESS; + } + // Are we obtaining the URL path for a synced file? if (cfg.getValueString("get_file_link") != "") { + // Query OneDrive for the file link sync.queryOneDriveForFileURL(cfg.getValueString("get_file_link"), syncDir); // Exit application // Use exit scopes to shutdown API diff --git a/src/onedrive.d b/src/onedrive.d index 48983845..505cec39 100644 --- a/src/onedrive.d +++ b/src/onedrive.d @@ -567,7 +567,6 @@ final class OneDriveApi return get(url); } - // Return the requested details of the specified id // https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_get JSONValue getFileDetails(const(char)[] driveId, const(char)[] id) @@ -579,6 +578,17 @@ final class OneDriveApi return get(url); } + // Create an anonymous read-only shareable link for an existing file on OneDrive + // https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createlink + JSONValue createShareableLink(const(char)[] driveId, const(char)[] id, JSONValue accessScope) + { + checkAccessTokenExpired(); + const(char)[] url; + url = driveByIdUrl ~ driveId ~ "/items/" ~ id ~ "/createLink"; + http.addRequestHeader("Content-Type", "application/json"); + return post(url, accessScope.toString()); + } + // https://dev.onedrive.com/items/move.htm JSONValue moveByPath(const(char)[] sourcePath, JSONValue moveData) { diff --git a/src/sync.d b/src/sync.d index aefa60e4..91f252fe 100644 --- a/src/sync.d +++ b/src/sync.d @@ -5173,6 +5173,83 @@ final class SyncEngine } } + // Create an anonymous read-only shareable link for an existing file on OneDrive + void createShareableLinkForFile(string filePath) + { + JSONValue onedrivePathDetails; + JSONValue createShareableLinkResponse; + string driveId; + string itemId; + string fileShareLink; + + // Get the path details from OneDrive + try { + onedrivePathDetails = onedrive.getPathDetails(filePath); // Returns a JSON String for the OneDrive Path + } catch (OneDriveException e) { + log.vdebug("onedrivePathDetails = onedrive.getPathDetails(filePath); generated a OneDriveException"); + 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 (e.httpStatusCode == 429) { + // HTTP request returned status code 429 (Too Many Requests). We need to leverage the response Retry-After HTTP header to ensure minimum delay until the throttle is removed. + handleOneDriveThrottleRequest(); + // Retry original request by calling function again to avoid replicating any further error handling + log.vdebug("Retrying original request that generated the OneDrive HTTP 429 Response Code (Too Many Requests) - calling queryDriveForChanges(path);"); + createShareableLinkForFile(filePath); + // return back to original call + return; + } + + if (e.httpStatusCode == 504) { + // HTTP request returned status code 504 (Gateway Timeout) + log.log("OneDrive returned a 'HTTP 504 - Gateway Timeout' - retrying request"); + // Retry original request by calling function again to avoid replicating any further error handling + createShareableLinkForFile(filePath); + // return back to original call + return; + } else { + // display what the error is + displayOneDriveErrorMessage(e.msg); + return; + } + } + + // Was a valid JSON response received? + if (onedrivePathDetails.type() == JSONType.object) { + // valid JSON response for the file was received + // Configure the required variables + driveId = onedrivePathDetails["parentReference"]["driveId"].str; + itemId = onedrivePathDetails["id"].str; + + // configure the access scope + JSONValue accessScope = [ + "type": "view", + "scope": "anonymous" + ]; + + // Create the shareable file link + createShareableLinkResponse = onedrive.createShareableLink(driveId, itemId, accessScope); + if ((createShareableLinkResponse.type() == JSONType.object) && ("link" in createShareableLinkResponse)) { + // Extract the file share link from the JSON response + fileShareLink = createShareableLinkResponse["link"]["webUrl"].str; + writeln("File Shareable Link: ", fileShareLink); + } else { + // not a valid JSON object + log.error("ERROR: There was an error performing this operation on OneDrive"); + log.error("ERROR: Increase logging verbosity to assist determining why."); + return; + } + } else { + // not a valid JSON object + log.error("ERROR: There was an error performing this operation on OneDrive"); + log.error("ERROR: Increase logging verbosity to assist determining why."); + return; + } + } + // Query OneDrive for a URL path of a file void queryOneDriveForFileURL(string localFilePath, string syncDir) {