mirror of
https://github.com/abraunegg/onedrive
synced 2024-06-15 20:25:18 +02:00
Initial commit for #1300
* Remove legacy & unused items for Shared Business Items * Add option: --list-shared-items * Add option: --sync-shared-files
This commit is contained in:
parent
e895a1174c
commit
4fd7b88173
|
@ -20,7 +20,6 @@ class ClientSideFiltering {
|
|||
// Class variables
|
||||
ApplicationConfig appConfig;
|
||||
string[] paths;
|
||||
string[] businessSharedItemsList;
|
||||
Regex!char fileMask;
|
||||
Regex!char directoryMask;
|
||||
bool skipDirStrictMatch = false;
|
||||
|
@ -41,11 +40,6 @@ class ClientSideFiltering {
|
|||
loadSyncList(appConfig.syncListFilePath);
|
||||
}
|
||||
|
||||
// Load the Business Shared Items file if it exists
|
||||
if (exists(appConfig.businessSharedItemsFilePath)){
|
||||
loadBusinessSharedItems(appConfig.businessSharedItemsFilePath);
|
||||
}
|
||||
|
||||
// Configure skip_dir, skip_file, skip-dir-strict-match & skip_dotfiles from config entries
|
||||
// Handle skip_dir configuration in config file
|
||||
addLogEntry("Configuring skip_dir ...", ["debug"]);
|
||||
|
@ -91,7 +85,6 @@ class ClientSideFiltering {
|
|||
void shutdown() {
|
||||
object.destroy(appConfig);
|
||||
object.destroy(paths);
|
||||
object.destroy(businessSharedItemsList);
|
||||
object.destroy(fileMask);
|
||||
object.destroy(directoryMask);
|
||||
}
|
||||
|
@ -109,19 +102,6 @@ class ClientSideFiltering {
|
|||
file.close();
|
||||
}
|
||||
|
||||
// load business_shared_folders file
|
||||
void loadBusinessSharedItems(string filepath) {
|
||||
// open file as read only
|
||||
auto file = File(filepath, "r");
|
||||
auto range = file.byLine();
|
||||
foreach (line; range) {
|
||||
// Skip comments in file
|
||||
if (line.length == 0 || line[0] == ';' || line[0] == '#') continue;
|
||||
businessSharedItemsList ~= buildNormalizedPath(line);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Configure the regex that will be used for 'skip_file'
|
||||
void setFileMask(const(char)[] mask) {
|
||||
fileMask = wild2regex(mask);
|
||||
|
|
105
src/config.d
105
src/config.d
|
@ -41,6 +41,8 @@ class ApplicationConfig {
|
|||
immutable string defaultLogFileDir = "/var/log/onedrive";
|
||||
// - Default configuration directory
|
||||
immutable string defaultConfigDirName = "~/.config/onedrive";
|
||||
// - Default 'OneDrive Business Shared Files' Folder Name
|
||||
immutable string defaultBusinessSharedFilesDirectoryName = "Files Shared With Me";
|
||||
|
||||
// Microsoft Requirements
|
||||
// - Default Application ID (abraunegg)
|
||||
|
@ -106,7 +108,6 @@ class ApplicationConfig {
|
|||
bool debugLogging = false;
|
||||
long verbosityCount = 0;
|
||||
|
||||
|
||||
// Was the application just authorised - paste of response uri
|
||||
bool applicationAuthorizeResponseUri = false;
|
||||
|
||||
|
@ -121,6 +122,7 @@ class ApplicationConfig {
|
|||
// Store the 'session_upload.CRC32-HASH' file path
|
||||
string uploadSessionFilePath = "";
|
||||
|
||||
// API initialisation flags
|
||||
bool apiWasInitialised = false;
|
||||
bool syncEngineWasInitialised = false;
|
||||
|
||||
|
@ -161,24 +163,22 @@ class ApplicationConfig {
|
|||
private string applicableConfigFilePath = "";
|
||||
// - Store the 'sync_list' file path
|
||||
string syncListFilePath = "";
|
||||
// - Store the 'business_shared_items' file path
|
||||
string businessSharedItemsFilePath = "";
|
||||
|
||||
// OneDrive Business Shared File handling - what directory will be used?
|
||||
string configuredBusinessSharedFilesDirectoryName = "";
|
||||
|
||||
// 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 = "";
|
||||
|
||||
// Store the actual 'runtime' hash
|
||||
private string currentConfigHash = "";
|
||||
private string currentSyncListHash = "";
|
||||
private string currentBusinessSharedItemsHash = "";
|
||||
|
||||
// Store the previous config files hash values (file contents)
|
||||
private string previousConfigHash = "";
|
||||
private string previousSyncListHash = "";
|
||||
private string previousBusinessSharedItemsHash = "";
|
||||
|
||||
// Store items that come in from the 'config' file, otherwise these need to be set the the defaults
|
||||
private string configFileSyncDir = defaultSyncDir;
|
||||
|
@ -197,7 +197,6 @@ class ApplicationConfig {
|
|||
string[string] stringValues;
|
||||
long[string] longValues;
|
||||
bool[string] boolValues;
|
||||
|
||||
bool shellEnvironmentSet = false;
|
||||
|
||||
// Initialise the application configuration
|
||||
|
@ -275,7 +274,7 @@ class ApplicationConfig {
|
|||
longValues["ip_protocol_version"] = defaultIpProtocol; // 0 = IPv4 + IPv6, 1 = IPv4 Only, 2 = IPv6 Only
|
||||
|
||||
// Number of concurrent threads
|
||||
longValues["threads"] = defaultConcurrentThreads; // Default is 8, user can increase or decrease
|
||||
longValues["threads"] = defaultConcurrentThreads; // Default is 8, user can increase to max of 16 or decrease
|
||||
|
||||
// - Do we wish to upload only?
|
||||
boolValues["upload_only"] = false;
|
||||
|
@ -469,9 +468,6 @@ class ApplicationConfig {
|
|||
// - What is the full path for the system 'config' file if it is required
|
||||
systemConfigFilePath = buildNormalizedPath(buildPath(systemConfigDirName, "config"));
|
||||
|
||||
// - What is the full path for the 'business_shared_items'
|
||||
businessSharedItemsFilePath = buildNormalizedPath(buildPath(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
|
||||
|
@ -480,7 +476,6 @@ class ApplicationConfig {
|
|||
configBackupFile = buildNormalizedPath(buildPath(configDirName, ".config.backup"));
|
||||
configHashFile = buildNormalizedPath(buildPath(configDirName, ".config.hash"));
|
||||
syncListHashFile = buildNormalizedPath(buildPath(configDirName, ".sync_list.hash"));
|
||||
businessSharedItemsHashFile = buildNormalizedPath(buildPath(configDirName, ".business_shared_items.hash"));
|
||||
|
||||
// Debug Output for application set variables based on configDirName
|
||||
addLogEntry("refreshTokenFilePath = " ~ refreshTokenFilePath, ["debug"]);
|
||||
|
@ -494,8 +489,6 @@ class ApplicationConfig {
|
|||
addLogEntry("configBackupFile = " ~ configBackupFile, ["debug"]);
|
||||
addLogEntry("configHashFile = " ~ configHashFile, ["debug"]);
|
||||
addLogEntry("syncListHashFile = " ~ syncListHashFile, ["debug"]);
|
||||
addLogEntry("businessSharedItemsFilePath = " ~ businessSharedItemsFilePath, ["debug"]);
|
||||
addLogEntry("businessSharedItemsHashFile = " ~ businessSharedItemsHashFile, ["debug"]);
|
||||
|
||||
// Configure the Hash and Backup File Permission Value
|
||||
string valueToConvert = to!string(defaultFilePermissionMode);
|
||||
|
@ -900,6 +893,7 @@ class ApplicationConfig {
|
|||
boolValues["synchronize"] = false;
|
||||
boolValues["force"] = false;
|
||||
boolValues["list_business_shared_items"] = false;
|
||||
boolValues["sync_business_shared_files"] = false;
|
||||
boolValues["force_sync"] = false;
|
||||
boolValues["with_editing_perms"] = false;
|
||||
|
||||
|
@ -995,6 +989,12 @@ class ApplicationConfig {
|
|||
"get-O365-drive-id",
|
||||
"Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library (DEPRECIATED)",
|
||||
&stringValues["sharepoint_library_name"],
|
||||
"list-shared-items",
|
||||
"List OneDrive Business Shared Items",
|
||||
&boolValues["list_business_shared_items"],
|
||||
"sync-shared-files",
|
||||
"Sync OneDrive Business Shared Files to the local filesystem",
|
||||
&boolValues["sync_business_shared_files"],
|
||||
"local-first",
|
||||
"Synchronize from the local directory source first, before downloading changes from OneDrive.",
|
||||
&boolValues["local_first"],
|
||||
|
@ -1365,20 +1365,7 @@ class ApplicationConfig {
|
|||
// Is sync_business_shared_items enabled and configured ?
|
||||
addLogEntry(); // used instead of an empty 'writeln();' to ensure the line break is correct in the buffered console output ordering
|
||||
addLogEntry("Config option 'sync_business_shared_items' = " ~ to!string(getValueBool("sync_business_shared_items")));
|
||||
|
||||
if (exists(businessSharedItemsFilePath)){
|
||||
addLogEntry("Selective Business Shared Items configured = true");
|
||||
addLogEntry("sync_business_shared_items contents:");
|
||||
// Output the sync_business_shared_items contents
|
||||
auto businessSharedItemsFileList = File(businessSharedItemsFilePath, "r");
|
||||
auto range = businessSharedItemsFileList.byLine();
|
||||
foreach (line; range)
|
||||
{
|
||||
addLogEntry(to!string(line));
|
||||
}
|
||||
} else {
|
||||
addLogEntry("Selective Business Shared Items configured = false");
|
||||
}
|
||||
addLogEntry("Config option 'Shared Files Directory' = " ~ configuredBusinessSharedFilesDirectoryName);
|
||||
|
||||
// Are webhooks enabled?
|
||||
addLogEntry(); // used instead of an empty 'writeln();' to ensure the line break is correct in the buffered console output ordering
|
||||
|
@ -1518,9 +1505,6 @@ class ApplicationConfig {
|
|||
if (currentSyncListHash != previousSyncListHash)
|
||||
logAndSetDifference("sync_list file has been updated, --resync needed", 0);
|
||||
|
||||
if (currentBusinessSharedItemsHash != previousBusinessSharedItemsHash)
|
||||
logAndSetDifference("business_shared_folders file has been updated, --resync needed", 1);
|
||||
|
||||
// Check for updates in the config file
|
||||
if (currentConfigHash != previousConfigHash) {
|
||||
addLogEntry("Application configuration file has been updated, checking if --resync needed");
|
||||
|
@ -1666,6 +1650,13 @@ class ApplicationConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// Final override
|
||||
// In certain situations, regardless of config 'resync' needed status, ignore this so that the application can display 'non-syncable' information
|
||||
// Options that should now be looked at are:
|
||||
// --list-shared-items
|
||||
if (getValueBool("list_business_shared_items")) resyncRequired = false;
|
||||
|
||||
// Return the calculated boolean
|
||||
return resyncRequired;
|
||||
}
|
||||
|
||||
|
@ -1676,7 +1667,6 @@ class ApplicationConfig {
|
|||
addLogEntry("Cleaning up configuration hash files", ["debug"]);
|
||||
safeRemove(configHashFile);
|
||||
safeRemove(syncListHashFile);
|
||||
safeRemove(businessSharedItemsHashFile);
|
||||
} else {
|
||||
// --dry-run scenario ... technically we should not be making any local file changes .......
|
||||
addLogEntry("DRY RUN: Not removing hash files as --dry-run has been used");
|
||||
|
@ -1704,17 +1694,6 @@ 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
|
||||
addLogEntry("Updating business_shared_items hash", ["debug"]);
|
||||
std.file.write(businessSharedItemsHashFile, computeQuickXorHash(businessSharedItemsFilePath));
|
||||
// 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 .......
|
||||
addLogEntry("DRY RUN: Not updating hash files as --dry-run has been used");
|
||||
|
@ -1746,18 +1725,6 @@ class ApplicationConfig {
|
|||
// Generate the runtime hash for the 'sync_list' file
|
||||
currentSyncListHash = computeQuickXorHash(syncListFilePath);
|
||||
}
|
||||
|
||||
// Does a 'business_shared_items' file exist with a valid hash file
|
||||
if (exists(businessSharedItemsFilePath)) {
|
||||
if (!exists(businessSharedItemsHashFile)) {
|
||||
// no existing hash file exists
|
||||
std.file.write(businessSharedItemsHashFile, "initial-hash");
|
||||
// Hash file should only be readable by the user who created it - 0600 permissions needed
|
||||
businessSharedItemsHashFile.setAttributes(convertedPermissionValue);
|
||||
}
|
||||
// Generate the runtime hash for the 'sync_list' file
|
||||
currentBusinessSharedItemsHash = computeQuickXorHash(businessSharedItemsFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Read in the text values of the previous configurations
|
||||
|
@ -1783,16 +1750,7 @@ class ApplicationConfig {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (exists(businessSharedItemsHashFile)) {
|
||||
try {
|
||||
previousBusinessSharedItemsHash = readText(businessSharedItemsHashFile);
|
||||
} catch (std.file.FileException e) {
|
||||
// Unable to access required hash file
|
||||
addLogEntry("ERROR: Unable to access " ~ e.msg);
|
||||
// Use exit scopes to shutdown API
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1850,7 +1808,19 @@ class ApplicationConfig {
|
|||
|
||||
// --list-shared-folders cannot be used with --resync and/or --resync-auth
|
||||
if ((getValueBool("list_business_shared_items")) && ((getValueBool("resync")) || (getValueBool("resync_auth")))) {
|
||||
addLogEntry("ERROR: --list-shared-folders cannot be used with --resync or --resync-auth");
|
||||
addLogEntry("ERROR: --list-shared-items cannot be used with --resync or --resync-auth");
|
||||
operationalConflictDetected = true;
|
||||
}
|
||||
|
||||
// --list-shared-folders cannot be used with --sync or --monitor
|
||||
if ((getValueBool("list_business_shared_items")) && ((getValueBool("synchronize")) || (getValueBool("monitor")))) {
|
||||
addLogEntry("ERROR: --list-shared-items cannot be used with --sync or --monitor");
|
||||
operationalConflictDetected = true;
|
||||
}
|
||||
|
||||
// --sync-shared-files can ONLY be used with sync_business_shared_items
|
||||
if ((getValueBool("sync_business_shared_files")) && (!getValueBool("sync_business_shared_items"))) {
|
||||
addLogEntry("ERROR: The --sync-shared-files option can only be utilised if the 'sync_business_shared_items' configuration setting is enabled.");
|
||||
operationalConflictDetected = true;
|
||||
}
|
||||
|
||||
|
@ -2057,6 +2027,9 @@ class ApplicationConfig {
|
|||
// What will runtimeSyncDirectory be actually set to?
|
||||
addLogEntry("sync_dir: runtimeSyncDirectory set to: " ~ runtimeSyncDirectory, ["debug"]);
|
||||
|
||||
// Configure configuredBusinessSharedFilesDirectoryName
|
||||
configuredBusinessSharedFilesDirectoryName = buildNormalizedPath(buildPath(runtimeSyncDirectory, defaultBusinessSharedFilesDirectoryName));
|
||||
|
||||
return runtimeSyncDirectory;
|
||||
}
|
||||
|
||||
|
|
22
src/main.d
22
src/main.d
|
@ -346,12 +346,15 @@ int main(string[] cliArgs) {
|
|||
return EXIT_RESYNC_REQUIRED;
|
||||
} else {
|
||||
// No configuration change that requires a --resync to be issued
|
||||
// Special cases need to be checked - if these options were enabled, it creates a false 'Resync Required' flag, so do not create a backup
|
||||
if ((!appConfig.getValueBool("list_business_shared_items"))) {
|
||||
// Make a backup of the applicable configuration file
|
||||
appConfig.createBackupConfigFile();
|
||||
// Update hash files and generate a new config backup
|
||||
appConfig.updateHashContentsForConfigFiles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement https://github.com/abraunegg/onedrive/issues/1129
|
||||
// Force a synchronization of a specific folder, only when using --synchronize --single-directory and ignoring all non-default skip_dir and skip_file rules
|
||||
|
@ -446,8 +449,9 @@ int main(string[] cliArgs) {
|
|||
// Are we performing some sort of 'no-sync' task?
|
||||
// - Are we obtaining the Office 365 Drive ID for a given Office 365 SharePoint Shared Library?
|
||||
// - Are we displaying the sync satus?
|
||||
// - Are we getting the URL for a file online
|
||||
// - Are we listing who modified a file last online
|
||||
// - Are we getting the URL for a file online?
|
||||
// - Are we listing who modified a file last online?
|
||||
// - Are we listing OneDrive Business Shared Items?
|
||||
// - Are we createing a shareable link for an existing file on OneDrive?
|
||||
// - Are we just creating a directory online, without any sync being performed?
|
||||
// - Are we just deleting a directory online, without any sync being performed?
|
||||
|
@ -499,6 +503,20 @@ int main(string[] cliArgs) {
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// --list-shared-items - Are we listing OneDrive Business Shared Items
|
||||
if (appConfig.getValueBool("list_business_shared_items")) {
|
||||
// Is this a business account type?
|
||||
if (appConfig.accountType == "business") {
|
||||
// List OneDrive Business Shared Items
|
||||
syncEngineInstance.listBusinessSharedObjects();
|
||||
} else {
|
||||
addLogEntry("ERROR: Unsupported account type for listing OneDrive Business Shared Items");
|
||||
}
|
||||
// Exit application
|
||||
// Use exit scopes to shutdown API
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// --create-share-link - Are we createing a shareable link for an existing file on OneDrive?
|
||||
if (appConfig.getValueString("create_share_link") != "") {
|
||||
// Query OneDrive for the file, and if valid, create a shareable link for the file
|
||||
|
|
|
@ -512,6 +512,13 @@ class OneDriveApi {
|
|||
return get(url);
|
||||
}
|
||||
|
||||
// Return all the items that are shared with the user
|
||||
// https://docs.microsoft.com/en-us/graph/api/drive-sharedwithme
|
||||
JSONValue getSharedWithMe() {
|
||||
checkAccessTokenExpired();
|
||||
return get(sharedWithMeUrl);
|
||||
}
|
||||
|
||||
// Create a shareable link for an existing file on OneDrive based on the accessScope JSON permissions
|
||||
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createlink
|
||||
JSONValue createShareableLink(string driveId, string id, JSONValue accessScope) {
|
||||
|
|
93
src/sync.d
93
src/sync.d
|
@ -8155,4 +8155,97 @@ class SyncEngine {
|
|||
addLogEntry("Creating|Updating into local database a DB Tie record: " ~ to!string(tieDBItem), ["debug"]);
|
||||
itemDB.upsert(tieDBItem);
|
||||
}
|
||||
|
||||
void listBusinessSharedObjects() {
|
||||
|
||||
JSONValue sharedWithMeItems;
|
||||
|
||||
// Create a new API Instance for this thread and initialise it
|
||||
OneDriveApi sharedWithMeOneDriveApiInstance;
|
||||
sharedWithMeOneDriveApiInstance = new OneDriveApi(appConfig);
|
||||
sharedWithMeOneDriveApiInstance.initialise();
|
||||
|
||||
try {
|
||||
sharedWithMeItems = sharedWithMeOneDriveApiInstance.getSharedWithMe();
|
||||
} catch (OneDriveException e) {
|
||||
|
||||
// Display error message
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
// Must exit here
|
||||
sharedWithMeOneDriveApiInstance.shutdown();
|
||||
// Free object and memory
|
||||
object.destroy(sharedWithMeOneDriveApiInstance);
|
||||
}
|
||||
|
||||
if (sharedWithMeItems.type() == JSONType.object) {
|
||||
|
||||
if (count(sharedWithMeItems["value"].array) > 0) {
|
||||
// No shared items
|
||||
addLogEntry();
|
||||
addLogEntry("Listing available OneDrive Business Shared Items:");
|
||||
addLogEntry();
|
||||
|
||||
// Iterate through the array
|
||||
foreach (searchResult; sharedWithMeItems["value"].array) {
|
||||
|
||||
// loop variables for each item
|
||||
string sharedByName;
|
||||
string sharedByEmail;
|
||||
|
||||
// Debug response output
|
||||
addLogEntry("shared folder entry: " ~ to!string(searchResult), ["debug"]);
|
||||
|
||||
// Configure 'who' this was shared by
|
||||
if ("sharedBy" in searchResult["remoteItem"]["shared"]) {
|
||||
// we have shared by details we can use
|
||||
if ("displayName" in searchResult["remoteItem"]["shared"]["sharedBy"]["user"]) {
|
||||
sharedByName = searchResult["remoteItem"]["shared"]["sharedBy"]["user"]["displayName"].str;
|
||||
}
|
||||
if ("email" in searchResult["remoteItem"]["shared"]["sharedBy"]["user"]) {
|
||||
sharedByEmail = searchResult["remoteItem"]["shared"]["sharedBy"]["user"]["email"].str;
|
||||
}
|
||||
}
|
||||
|
||||
// Output query result
|
||||
addLogEntry("-----------------------------------------------------------------------------------");
|
||||
if (isItemFile(searchResult)) {
|
||||
addLogEntry("Shared File: " ~ to!string(searchResult["name"].str));
|
||||
} else {
|
||||
addLogEntry("Shared Folder: " ~ to!string(searchResult["name"].str));
|
||||
}
|
||||
|
||||
// Detail 'who' shared this
|
||||
if ((sharedByName != "") && (sharedByEmail != "")) {
|
||||
addLogEntry("Shared By: " ~ sharedByName ~ " (" ~ sharedByEmail ~ ")");
|
||||
} else {
|
||||
if (sharedByName != "") {
|
||||
addLogEntry("Shared By: " ~ sharedByName);
|
||||
}
|
||||
}
|
||||
|
||||
// More detail if --verbose is being used
|
||||
addLogEntry("Item Id: " ~ searchResult["remoteItem"]["id"].str, ["verbose"]);
|
||||
addLogEntry("Parent Drive Id: " ~ searchResult["remoteItem"]["parentReference"]["driveId"].str, ["verbose"]);
|
||||
if ("id" in searchResult["remoteItem"]["parentReference"]) {
|
||||
addLogEntry("Parent Item Id: " ~ searchResult["remoteItem"]["parentReference"]["id"].str, ["verbose"]);
|
||||
}
|
||||
}
|
||||
|
||||
// Close out the loop
|
||||
addLogEntry("-----------------------------------------------------------------------------------");
|
||||
addLogEntry();
|
||||
|
||||
} else {
|
||||
// No shared items
|
||||
addLogEntry();
|
||||
addLogEntry("No OneDrive Business Shared Folders were returned");
|
||||
addLogEntry();
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown API access
|
||||
sharedWithMeOneDriveApiInstance.shutdown();
|
||||
// Free object and memory
|
||||
object.destroy(sharedWithMeOneDriveApiInstance);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue