mirror of
https://github.com/abraunegg/onedrive
synced 2024-05-22 15:46:36 +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
|
||||
if (exists(appConfig.businessSharedItemsFilePath)){
|
||||
loadSyncList(appConfig.businessSharedItemsFilePath);
|
||||
loadBusinessSharedItems(appConfig.businessSharedItemsFilePath);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
//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");
|
||||
|
|
112
src/itemdb.d
112
src/itemdb.d
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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!({}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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;
|
||||
|
||||
// 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;
|
||||
}
|
Loading…
Reference in a new issue