mirror of
https://github.com/abraunegg/onedrive
synced 2024-06-26 17:30:25 +02:00
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:
parent
3502e0cee4
commit
4bd9ae5092
|
@ -41,7 +41,7 @@ class ClientSideFiltering {
|
||||||
|
|
||||||
// Load the Business Shared Items file if it exists
|
// Load the Business Shared Items file if it exists
|
||||||
if (exists(appConfig.businessSharedItemsFilePath)){
|
if (exists(appConfig.businessSharedItemsFilePath)){
|
||||||
loadSyncList(appConfig.businessSharedItemsFilePath);
|
loadBusinessSharedItems(appConfig.businessSharedItemsFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure skip_dir, skip_file, skip-dir-strict-match & skip_dotfiles from config entries
|
// Configure skip_dir, skip_file, skip-dir-strict-match & skip_dotfiles from config entries
|
||||||
|
|
102
src/config.d
102
src/config.d
|
@ -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
|
// - 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 ~ "/" ~ 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()
|
// HTTP Struct items, used for configuring HTTP()
|
||||||
// Curl Timeout Handling
|
// Curl Timeout Handling
|
||||||
|
@ -146,20 +146,18 @@ class ApplicationConfig {
|
||||||
private string userConfigFilePath = "";
|
private string userConfigFilePath = "";
|
||||||
// - Store the system 'config' file path
|
// - Store the system 'config' file path
|
||||||
private string systemConfigFilePath = "";
|
private string systemConfigFilePath = "";
|
||||||
|
// - What is the 'config' file path that will be used?
|
||||||
|
private string applicableConfigFilePath = "";
|
||||||
// - Store the 'sync_list' file path
|
// - Store the 'sync_list' file path
|
||||||
string syncListFilePath = "";
|
string syncListFilePath = "";
|
||||||
// - Store the 'business_shared_items' file path
|
// - Store the 'business_shared_items' file path
|
||||||
string businessSharedItemsFilePath = "";
|
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
|
// Hash files so that we can detect when the configuration has changed, in items that will require a --resync
|
||||||
private string configHashFile = "";
|
private string configHashFile = "";
|
||||||
private string configBackupFile = "";
|
private string configBackupFile = "";
|
||||||
private string syncListHashFile = "";
|
private string syncListHashFile = "";
|
||||||
private string businessSharedItemsHashFile = "";
|
private string businessSharedItemsHashFile = "";
|
||||||
// hash file permission values (set via initialize function)
|
|
||||||
private int convertedPermissionValue;
|
|
||||||
|
|
||||||
// Store the actual 'runtime' hash
|
// Store the actual 'runtime' hash
|
||||||
private string currentConfigHash = "";
|
private string currentConfigHash = "";
|
||||||
|
@ -178,6 +176,10 @@ class ApplicationConfig {
|
||||||
private string configFileDriveId = ""; // Default here is that no drive id is specified
|
private string configFileDriveId = ""; // Default here is that no drive id is specified
|
||||||
private bool configFileSkipDotfiles = false;
|
private bool configFileSkipDotfiles = false;
|
||||||
private bool configFileSkipSymbolicLinks = 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
|
// 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
|
// 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
|
// Print in debug the application version as soon as possible
|
||||||
//log.vdebug("Application Version: ", strip(import("version")));
|
//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);
|
log.vdebug("Application Version: ", tempVersion);
|
||||||
|
|
||||||
// EXPAND USERS HOME DIRECTORY
|
// EXPAND USERS HOME DIRECTORY
|
||||||
|
@ -448,9 +450,14 @@ class ApplicationConfig {
|
||||||
userConfigFilePath = buildNormalizedPath(configDirName ~ "/config");
|
userConfigFilePath = buildNormalizedPath(configDirName ~ "/config");
|
||||||
// - What is the full path for the system 'config' file if it is required
|
// - What is the full path for the system 'config' file if it is required
|
||||||
systemConfigFilePath = buildNormalizedPath(systemConfigDirName ~ "/config");
|
systemConfigFilePath = buildNormalizedPath(systemConfigDirName ~ "/config");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// - What is the full path for the 'business_shared_items'
|
// - What is the full path for the 'business_shared_items'
|
||||||
businessSharedItemsFilePath = buildNormalizedPath(configDirName ~ "/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
|
// 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
|
// - 'config.backup' file
|
||||||
// - applicable 'config' file
|
// - applicable 'config' file
|
||||||
|
@ -707,6 +714,12 @@ class ApplicationConfig {
|
||||||
if (key == "skip_symlinks") {
|
if (key == "skip_symlinks") {
|
||||||
configFileSkipSymbolicLinks = true;
|
configFileSkipSymbolicLinks = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sync_business_shared_items tracking for change
|
||||||
|
if (key == "sync_business_shared_items") {
|
||||||
|
configFileSyncBusinessSharedItems = true;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto pp = key in stringValues;
|
auto pp = key in stringValues;
|
||||||
if (pp) {
|
if (pp) {
|
||||||
|
@ -1084,12 +1097,6 @@ class ApplicationConfig {
|
||||||
"version",
|
"version",
|
||||||
"Print the version and exit",
|
"Print the version and exit",
|
||||||
&tmpBol,
|
&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",
|
"with-editing-perms",
|
||||||
"Create a read-write shareable link for an existing file on OneDrive when used with --create-share-link <file>",
|
"Create a read-write shareable link for an existing file on OneDrive when used with --create-share-link <file>",
|
||||||
&boolValues["with_editing_perms"]
|
&boolValues["with_editing_perms"]
|
||||||
|
@ -1119,7 +1126,7 @@ class ApplicationConfig {
|
||||||
// Display application version
|
// Display application version
|
||||||
//writeln("onedrive version = ", strip(import("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);
|
writeln("onedrive version = ", tempVersion);
|
||||||
|
|
||||||
// Display all of the pertinent configuration options
|
// Display all of the pertinent configuration options
|
||||||
|
@ -1188,7 +1195,7 @@ class ApplicationConfig {
|
||||||
writeln("Config option 'ip_protocol_version' = ", getValueLong("ip_protocol_version"));
|
writeln("Config option 'ip_protocol_version' = ", getValueLong("ip_protocol_version"));
|
||||||
|
|
||||||
// Is sync_list configured ?
|
// 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)){
|
if (exists(syncListFilePath)){
|
||||||
|
|
||||||
writeln("Selective sync 'sync_list' configured = true");
|
writeln("Selective sync 'sync_list' configured = true");
|
||||||
|
@ -1206,9 +1213,10 @@ class ApplicationConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is sync_business_shared_items enabled and configured ?
|
// 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)){
|
if (exists(businessSharedItemsFilePath)){
|
||||||
writeln("Business Shared Items configured = true");
|
writeln("Selective Business Shared Items configured = true");
|
||||||
writeln("sync_business_shared_items contents:");
|
writeln("sync_business_shared_items contents:");
|
||||||
// Output the sync_business_shared_items contents
|
// Output the sync_business_shared_items contents
|
||||||
auto businessSharedItemsFileList = File(businessSharedItemsFilePath, "r");
|
auto businessSharedItemsFileList = File(businessSharedItemsFilePath, "r");
|
||||||
|
@ -1218,11 +1226,13 @@ class ApplicationConfig {
|
||||||
writeln(line);
|
writeln(line);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
writeln("Business Shared Items configured = false");
|
writeln("Selective Business Shared Items configured = false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Are webhooks enabled?
|
// Are webhooks enabled?
|
||||||
writeln("Config option 'webhook_enabled' = ", getValueBool("webhook_enabled"));
|
writeln("\nConfig option 'webhook_enabled' = ", getValueBool("webhook_enabled"));
|
||||||
if (getValueBool("webhook_enabled")) {
|
if (getValueBool("webhook_enabled")) {
|
||||||
writeln("Config option 'webhook_public_url' = ", getValueString("webhook_public_url"));
|
writeln("Config option 'webhook_public_url' = ", getValueString("webhook_public_url"));
|
||||||
writeln("Config option 'webhook_listening_host' = ", getValueString("webhook_listening_host"));
|
writeln("Config option 'webhook_listening_host' = ", getValueString("webhook_listening_host"));
|
||||||
|
@ -1292,13 +1302,14 @@ class ApplicationConfig {
|
||||||
// Configuration File Flags
|
// Configuration File Flags
|
||||||
bool configFileOptionsDifferent = false;
|
bool configFileOptionsDifferent = false;
|
||||||
bool syncListFileDifferent = false;
|
bool syncListFileDifferent = false;
|
||||||
bool businessSharedItemsFileDifferent = false;
|
|
||||||
bool syncDirDifferent = false;
|
bool syncDirDifferent = false;
|
||||||
bool skipFileDifferent = false;
|
bool skipFileDifferent = false;
|
||||||
bool skipDirDifferent = false;
|
bool skipDirDifferent = false;
|
||||||
bool skipDotFilesDifferent = false;
|
bool skipDotFilesDifferent = false;
|
||||||
bool skipSymbolicLinksDifferent = false;
|
bool skipSymbolicLinksDifferent = false;
|
||||||
bool driveIdDifferent = false;
|
bool driveIdDifferent = false;
|
||||||
|
bool syncBusinessSharedItemsDifferent = false;
|
||||||
|
bool businessSharedItemsFileDifferent = false;
|
||||||
|
|
||||||
// Create the required initial hash files
|
// Create the required initial hash files
|
||||||
createRequiredInitialConfigurationHashFiles();
|
createRequiredInitialConfigurationHashFiles();
|
||||||
|
@ -1334,6 +1345,7 @@ class ApplicationConfig {
|
||||||
// # skip_dir = ""
|
// # skip_dir = ""
|
||||||
// # skip_dotfiles = ""
|
// # skip_dotfiles = ""
|
||||||
// # skip_symlinks = ""
|
// # skip_symlinks = ""
|
||||||
|
// # sync_business_shared_items = ""
|
||||||
string[string] backupConfigStringValues;
|
string[string] backupConfigStringValues;
|
||||||
backupConfigStringValues["drive_id"] = "";
|
backupConfigStringValues["drive_id"] = "";
|
||||||
backupConfigStringValues["sync_dir"] = "";
|
backupConfigStringValues["sync_dir"] = "";
|
||||||
|
@ -1341,6 +1353,7 @@ class ApplicationConfig {
|
||||||
backupConfigStringValues["skip_dir"] = "";
|
backupConfigStringValues["skip_dir"] = "";
|
||||||
backupConfigStringValues["skip_dotfiles"] = "";
|
backupConfigStringValues["skip_dotfiles"] = "";
|
||||||
backupConfigStringValues["skip_symlinks"] = "";
|
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
|
// 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
|
// 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_dir_present = false;
|
||||||
bool skip_dotfiles_present = false;
|
bool skip_dotfiles_present = false;
|
||||||
bool skip_symlinks_present = false;
|
bool skip_symlinks_present = false;
|
||||||
|
bool sync_business_shared_items_present = false;
|
||||||
|
|
||||||
// Common debug message if an element is different
|
// Common debug message if an element is different
|
||||||
string configOptionModifiedMessage = " was modified since the last time the application was successfully run, --resync required";
|
string configOptionModifiedMessage = " was modified since the last time the application was successfully run, --resync required";
|
||||||
|
@ -1421,6 +1435,14 @@ class ApplicationConfig {
|
||||||
configFileOptionsDifferent = true;
|
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?
|
// 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)) {
|
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("drive_id present in config backup: ", drive_id_present);
|
||||||
log.vdebug("sync_dir present in config backup: ", sync_dir_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_file present in config backup: ", skip_file_present);
|
||||||
log.vdebug("skip_dir present in config backup: ", skip_dir_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_dotfiles present in config backup: ", skip_dotfiles_present);
|
||||||
log.vdebug("skip_symlinks present in config backup: ", skip_symlinks_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 != "")) {
|
if ((!drive_id_present) && (configFileDriveId != "")) {
|
||||||
writeln("drive_id newly added ... --resync needed");
|
writeln("drive_id newly added ... --resync needed");
|
||||||
|
@ -1475,6 +1498,12 @@ class ApplicationConfig {
|
||||||
configFileOptionsDifferent = true;
|
configFileOptionsDifferent = true;
|
||||||
skipSymbolicLinksDifferent = true;
|
skipSymbolicLinksDifferent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((!sync_business_shared_items_present) && (configFileSyncBusinessSharedItems)) {
|
||||||
|
writeln("sync_business_shared_items newly added ... --resync needed");
|
||||||
|
configFileOptionsDifferent = true;
|
||||||
|
syncBusinessSharedItemsDifferent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// no backup to check
|
// no backup to check
|
||||||
|
@ -1543,16 +1572,20 @@ class ApplicationConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Did any of the config files or CLI options trigger a --resync requirement?
|
// Did any of the config files or CLI options trigger a --resync requirement?
|
||||||
log.vdebug("configFileOptionsDifferent: ", configFileOptionsDifferent);
|
log.vdebug("configFileOptionsDifferent: ", configFileOptionsDifferent);
|
||||||
log.vdebug("syncListFileDifferent: ", syncListFileDifferent);
|
// 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("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
|
// set the flag
|
||||||
resyncRequired = true;
|
resyncRequired = true;
|
||||||
}
|
}
|
||||||
|
@ -1594,6 +1627,8 @@ class ApplicationConfig {
|
||||||
// Hash file should only be readable by the user who created it - 0600 permissions needed
|
// Hash file should only be readable by the user who created it - 0600 permissions needed
|
||||||
syncListHashFile.setAttributes(convertedPermissionValue);
|
syncListHashFile.setAttributes(convertedPermissionValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update 'update business_shared_items' files
|
// Update 'update business_shared_items' files
|
||||||
if (exists(businessSharedItemsFilePath)) {
|
if (exists(businessSharedItemsFilePath)) {
|
||||||
// update business_shared_folders hash
|
// 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
|
// Hash file should only be readable by the user who created it - 0600 permissions needed
|
||||||
businessSharedItemsHashFile.setAttributes(convertedPermissionValue);
|
businessSharedItemsHashFile.setAttributes(convertedPermissionValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// --dry-run scenario ... technically we should not be making any local file changes .......
|
// --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");
|
log.log("DRY RUN: Not updating hash files as --dry-run has been used");
|
||||||
|
|
112
src/itemdb.d
112
src/itemdb.d
|
@ -28,6 +28,7 @@ struct Item {
|
||||||
string driveId;
|
string driveId;
|
||||||
string id;
|
string id;
|
||||||
string name;
|
string name;
|
||||||
|
string remoteName;
|
||||||
ItemType type;
|
ItemType type;
|
||||||
string eTag;
|
string eTag;
|
||||||
string cTag;
|
string cTag;
|
||||||
|
@ -43,11 +44,15 @@ struct Item {
|
||||||
|
|
||||||
// Construct an Item struct from a JSON driveItem
|
// Construct an Item struct from a JSON driveItem
|
||||||
Item makeDatabaseItem(JSONValue driveItem) {
|
Item makeDatabaseItem(JSONValue driveItem) {
|
||||||
|
|
||||||
|
log.vdebug("Starting this function: ", getFunctionName!({}));
|
||||||
|
|
||||||
Item item = {
|
Item item = {
|
||||||
id: driveItem["id"].str,
|
id: driveItem["id"].str,
|
||||||
name: "name" in driveItem ? driveItem["name"].str : null, // name may be missing for deleted files 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 Biz
|
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 Biz)
|
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
|
// OneDrive API Change: https://github.com/OneDrive/onedrive-api-docs/issues/834
|
||||||
|
@ -164,6 +169,7 @@ final class ItemDatabase {
|
||||||
string insertItemStmt;
|
string insertItemStmt;
|
||||||
string updateItemStmt;
|
string updateItemStmt;
|
||||||
string selectItemByIdStmt;
|
string selectItemByIdStmt;
|
||||||
|
string selectItemByRemoteIdStmt;
|
||||||
string selectItemByParentIdStmt;
|
string selectItemByParentIdStmt;
|
||||||
string deleteItemByIdStmt;
|
string deleteItemByIdStmt;
|
||||||
bool databaseInitialised = false;
|
bool databaseInitialised = false;
|
||||||
|
@ -205,7 +211,11 @@ final class ItemDatabase {
|
||||||
db.exec("PRAGMA recursive_triggers = TRUE");
|
db.exec("PRAGMA recursive_triggers = TRUE");
|
||||||
// Set the journal mode for databases associated with the current connection
|
// Set the journal mode for databases associated with the current connection
|
||||||
// https://www.sqlite.org/pragma.html#pragma_journal_mode
|
// 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
|
// Automatic indexing is enabled by default as of version 3.7.17
|
||||||
// https://www.sqlite.org/pragma.html#pragma_automatic_index
|
// https://www.sqlite.org/pragma.html#pragma_automatic_index
|
||||||
// PRAGMA automatic_index = boolean;
|
// PRAGMA automatic_index = boolean;
|
||||||
|
@ -223,12 +233,12 @@ final class ItemDatabase {
|
||||||
db.exec("PRAGMA locking_mode = EXCLUSIVE");
|
db.exec("PRAGMA locking_mode = EXCLUSIVE");
|
||||||
|
|
||||||
insertItemStmt = "
|
insertItemStmt = "
|
||||||
INSERT OR REPLACE INTO item (driveId, id, name, type, eTag, cTag, mtime, parentId, quickXorHash, sha256Hash, remoteDriveId, remoteId, syncStatus, size)
|
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)
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)
|
||||||
";
|
";
|
||||||
updateItemStmt = "
|
updateItemStmt = "
|
||||||
UPDATE item
|
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
|
WHERE driveId = ?1 AND id = ?2
|
||||||
";
|
";
|
||||||
selectItemByIdStmt = "
|
selectItemByIdStmt = "
|
||||||
|
@ -236,6 +246,11 @@ final class ItemDatabase {
|
||||||
FROM item
|
FROM item
|
||||||
WHERE driveId = ?1 AND id = ?2
|
WHERE driveId = ?1 AND id = ?2
|
||||||
";
|
";
|
||||||
|
selectItemByRemoteIdStmt = "
|
||||||
|
SELECT *
|
||||||
|
FROM item
|
||||||
|
WHERE remoteDriveId = ?1 AND remoteId = ?2
|
||||||
|
";
|
||||||
selectItemByParentIdStmt = "SELECT * FROM item WHERE driveId = ? AND parentId = ?";
|
selectItemByParentIdStmt = "SELECT * FROM item WHERE driveId = ? AND parentId = ?";
|
||||||
deleteItemByIdStmt = "DELETE FROM item WHERE driveId = ? AND id = ?";
|
deleteItemByIdStmt = "DELETE FROM item WHERE driveId = ? AND id = ?";
|
||||||
|
|
||||||
|
@ -252,6 +267,7 @@ final class ItemDatabase {
|
||||||
driveId TEXT NOT NULL,
|
driveId TEXT NOT NULL,
|
||||||
id TEXT NOT NULL,
|
id TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
|
remoteName TEXT,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
eTag TEXT,
|
eTag TEXT,
|
||||||
cTag TEXT,
|
cTag TEXT,
|
||||||
|
@ -334,6 +350,18 @@ final class ItemDatabase {
|
||||||
return false;
|
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
|
// returns true if an item id is in the database
|
||||||
bool idInLocalDatabase(const(string) driveId, const(string)id) {
|
bool idInLocalDatabase(const(string) driveId, const(string)id) {
|
||||||
auto p = db.prepare(selectItemByIdStmt);
|
auto p = db.prepare(selectItemByIdStmt);
|
||||||
|
@ -420,6 +448,7 @@ final class ItemDatabase {
|
||||||
bind(1, driveId);
|
bind(1, driveId);
|
||||||
bind(2, id);
|
bind(2, id);
|
||||||
bind(3, name);
|
bind(3, name);
|
||||||
|
bind(4, remoteName);
|
||||||
string typeStr = null;
|
string typeStr = null;
|
||||||
final switch (type) with (ItemType) {
|
final switch (type) with (ItemType) {
|
||||||
case file: typeStr = "file"; break;
|
case file: typeStr = "file"; break;
|
||||||
|
@ -427,41 +456,60 @@ final class ItemDatabase {
|
||||||
case remote: typeStr = "remote"; break;
|
case remote: typeStr = "remote"; break;
|
||||||
case unknown: typeStr = "unknown"; break;
|
case unknown: typeStr = "unknown"; break;
|
||||||
}
|
}
|
||||||
bind(4, typeStr);
|
bind(5, typeStr);
|
||||||
bind(5, eTag);
|
bind(6, eTag);
|
||||||
bind(6, cTag);
|
bind(7, cTag);
|
||||||
bind(7, mtime.toISOExtString());
|
bind(8, mtime.toISOExtString());
|
||||||
bind(8, parentId);
|
bind(9, parentId);
|
||||||
bind(9, quickXorHash);
|
bind(10, quickXorHash);
|
||||||
bind(10, sha256Hash);
|
bind(11, sha256Hash);
|
||||||
bind(11, remoteDriveId);
|
bind(12, remoteDriveId);
|
||||||
bind(12, remoteId);
|
bind(13, remoteId);
|
||||||
bind(13, syncStatus);
|
bind(14, syncStatus);
|
||||||
bind(14, size);
|
bind(15, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Item buildItem(Statement.Result result) {
|
private Item buildItem(Statement.Result result) {
|
||||||
assert(!result.empty, "The result must not be empty");
|
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 = {
|
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,
|
driveId: result.front[0].dup,
|
||||||
id: result.front[1].dup,
|
id: result.front[1].dup,
|
||||||
name: result.front[2].dup,
|
name: result.front[2].dup,
|
||||||
// Column 3 is type - not set here
|
remoteName: result.front[3].dup,
|
||||||
eTag: result.front[4].dup,
|
// Column 4 is type - not set here
|
||||||
cTag: result.front[5].dup,
|
eTag: result.front[5].dup,
|
||||||
mtime: SysTime.fromISOExtString(result.front[6]),
|
cTag: result.front[6].dup,
|
||||||
parentId: result.front[7].dup,
|
mtime: SysTime.fromISOExtString(result.front[7]),
|
||||||
quickXorHash: result.front[8].dup,
|
parentId: result.front[8].dup,
|
||||||
sha256Hash: result.front[9].dup,
|
quickXorHash: result.front[9].dup,
|
||||||
remoteDriveId: result.front[10].dup,
|
sha256Hash: result.front[10].dup,
|
||||||
remoteId: result.front[11].dup,
|
remoteDriveId: result.front[11].dup,
|
||||||
// Column 12 is deltaLink - not set here
|
remoteId: result.front[12].dup,
|
||||||
syncStatus: result.front[13].dup,
|
// Column 13 is deltaLink - not set here
|
||||||
size: result.front[14].dup
|
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 "file": item.type = ItemType.file; break;
|
||||||
case "dir": item.type = ItemType.dir; break;
|
case "dir": item.type = ItemType.dir; break;
|
||||||
case "remote": item.type = ItemType.remote; break;
|
case "remote": item.type = ItemType.remote; break;
|
||||||
|
|
|
@ -112,7 +112,7 @@ int main(string[] cliArgs) {
|
||||||
// Print the version and exit
|
// Print the version and exit
|
||||||
if (printVersion) {
|
if (printVersion) {
|
||||||
//writeln("onedrive ", strip(import("version")));
|
//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);
|
writeln(tempVersion);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,7 +284,7 @@ class OneDriveApi {
|
||||||
} else {
|
} else {
|
||||||
// Try and read the value from the appConfig if it is set, rather than trying to read the value from disk
|
// 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) {
|
if (!appConfig.refreshToken.empty) {
|
||||||
log.vdebug("read token from appConfig");
|
log.vdebug("Read token from appConfig");
|
||||||
refreshToken = strip(appConfig.refreshToken);
|
refreshToken = strip(appConfig.refreshToken);
|
||||||
authorised = true;
|
authorised = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -494,6 +494,12 @@ class OneDriveApi {
|
||||||
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_delta
|
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_delta
|
||||||
JSONValue viewChangesByItemId(string driveId, string id, string deltaLink) {
|
JSONValue viewChangesByItemId(string driveId, string id, string deltaLink) {
|
||||||
checkAccessTokenExpired();
|
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;
|
string url;
|
||||||
// configure deltaLink to query
|
// configure deltaLink to query
|
||||||
if (deltaLink.empty) {
|
if (deltaLink.empty) {
|
||||||
|
@ -507,6 +513,12 @@ class OneDriveApi {
|
||||||
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_list_children
|
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_list_children
|
||||||
JSONValue listChildren(string driveId, string id, string nextLink) {
|
JSONValue listChildren(string driveId, string id, string nextLink) {
|
||||||
checkAccessTokenExpired();
|
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;
|
string url;
|
||||||
// configure URL to query
|
// configure URL to query
|
||||||
if (nextLink.empty) {
|
if (nextLink.empty) {
|
||||||
|
@ -677,6 +689,11 @@ class OneDriveApi {
|
||||||
curlEngine.http.addRequestHeader("Authorization", accessToken);
|
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) {
|
private void acquireToken(char[] postData) {
|
||||||
JSONValue response;
|
JSONValue response;
|
||||||
|
|
||||||
|
@ -684,11 +701,16 @@ class OneDriveApi {
|
||||||
response = post(tokenUrl, postData);
|
response = post(tokenUrl, postData);
|
||||||
} catch (OneDriveException e) {
|
} catch (OneDriveException e) {
|
||||||
// an error was generated
|
// an error was generated
|
||||||
if (e.httpStatusCode >= 500) {
|
if ((e.httpStatusCode == 400) || (e.httpStatusCode == 401)) {
|
||||||
// There was a HTTP 5xx Server Side Error - retry
|
// Handle an unauthorised client
|
||||||
acquireToken(postData);
|
handleClientUnauthorised(e.httpStatusCode, e.msg);
|
||||||
} else {
|
} 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!({}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
483
src/sync.d
483
src/sync.d
File diff suppressed because it is too large
Load diff
100
src/util.d
100
src/util.d
|
@ -2,6 +2,7 @@
|
||||||
module util;
|
module util;
|
||||||
|
|
||||||
// What does this module require to function?
|
// What does this module require to function?
|
||||||
|
import core.stdc.stdlib: EXIT_SUCCESS, EXIT_FAILURE, exit;
|
||||||
import std.base64;
|
import std.base64;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
import std.digest.crc;
|
import std.digest.crc;
|
||||||
|
@ -341,7 +342,7 @@ bool containsASCIIHTMLCodes(string path) {
|
||||||
return m.empty;
|
return m.empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
// Parse and display error message received from OneDrive
|
// Parse and display error message received from OneDrive
|
||||||
void displayOneDriveErrorMessage(string message, string callingFunction) {
|
void displayOneDriveErrorMessage(string message, string callingFunction) {
|
||||||
writeln();
|
writeln();
|
||||||
|
@ -412,85 +413,40 @@ void displayOneDriveErrorMessage(string message, string callingFunction) {
|
||||||
log.vdebug("Raw Error Data: ", message);
|
log.vdebug("Raw Error Data: ", message);
|
||||||
log.vdebug("JSON Message: ", errorMessage);
|
log.vdebug("JSON Message: ", errorMessage);
|
||||||
}
|
}
|
||||||
**/
|
|
||||||
|
|
||||||
// Alpha-0 Testing .....
|
// Common code for handling when a client is unauthorised
|
||||||
void displayOneDriveErrorMessage(string message, string callingFunction) {
|
void handleClientUnauthorised(int httpStatusCode, string message) {
|
||||||
writeln();
|
// Split the lines of the error message
|
||||||
log.log("ERROR: Microsoft OneDrive API returned an error with the following message:");
|
|
||||||
auto errorArray = splitLines(message);
|
auto errorArray = splitLines(message);
|
||||||
log.log(" Error Message: ", errorArray[0]);
|
|
||||||
// Extract 'message' as the reason
|
// Extract 'message' as the reason
|
||||||
JSONValue errorMessage = parseJSON(replace(message, errorArray[0], ""));
|
JSONValue errorMessage = parseJSON(replace(message, errorArray[0], ""));
|
||||||
|
log.vdebug("errorMessage: ", errorMessage);
|
||||||
|
|
||||||
// What is the reason for the error
|
if (httpStatusCode == 400) {
|
||||||
if (errorMessage.type() == JSONType.object) {
|
// bad request or a new auth token is needed
|
||||||
// configure the error reason
|
// configure the error reason
|
||||||
string errorReason;
|
writeln();
|
||||||
string requestDate;
|
string[] errorReason = splitLines(errorMessage["error_description"].str);
|
||||||
string requestId;
|
log.errorAndNotify(errorReason[0]);
|
||||||
|
writeln();
|
||||||
// set the reason for the error
|
log.errorAndNotify("ERROR: You will need to issue a --reauth and re-authorise this client to obtain a fresh auth token.");
|
||||||
try {
|
writeln();
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where in the code was this error generated
|
if (httpStatusCode == 401) {
|
||||||
log.log(" Calling Function: ", callingFunction);
|
|
||||||
|
|
||||||
// Extra Debug if we are using --verbose --verbose
|
writeln("CODING TO DO: Triggered a 401 HTTP unauthorised response");
|
||||||
log.log("Raw Error Data: ", message);
|
|
||||||
log.log("JSON Message: ", errorMessage);
|
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
|
// Parse and display error message received from the local file system
|
||||||
void displayFileSystemErrorMessage(string message, string callingFunction) {
|
void displayFileSystemErrorMessage(string message, string callingFunction) {
|
||||||
writeln();
|
writeln();
|
||||||
|
@ -504,7 +460,7 @@ void displayFileSystemErrorMessage(string message, string callingFunction) {
|
||||||
ulong localActualFreeSpace = to!ulong(getAvailableDiskSpace("."));
|
ulong localActualFreeSpace = to!ulong(getAvailableDiskSpace("."));
|
||||||
if (localActualFreeSpace == 0) {
|
if (localActualFreeSpace == 0) {
|
||||||
// force exit
|
// force exit
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,4 +776,8 @@ bool hasLocalPath(const ref JSONValue item) {
|
||||||
|
|
||||||
bool hasETag(const ref JSONValue item) {
|
bool hasETag(const ref JSONValue item) {
|
||||||
return ("eTag" in item) != null;
|
return ("eTag" in item) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasSharedElement(const ref JSONValue item) {
|
||||||
|
return ("eTag" in item) != null;
|
||||||
}
|
}
|
Loading…
Reference in a new issue