Initial code commit for onedrive client version v2.5.0-alpha-1

Add initial code for onedrive client version v2.5.0-alpha-1, adding in support for OneDrive Business Shared Folders
This commit is contained in:
abraunegg 2023-09-22 05:34:42 +10:00
parent 3502e0cee4
commit 4bd9ae5092
7 changed files with 548 additions and 285 deletions

View file

@ -41,7 +41,7 @@ class ClientSideFiltering {
// Load the Business Shared Items file if it exists
if (exists(appConfig.businessSharedItemsFilePath)){
loadSyncList(appConfig.businessSharedItemsFilePath);
loadBusinessSharedItems(appConfig.businessSharedItemsFilePath);
}
// Configure skip_dir, skip_file, skip-dir-strict-match & skip_dotfiles from config entries

View file

@ -55,7 +55,7 @@ class ApplicationConfig {
// - Identify as ISV and include Company Name, App Name separated by a pipe character and then adding Version number separated with a slash character
//immutable string defaultUserAgent = isvTag ~ "|" ~ companyName ~ "|" ~ appTitle ~ "/" ~ strip(import("version"));
immutable string defaultUserAgent = isvTag ~ "|" ~ companyName ~ "|" ~ appTitle ~ "/" ~ "v2.5.0-alpha-0";
immutable string defaultUserAgent = isvTag ~ "|" ~ companyName ~ "|" ~ appTitle ~ "/" ~ "v2.5.0-alpha-1";
// HTTP Struct items, used for configuring HTTP()
// Curl Timeout Handling
@ -146,20 +146,18 @@ class ApplicationConfig {
private string userConfigFilePath = "";
// - Store the system 'config' file path
private string systemConfigFilePath = "";
// - What is the 'config' file path that will be used?
private string applicableConfigFilePath = "";
// - Store the 'sync_list' file path
string syncListFilePath = "";
// - Store the 'business_shared_items' file path
string businessSharedItemsFilePath = "";
// - What is the 'config' file path that will be used?
private string applicableConfigFilePath = "";
// Hash files so that we can detect when the configuration has changed, in items that will require a --resync
private string configHashFile = "";
private string configBackupFile = "";
private string syncListHashFile = "";
private string businessSharedItemsHashFile = "";
// hash file permission values (set via initialize function)
private int convertedPermissionValue;
// Store the actual 'runtime' hash
private string currentConfigHash = "";
@ -178,6 +176,10 @@ class ApplicationConfig {
private string configFileDriveId = ""; // Default here is that no drive id is specified
private bool configFileSkipDotfiles = false;
private bool configFileSkipSymbolicLinks = false;
private bool configFileSyncBusinessSharedItems = false;
// File permission values (set via initialize function)
private int convertedPermissionValue;
// Array of values that are the actual application runtime configuration
// The values stored in these array's are the actual application configuration which can then be accessed by getValue & setValue
@ -323,7 +325,7 @@ class ApplicationConfig {
// Print in debug the application version as soon as possible
//log.vdebug("Application Version: ", strip(import("version")));
string tempVersion = "v2.5.0-alpha-0" ~ " GitHub version: " ~ strip(import("version"));
string tempVersion = "v2.5.0-alpha-1" ~ " GitHub version: " ~ strip(import("version"));
log.vdebug("Application Version: ", tempVersion);
// EXPAND USERS HOME DIRECTORY
@ -448,9 +450,14 @@ class ApplicationConfig {
userConfigFilePath = buildNormalizedPath(configDirName ~ "/config");
// - What is the full path for the system 'config' file if it is required
systemConfigFilePath = buildNormalizedPath(systemConfigDirName ~ "/config");
// - What is the full path for the 'business_shared_items'
businessSharedItemsFilePath = buildNormalizedPath(configDirName ~ "/business_shared_items");
// To determine if any configuration items has changed, where a --resync would be required, we need to have a hash file for the following items
// - 'config.backup' file
// - applicable 'config' file
@ -707,6 +714,12 @@ class ApplicationConfig {
if (key == "skip_symlinks") {
configFileSkipSymbolicLinks = true;
}
// sync_business_shared_items tracking for change
if (key == "sync_business_shared_items") {
configFileSyncBusinessSharedItems = true;
}
} else {
auto pp = key in stringValues;
if (pp) {
@ -1084,12 +1097,6 @@ class ApplicationConfig {
"version",
"Print the version and exit",
&tmpBol,
"list-shared-folders",
"List OneDrive Business Shared Items",
&boolValues["list_business_shared_items"],
"sync-shared-folders",
"Sync OneDrive Business Shared Items",
&boolValues["sync_business_shared_items"],
"with-editing-perms",
"Create a read-write shareable link for an existing file on OneDrive when used with --create-share-link <file>",
&boolValues["with_editing_perms"]
@ -1119,7 +1126,7 @@ class ApplicationConfig {
// Display application version
//writeln("onedrive version = ", strip(import("version")));
string tempVersion = "v2.5.0-alpha-0" ~ " GitHub version: " ~ strip(import("version"));
string tempVersion = "v2.5.0-alpha-1" ~ " GitHub version: " ~ strip(import("version"));
writeln("onedrive version = ", tempVersion);
// Display all of the pertinent configuration options
@ -1188,7 +1195,7 @@ class ApplicationConfig {
writeln("Config option 'ip_protocol_version' = ", getValueLong("ip_protocol_version"));
// Is sync_list configured ?
writeln("Config option 'sync_root_files' = ", getValueBool("sync_root_files"));
writeln("\nConfig option 'sync_root_files' = ", getValueBool("sync_root_files"));
if (exists(syncListFilePath)){
writeln("Selective sync 'sync_list' configured = true");
@ -1206,9 +1213,10 @@ class ApplicationConfig {
}
// Is sync_business_shared_items enabled and configured ?
writeln("Config option 'sync_business_shared_items' = ", getValueBool("sync_business_shared_items"));
writeln("\nConfig option 'sync_business_shared_items' = ", getValueBool("sync_business_shared_items"));
if (exists(businessSharedItemsFilePath)){
writeln("Business Shared Items configured = true");
writeln("Selective Business Shared Items configured = true");
writeln("sync_business_shared_items contents:");
// Output the sync_business_shared_items contents
auto businessSharedItemsFileList = File(businessSharedItemsFilePath, "r");
@ -1218,11 +1226,13 @@ class ApplicationConfig {
writeln(line);
}
} else {
writeln("Business Shared Items configured = false");
writeln("Selective Business Shared Items configured = false");
}
// Are webhooks enabled?
writeln("Config option 'webhook_enabled' = ", getValueBool("webhook_enabled"));
writeln("\nConfig option 'webhook_enabled' = ", getValueBool("webhook_enabled"));
if (getValueBool("webhook_enabled")) {
writeln("Config option 'webhook_public_url' = ", getValueString("webhook_public_url"));
writeln("Config option 'webhook_listening_host' = ", getValueString("webhook_listening_host"));
@ -1292,13 +1302,14 @@ class ApplicationConfig {
// Configuration File Flags
bool configFileOptionsDifferent = false;
bool syncListFileDifferent = false;
bool businessSharedItemsFileDifferent = false;
bool syncDirDifferent = false;
bool skipFileDifferent = false;
bool skipDirDifferent = false;
bool skipDotFilesDifferent = false;
bool skipSymbolicLinksDifferent = false;
bool driveIdDifferent = false;
bool syncBusinessSharedItemsDifferent = false;
bool businessSharedItemsFileDifferent = false;
// Create the required initial hash files
createRequiredInitialConfigurationHashFiles();
@ -1334,6 +1345,7 @@ class ApplicationConfig {
// # skip_dir = ""
// # skip_dotfiles = ""
// # skip_symlinks = ""
// # sync_business_shared_items = ""
string[string] backupConfigStringValues;
backupConfigStringValues["drive_id"] = "";
backupConfigStringValues["sync_dir"] = "";
@ -1341,6 +1353,7 @@ class ApplicationConfig {
backupConfigStringValues["skip_dir"] = "";
backupConfigStringValues["skip_dotfiles"] = "";
backupConfigStringValues["skip_symlinks"] = "";
backupConfigStringValues["sync_business_shared_items"] = "";
// bool flags to trigger if the entries that trigger a --resync were found in the backup config file
// if these were not in the backup file, they may have been added ... thus new, thus we need to double check the existing
@ -1351,6 +1364,7 @@ class ApplicationConfig {
bool skip_dir_present = false;
bool skip_dotfiles_present = false;
bool skip_symlinks_present = false;
bool sync_business_shared_items_present = false;
// Common debug message if an element is different
string configOptionModifiedMessage = " was modified since the last time the application was successfully run, --resync required";
@ -1421,6 +1435,14 @@ class ApplicationConfig {
configFileOptionsDifferent = true;
}
}
if (key == "sync_business_shared_items") {
sync_business_shared_items_present = true;
if (c.front.dup != to!string(getValueBool("sync_business_shared_items"))) {
log.vdebug(key, configOptionModifiedMessage);
configFileOptionsDifferent = true;
}
}
}
}
}
@ -1433,12 +1455,13 @@ class ApplicationConfig {
// Were any of the items that trigger a --resync not in the existing backup 'config' file .. thus newly added?
if ((!drive_id_present) || (!sync_dir_present) || (! skip_file_present) || (!skip_dir_present) || (!skip_dotfiles_present) || (!skip_symlinks_present)) {
log.vdebug("drive_id present in config backup: ", drive_id_present);
log.vdebug("sync_dir present in config backup: ", sync_dir_present);
log.vdebug("skip_file present in config backup: ", skip_file_present);
log.vdebug("skip_dir present in config backup: ", skip_dir_present);
log.vdebug("skip_dotfiles present in config backup: ", skip_dotfiles_present);
log.vdebug("skip_symlinks present in config backup: ", skip_symlinks_present);
log.vdebug("drive_id present in config backup: ", drive_id_present);
log.vdebug("sync_dir present in config backup: ", sync_dir_present);
log.vdebug("skip_file present in config backup: ", skip_file_present);
log.vdebug("skip_dir present in config backup: ", skip_dir_present);
log.vdebug("skip_dotfiles present in config backup: ", skip_dotfiles_present);
log.vdebug("skip_symlinks present in config backup: ", skip_symlinks_present);
log.vdebug("sync_business_shared_items present in config backup: ", sync_business_shared_items_present);
if ((!drive_id_present) && (configFileDriveId != "")) {
writeln("drive_id newly added ... --resync needed");
@ -1475,6 +1498,12 @@ class ApplicationConfig {
configFileOptionsDifferent = true;
skipSymbolicLinksDifferent = true;
}
if ((!sync_business_shared_items_present) && (configFileSyncBusinessSharedItems)) {
writeln("sync_business_shared_items newly added ... --resync needed");
configFileOptionsDifferent = true;
syncBusinessSharedItemsDifferent = true;
}
}
} else {
// no backup to check
@ -1543,16 +1572,20 @@ class ApplicationConfig {
}
// Did any of the config files or CLI options trigger a --resync requirement?
log.vdebug("configFileOptionsDifferent: ", configFileOptionsDifferent);
log.vdebug("syncListFileDifferent: ", syncListFileDifferent);
log.vdebug("configFileOptionsDifferent: ", configFileOptionsDifferent);
// Options
log.vdebug("driveIdDifferent: ", driveIdDifferent);
log.vdebug("syncDirDifferent: ", syncDirDifferent);
log.vdebug("skipFileDifferent: ", skipFileDifferent);
log.vdebug("skipDirDifferent: ", skipDirDifferent);
log.vdebug("skipDotFilesDifferent: ", skipDotFilesDifferent);
log.vdebug("skipSymbolicLinksDifferent: ", skipSymbolicLinksDifferent);
log.vdebug("syncBusinessSharedItemsDifferent: ", syncBusinessSharedItemsDifferent);
// Files
log.vdebug("syncListFileDifferent: ", syncListFileDifferent);
log.vdebug("businessSharedItemsFileDifferent: ", businessSharedItemsFileDifferent);
log.vdebug("syncDirDifferent: ", syncDirDifferent);
log.vdebug("skipFileDifferent: ", skipFileDifferent);
log.vdebug("skipDirDifferent: ", skipDirDifferent);
log.vdebug("driveIdDifferent: ", driveIdDifferent);
log.vdebug("skipDotFilesDifferent: ", skipDotFilesDifferent);
if ((configFileOptionsDifferent) || (syncListFileDifferent) || (businessSharedItemsFileDifferent) || (syncDirDifferent) || (skipFileDifferent) || (skipDirDifferent) || (driveIdDifferent) || (skipDotFilesDifferent) || (skipSymbolicLinksDifferent) ) {
if ((configFileOptionsDifferent) || (syncListFileDifferent) || (businessSharedItemsFileDifferent) || (syncDirDifferent) || (skipFileDifferent) || (skipDirDifferent) || (driveIdDifferent) || (skipDotFilesDifferent) || (skipSymbolicLinksDifferent) || (syncBusinessSharedItemsDifferent) ) {
// set the flag
resyncRequired = true;
}
@ -1594,6 +1627,8 @@ class ApplicationConfig {
// Hash file should only be readable by the user who created it - 0600 permissions needed
syncListHashFile.setAttributes(convertedPermissionValue);
}
// Update 'update business_shared_items' files
if (exists(businessSharedItemsFilePath)) {
// update business_shared_folders hash
@ -1602,6 +1637,7 @@ class ApplicationConfig {
// Hash file should only be readable by the user who created it - 0600 permissions needed
businessSharedItemsHashFile.setAttributes(convertedPermissionValue);
}
} else {
// --dry-run scenario ... technically we should not be making any local file changes .......
log.log("DRY RUN: Not updating hash files as --dry-run has been used");

View file

@ -28,6 +28,7 @@ struct Item {
string driveId;
string id;
string name;
string remoteName;
ItemType type;
string eTag;
string cTag;
@ -43,11 +44,15 @@ struct Item {
// Construct an Item struct from a JSON driveItem
Item makeDatabaseItem(JSONValue driveItem) {
log.vdebug("Starting this function: ", getFunctionName!({}));
Item item = {
id: driveItem["id"].str,
name: "name" in driveItem ? driveItem["name"].str : null, // name may be missing for deleted files in OneDrive Biz
eTag: "eTag" in driveItem ? driveItem["eTag"].str : null, // eTag is not returned for the root in OneDrive Biz
cTag: "cTag" in driveItem ? driveItem["cTag"].str : null, // cTag is missing in old files (and all folders in OneDrive Biz)
name: "name" in driveItem ? driveItem["name"].str : null, // name may be missing for deleted files in OneDrive Business
eTag: "eTag" in driveItem ? driveItem["eTag"].str : null, // eTag is not returned for the root in OneDrive Business
cTag: "cTag" in driveItem ? driveItem["cTag"].str : null, // cTag is missing in old files (and all folders in OneDrive Business)
remoteName: "actualOnlineName" in driveItem ? driveItem["actualOnlineName"].str : null, // actualOnlineName is only used with OneDrive Business Shared Folders
};
// OneDrive API Change: https://github.com/OneDrive/onedrive-api-docs/issues/834
@ -164,6 +169,7 @@ final class ItemDatabase {
string insertItemStmt;
string updateItemStmt;
string selectItemByIdStmt;
string selectItemByRemoteIdStmt;
string selectItemByParentIdStmt;
string deleteItemByIdStmt;
bool databaseInitialised = false;
@ -205,7 +211,11 @@ final class ItemDatabase {
db.exec("PRAGMA recursive_triggers = TRUE");
// Set the journal mode for databases associated with the current connection
// https://www.sqlite.org/pragma.html#pragma_journal_mode
db.exec("PRAGMA journal_mode = WAL");
//db.exec("PRAGMA journal_mode = WAL");
// Automatic indexing is enabled by default as of version 3.7.17
// https://www.sqlite.org/pragma.html#pragma_automatic_index
// PRAGMA automatic_index = boolean;
@ -223,12 +233,12 @@ final class ItemDatabase {
db.exec("PRAGMA locking_mode = EXCLUSIVE");
insertItemStmt = "
INSERT OR REPLACE INTO item (driveId, id, name, type, eTag, cTag, mtime, parentId, quickXorHash, sha256Hash, remoteDriveId, remoteId, syncStatus, size)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)
INSERT OR REPLACE INTO item (driveId, id, name, remoteName, type, eTag, cTag, mtime, parentId, quickXorHash, sha256Hash, remoteDriveId, remoteId, syncStatus, size)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)
";
updateItemStmt = "
UPDATE item
SET name = ?3, type = ?4, eTag = ?5, cTag = ?6, mtime = ?7, parentId = ?8, quickXorHash = ?9, sha256Hash = ?10, remoteDriveId = ?11, remoteId = ?12, syncStatus = ?13, size = ?14
SET name = ?3, remoteName = ?4, type = ?5, eTag = ?6, cTag = ?7, mtime = ?8, parentId = ?9, quickXorHash = ?10, sha256Hash = ?11, remoteDriveId = ?12, remoteId = ?13, syncStatus = ?14, size = ?15
WHERE driveId = ?1 AND id = ?2
";
selectItemByIdStmt = "
@ -236,6 +246,11 @@ final class ItemDatabase {
FROM item
WHERE driveId = ?1 AND id = ?2
";
selectItemByRemoteIdStmt = "
SELECT *
FROM item
WHERE remoteDriveId = ?1 AND remoteId = ?2
";
selectItemByParentIdStmt = "SELECT * FROM item WHERE driveId = ? AND parentId = ?";
deleteItemByIdStmt = "DELETE FROM item WHERE driveId = ? AND id = ?";
@ -252,6 +267,7 @@ final class ItemDatabase {
driveId TEXT NOT NULL,
id TEXT NOT NULL,
name TEXT NOT NULL,
remoteName TEXT,
type TEXT NOT NULL,
eTag TEXT,
cTag TEXT,
@ -334,6 +350,18 @@ final class ItemDatabase {
return false;
}
bool selectByRemoteId(const(char)[] remoteDriveId, const(char)[] remoteId, out Item item) {
auto p = db.prepare(selectItemByRemoteIdStmt);
p.bind(1, remoteDriveId);
p.bind(2, remoteId);
auto r = p.exec();
if (!r.empty) {
item = buildItem(r);
return true;
}
return false;
}
// returns true if an item id is in the database
bool idInLocalDatabase(const(string) driveId, const(string)id) {
auto p = db.prepare(selectItemByIdStmt);
@ -420,6 +448,7 @@ final class ItemDatabase {
bind(1, driveId);
bind(2, id);
bind(3, name);
bind(4, remoteName);
string typeStr = null;
final switch (type) with (ItemType) {
case file: typeStr = "file"; break;
@ -427,41 +456,60 @@ final class ItemDatabase {
case remote: typeStr = "remote"; break;
case unknown: typeStr = "unknown"; break;
}
bind(4, typeStr);
bind(5, eTag);
bind(6, cTag);
bind(7, mtime.toISOExtString());
bind(8, parentId);
bind(9, quickXorHash);
bind(10, sha256Hash);
bind(11, remoteDriveId);
bind(12, remoteId);
bind(13, syncStatus);
bind(14, size);
bind(5, typeStr);
bind(6, eTag);
bind(7, cTag);
bind(8, mtime.toISOExtString());
bind(9, parentId);
bind(10, quickXorHash);
bind(11, sha256Hash);
bind(12, remoteDriveId);
bind(13, remoteId);
bind(14, syncStatus);
bind(15, size);
}
}
private Item buildItem(Statement.Result result) {
assert(!result.empty, "The result must not be empty");
assert(result.front.length == 15, "The result must have 15 columns");
assert(result.front.length == 16, "The result must have 16 columns");
Item item = {
// column 0: driveId
// column 1: id
// column 2: name
// column 3: remoteName - only used when there is a difference in the local name & remote shared folder name
// column 4: type
// column 5: eTag
// column 6: cTag
// column 7: mtime
// column 8: parentId
// column 9: quickXorHash
// column 10: sha256Hash
// column 11: remoteDriveId
// column 12: remoteId
// column 13: deltaLink
// column 14: syncStatus
// column 15: size
driveId: result.front[0].dup,
id: result.front[1].dup,
name: result.front[2].dup,
// Column 3 is type - not set here
eTag: result.front[4].dup,
cTag: result.front[5].dup,
mtime: SysTime.fromISOExtString(result.front[6]),
parentId: result.front[7].dup,
quickXorHash: result.front[8].dup,
sha256Hash: result.front[9].dup,
remoteDriveId: result.front[10].dup,
remoteId: result.front[11].dup,
// Column 12 is deltaLink - not set here
syncStatus: result.front[13].dup,
size: result.front[14].dup
remoteName: result.front[3].dup,
// Column 4 is type - not set here
eTag: result.front[5].dup,
cTag: result.front[6].dup,
mtime: SysTime.fromISOExtString(result.front[7]),
parentId: result.front[8].dup,
quickXorHash: result.front[9].dup,
sha256Hash: result.front[10].dup,
remoteDriveId: result.front[11].dup,
remoteId: result.front[12].dup,
// Column 13 is deltaLink - not set here
syncStatus: result.front[14].dup,
size: result.front[15].dup
};
switch (result.front[3]) {
switch (result.front[4]) {
case "file": item.type = ItemType.file; break;
case "dir": item.type = ItemType.dir; break;
case "remote": item.type = ItemType.remote; break;

View file

@ -112,7 +112,7 @@ int main(string[] cliArgs) {
// Print the version and exit
if (printVersion) {
//writeln("onedrive ", strip(import("version")));
string tempVersion = "v2.5.0-alpha-0" ~ " GitHub version: " ~ strip(import("version"));
string tempVersion = "v2.5.0-alpha-1" ~ " GitHub version: " ~ strip(import("version"));
writeln(tempVersion);
return EXIT_SUCCESS;
}

View file

@ -284,7 +284,7 @@ class OneDriveApi {
} else {
// Try and read the value from the appConfig if it is set, rather than trying to read the value from disk
if (!appConfig.refreshToken.empty) {
log.vdebug("read token from appConfig");
log.vdebug("Read token from appConfig");
refreshToken = strip(appConfig.refreshToken);
authorised = true;
} else {
@ -494,6 +494,12 @@ class OneDriveApi {
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_delta
JSONValue viewChangesByItemId(string driveId, string id, string deltaLink) {
checkAccessTokenExpired();
// If Business Account add addIncludeFeatureRequestHeader() which should add Prefer: Include-Feature=AddToOneDrive
if ((appConfig.accountType != "personal") && ( appConfig.getValueBool("sync_business_shared_items"))) {
addIncludeFeatureRequestHeader();
}
string url;
// configure deltaLink to query
if (deltaLink.empty) {
@ -507,6 +513,12 @@ class OneDriveApi {
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_list_children
JSONValue listChildren(string driveId, string id, string nextLink) {
checkAccessTokenExpired();
// If Business Account add addIncludeFeatureRequestHeader() which should add Prefer: Include-Feature=AddToOneDrive
if ((appConfig.accountType != "personal") && ( appConfig.getValueBool("sync_business_shared_items"))) {
addIncludeFeatureRequestHeader();
}
string url;
// configure URL to query
if (nextLink.empty) {
@ -677,6 +689,11 @@ class OneDriveApi {
curlEngine.http.addRequestHeader("Authorization", accessToken);
}
private void addIncludeFeatureRequestHeader() {
log.vdebug("Adding 'Include-Feature=AddToOneDrive' API request header as 'sync_business_shared_items' config option is enabled");
curlEngine.http.addRequestHeader("Prefer", "Include-Feature=AddToOneDrive");
}
private void acquireToken(char[] postData) {
JSONValue response;
@ -684,11 +701,16 @@ class OneDriveApi {
response = post(tokenUrl, postData);
} catch (OneDriveException e) {
// an error was generated
if (e.httpStatusCode >= 500) {
// There was a HTTP 5xx Server Side Error - retry
acquireToken(postData);
if ((e.httpStatusCode == 400) || (e.httpStatusCode == 401)) {
// Handle an unauthorised client
handleClientUnauthorised(e.httpStatusCode, e.msg);
} else {
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
if (e.httpStatusCode >= 500) {
// There was a HTTP 5xx Server Side Error - retry
acquireToken(postData);
} else {
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
module util;
// What does this module require to function?
import core.stdc.stdlib: EXIT_SUCCESS, EXIT_FAILURE, exit;
import std.base64;
import std.conv;
import std.digest.crc;
@ -341,7 +342,7 @@ bool containsASCIIHTMLCodes(string path) {
return m.empty;
}
/**
// Parse and display error message received from OneDrive
void displayOneDriveErrorMessage(string message, string callingFunction) {
writeln();
@ -412,85 +413,40 @@ void displayOneDriveErrorMessage(string message, string callingFunction) {
log.vdebug("Raw Error Data: ", message);
log.vdebug("JSON Message: ", errorMessage);
}
**/
// Alpha-0 Testing .....
void displayOneDriveErrorMessage(string message, string callingFunction) {
writeln();
log.log("ERROR: Microsoft OneDrive API returned an error with the following message:");
// Common code for handling when a client is unauthorised
void handleClientUnauthorised(int httpStatusCode, string message) {
// Split the lines of the error message
auto errorArray = splitLines(message);
log.log(" Error Message: ", errorArray[0]);
// Extract 'message' as the reason
JSONValue errorMessage = parseJSON(replace(message, errorArray[0], ""));
log.vdebug("errorMessage: ", errorMessage);
// What is the reason for the error
if (errorMessage.type() == JSONType.object) {
if (httpStatusCode == 400) {
// bad request or a new auth token is needed
// configure the error reason
string errorReason;
string requestDate;
string requestId;
// set the reason for the error
try {
// Use error_description as reason
errorReason = errorMessage["error_description"].str;
} catch (JSONException e) {
// we dont want to do anything here
}
// set the reason for the error
try {
// Use ["error"]["message"] as reason
errorReason = errorMessage["error"]["message"].str;
} catch (JSONException e) {
// we dont want to do anything here
}
// Display the error reason
if (errorReason.startsWith("<!DOCTYPE")) {
// a HTML Error Reason was given
log.log(" Error Reason: A HTML Error response was provided. Use debug logging (--verbose --verbose) to view this error");
log.log(errorReason);
} else {
// a non HTML Error Reason was given
log.log(" Error Reason: ", errorReason);
}
// Get the date of request if available
try {
// Use ["error"]["innerError"]["date"] as date
requestDate = errorMessage["error"]["innerError"]["date"].str;
} catch (JSONException e) {
// we dont want to do anything here
}
// Get the request-id if available
try {
// Use ["error"]["innerError"]["request-id"] as request-id
requestId = errorMessage["error"]["innerError"]["request-id"].str;
} catch (JSONException e) {
// we dont want to do anything here
}
// Display the date and request id if available
if (requestDate != "") log.error(" Error Timestamp: ", requestDate);
if (requestId != "") log.error(" API Request ID: ", requestId);
writeln();
string[] errorReason = splitLines(errorMessage["error_description"].str);
log.errorAndNotify(errorReason[0]);
writeln();
log.errorAndNotify("ERROR: You will need to issue a --reauth and re-authorise this client to obtain a fresh auth token.");
writeln();
}
// Where in the code was this error generated
log.log(" Calling Function: ", callingFunction);
if (httpStatusCode == 401) {
// Extra Debug if we are using --verbose --verbose
log.log("Raw Error Data: ", message);
log.log("JSON Message: ", errorMessage);
writeln("CODING TO DO: Triggered a 401 HTTP unauthorised response");
writeln();
log.errorAndNotify("ERROR: Check your configuration as your refresh_token may be empty or invalid. You may need to issue a --reauth and re-authorise this client.");
writeln();
}
// Must exit here
exit(EXIT_FAILURE);
}
// Parse and display error message received from the local file system
void displayFileSystemErrorMessage(string message, string callingFunction) {
writeln();
@ -504,7 +460,7 @@ void displayFileSystemErrorMessage(string message, string callingFunction) {
ulong localActualFreeSpace = to!ulong(getAvailableDiskSpace("."));
if (localActualFreeSpace == 0) {
// force exit
exit(-1);
exit(EXIT_FAILURE);
}
}
@ -820,4 +776,8 @@ bool hasLocalPath(const ref JSONValue item) {
bool hasETag(const ref JSONValue item) {
return ("eTag" in item) != null;
}
bool hasSharedElement(const ref JSONValue item) {
return ("eTag" in item) != null;
}