mirror of
https://github.com/abraunegg/onedrive
synced 2024-05-22 23:52:13 +02:00
config options for command line switches (#449)
* config options for command line switches to allow for better config handling in docker containers
This commit is contained in:
parent
e5a9747d7b
commit
ea22d8fef5
54
README.md
54
README.md
|
@ -454,8 +454,12 @@ To re-authorise the client, follow the steps below:
|
||||||
|
|
||||||
The application will now sync with OneDrive with the new credentials.
|
The application will now sync with OneDrive with the new credentials.
|
||||||
|
|
||||||
## Additional Configuration
|
## Configuration
|
||||||
Additional configuration is optional.
|
|
||||||
|
Configuration is determined by three layer: the default values, values set in the configuration file, and values passed in via the command line. The default values provide a reasonable default, and configuration is optionally.
|
||||||
|
|
||||||
|
Most command line options have a respective configuration file setting.
|
||||||
|
|
||||||
If you want to change the defaults, you can copy and edit the included config file into your `~/.config/onedrive` directory:
|
If you want to change the defaults, you can copy and edit the included config file into your `~/.config/onedrive` directory:
|
||||||
```text
|
```text
|
||||||
mkdir -p ~/.config/onedrive
|
mkdir -p ~/.config/onedrive
|
||||||
|
@ -464,13 +468,9 @@ nano ~/.config/onedrive/config
|
||||||
```
|
```
|
||||||
This file does not get created by default, and should only be created if you want to change the 'default' operational parameters.
|
This file does not get created by default, and should only be created if you want to change the 'default' operational parameters.
|
||||||
|
|
||||||
Available options:
|
See the [config](config) file for the full list of options, and below under "All available commands" for all possible keys and there default values.
|
||||||
* `sync_dir`: directory where the files will be synced
|
|
||||||
* `skip_file`: any files or directories that match this pattern will be skipped during sync
|
Comments regarding some of the options:
|
||||||
* `skip_dotfiles`: skip any .files or .folders during sync
|
|
||||||
* `skip_symlinks`: any files or directories that are symlinked will be skipped during sync
|
|
||||||
* `monitor_interval`: time interval in seconds by which the monitor process will process local and remote changes
|
|
||||||
* `min_notif_changes`: minimum number of pending incoming changes to trigger a desktop notification
|
|
||||||
|
|
||||||
### sync_dir
|
### sync_dir
|
||||||
Example: `sync_dir="~/MyDirToSync"`
|
Example: `sync_dir="~/MyDirToSync"`
|
||||||
|
@ -715,46 +715,60 @@ Options:
|
||||||
Set the directory used to store the configuration files
|
Set the directory used to store the configuration files
|
||||||
--create-directory ARG
|
--create-directory ARG
|
||||||
Create a directory on OneDrive - no sync will be performed.
|
Create a directory on OneDrive - no sync will be performed.
|
||||||
--destination-directory ARG
|
|
||||||
Destination directory for renamed or move on OneDrive - no sync will be performed.
|
|
||||||
--debug-https
|
--debug-https
|
||||||
Debug OneDrive HTTPS communication.
|
Debug OneDrive HTTPS communication.
|
||||||
|
--destination-directory ARG
|
||||||
|
Destination directory for renamed or move on OneDrive - no sync will be performed.
|
||||||
--disable-notifications
|
--disable-notifications
|
||||||
Do not use desktop notifications in monitor mode.
|
Do not use desktop notifications in monitor mode.
|
||||||
|
--disable-upload-validation
|
||||||
|
Disable upload validation when uploading to OneDrive
|
||||||
--display-config
|
--display-config
|
||||||
Display what options the client will use as currently configured - no sync will be performed.
|
Display what options the client will use as currently configured - no sync will be performed.
|
||||||
--display-sync-status
|
--display-sync-status
|
||||||
Display the sync status of the client - no sync will be performed.
|
Display the sync status of the client - no sync will be performed.
|
||||||
-d --download-only
|
--download-only -d
|
||||||
Only download remote changes
|
Only download remote changes
|
||||||
--disable-upload-validation
|
|
||||||
Disable upload validation when uploading to OneDrive
|
|
||||||
--dry-run
|
--dry-run
|
||||||
Perform a trial sync with no changes made
|
Perform a trial sync with no changes made
|
||||||
--enable-logging
|
--enable-logging
|
||||||
Enable client activity to a separate log file
|
Enable client activity to a separate log file
|
||||||
--force-http-1.1
|
--force-http-1.1
|
||||||
Force the use of HTTP 1.1 for all operations
|
Force the use of HTTP 1.1 for all operations
|
||||||
--get-O365-drive-id ARG
|
--get-O365-drive-id ARG
|
||||||
Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library
|
Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library
|
||||||
|
--help -h
|
||||||
|
This help information.
|
||||||
--local-first
|
--local-first
|
||||||
Synchronize from the local directory source first, before downloading changes from OneDrive.
|
Synchronize from the local directory source first, before downloading changes from OneDrive.
|
||||||
|
--log-dir ARG
|
||||||
|
Directory where logging output is saved to, needs to end with a slash.
|
||||||
--logout
|
--logout
|
||||||
Logout the current user
|
Logout the current user
|
||||||
-m --monitor
|
--min-notif-changes ARG
|
||||||
|
Minimum number of pending incoming changes necessary to trigger a desktop notification
|
||||||
|
--monitor -m
|
||||||
Keep monitoring for local and remote changes
|
Keep monitoring for local and remote changes
|
||||||
|
--monitor-fullscan-frequency ARG
|
||||||
|
Number of sync runs before performing a full local scan of the synced directory
|
||||||
|
--monitor-interval ARG
|
||||||
|
Number of seconds by which each sync operation is undertaken when idle under monitor mode.
|
||||||
|
--monitor-log-frequency ARG
|
||||||
|
Frequency of logging in monitor mode
|
||||||
--no-remote-delete
|
--no-remote-delete
|
||||||
Do not delete local file 'deletes' from OneDrive when using --upload-only
|
Do not delete local file 'deletes' from OneDrive when using --upload-only
|
||||||
--print-token
|
--print-token
|
||||||
Print the access token, useful for debugging
|
Print the access token, useful for debugging
|
||||||
--resync
|
|
||||||
Forget the last saved state, perform a full sync
|
|
||||||
--remove-directory ARG
|
--remove-directory ARG
|
||||||
Remove a directory on OneDrive - no sync will be performed.
|
Remove a directory on OneDrive - no sync will be performed.
|
||||||
|
--resync
|
||||||
|
Forget the last saved state, perform a full sync
|
||||||
--single-directory ARG
|
--single-directory ARG
|
||||||
Specify a single local directory within the OneDrive root to sync.
|
Specify a single local directory within the OneDrive root to sync.
|
||||||
--skip-dot-files
|
--skip-dot-files
|
||||||
Skip dot files and folders from syncing
|
Skip dot files and folders from syncing
|
||||||
|
--skip-file ARG
|
||||||
|
Skip any files that match this pattern from syncing
|
||||||
--skip-symlinks
|
--skip-symlinks
|
||||||
Skip syncing of symlinks
|
Skip syncing of symlinks
|
||||||
--source-directory ARG
|
--source-directory ARG
|
||||||
|
@ -765,12 +779,10 @@ Options:
|
||||||
Perform a synchronization
|
Perform a synchronization
|
||||||
--upload-only
|
--upload-only
|
||||||
Only upload to OneDrive, do not sync changes from OneDrive locally
|
Only upload to OneDrive, do not sync changes from OneDrive locally
|
||||||
-v+ --verbose
|
--verbose -v+
|
||||||
Print more details, useful for debugging (repeat for extra debugging)
|
Print more details, useful for debugging (repeat for extra debugging)
|
||||||
--version
|
--version
|
||||||
Print the version and exit
|
Print the version and exit
|
||||||
-h --help
|
|
||||||
This help information.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### File naming
|
### File naming
|
||||||
|
|
36
config
36
config
|
@ -1,6 +1,30 @@
|
||||||
# Directory where the files will be synced
|
# Configuration for OneDrive Linux Client
|
||||||
sync_dir = "~/OneDrive"
|
# This file contains the list of supported configuration fields
|
||||||
# Skip files and directories that match this pattern
|
# with their default values.
|
||||||
skip_file = "~*|.~*|*.tmp"
|
# All values need to be enclosed in quotes
|
||||||
# Wait time (seconds) between sync operations in monitor mode
|
# For explanations see the README.md or the man page.
|
||||||
monitor_interval = "45"
|
#
|
||||||
|
# sync_dir = "~/OneDrive"
|
||||||
|
# skip_file = "~*|.~*|*.tmp"
|
||||||
|
# monitor_interval = "45"
|
||||||
|
# skip_dir = ""
|
||||||
|
# log_dir = "/var/log/onedrive/"
|
||||||
|
# drive_id = ""
|
||||||
|
# upload_only = "false"
|
||||||
|
# check_nomount = "false"
|
||||||
|
# check_nosync = "false"
|
||||||
|
# download_only = "false"
|
||||||
|
# disable_notifications = "false"
|
||||||
|
# disable_upload_validation = "false"
|
||||||
|
# enable_logging = "false"
|
||||||
|
# force_http_11 = "false"
|
||||||
|
# local_first = "false"
|
||||||
|
# no_remote_delete = "false"
|
||||||
|
# skip_symlinks = "false"
|
||||||
|
# debug_https = "false"
|
||||||
|
# skip_dotfiles = "false"
|
||||||
|
# dry_run = "false"
|
||||||
|
# monitor_interval = "45"
|
||||||
|
# min_notif_changes = "5"
|
||||||
|
# monitor_log_frequency = "5"
|
||||||
|
# monitor_fullscan_frequency = "10"
|
||||||
|
|
|
@ -16,13 +16,17 @@ onedrive \- folder synchronization with OneDrive
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
A complete tool to interact with OneDrive on Linux.
|
A complete tool to interact with OneDrive on Linux.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
Without any option given, no sync is done and the program exits.
|
Without any option given, no sync is done and the program exits.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-check\-for\-nomount\fP
|
\fB\-\-check\-for\-nomount\fP
|
||||||
Check for the presence of .nosync in the syncdir root. If found, do not perform sync.
|
Check for the presence of .nosync in the syncdir root. If found, do not perform sync.
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBcheck_nomount\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-check\-for\-nosync\fP
|
\fB\-\-check\-for\-nosync\fP
|
||||||
Check for the presence of .nosync in each directory. If found, skip directory from sync.
|
Check for the presence of .nosync in each directory. If found, skip directory from sync.
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBcheck_nosync\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-confdir\fP ARG
|
\fB\-\-confdir\fP ARG
|
||||||
Set the directory used to store the configuration files
|
Set the directory used to store the configuration files
|
||||||
|
@ -35,12 +39,18 @@ Destination directory for renamed or move on OneDrive \- no sync will be perform
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-debug\-https\fP
|
\fB\-\-debug\-https\fP
|
||||||
Debug OneDrive HTTPS communication.
|
Debug OneDrive HTTPS communication.
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBdebug_https\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-disable\-notifications\fP
|
\fB\-\-disable\-notifications\fP
|
||||||
Do not use desktop notifications in monitor mode
|
Do not use desktop notifications in monitor mode
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBdisable_notifications\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-disable\-upload\-validation\fP
|
\fB\-\-disable\-upload\-validation\fP
|
||||||
Disable upload validation when uploading to OneDrive
|
Disable upload validation when uploading to OneDrive
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBdisable_upload_validation\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-display\-config\fP
|
\fB\-\-display\-config\fP
|
||||||
Display what options the client will use as currently configured \- no sync will be performed.
|
Display what options the client will use as currently configured \- no sync will be performed.
|
||||||
|
@ -50,30 +60,64 @@ Display the sync status of the client \- no sync will be performed.
|
||||||
.TP
|
.TP
|
||||||
\fB\-d \-\-download\-only\fP
|
\fB\-d \-\-download\-only\fP
|
||||||
Only download remote changes
|
Only download remote changes
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBdownload_only\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-dry\-run\fP
|
\fB\-\-dry\-run\fP
|
||||||
Perform a trial sync with no changes made. Can ONLY be used with --synchronize. Will be ignored for --monitor
|
Perform a trial sync with no changes made. Can ONLY be used with --synchronize. Will be ignored for --monitor
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBdry_run\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-enable\-logging\fP
|
\fB\-\-enable\-logging\fP
|
||||||
Enable client activity to a separate log file
|
Enable client activity to a separate log file
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBenable_logging\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-force\-http\-1.1\fP
|
\fB\-\-force\-http\-1.1\fP
|
||||||
Force the use of HTTP 1.1 for all operations
|
Force the use of HTTP 1.1 for all operations
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBforce_http_11\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-get\-O365\-drive\-id\fP ARG
|
\fB\-\-get\-O365\-drive\-id\fP ARG
|
||||||
Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library
|
Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-local\-first\fP
|
\fB\-\-local\-first\fP
|
||||||
Synchronize from the local directory source first, before downloading changes from OneDrive.
|
Synchronize from the local directory source first, before downloading changes from OneDrive.
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBlocal_first\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-logout\fP
|
\fB\-\-logout\fP
|
||||||
Logout the current user
|
Logout the current user
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-\-log\-dir\fP ARG
|
||||||
|
defines the directory where logging output is saved to, needs to end with a slash
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBlog_dir\fP (default: \fB/var/log/onedrive/\fP)
|
||||||
|
.TP
|
||||||
|
\fB\-\-min-notif-changes\fP
|
||||||
|
the minimum number of pending incoming changes necessary to trigger
|
||||||
|
a desktop notification
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBmin_notif_changes\fP (default: \fB5\fP)
|
||||||
|
.TP
|
||||||
\fB\-m \-\-monitor\fP
|
\fB\-m \-\-monitor\fP
|
||||||
Keep monitoring for local and remote changes
|
Keep monitoring for local and remote changes
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-\-monitor\-interval\fP ARG
|
||||||
|
The number of seconds by which each sync operation is undertaken when
|
||||||
|
idle under monitor mode
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBmonitor_interval\fP (default: \fB45\fP)
|
||||||
|
.TP
|
||||||
|
\fB\-\-monitor\-fullscan-frequency\fP ARG
|
||||||
|
Number of sync runs before performing a full local scan of the synced directory
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBmonitor_fullscan_frequency\fP (default: \fB10\fP)
|
||||||
|
.TP
|
||||||
\fB\-\-no\-remote\-delete\fP
|
\fB\-\-no\-remote\-delete\fP
|
||||||
Do not delete local file 'deletes' from OneDrive when using \fB\-\-upload\-only\fR
|
Do not delete local file 'deletes' from OneDrive when using \fB\-\-upload\-only\fR
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBno_remote_delete\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-print\-token\fP
|
\fB\-\-print\-token\fP
|
||||||
Print the access token, useful for debugging
|
Print the access token, useful for debugging
|
||||||
|
@ -89,21 +133,34 @@ Specify a single local directory within the OneDrive root to sync.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-skip\-dot\-files\fP
|
\fB\-\-skip\-dot\-files\fP
|
||||||
Skip dot files and folders from syncing
|
Skip dot files and folders from syncing
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBskip_dotfiles\fP (default: \fBfalse\fP)
|
||||||
|
.TP
|
||||||
|
\fB\-\-skip\-file\fP
|
||||||
|
Skip any files that match this pattern from syncing
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBskip_file\fP (default: \fB~*|.~*|*.tmp\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-skip\-symlinks\fP
|
\fB\-\-skip\-symlinks\fP
|
||||||
Skip syncing of symlinks
|
Skip syncing of symlinks
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBskip_symlinks\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-source\-directory\fP ARG
|
\fB\-\-source\-directory\fP ARG
|
||||||
Source directory to rename or move on OneDrive \- no sync will be performed.
|
Source directory to rename or move on OneDrive \- no sync will be performed.
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-syncdir\fP ARG
|
\fB\-\-syncdir\fP ARG
|
||||||
Set the directory used to sync the files that are synced
|
Set the directory used to sync the files that are synced
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBsync_dir\fP (default: \fB~/OneDrive\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-synchronize\fP
|
\fB\-\-synchronize\fP
|
||||||
Perform a synchronization
|
Perform a synchronization
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-upload\-only\fP
|
\fB\-\-upload\-only\fP
|
||||||
Only upload to OneDrive, do not sync changes from OneDrive locally
|
Only upload to OneDrive, do not sync changes from OneDrive locally
|
||||||
|
.br
|
||||||
|
Configuration file key: \fBupload_only\fP (default: \fBfalse\fP)
|
||||||
.TP
|
.TP
|
||||||
\fB\-v \-\-verbose\fP
|
\fB\-v \-\-verbose\fP
|
||||||
Print more details, useful for debugging. Given two times (or more)
|
Print more details, useful for debugging. Given two times (or more)
|
||||||
|
@ -141,27 +198,8 @@ cp\ @DOCDIR@/config\ ~/.config/onedrive/config
|
||||||
\fP
|
\fP
|
||||||
.fi
|
.fi
|
||||||
|
|
||||||
Available options:
|
For the supported options see the above list of command line options
|
||||||
.TP
|
for the availability of a configuration key.
|
||||||
\fBsync_dir\fP
|
|
||||||
directory where the files will be synced
|
|
||||||
.TP
|
|
||||||
\fBskip_file\fP
|
|
||||||
any files that match this pattern will be skipped during sync
|
|
||||||
.TP
|
|
||||||
\fBskip_symlinks\fP
|
|
||||||
skip symbolic links during sync, defaults to \fB"false"\fP
|
|
||||||
.TP
|
|
||||||
\fBmonitor_interval\fP
|
|
||||||
the number of seconds by which each sync operation is undertaken when
|
|
||||||
idle under monitor mode, defaults to \fB"45"\fP
|
|
||||||
.TP
|
|
||||||
\fBmin_notif_changes\fP
|
|
||||||
the minimum number of pending incoming changes necessary to trigger
|
|
||||||
a desktop notification, defaults to \fB"5"\fP
|
|
||||||
.TP
|
|
||||||
\fBlog_dir\fP
|
|
||||||
defines the directory where logging output is saved to, needs to end with a slash
|
|
||||||
.PP
|
.PP
|
||||||
Pattern are case insensitive.
|
Pattern are case insensitive.
|
||||||
\fB*\fP and \fB?\fP wildcards characters are supported.
|
\fB*\fP and \fB?\fP wildcards characters are supported.
|
||||||
|
|
400
src/config.d
400
src/config.d
|
@ -1,4 +1,6 @@
|
||||||
import std.file, std.string, std.regex, std.stdio;
|
import core.stdc.stdlib: EXIT_SUCCESS, EXIT_FAILURE, exit;
|
||||||
|
import std.file, std.string, std.regex, std.stdio, std.process, std.algorithm.searching, std.getopt, std.conv;
|
||||||
|
import std.algorithm.sorting: sort;
|
||||||
import selective;
|
import selective;
|
||||||
static import log;
|
static import log;
|
||||||
|
|
||||||
|
@ -10,13 +12,108 @@ final class Config
|
||||||
public string databaseFilePathDryRun;
|
public string databaseFilePathDryRun;
|
||||||
public string uploadStateFilePath;
|
public string uploadStateFilePath;
|
||||||
public string syncListFilePath;
|
public string syncListFilePath;
|
||||||
|
public string homePath;
|
||||||
|
public string configDirName;
|
||||||
|
|
||||||
private string userConfigFilePath;
|
private string userConfigFilePath;
|
||||||
// hashmap for the values found in the user config file
|
// hashmap for the values found in the user config file
|
||||||
private string[string] values;
|
// ARGGGG D is stupid and cannot make hashmap initializations!!!
|
||||||
|
// private string[string] foobar = [ "aa": "bb" ] does NOT work!!!
|
||||||
|
private string[string] stringValues;
|
||||||
|
private bool[string] boolValues;
|
||||||
|
private long[string] longValues;
|
||||||
|
|
||||||
this(string configDirName)
|
|
||||||
|
this(string confdirOption)
|
||||||
{
|
{
|
||||||
|
// default configuration
|
||||||
|
stringValues["sync_dir"] = "~/OneDrive";
|
||||||
|
stringValues["skip_file"] = "~*|.~*|*.tmp";
|
||||||
|
stringValues["skip_dir"] = "";
|
||||||
|
stringValues["log_dir"] = "/var/log/onedrive/";
|
||||||
|
stringValues["drive_id"] = "";
|
||||||
|
boolValues["upload_only"] = false;
|
||||||
|
boolValues["check_nomount"] = false;
|
||||||
|
boolValues["check_nosync"] = false;
|
||||||
|
boolValues["download_only"] = false;
|
||||||
|
boolValues["disable_notifications"] = false;
|
||||||
|
boolValues["disable_upload_validation"] = false;
|
||||||
|
boolValues["enable_logging"] = false;
|
||||||
|
boolValues["force_http_11"] = false;
|
||||||
|
boolValues["local_first"] = false;
|
||||||
|
boolValues["no_remote_delete"] = false;
|
||||||
|
boolValues["skip_symlinks"] = false;
|
||||||
|
boolValues["debug_https"] = false;
|
||||||
|
boolValues["skip_dotfiles"] = false;
|
||||||
|
boolValues["dry_run"] = false;
|
||||||
|
longValues["verbose"] = log.verbose; // might be initialized by the first getopt call!
|
||||||
|
longValues["monitor_interval"] = 45,
|
||||||
|
longValues["min_notif_changes"] = 5;
|
||||||
|
longValues["monitor_log_frequency"] = 5;
|
||||||
|
// Number of n sync runs before performing a full local scan of sync_dir
|
||||||
|
// By default 10 which means every ~7.5 minutes a full disk scan of sync_dir will occur
|
||||||
|
longValues["monitor_fullscan_frequency"] = 10;
|
||||||
|
|
||||||
|
// Determine the users home directory.
|
||||||
|
// Need to avoid using ~ here as expandTilde() below does not interpret correctly when running under init.d or systemd scripts
|
||||||
|
// Check for HOME environment variable
|
||||||
|
if (environment.get("HOME") != ""){
|
||||||
|
// Use HOME environment variable
|
||||||
|
log.vdebug("homePath: HOME environment variable set");
|
||||||
|
homePath = environment.get("HOME");
|
||||||
|
} else {
|
||||||
|
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
|
||||||
|
// No shell is set or username - observed case when running as systemd service under CentOS 7.x
|
||||||
|
log.vdebug("homePath: WARNING - no HOME environment variable set");
|
||||||
|
log.vdebug("homePath: WARNING - no SHELL environment variable set");
|
||||||
|
log.vdebug("homePath: WARNING - no USER environment variable set");
|
||||||
|
homePath = "/root";
|
||||||
|
} else {
|
||||||
|
// A shell & valid user is set, but no HOME is set, use ~ which can be expanded
|
||||||
|
log.vdebug("homePath: WARNING - no HOME environment variable set");
|
||||||
|
homePath = "~";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output homePath calculation
|
||||||
|
log.vdebug("homePath: ", homePath);
|
||||||
|
|
||||||
|
|
||||||
|
// Determine the correct configuration directory to use
|
||||||
|
string configDirBase;
|
||||||
|
if (confdirOption != "") {
|
||||||
|
// A CLI 'confdir' was passed in
|
||||||
|
log.vdebug("configDirName: CLI override to set configDirName to: ", confdirOption);
|
||||||
|
if (canFind(confdirOption,"~")) {
|
||||||
|
// A ~ was found
|
||||||
|
log.vdebug("configDirName: A '~' was found in configDirName, using the calculated 'homePath' to replace '~'");
|
||||||
|
configDirName = homePath ~ strip(confdirOption,"~","~");
|
||||||
|
} else {
|
||||||
|
configDirName = confdirOption;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Determine the base directory relative to which user specific configuration files should be stored.
|
||||||
|
if (environment.get("XDG_CONFIG_HOME") != ""){
|
||||||
|
log.vdebug("configDirBase: XDG_CONFIG_HOME environment variable set");
|
||||||
|
configDirBase = environment.get("XDG_CONFIG_HOME");
|
||||||
|
} else {
|
||||||
|
// XDG_CONFIG_HOME does not exist on systems where X11 is not present - ie - headless systems / servers
|
||||||
|
log.vdebug("configDirBase: WARNING - no XDG_CONFIG_HOME environment variable set");
|
||||||
|
configDirBase = homePath ~ "/.config";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output configDirBase calculation
|
||||||
|
log.vdebug("configDirBase: ", configDirBase);
|
||||||
|
// Set the default application configuration directory
|
||||||
|
log.vdebug("configDirName: Configuring application to use default config path");
|
||||||
|
// configDirBase contains the correct path so we do not need to check for presence of '~'
|
||||||
|
configDirName = configDirBase ~ "/onedrive";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log.vlog("Using Config Dir: ", configDirName);
|
||||||
|
if (!exists(configDirName)) mkdirRecurse(configDirName);
|
||||||
|
|
||||||
refreshTokenFilePath = configDirName ~ "/refresh_token";
|
refreshTokenFilePath = configDirName ~ "/refresh_token";
|
||||||
deltaLinkFilePath = configDirName ~ "/delta_link";
|
deltaLinkFilePath = configDirName ~ "/delta_link";
|
||||||
databaseFilePath = configDirName ~ "/items.sqlite3";
|
databaseFilePath = configDirName ~ "/items.sqlite3";
|
||||||
|
@ -26,39 +123,8 @@ final class Config
|
||||||
syncListFilePath = configDirName ~ "/sync_list";
|
syncListFilePath = configDirName ~ "/sync_list";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init()
|
bool initialize()
|
||||||
{
|
{
|
||||||
// Default configuration directory
|
|
||||||
setValue("sync_dir", "~/OneDrive");
|
|
||||||
// Skip Directories - no directories are skipped by default
|
|
||||||
setValue("skip_dir", "");
|
|
||||||
// 'skilion' configuration was: .*|~*
|
|
||||||
// Skip files that start with ~
|
|
||||||
// Skip files that start with .~ (like .~lock.* files generated by LibreOffice)
|
|
||||||
// Skip files that end in .tmp
|
|
||||||
setValue("skip_file", "~*|.~*|*.tmp");
|
|
||||||
// By default skip dot files & folders are not skipped
|
|
||||||
setValue("skip_dotfiles", "false");
|
|
||||||
// By default symlinks are not skipped (using string type
|
|
||||||
// instead of boolean because hashmap only stores string types)
|
|
||||||
setValue("skip_symlinks", "false");
|
|
||||||
// Configure the monitor mode loop - the number of seconds by which
|
|
||||||
// each sync operation is undertaken when idle under monitor mode
|
|
||||||
setValue("monitor_interval", "45");
|
|
||||||
// Configure the default logging directory to be /var/log/onedrive/
|
|
||||||
setValue("log_dir", "/var/log/onedrive/");
|
|
||||||
// Configure a default empty value for drive_id
|
|
||||||
setValue("drive_id", "");
|
|
||||||
// Minimal changes that trigger a log and notification on sync
|
|
||||||
setValue("min_notif_changes", "5");
|
|
||||||
// Frequency of log messages in monitor, ie after n sync runs ship out a log message
|
|
||||||
setValue("monitor_log_frequency", "5");
|
|
||||||
// Number of n sync runs before performing a full local scan of sync_dir
|
|
||||||
// By default 10 which means every ~7.5 minutes a full disk scan of sync_dir will occur
|
|
||||||
setValue("monitor_fullscan_frequency", "10");
|
|
||||||
// Check if we should ignore a directory if a special file (.nosync) is present
|
|
||||||
setValue("check_nosync", "false");
|
|
||||||
|
|
||||||
if (!load(userConfigFilePath)) {
|
if (!load(userConfigFilePath)) {
|
||||||
// What was the reason for failure?
|
// What was the reason for failure?
|
||||||
if (!exists(userConfigFilePath)) {
|
if (!exists(userConfigFilePath)) {
|
||||||
|
@ -72,9 +138,169 @@ final class Config
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string getValue(string key)
|
|
||||||
|
void update_from_args(string[] args)
|
||||||
{
|
{
|
||||||
auto p = key in values;
|
|
||||||
|
// Add additional options that are NOT configurable via config file
|
||||||
|
stringValues["create_directory"] = "";
|
||||||
|
stringValues["destination_directory"] = "";
|
||||||
|
stringValues["get_o365_drive_id"] = "";
|
||||||
|
stringValues["remove_directory"] = "";
|
||||||
|
stringValues["single_directory"] = "";
|
||||||
|
stringValues["source_directory"] = "";
|
||||||
|
boolValues["display_config"] = false;
|
||||||
|
boolValues["display_sync_status"] = false;
|
||||||
|
boolValues["resync"] = false;
|
||||||
|
boolValues["print_token"] = false;
|
||||||
|
boolValues["logout"] = false;
|
||||||
|
boolValues["monitor"] = false;
|
||||||
|
boolValues["synchronize"] = false;
|
||||||
|
|
||||||
|
|
||||||
|
// Application Startup option validation
|
||||||
|
try {
|
||||||
|
string tmpStr;
|
||||||
|
bool tmpBol;
|
||||||
|
long tmpVerb;
|
||||||
|
auto opt = getopt(
|
||||||
|
args,
|
||||||
|
std.getopt.config.bundling,
|
||||||
|
std.getopt.config.caseSensitive,
|
||||||
|
"check-for-nomount",
|
||||||
|
"Check for the presence of .nosync in the syncdir root. If found, do not perform sync.",
|
||||||
|
&boolValues["check_nomount"],
|
||||||
|
"check-for-nosync",
|
||||||
|
"Check for the presence of .nosync in each directory. If found, skip directory from sync.",
|
||||||
|
&boolValues["check_nosync"],
|
||||||
|
"create-directory",
|
||||||
|
"Create a directory on OneDrive - no sync will be performed.",
|
||||||
|
&stringValues["create_directory"],
|
||||||
|
"debug-https",
|
||||||
|
"Debug OneDrive HTTPS communication.",
|
||||||
|
&boolValues["debug_https"],
|
||||||
|
"destination-directory",
|
||||||
|
"Destination directory for renamed or move on OneDrive - no sync will be performed.",
|
||||||
|
&stringValues["destination_directory"],
|
||||||
|
"disable-notifications",
|
||||||
|
"Do not use desktop notifications in monitor mode.",
|
||||||
|
&boolValues["disable_notifications"],
|
||||||
|
"disable-upload-validation",
|
||||||
|
"Disable upload validation when uploading to OneDrive",
|
||||||
|
&boolValues["disable_upload_validation"],
|
||||||
|
"display-config",
|
||||||
|
"Display what options the client will use as currently configured - no sync will be performed.",
|
||||||
|
&boolValues["display_config"],
|
||||||
|
"display-sync-status",
|
||||||
|
"Display the sync status of the client - no sync will be performed.",
|
||||||
|
&boolValues["display_sync_status"],
|
||||||
|
"download-only|d",
|
||||||
|
"Only download remote changes",
|
||||||
|
&boolValues["download_only"],
|
||||||
|
"dry-run",
|
||||||
|
"Perform a trial sync with no changes made",
|
||||||
|
&boolValues["dry_run"],
|
||||||
|
"enable-logging",
|
||||||
|
"Enable client activity to a separate log file",
|
||||||
|
&boolValues["enable_logging"],
|
||||||
|
"force-http-1.1",
|
||||||
|
"Force the use of HTTP 1.1 for all operations",
|
||||||
|
&boolValues["force_http_11"],
|
||||||
|
"get-O365-drive-id",
|
||||||
|
"Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library",
|
||||||
|
&stringValues["get_o365_drive_id"],
|
||||||
|
"local-first",
|
||||||
|
"Synchronize from the local directory source first, before downloading changes from OneDrive.",
|
||||||
|
&boolValues["local_first"],
|
||||||
|
"log-dir",
|
||||||
|
"Directory where logging output is saved to, needs to end with a slash.",
|
||||||
|
&stringValues["log_dir"],
|
||||||
|
"logout",
|
||||||
|
"Logout the current user",
|
||||||
|
&boolValues["logout"],
|
||||||
|
"min-notif-changes",
|
||||||
|
"Minimum number of pending incoming changes necessary to trigger a desktop notification",
|
||||||
|
&longValues["min_notif_changes"],
|
||||||
|
"monitor|m",
|
||||||
|
"Keep monitoring for local and remote changes",
|
||||||
|
&boolValues["monitor"],
|
||||||
|
"monitor-interval",
|
||||||
|
"Number of seconds by which each sync operation is undertaken when idle under monitor mode.",
|
||||||
|
&longValues["monitor_interval"],
|
||||||
|
"monitor-fullscan-frequency",
|
||||||
|
"Number of sync runs before performing a full local scan of the synced directory",
|
||||||
|
&longValues["monitor_fullscan_frequency"],
|
||||||
|
"monitor-log-frequency",
|
||||||
|
"Frequency of logging in monitor mode",
|
||||||
|
&longValues["monitor_log_frequency"],
|
||||||
|
"no-remote-delete",
|
||||||
|
"Do not delete local file 'deletes' from OneDrive when using --upload-only",
|
||||||
|
&boolValues["no_remote_delete"],
|
||||||
|
"print-token",
|
||||||
|
"Print the access token, useful for debugging",
|
||||||
|
&boolValues["print_token"],
|
||||||
|
"resync",
|
||||||
|
"Forget the last saved state, perform a full sync",
|
||||||
|
&boolValues["resync"],
|
||||||
|
"remove-directory",
|
||||||
|
"Remove a directory on OneDrive - no sync will be performed.",
|
||||||
|
&stringValues["remove_directory"],
|
||||||
|
"single-directory",
|
||||||
|
"Specify a single local directory within the OneDrive root to sync.",
|
||||||
|
&stringValues["single_directory"],
|
||||||
|
"skip-dot-files",
|
||||||
|
"Skip dot files and folders from syncing",
|
||||||
|
&boolValues["skip_dotfiles"],
|
||||||
|
"skip-file",
|
||||||
|
"Skip any files that match this pattern from syncing",
|
||||||
|
&stringValues["skip_file"],
|
||||||
|
"skip-symlinks",
|
||||||
|
"Skip syncing of symlinks",
|
||||||
|
&boolValues["skip_symlinks"],
|
||||||
|
"source-directory",
|
||||||
|
"Source directory to rename or move on OneDrive - no sync will be performed.",
|
||||||
|
&stringValues["source_directory"],
|
||||||
|
"syncdir",
|
||||||
|
"Specify the local directory used for synchronization to OneDrive",
|
||||||
|
&stringValues["sync_dir"],
|
||||||
|
"synchronize",
|
||||||
|
"Perform a synchronization",
|
||||||
|
&boolValues["synchronize"],
|
||||||
|
"upload-only",
|
||||||
|
"Only upload to OneDrive, do not sync changes from OneDrive locally",
|
||||||
|
&boolValues["upload_only"],
|
||||||
|
// duplicated from main.d to get full help output!
|
||||||
|
"confdir",
|
||||||
|
"Set the directory used to store the configuration files",
|
||||||
|
&tmpStr,
|
||||||
|
"verbose|v+",
|
||||||
|
"Print more details, useful for debugging (repeat for extra debugging)",
|
||||||
|
&tmpVerb,
|
||||||
|
"version",
|
||||||
|
"Print the version and exit",
|
||||||
|
&tmpBol
|
||||||
|
|
||||||
|
);
|
||||||
|
if (opt.helpWanted) {
|
||||||
|
outputLongHelp(opt.options);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
} catch (GetOptException e) {
|
||||||
|
log.error(e.msg);
|
||||||
|
log.error("Try 'onedrive -h' for more information");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// error
|
||||||
|
log.error(e.msg);
|
||||||
|
log.error("Try 'onedrive -h' for more information");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string getValueString(string key)
|
||||||
|
{
|
||||||
|
auto p = key in stringValues;
|
||||||
if (p) {
|
if (p) {
|
||||||
return *p;
|
return *p;
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,19 +308,39 @@ final class Config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string getValue(string key, string value)
|
long getValueLong(string key)
|
||||||
{
|
{
|
||||||
auto p = key in values;
|
auto p = key in longValues;
|
||||||
if (p) {
|
if (p) {
|
||||||
return *p;
|
return *p;
|
||||||
} else {
|
} else {
|
||||||
return value;
|
throw new Exception("Missing config value: " ~ key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setValue(string key, string value)
|
bool getValueBool(string key)
|
||||||
{
|
{
|
||||||
values[key] = value;
|
auto p = key in boolValues;
|
||||||
|
if (p) {
|
||||||
|
return *p;
|
||||||
|
} else {
|
||||||
|
throw new Exception("Missing config value: " ~ key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValueBool(string key, bool value)
|
||||||
|
{
|
||||||
|
boolValues[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValueString(string key, string value)
|
||||||
|
{
|
||||||
|
stringValues[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValueLong(string key, long value)
|
||||||
|
{
|
||||||
|
longValues[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool load(string filename)
|
private bool load(string filename)
|
||||||
|
@ -109,13 +355,26 @@ final class Config
|
||||||
if (!c.empty) {
|
if (!c.empty) {
|
||||||
c.popFront(); // skip the whole match
|
c.popFront(); // skip the whole match
|
||||||
string key = c.front.dup;
|
string key = c.front.dup;
|
||||||
auto p = key in values;
|
auto p = key in boolValues;
|
||||||
if (p) {
|
if (p) {
|
||||||
c.popFront();
|
c.popFront();
|
||||||
setValue(key, c.front.dup);
|
// only accept "true" as true value. TODO Should we support other formats?
|
||||||
|
setValueBool(key, c.front.dup == "true" ? true : false);
|
||||||
} else {
|
} else {
|
||||||
log.log("Unknown key in config file: ", key);
|
auto pp = key in stringValues;
|
||||||
return false;
|
if (pp) {
|
||||||
|
c.popFront();
|
||||||
|
setValueString(key, c.front.dup);
|
||||||
|
} else {
|
||||||
|
auto ppp = key in longValues;
|
||||||
|
if (ppp) {
|
||||||
|
c.popFront();
|
||||||
|
setValueLong(key, to!long(c.front.dup));
|
||||||
|
} else {
|
||||||
|
log.log("Unknown key in config file: ", key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.log("Malformed config line: ", line);
|
log.log("Malformed config line: ", line);
|
||||||
|
@ -126,10 +385,55 @@ final class Config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void outputLongHelp(Option[] opt)
|
||||||
|
{
|
||||||
|
auto argsNeedingOptions = [
|
||||||
|
"--confdir",
|
||||||
|
"--create-directory",
|
||||||
|
"--destination-directory",
|
||||||
|
"--get-O365-drive-id",
|
||||||
|
"--log-dir",
|
||||||
|
"--min-notif-changes",
|
||||||
|
"--monitor-interval",
|
||||||
|
"--monitor-log-frequency",
|
||||||
|
"--monitor-fullscan-frequency",
|
||||||
|
"--remove-directory",
|
||||||
|
"--single-directory",
|
||||||
|
"--skip-file",
|
||||||
|
"--source-directory",
|
||||||
|
"--syncdir" ];
|
||||||
|
writeln(`OneDrive - a client for OneDrive Cloud Services
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
onedrive [options] --synchronize
|
||||||
|
Do a one time synchronization
|
||||||
|
onedrive [options] --monitor
|
||||||
|
Monitor filesystem and sync regularly
|
||||||
|
onedrive [options] --display-config
|
||||||
|
Display the currently used configuration
|
||||||
|
onedrive [options] --display-sync-status
|
||||||
|
Query OneDrive service and report on pending changes
|
||||||
|
onedrive -h | --help
|
||||||
|
Show this help screen
|
||||||
|
onedrive --version
|
||||||
|
Show version
|
||||||
|
|
||||||
|
Options:
|
||||||
|
`);
|
||||||
|
foreach (it; opt.sort!("a.optLong < b.optLong")) {
|
||||||
|
writefln(" %s%s%s%s\n %s",
|
||||||
|
it.optLong,
|
||||||
|
it.optShort == "" ? "" : " " ~ it.optShort,
|
||||||
|
argsNeedingOptions.canFind(it.optLong) ? " ARG" : "",
|
||||||
|
it.required ? " (required)" : "", it.help);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
auto cfg = new Config("");
|
auto cfg = new Config("");
|
||||||
cfg.load("config");
|
cfg.load("config");
|
||||||
assert(cfg.getValue("sync_dir") == "~/OneDrive");
|
assert(cfg.getValueString("sync_dir") == "~/OneDrive");
|
||||||
assert(cfg.getValue("empty", "default") == "default");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ version(Notifications) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable verbose logging
|
// enable verbose logging
|
||||||
int verbose;
|
long verbose;
|
||||||
bool writeLogFile = false;
|
bool writeLogFile = false;
|
||||||
|
|
||||||
private bool doNotifications;
|
private bool doNotifications;
|
||||||
|
|
427
src/main.d
427
src/main.d
|
@ -19,114 +19,26 @@ int main(string[] args)
|
||||||
// Disable buffering on stdout
|
// Disable buffering on stdout
|
||||||
stdout.setvbuf(0, _IONBF);
|
stdout.setvbuf(0, _IONBF);
|
||||||
|
|
||||||
// Application Option Variables
|
|
||||||
// Add a check mounts option to resolve https://github.com/abraunegg/onedrive/issues/8
|
|
||||||
bool checkMount = false;
|
|
||||||
// Check if we should ignore a directory if a special file (.nosync) is present - https://github.com/abraunegg/onedrive/issues/163
|
|
||||||
bool checkNoSync = false;
|
|
||||||
// configuration directory
|
// configuration directory
|
||||||
string configDirName;
|
string confdirOption;
|
||||||
// Create a single root directory on OneDrive
|
|
||||||
string createDirectory;
|
|
||||||
// The destination directory if we are using the OneDrive client to rename a directory
|
|
||||||
string destinationDirectory;
|
|
||||||
// Debug the HTTPS submit operations if required
|
|
||||||
bool debugHttp = false;
|
|
||||||
// Do not use notifications in monitor mode
|
|
||||||
bool disableNotifications = false;
|
|
||||||
// Display application configuration but do not sync
|
|
||||||
bool displayConfiguration = false;
|
|
||||||
// Display sync status
|
|
||||||
bool displaySyncStatus = false;
|
|
||||||
// only download remote changes
|
|
||||||
bool downloadOnly = false;
|
|
||||||
// Does the user want to disable upload validation - https://github.com/abraunegg/onedrive/issues/205
|
|
||||||
// SharePoint will associate some metadata from the library the file is uploaded to directly in the file - thus change file size & checksums
|
|
||||||
bool disableUploadValidation = false;
|
|
||||||
// Perform only a dry run - not applicable for --monitor mode
|
|
||||||
bool dryRun = false;
|
|
||||||
// Do we enable a log file
|
|
||||||
bool enableLogFile = false;
|
|
||||||
// Force the use of HTTP 1.1 to overcome curl => 7.62.0 where some operations are now sent via HTTP/2
|
|
||||||
// Whilst HTTP/2 operations are handled, in some cases the handling of this outside of the client is not being done correctly (router, other) thus the client breaks
|
|
||||||
// This flag then allows the user to downgrade all HTTP operations to HTTP 1.1 for maximum network path compatibility
|
|
||||||
bool forceHTTP11 = false;
|
|
||||||
// SharePoint / Office 365 Shared Library name to query
|
|
||||||
string o365SharedLibraryName;
|
|
||||||
// Local sync - Upload local changes first before downloading changes from OneDrive
|
|
||||||
bool localFirst = false;
|
|
||||||
// remove the current user and sync state
|
|
||||||
bool logout = false;
|
|
||||||
// enable monitor mode
|
|
||||||
bool monitor = false;
|
|
||||||
// Add option for no remote delete
|
|
||||||
bool noRemoteDelete = false;
|
|
||||||
// print the access token
|
|
||||||
bool printAccessToken = false;
|
|
||||||
// force a full resync
|
|
||||||
bool resync = false;
|
|
||||||
// Remove a single directory on OneDrive
|
|
||||||
string removeDirectory;
|
|
||||||
// This allows for selective directory syncing instead of everything under ~/OneDrive/
|
|
||||||
string singleDirectory;
|
|
||||||
// Skip dot files & folders - eg .file or /.folder/
|
|
||||||
bool skipDotFiles = false;
|
|
||||||
// Add option to skip symlinks
|
|
||||||
bool skipSymlinks = false;
|
|
||||||
// The source directory if we are using the OneDrive client to rename a directory
|
|
||||||
string sourceDirectory;
|
|
||||||
// override the sync directory
|
|
||||||
string syncDirName;
|
|
||||||
// Configure a flag to perform a sync
|
|
||||||
// This is beneficial so that if just running the client itself - without any options, or sync check, the client does not perform a sync
|
|
||||||
bool synchronize = false;
|
|
||||||
// Upload Only
|
|
||||||
bool uploadOnly = false;
|
|
||||||
// enable verbose logging
|
|
||||||
bool verbose = false;
|
|
||||||
// print the version and exit
|
|
||||||
bool printVersion = false;
|
|
||||||
|
|
||||||
// Application Startup option validation
|
|
||||||
try {
|
try {
|
||||||
|
// print the version and exit
|
||||||
|
bool printVersion = false;
|
||||||
auto opt = getopt(
|
auto opt = getopt(
|
||||||
args,
|
args,
|
||||||
|
std.getopt.config.passThrough,
|
||||||
std.getopt.config.bundling,
|
std.getopt.config.bundling,
|
||||||
std.getopt.config.caseSensitive,
|
std.getopt.config.caseSensitive,
|
||||||
"check-for-nomount", "Check for the presence of .nosync in the syncdir root. If found, do not perform sync.", &checkMount,
|
"confdir", "Set the directory used to store the configuration files", &confdirOption,
|
||||||
"check-for-nosync", "Check for the presence of .nosync in each directory. If found, skip directory from sync.", &checkNoSync,
|
|
||||||
"confdir", "Set the directory used to store the configuration files", &configDirName,
|
|
||||||
"create-directory", "Create a directory on OneDrive - no sync will be performed.", &createDirectory,
|
|
||||||
"destination-directory", "Destination directory for renamed or move on OneDrive - no sync will be performed.", &destinationDirectory,
|
|
||||||
"debug-https", "Debug OneDrive HTTPS communication.", &debugHttp,
|
|
||||||
"disable-notifications", "Do not use desktop notifications in monitor mode.", &disableNotifications,
|
|
||||||
"display-config", "Display what options the client will use as currently configured - no sync will be performed.", &displayConfiguration,
|
|
||||||
"display-sync-status", "Display the sync status of the client - no sync will be performed.", &displaySyncStatus,
|
|
||||||
"download-only|d", "Only download remote changes", &downloadOnly,
|
|
||||||
"disable-upload-validation", "Disable upload validation when uploading to OneDrive", &disableUploadValidation,
|
|
||||||
"dry-run", "Perform a trial sync with no changes made", &dryRun,
|
|
||||||
"enable-logging", "Enable client activity to a separate log file", &enableLogFile,
|
|
||||||
"force-http-1.1", "Force the use of HTTP 1.1 for all operations", &forceHTTP11,
|
|
||||||
"get-O365-drive-id", "Query and return the Office 365 Drive ID for a given Office 365 SharePoint Shared Library", &o365SharedLibraryName,
|
|
||||||
"local-first", "Synchronize from the local directory source first, before downloading changes from OneDrive.", &localFirst,
|
|
||||||
"logout", "Logout the current user", &logout,
|
|
||||||
"monitor|m", "Keep monitoring for local and remote changes", &monitor,
|
|
||||||
"no-remote-delete", "Do not delete local file 'deletes' from OneDrive when using --upload-only", &noRemoteDelete,
|
|
||||||
"print-token", "Print the access token, useful for debugging", &printAccessToken,
|
|
||||||
"resync", "Forget the last saved state, perform a full sync", &resync,
|
|
||||||
"remove-directory", "Remove a directory on OneDrive - no sync will be performed.", &removeDirectory,
|
|
||||||
"single-directory", "Specify a single local directory within the OneDrive root to sync.", &singleDirectory,
|
|
||||||
"skip-dot-files", "Skip dot files and folders from syncing", &skipDotFiles,
|
|
||||||
"skip-symlinks", "Skip syncing of symlinks", &skipSymlinks,
|
|
||||||
"source-directory", "Source directory to rename or move on OneDrive - no sync will be performed.", &sourceDirectory,
|
|
||||||
"syncdir", "Specify the local directory used for synchronization to OneDrive", &syncDirName,
|
|
||||||
"synchronize", "Perform a synchronization", &synchronize,
|
|
||||||
"upload-only", "Only upload to OneDrive, do not sync changes from OneDrive locally", &uploadOnly,
|
|
||||||
"verbose|v+", "Print more details, useful for debugging (repeat for extra debugging)", &log.verbose,
|
"verbose|v+", "Print more details, useful for debugging (repeat for extra debugging)", &log.verbose,
|
||||||
"version", "Print the version and exit", &printVersion
|
"version", "Print the version and exit", &printVersion
|
||||||
);
|
);
|
||||||
if (opt.helpWanted) {
|
if (opt.helpWanted) {
|
||||||
outputLongHelp(opt.options);
|
args ~= "--help";
|
||||||
|
}
|
||||||
|
if (printVersion) {
|
||||||
|
std.stdio.write("onedrive ", import("version"));
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
} catch (GetOptException e) {
|
} catch (GetOptException e) {
|
||||||
|
@ -140,92 +52,28 @@ int main(string[] args)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main function variables
|
|
||||||
string homePath = "";
|
|
||||||
string configDirBase = "";
|
|
||||||
// Debug the HTTPS response operations if required
|
|
||||||
bool debugHttpSubmit;
|
|
||||||
// Are we able to reach the OneDrive Service
|
|
||||||
bool online = false;
|
|
||||||
// simulateNoRefreshTokenFile in case of --dry-run & --logout
|
|
||||||
bool simulateNoRefreshTokenFile = false;
|
|
||||||
|
|
||||||
// Determine the users home directory.
|
|
||||||
// Need to avoid using ~ here as expandTilde() below does not interpret correctly when running under init.d or systemd scripts
|
|
||||||
// Check for HOME environment variable
|
|
||||||
if (environment.get("HOME") != ""){
|
|
||||||
// Use HOME environment variable
|
|
||||||
log.vdebug("homePath: HOME environment variable set");
|
|
||||||
homePath = environment.get("HOME");
|
|
||||||
} else {
|
|
||||||
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
|
|
||||||
// No shell is set or username - observed case when running as systemd service under CentOS 7.x
|
|
||||||
log.vdebug("homePath: WARNING - no HOME environment variable set");
|
|
||||||
log.vdebug("homePath: WARNING - no SHELL environment variable set");
|
|
||||||
log.vdebug("homePath: WARNING - no USER environment variable set");
|
|
||||||
homePath = "/root";
|
|
||||||
} else {
|
|
||||||
// A shell & valid user is set, but no HOME is set, use ~ which can be expanded
|
|
||||||
log.vdebug("homePath: WARNING - no HOME environment variable set");
|
|
||||||
homePath = "~";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output homePath calculation
|
|
||||||
log.vdebug("homePath: ", homePath);
|
|
||||||
|
|
||||||
// Determine the base directory relative to which user specific configuration files should be stored.
|
// load configuration file if available
|
||||||
if (environment.get("XDG_CONFIG_HOME") != ""){
|
auto cfg = new config.Config(confdirOption);
|
||||||
log.vdebug("configDirBase: XDG_CONFIG_HOME environment variable set");
|
if (!cfg.initialize()) {
|
||||||
configDirBase = environment.get("XDG_CONFIG_HOME");
|
|
||||||
} else {
|
|
||||||
// XDG_CONFIG_HOME does not exist on systems where X11 is not present - ie - headless systems / servers
|
|
||||||
log.vdebug("configDirBase: WARNING - no XDG_CONFIG_HOME environment variable set");
|
|
||||||
configDirBase = homePath ~ "/.config";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output configDirBase calculation
|
|
||||||
log.vdebug("configDirBase: ", configDirBase);
|
|
||||||
|
|
||||||
// Determine the correct configuration directory to use
|
|
||||||
if (configDirName != "") {
|
|
||||||
// A CLI 'confdir' was passed in
|
|
||||||
log.vdebug("configDirName: CLI override to set configDirName to: ", configDirName);
|
|
||||||
if (canFind(configDirName,"~")) {
|
|
||||||
// A ~ was found
|
|
||||||
log.vdebug("configDirName: A '~' was found in configDirName, using the calculated 'homePath' to replace '~'");
|
|
||||||
configDirName = homePath ~ strip(configDirName,"~","~");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set the default application configuration directory
|
|
||||||
log.vdebug("configDirName: Configuring application to use default config path");
|
|
||||||
// configDirBase contains the correct path so we do not need to check for presence of '~'
|
|
||||||
configDirName = configDirBase ~ "/onedrive";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (printVersion) {
|
|
||||||
std.stdio.write("onedrive ", import("version"));
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// dry-run notification
|
|
||||||
if (dryRun) {
|
|
||||||
log.log("DRY-RUN Configured. Output below shows what 'would' have occurred.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// load application configuration
|
|
||||||
log.vlog("Loading config ...");
|
|
||||||
log.vlog("Using Config Dir: ", configDirName);
|
|
||||||
if (!exists(configDirName)) mkdirRecurse(configDirName);
|
|
||||||
auto cfg = new config.Config(configDirName);
|
|
||||||
if(!cfg.init()){
|
|
||||||
// There was an error loading the configuration
|
// There was an error loading the configuration
|
||||||
// Error message already printed
|
// Error message already printed
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
// update configuration from command line args
|
||||||
|
cfg.update_from_args(args);
|
||||||
|
|
||||||
|
// dry-run notification
|
||||||
|
if (cfg.getValueBool("dry_run")) {
|
||||||
|
log.log("DRY-RUN Configured. Output below shows what 'would' have occurred.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Are we able to reach the OneDrive Service
|
||||||
|
bool online = false;
|
||||||
|
|
||||||
// dry-run database setup
|
// dry-run database setup
|
||||||
if (dryRun) {
|
if (cfg.getValueBool("dry_run")) {
|
||||||
// Make a copy of the original items.sqlite3 for use as the dry run copy if it exists
|
// Make a copy of the original items.sqlite3 for use as the dry run copy if it exists
|
||||||
if (exists(cfg.databaseFilePath)) {
|
if (exists(cfg.databaseFilePath)) {
|
||||||
// copy the file
|
// copy the file
|
||||||
|
@ -234,58 +82,29 @@ int main(string[] args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// command line parameters to override default 'config' & take precedence
|
|
||||||
// Set the client to skip specific directories if .nosync is found AND ONLY if --check-for-nosync was passed in
|
|
||||||
if (checkNoSync) {
|
|
||||||
log.vdebug("CLI override to set check_nosync to: true");
|
|
||||||
cfg.setValue("check_nosync", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the client to skip dot files & folders if --skip-dot-files was passed in
|
|
||||||
if (skipDotFiles) {
|
|
||||||
// The user passed in an alternate skip_dotfiles as to what was either in 'config' file or application default
|
|
||||||
log.vdebug("CLI override to set skip_dotfiles to: true");
|
|
||||||
cfg.setValue("skip_dotfiles", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the client to skip symbolic links if --skip-symlinks was passed in
|
|
||||||
if (skipSymlinks) {
|
|
||||||
// The user passed in an alternate skip_symlinks as to what was either in 'config' file or application default
|
|
||||||
log.vdebug("CLI override to set skip_symlinks to: true");
|
|
||||||
cfg.setValue("skip_symlinks", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the OneDrive Local Sync Directory if was passed in via --syncdir
|
|
||||||
if (syncDirName) {
|
|
||||||
// The user passed in an alternate sync_dir as to what was either in 'config' file or application default
|
|
||||||
// Do not expandTilde here as we do not know if we reliably can
|
|
||||||
log.vdebug("CLI override to set sync_dir to: ", syncDirName);
|
|
||||||
cfg.setValue("sync_dir", syncDirName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync_dir environment handling to handle ~ expansion properly
|
// sync_dir environment handling to handle ~ expansion properly
|
||||||
string syncDir;
|
string syncDir;
|
||||||
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
|
if ((environment.get("SHELL") == "") && (environment.get("USER") == "")){
|
||||||
log.vdebug("sync_dir: No SHELL or USER environment variable configuration detected");
|
log.vdebug("sync_dir: No SHELL or USER environment variable configuration detected");
|
||||||
// No shell or user set, so expandTilde() will fail - usually headless system running under init.d / systemd or potentially Docker
|
// No shell or user set, so expandTilde() will fail - usually headless system running under init.d / systemd or potentially Docker
|
||||||
// Does the 'currently configured' sync_dir include a ~
|
// Does the 'currently configured' sync_dir include a ~
|
||||||
if (canFind(cfg.getValue("sync_dir"),"~")) {
|
if (canFind(cfg.getValueString("sync_dir"), "~")) {
|
||||||
// A ~ was found
|
// A ~ was found
|
||||||
log.vdebug("sync_dir: A '~' was found in sync_dir, using the calculated 'homePath' to replace '~'");
|
log.vdebug("sync_dir: A '~' was found in sync_dir, using the calculated 'homePath' to replace '~'");
|
||||||
syncDir = homePath ~ strip(cfg.getValue("sync_dir"),"~","~");
|
syncDir = cfg.homePath ~ strip(cfg.getValueString("sync_dir"), "~");
|
||||||
} else {
|
} else {
|
||||||
// No ~ found in sync_dir, use as is
|
// No ~ found in sync_dir, use as is
|
||||||
log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
|
log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
|
||||||
syncDir = cfg.getValue("sync_dir");
|
syncDir = cfg.getValueString("sync_dir");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// A shell and user is set, expand any ~ as this will be expanded correctly if present
|
// A shell and user is set, expand any ~ as this will be expanded correctly if present
|
||||||
log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
|
log.vdebug("sync_dir: Getting syncDir from config value sync_dir");
|
||||||
if (canFind(cfg.getValue("sync_dir"),"~")) {
|
if (canFind(cfg.getValueString("sync_dir"), "~")) {
|
||||||
log.vdebug("sync_dir: A '~' was found in configured sync_dir, automatically expanding as SHELL and USER environment variable is set");
|
log.vdebug("sync_dir: A '~' was found in configured sync_dir, automatically expanding as SHELL and USER environment variable is set");
|
||||||
syncDir = expandTilde(cfg.getValue("sync_dir"));
|
syncDir = expandTilde(cfg.getValueString("sync_dir"));
|
||||||
} else {
|
} else {
|
||||||
syncDir = cfg.getValue("sync_dir");
|
syncDir = cfg.getValueString("sync_dir");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,50 +112,47 @@ int main(string[] args)
|
||||||
log.vdebug("syncDir: ", syncDir);
|
log.vdebug("syncDir: ", syncDir);
|
||||||
|
|
||||||
// Configure logging if enabled
|
// Configure logging if enabled
|
||||||
if (enableLogFile){
|
if (cfg.getValueBool("enable_logging")){
|
||||||
// Read in a user defined log directory or use the default
|
// Read in a user defined log directory or use the default
|
||||||
string logDir = cfg.getValue("log_dir");
|
string logDir = cfg.getValueString("log_dir");
|
||||||
log.vlog("Using logfile dir: ", logDir);
|
log.vlog("Using logfile dir: ", logDir);
|
||||||
log.init(logDir);
|
log.init(logDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure whether notifications are used
|
// Configure whether notifications are used
|
||||||
log.setNotifications(monitor && !disableNotifications);
|
log.setNotifications(cfg.getValueBool("monitor") && !cfg.getValueBool("disable_notifications"));
|
||||||
|
|
||||||
// upgrades
|
// upgrades
|
||||||
if (exists(configDirName ~ "/items.db")) {
|
if (exists(cfg.configDirName ~ "/items.db")) {
|
||||||
if (!dryRun) {
|
if (!cfg.getValueBool("dry_run")) {
|
||||||
safeRemove(configDirName ~ "/items.db");
|
safeRemove(cfg.configDirName ~ "/items.db");
|
||||||
}
|
}
|
||||||
log.logAndNotify("Database schema changed, resync needed");
|
log.logAndNotify("Database schema changed, resync needed");
|
||||||
resync = true;
|
cfg.setValueBool("resync", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resync || logout) {
|
if (cfg.getValueBool("resync") || cfg.getValueBool("logout")) {
|
||||||
log.vlog("Deleting the saved status ...");
|
log.vlog("Deleting the saved status ...");
|
||||||
if (!dryRun) {
|
if (!cfg.getValueBool("dry_run")) {
|
||||||
safeRemove(cfg.databaseFilePath);
|
safeRemove(cfg.databaseFilePath);
|
||||||
safeRemove(cfg.deltaLinkFilePath);
|
safeRemove(cfg.deltaLinkFilePath);
|
||||||
safeRemove(cfg.uploadStateFilePath);
|
safeRemove(cfg.uploadStateFilePath);
|
||||||
}
|
}
|
||||||
if (logout) {
|
if (cfg.getValueBool("logout")) {
|
||||||
if (!dryRun) {
|
if (!cfg.getValueBool("dry_run")) {
|
||||||
safeRemove(cfg.refreshTokenFilePath);
|
safeRemove(cfg.refreshTokenFilePath);
|
||||||
} else {
|
|
||||||
// simulate file being removed / unavailable
|
|
||||||
simulateNoRefreshTokenFile = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display current application configuration, no application initialisation
|
// Display current application configuration, no application initialisation
|
||||||
if (displayConfiguration){
|
if (cfg.getValueBool("display_config")){
|
||||||
string userConfigFilePath = configDirName ~ "/config";
|
string userConfigFilePath = cfg.configDirName ~ "/config";
|
||||||
string userSyncList = configDirName ~ "/sync_list";
|
string userSyncList = cfg.configDirName ~ "/sync_list";
|
||||||
// Display application version
|
// Display application version
|
||||||
std.stdio.write("onedrive version = ", import("version"));
|
std.stdio.write("onedrive version = ", import("version"));
|
||||||
// Display all of the pertinent configuration options
|
// Display all of the pertinent configuration options
|
||||||
writeln("Config path = ", configDirName);
|
writeln("Config path = ", cfg.configDirName);
|
||||||
|
|
||||||
// Does a config file exist or are we using application defaults
|
// Does a config file exist or are we using application defaults
|
||||||
if (exists(userConfigFilePath)){
|
if (exists(userConfigFilePath)){
|
||||||
|
@ -346,19 +162,19 @@ int main(string[] args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config Options
|
// Config Options
|
||||||
writeln("Config option 'check_nosync' = ", cfg.getValue("check_nosync"));
|
writeln("Config option 'check_nosync' = ", cfg.getValueBool("check_nosync"));
|
||||||
writeln("Config option 'sync_dir' = ", syncDir);
|
writeln("Config option 'sync_dir' = ", syncDir);
|
||||||
writeln("Config option 'skip_dir' = ", cfg.getValue("skip_dir"));
|
writeln("Config option 'skip_dir' = ", cfg.getValueString("skip_dir"));
|
||||||
writeln("Config option 'skip_file' = ", cfg.getValue("skip_file"));
|
writeln("Config option 'skip_file' = ", cfg.getValueString("skip_file"));
|
||||||
writeln("Config option 'skip_dotfiles' = ", cfg.getValue("skip_dotfiles"));
|
writeln("Config option 'skip_dotfiles' = ", cfg.getValueBool("skip_dotfiles"));
|
||||||
writeln("Config option 'skip_symlinks' = ", cfg.getValue("skip_symlinks"));
|
writeln("Config option 'skip_symlinks' = ", cfg.getValueBool("skip_symlinks"));
|
||||||
writeln("Config option 'monitor_interval' = ", cfg.getValue("monitor_interval"));
|
writeln("Config option 'monitor_interval' = ", cfg.getValueLong("monitor_interval"));
|
||||||
writeln("Config option 'min_notif_changes' = ", cfg.getValue("min_notif_changes"));
|
writeln("Config option 'min_notif_changes' = ", cfg.getValueLong("min_notif_changes"));
|
||||||
writeln("Config option 'log_dir' = ", cfg.getValue("log_dir"));
|
writeln("Config option 'log_dir' = ", cfg.getValueString("log_dir"));
|
||||||
|
|
||||||
// Is config option drive_id configured?
|
// Is config option drive_id configured?
|
||||||
if (cfg.getValue("drive_id", "") != ""){
|
if (cfg.getValueString("drive_id") != ""){
|
||||||
writeln("Config option 'drive_id' = ", cfg.getValue("drive_id"));
|
writeln("Config option 'drive_id' = ", cfg.getValueString("drive_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is sync_list configured?
|
// Is sync_list configured?
|
||||||
|
@ -385,14 +201,14 @@ int main(string[] args)
|
||||||
} catch (CurlException e) {
|
} catch (CurlException e) {
|
||||||
// No network connection to OneDrive Service
|
// No network connection to OneDrive Service
|
||||||
log.error("No network connection to Microsoft OneDrive Service");
|
log.error("No network connection to Microsoft OneDrive Service");
|
||||||
if (!monitor) {
|
if (!cfg.getValueBool("monitor")) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize OneDrive, check for authorization
|
// Initialize OneDrive, check for authorization
|
||||||
oneDrive = new OneDriveApi(cfg, debugHttp, forceHTTP11, dryRun, simulateNoRefreshTokenFile);
|
oneDrive = new OneDriveApi(cfg);
|
||||||
oneDrive.printAccessToken = printAccessToken;
|
oneDrive.printAccessToken = cfg.getValueBool("print_token");
|
||||||
if (!oneDrive.init()) {
|
if (!oneDrive.init()) {
|
||||||
log.error("Could not initialize the OneDrive API");
|
log.error("Could not initialize the OneDrive API");
|
||||||
// workaround for segfault in std.net.curl.Curl.shutdown() on exit
|
// workaround for segfault in std.net.curl.Curl.shutdown() on exit
|
||||||
|
@ -402,13 +218,14 @@ int main(string[] args)
|
||||||
|
|
||||||
// if --synchronize or --monitor not passed in, exit & display help
|
// if --synchronize or --monitor not passed in, exit & display help
|
||||||
auto performSyncOK = false;
|
auto performSyncOK = false;
|
||||||
if (synchronize || monitor) {
|
|
||||||
|
if (cfg.getValueBool("synchronize") || cfg.getValueBool("monitor")) {
|
||||||
performSyncOK = true;
|
performSyncOK = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create-directory, remove-directory, source-directory, destination-directory
|
// create-directory, remove-directory, source-directory, destination-directory
|
||||||
// are activities that dont perform a sync no error message for these items either
|
// are activities that dont perform a sync no error message for these items either
|
||||||
if (((createDirectory != "") || (removeDirectory != "")) || ((sourceDirectory != "") && (destinationDirectory != "")) || (o365SharedLibraryName != "") || (displaySyncStatus == true)) {
|
if (((cfg.getValueString("create_directory") != "") || (cfg.getValueString("remove_directory") != "")) || ((cfg.getValueString("source_directory") != "") && (cfg.getValueString("destination_directory") != "")) || (cfg.getValueString("get_o365_drive_id") != "") || cfg.getValueBool("display_sync_status")) {
|
||||||
performSyncOK = true;
|
performSyncOK = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +237,7 @@ int main(string[] args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if --synchronize && --monitor passed in, exit & display help as these conflict with each other
|
// if --synchronize && --monitor passed in, exit & display help as these conflict with each other
|
||||||
if (synchronize && monitor) {
|
if (cfg.getValueBool("synchronize") && cfg.getValueBool("monitor")) {
|
||||||
writeln("\nERROR: --synchronize and --monitor cannot be used together\n");
|
writeln("\nERROR: --synchronize and --monitor cannot be used together\n");
|
||||||
writeln("Refer to --help to determine which command option you should use.\n");
|
writeln("Refer to --help to determine which command option you should use.\n");
|
||||||
oneDrive.http.shutdown();
|
oneDrive.http.shutdown();
|
||||||
|
@ -429,7 +246,7 @@ int main(string[] args)
|
||||||
|
|
||||||
// Initialize the item database
|
// Initialize the item database
|
||||||
log.vlog("Opening the item database ...");
|
log.vlog("Opening the item database ...");
|
||||||
if (!dryRun) {
|
if (!cfg.getValueBool("dry_run")) {
|
||||||
// Load the items.sqlite3 file as the database
|
// Load the items.sqlite3 file as the database
|
||||||
log.vdebug("Using database file: ", cfg.databaseFilePath);
|
log.vdebug("Using database file: ", cfg.databaseFilePath);
|
||||||
itemDb = new ItemDatabase(cfg.databaseFilePath);
|
itemDb = new ItemDatabase(cfg.databaseFilePath);
|
||||||
|
@ -462,15 +279,15 @@ int main(string[] args)
|
||||||
|
|
||||||
// Configure skip_dir & skip_file from config entries
|
// Configure skip_dir & skip_file from config entries
|
||||||
log.vdebug("Configuring skip_dir ...");
|
log.vdebug("Configuring skip_dir ...");
|
||||||
log.vdebug("skip_dir: ", cfg.getValue("skip_dir"));
|
log.vdebug("skip_dir: ", cfg.getValueString("skip_dir"));
|
||||||
selectiveSync.setDirMask(cfg.getValue("skip_dir"));
|
selectiveSync.setDirMask(cfg.getValueString("skip_dir"));
|
||||||
log.vdebug("Configuring skip_file ...");
|
log.vdebug("Configuring skip_file ...");
|
||||||
log.vdebug("skip_file: ", cfg.getValue("skip_file"));
|
log.vdebug("skip_file: ", cfg.getValueString("skip_file"));
|
||||||
selectiveSync.setFileMask(cfg.getValue("skip_file"));
|
selectiveSync.setFileMask(cfg.getValueString("skip_file"));
|
||||||
|
|
||||||
// Initialize the sync engine
|
// Initialize the sync engine
|
||||||
log.logAndNotify("Initializing the Synchronization Engine ...");
|
log.logAndNotify("Initializing the Synchronization Engine ...");
|
||||||
auto sync = new SyncEngine(cfg, oneDrive, itemDb, selectiveSync, dryRun);
|
auto sync = new SyncEngine(cfg, oneDrive, itemDb, selectiveSync);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!initSyncEngine(sync)) {
|
if (!initSyncEngine(sync)) {
|
||||||
|
@ -478,21 +295,21 @@ int main(string[] args)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
} catch (CurlException e) {
|
} catch (CurlException e) {
|
||||||
if (!monitor) {
|
if (!cfg.getValueBool("monitor")) {
|
||||||
log.log("\nNo Internet connection.");
|
log.log("\nNo internet connection.");
|
||||||
oneDrive.http.shutdown();
|
oneDrive.http.shutdown();
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should only set noRemoteDelete in an upload-only scenario
|
// We should only set noRemoteDelete in an upload-only scenario
|
||||||
if ((uploadOnly)&&(noRemoteDelete)) sync.setNoRemoteDelete();
|
if ((cfg.getValueBool("upload_only"))&&(cfg.getValueBool("no_remote_delete"))) sync.setNoRemoteDelete();
|
||||||
|
|
||||||
// Do we configure to disable the upload validation routine
|
// Do we configure to disable the upload validation routine
|
||||||
if(disableUploadValidation) sync.setDisableUploadValidation();
|
if (cfg.getValueBool("disable_upload_validation")) sync.setDisableUploadValidation();
|
||||||
|
|
||||||
// Do we need to validate the syncDir to check for the presence of a '.nosync' file
|
// Do we need to validate the syncDir to check for the presence of a '.nosync' file
|
||||||
if (checkMount) {
|
if (cfg.getValueBool("check_nomount")) {
|
||||||
// we were asked to check the mounts
|
// we were asked to check the mounts
|
||||||
if (exists(syncDir ~ "/.nosync")) {
|
if (exists(syncDir ~ "/.nosync")) {
|
||||||
log.logAndNotify("ERROR: .nosync file found. Aborting synchronization process to safeguard data.");
|
log.logAndNotify("ERROR: .nosync file found. Aborting synchronization process to safeguard data.");
|
||||||
|
@ -502,53 +319,53 @@ int main(string[] args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we need to create or remove a directory?
|
// Do we need to create or remove a directory?
|
||||||
if ((createDirectory != "") || (removeDirectory != "")) {
|
if ((cfg.getValueString("create_directory") != "") || (cfg.getValueString("remove_directory") != "")) {
|
||||||
|
|
||||||
if (createDirectory != "") {
|
if (cfg.getValueString("create_directory") != "") {
|
||||||
// create a directory on OneDrive
|
// create a directory on OneDrive
|
||||||
sync.createDirectoryNoSync(createDirectory);
|
sync.createDirectoryNoSync(cfg.getValueString("create_directory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeDirectory != "") {
|
if (cfg.getValueString("remove_directory") != "") {
|
||||||
// remove a directory on OneDrive
|
// remove a directory on OneDrive
|
||||||
sync.deleteDirectoryNoSync(removeDirectory);
|
sync.deleteDirectoryNoSync(cfg.getValueString("remove_directory"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we renaming or moving a directory?
|
// Are we renaming or moving a directory?
|
||||||
if ((sourceDirectory != "") && (destinationDirectory != "")) {
|
if ((cfg.getValueString("source_directory") != "") && (cfg.getValueString("destination_directory") != "")) {
|
||||||
// We are renaming or moving a directory
|
// We are renaming or moving a directory
|
||||||
sync.renameDirectoryNoSync(sourceDirectory, destinationDirectory);
|
sync.renameDirectoryNoSync(cfg.getValueString("source_directory"), cfg.getValueString("destination_directory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we obtaining the Office 365 Drive ID for a given Office 365 SharePoint Shared Library?
|
// Are we obtaining the Office 365 Drive ID for a given Office 365 SharePoint Shared Library?
|
||||||
if (o365SharedLibraryName != ""){
|
if (cfg.getValueString("get_o365_drive_id") != ""){
|
||||||
sync.querySiteCollectionForDriveID(o365SharedLibraryName);
|
sync.querySiteCollectionForDriveID(cfg.getValueString("get_o365_drive_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we displaying the sync status of the client?
|
// Are we displaying the sync status of the client?
|
||||||
if (displaySyncStatus) {
|
if (cfg.getValueBool("display_sync_status")) {
|
||||||
string remotePath = "/";
|
string remotePath = "/";
|
||||||
string localPath = ".";
|
string localPath = ".";
|
||||||
|
|
||||||
// Are we doing a single directory check?
|
// Are we doing a single directory check?
|
||||||
if (singleDirectory != ""){
|
if (cfg.getValueString("single_directory") != ""){
|
||||||
// Need two different path strings here
|
// Need two different path strings here
|
||||||
remotePath = singleDirectory;
|
remotePath = cfg.getValueString("single_directory");
|
||||||
localPath = singleDirectory;
|
localPath = cfg.getValueString("single_directory");
|
||||||
}
|
}
|
||||||
sync.queryDriveForChanges(remotePath);
|
sync.queryDriveForChanges(remotePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we performing a sync, resync or monitor operation?
|
// Are we performing a sync, resync or monitor operation?
|
||||||
if ((synchronize) || (resync) || (monitor)) {
|
if ((cfg.getValueBool("synchronize")) || (cfg.getValueBool("resync")) || (cfg.getValueBool("monitor"))) {
|
||||||
|
|
||||||
if ((synchronize) || (resync)) {
|
if ((cfg.getValueBool("synchronize")) || (cfg.getValueBool("resync"))) {
|
||||||
if (online) {
|
if (online) {
|
||||||
// Check user entry for local path - the above chdir means we are already in ~/OneDrive/ thus singleDirectory is local to this path
|
// Check user entry for local path - the above chdir means we are already in ~/OneDrive/ thus singleDirectory is local to this path
|
||||||
if (singleDirectory != ""){
|
if (cfg.getValueString("single_directory") != ""){
|
||||||
// Does the directory we want to sync actually exist?
|
// Does the directory we want to sync actually exist?
|
||||||
if (!exists(singleDirectory)){
|
if (!exists(cfg.getValueString("single_directory"))){
|
||||||
// the requested directory does not exist ..
|
// the requested directory does not exist ..
|
||||||
log.logAndNotify("ERROR: The requested local directory does not exist. Please check ~/OneDrive/ for requested path");
|
log.logAndNotify("ERROR: The requested local directory does not exist. Please check ~/OneDrive/ for requested path");
|
||||||
oneDrive.http.shutdown();
|
oneDrive.http.shutdown();
|
||||||
|
@ -556,14 +373,13 @@ int main(string[] args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the sync
|
performSync(sync, cfg.getValueString("single_directory"), cfg.getValueBool("download_only"), cfg.getValueBool("local_first"), cfg.getValueBool("upload_only"), LOG_NORMAL, true);
|
||||||
performSync(sync, singleDirectory, downloadOnly, localFirst, uploadOnly, LOG_NORMAL, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monitor) {
|
if (cfg.getValueBool("monitor")) {
|
||||||
log.logAndNotify("Initializing monitor ...");
|
log.logAndNotify("Initializing monitor ...");
|
||||||
log.log("OneDrive monitor interval (seconds): ", to!long(cfg.getValue("monitor_interval")));
|
log.log("OneDrive monitor interval (seconds): ", cfg.getValueLong("monitor_interval"));
|
||||||
Monitor m = new Monitor(selectiveSync);
|
Monitor m = new Monitor(selectiveSync);
|
||||||
m.onDirCreated = delegate(string path) {
|
m.onDirCreated = delegate(string path) {
|
||||||
log.vlog("[M] Directory created: ", path);
|
log.vlog("[M] Directory created: ", path);
|
||||||
|
@ -615,19 +431,17 @@ int main(string[] args)
|
||||||
signal(SIGTERM, &exitHandler);
|
signal(SIGTERM, &exitHandler);
|
||||||
|
|
||||||
// initialise the monitor class
|
// initialise the monitor class
|
||||||
if (cfg.getValue("skip_symlinks") == "true") skipSymlinks = true;
|
if (!cfg.getValueBool("download_only")) m.init(cfg, cfg.getValueLong("verbose") > 0, cfg.getValueBool("skip_symlinks"), cfg.getValueBool("check_nosync"));
|
||||||
if (cfg.getValue("check_nosync") == "true") checkNoSync = true;
|
|
||||||
if (!downloadOnly) m.init(cfg, verbose, skipSymlinks, checkNoSync);
|
|
||||||
// monitor loop
|
// monitor loop
|
||||||
immutable auto checkInterval = dur!"seconds"(to!long(cfg.getValue("monitor_interval")));
|
immutable auto checkInterval = dur!"seconds"(cfg.getValueLong("monitor_interval"));
|
||||||
immutable auto logInterval = to!long(cfg.getValue("monitor_log_frequency"));
|
immutable auto logInterval = cfg.getValueLong("monitor_log_frequency");
|
||||||
immutable auto fullScanFrequency = to!long(cfg.getValue("monitor_fullscan_frequency"));
|
immutable auto fullScanFrequency = cfg.getValueLong("monitor_fullscan_frequency");
|
||||||
auto lastCheckTime = MonoTime.currTime();
|
auto lastCheckTime = MonoTime.currTime();
|
||||||
auto logMonitorCounter = 0;
|
auto logMonitorCounter = 0;
|
||||||
auto fullScanCounter = 0;
|
auto fullScanCounter = 0;
|
||||||
bool fullScanRequired = true;
|
bool fullScanRequired = true;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!downloadOnly) m.update(online);
|
if (!cfg.getValueBool("download_only")) m.update(online);
|
||||||
auto currTime = MonoTime.currTime();
|
auto currTime = MonoTime.currTime();
|
||||||
if (currTime - lastCheckTime > checkInterval) {
|
if (currTime - lastCheckTime > checkInterval) {
|
||||||
// log monitor output suppression
|
// log monitor output suppression
|
||||||
|
@ -653,8 +467,8 @@ int main(string[] args)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
performSync(sync, singleDirectory, downloadOnly, localFirst, uploadOnly, (logMonitorCounter == logInterval ? MONITOR_LOG_QUIET : MONITOR_LOG_SILENT), fullScanRequired);
|
performSync(sync, cfg.getValueString("single_directory"), cfg.getValueBool("download_only"), cfg.getValueBool("local_first"), cfg.getValueBool("upload_only"), (logMonitorCounter == logInterval ? MONITOR_LOG_QUIET : MONITOR_LOG_SILENT), fullScanRequired);
|
||||||
if (!downloadOnly) {
|
if (!cfg.getValueBool("download_only")) {
|
||||||
// discard all events that may have been generated by the sync
|
// discard all events that may have been generated by the sync
|
||||||
m.update(false);
|
m.update(false);
|
||||||
}
|
}
|
||||||
|
@ -685,7 +499,7 @@ int main(string[] args)
|
||||||
destroy(itemDb);
|
destroy(itemDb);
|
||||||
|
|
||||||
// --dry-run temp database cleanup
|
// --dry-run temp database cleanup
|
||||||
if (dryRun) {
|
if (cfg.getValueBool("dry_run")) {
|
||||||
if (exists(cfg.databaseFilePathDryRun)) {
|
if (exists(cfg.databaseFilePathDryRun)) {
|
||||||
// remove the file
|
// remove the file
|
||||||
log.vdebug("Removing items-dryrun.sqlite3 as dry run operations complete");
|
log.vdebug("Removing items-dryrun.sqlite3 as dry run operations complete");
|
||||||
|
@ -825,41 +639,4 @@ extern(C) nothrow @nogc @system void exitHandler(int value) {
|
||||||
} catch(Exception e) {}
|
} catch(Exception e) {}
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
void outputLongHelp(Option[] opt)
|
|
||||||
{
|
|
||||||
auto argsNeedingOptions = [
|
|
||||||
"--confdir",
|
|
||||||
"--create-directory",
|
|
||||||
"--destination-directory",
|
|
||||||
"--get-O365-drive-id",
|
|
||||||
"--remove-directory",
|
|
||||||
"--single-directory",
|
|
||||||
"--source-directory",
|
|
||||||
"--syncdir" ];
|
|
||||||
writeln(`OneDrive - a client for OneDrive Cloud Services
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
onedrive [options] --synchronize
|
|
||||||
Do a one time synchronization
|
|
||||||
onedrive [options] --monitor
|
|
||||||
Monitor filesystem and sync regularly
|
|
||||||
onedrive [options] --display-config
|
|
||||||
Display the currently used configuration
|
|
||||||
onedrive [options] --display-sync-status
|
|
||||||
Query OneDrive service and report on pending changes
|
|
||||||
onedrive -h | --help
|
|
||||||
Show this help screen
|
|
||||||
onedrive --version
|
|
||||||
Show version
|
|
||||||
|
|
||||||
Options:
|
|
||||||
`);
|
|
||||||
foreach (it; opt) {
|
|
||||||
writefln(" %s%s%s%s\n %s",
|
|
||||||
it.optShort == "" ? "" : it.optShort ~ " ",
|
|
||||||
it.optLong,
|
|
||||||
argsNeedingOptions.canFind(it.optLong) ? " ARG" : "",
|
|
||||||
it.required ? " (required)" : "", it.help);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import progress;
|
||||||
import config;
|
import config;
|
||||||
static import log;
|
static import log;
|
||||||
shared bool debugResponse = false;
|
shared bool debugResponse = false;
|
||||||
shared bool dryRun = false;
|
private bool dryRun = false;
|
||||||
shared bool simulateNoRefreshTokenFile = false;
|
private bool simulateNoRefreshTokenFile = false;
|
||||||
|
|
||||||
private immutable {
|
private immutable {
|
||||||
// Client Identifier
|
// Client Identifier
|
||||||
|
@ -66,7 +66,7 @@ final class OneDriveApi
|
||||||
// if true, every new access token is printed
|
// if true, every new access token is printed
|
||||||
bool printAccessToken;
|
bool printAccessToken;
|
||||||
|
|
||||||
this(Config cfg, bool debugHttp, bool forceHTTP11, bool dryRun, bool simulateNoRefreshTokenFile)
|
this(Config cfg)
|
||||||
{
|
{
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
http = HTTP();
|
http = HTTP();
|
||||||
|
@ -94,36 +94,36 @@ final class OneDriveApi
|
||||||
http.maxRedirects(5);
|
http.maxRedirects(5);
|
||||||
|
|
||||||
// Do we enable curl debugging?
|
// Do we enable curl debugging?
|
||||||
if (debugHttp) {
|
if (cfg.getValueBool("debug_https")) {
|
||||||
http.verbose = true;
|
http.verbose = true;
|
||||||
.debugResponse = true;
|
.debugResponse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// What version of HTTP protocol do we use?
|
// What version of HTTP protocol do we use?
|
||||||
// Curl >= 7.62.0 defaults to http2 for a significant number of operations
|
// Curl >= 7.62.0 defaults to http2 for a significant number of operations
|
||||||
if (forceHTTP11) {
|
if (cfg.getValueBool("force_http_11")) {
|
||||||
log.vdebug("Downgrading all HTTP operations to HTTP 1.1");
|
log.vdebug("Downgrading all HTTP operations to HTTP 1.1");
|
||||||
// Downgrade to HTTP 1.1 - yes version = 2 is HTTP 1.1
|
// Downgrade to HTTP 1.1 - yes version = 2 is HTTP 1.1
|
||||||
http.handle.set(CurlOption.http_version,2);
|
http.handle.set(CurlOption.http_version,2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we set the dryRun handlers?
|
// Do we set the dryRun handlers?
|
||||||
if (dryRun) {
|
if (cfg.getValueBool("dry_run")) {
|
||||||
.dryRun = true;
|
.dryRun = true;
|
||||||
}
|
if (cfg.getValueBool("logout")) {
|
||||||
if (simulateNoRefreshTokenFile) {
|
.simulateNoRefreshTokenFile = true;
|
||||||
.simulateNoRefreshTokenFile = true;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init()
|
bool init()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
driveId = cfg.getValue("drive_id");
|
driveId = cfg.getValueString("drive_id");
|
||||||
if (driveId.length) {
|
if (driveId.length) {
|
||||||
driveUrl = driveByIdUrl ~ driveId;
|
driveUrl = driveByIdUrl ~ driveId;
|
||||||
itemByIdUrl = driveUrl ~ "/items";
|
itemByIdUrl = driveUrl ~ "/items";
|
||||||
itemByPathUrl = driveUrl ~ "/root:/";
|
itemByPathUrl = driveUrl ~ "/root:/";
|
||||||
}
|
}
|
||||||
} catch (Exception e) {}
|
} catch (Exception e) {}
|
||||||
|
|
||||||
|
|
20
src/sync.d
20
src/sync.d
|
@ -208,7 +208,7 @@ final class SyncEngine
|
||||||
// sync engine dryRun flag
|
// sync engine dryRun flag
|
||||||
private bool dryRun = false;
|
private bool dryRun = false;
|
||||||
|
|
||||||
this(Config cfg, OneDriveApi onedrive, ItemDatabase itemdb, SelectiveSync selectiveSync, bool dryRun)
|
this(Config cfg, OneDriveApi onedrive, ItemDatabase itemdb, SelectiveSync selectiveSync)
|
||||||
{
|
{
|
||||||
assert(onedrive && itemdb && selectiveSync);
|
assert(onedrive && itemdb && selectiveSync);
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
|
@ -216,7 +216,7 @@ final class SyncEngine
|
||||||
this.itemdb = itemdb;
|
this.itemdb = itemdb;
|
||||||
this.selectiveSync = selectiveSync;
|
this.selectiveSync = selectiveSync;
|
||||||
// session = UploadSession(onedrive, cfg.uploadStateFilePath);
|
// session = UploadSession(onedrive, cfg.uploadStateFilePath);
|
||||||
this.dryRun = dryRun;
|
this.dryRun = cfg.getValueBool("dry_run");
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset()
|
void reset()
|
||||||
|
@ -243,7 +243,7 @@ final class SyncEngine
|
||||||
// OneDrive responded with 400 error: Bad Request
|
// OneDrive responded with 400 error: Bad Request
|
||||||
log.error("\nERROR: OneDrive returned a 'HTTP 400 Bad Request' - Cannot Initialize Sync Engine");
|
log.error("\nERROR: OneDrive returned a 'HTTP 400 Bad Request' - Cannot Initialize Sync Engine");
|
||||||
// Check this
|
// Check this
|
||||||
if (cfg.getValue("drive_id").length) {
|
if (cfg.getValueString("drive_id").length) {
|
||||||
log.error("ERROR: Check your 'drive_id' entry in your configuration file as it may be incorrect\n");
|
log.error("ERROR: Check your 'drive_id' entry in your configuration file as it may be incorrect\n");
|
||||||
}
|
}
|
||||||
// Must exit here
|
// Must exit here
|
||||||
|
@ -614,7 +614,7 @@ final class SyncEngine
|
||||||
if (("value" in changes) != null) {
|
if (("value" in changes) != null) {
|
||||||
auto nrChanges = count(changes["value"].array);
|
auto nrChanges = count(changes["value"].array);
|
||||||
|
|
||||||
if (nrChanges >= to!long(cfg.getValue("min_notif_changes"))) {
|
if (nrChanges >= cfg.getValueLong("min_notif_changes")) {
|
||||||
log.logAndNotify("Processing ", nrChanges, " changes");
|
log.logAndNotify("Processing ", nrChanges, " changes");
|
||||||
} else {
|
} else {
|
||||||
// There are valid changes
|
// There are valid changes
|
||||||
|
@ -768,7 +768,7 @@ final class SyncEngine
|
||||||
// Check if this is a directory to skip
|
// Check if this is a directory to skip
|
||||||
if (!unwanted) {
|
if (!unwanted) {
|
||||||
// Only check path if config is != ""
|
// Only check path if config is != ""
|
||||||
if (cfg.getValue("skip_dir") != "") {
|
if (cfg.getValueString("skip_dir") != "") {
|
||||||
unwanted = selectiveSync.isDirNameExcluded(item.name);
|
unwanted = selectiveSync.isDirNameExcluded(item.name);
|
||||||
if (unwanted) log.vlog("Skipping item - excluded by skip_dir config: ", item.name);
|
if (unwanted) log.vlog("Skipping item - excluded by skip_dir config: ", item.name);
|
||||||
}
|
}
|
||||||
|
@ -812,7 +812,7 @@ final class SyncEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip downloading dot files if configured
|
// skip downloading dot files if configured
|
||||||
if (cfg.getValue("skip_dotfiles") == "true") {
|
if (cfg.getValueBool("skip_dotfiles")) {
|
||||||
if (isDotFile(path)) {
|
if (isDotFile(path)) {
|
||||||
log.vlog("Skipping item - .file or .folder: ", path);
|
log.vlog("Skipping item - .file or .folder: ", path);
|
||||||
unwanted = true;
|
unwanted = true;
|
||||||
|
@ -1480,7 +1480,7 @@ final class SyncEngine
|
||||||
// path is less than maxPathLength
|
// path is less than maxPathLength
|
||||||
|
|
||||||
// skip dot files if configured
|
// skip dot files if configured
|
||||||
if (cfg.getValue("skip_dotfiles") == "true") {
|
if (cfg.getValueBool("skip_dotfiles")) {
|
||||||
if (isDotFile(path)) {
|
if (isDotFile(path)) {
|
||||||
log.vlog("Skipping item - .file or .folder: ", path);
|
log.vlog("Skipping item - .file or .folder: ", path);
|
||||||
return;
|
return;
|
||||||
|
@ -1488,7 +1488,7 @@ final class SyncEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we need to check for .nosync? Only if --check-for-nosync was passed in
|
// Do we need to check for .nosync? Only if --check-for-nosync was passed in
|
||||||
if (cfg.getValue("check_nosync") == "true") {
|
if (cfg.getValueBool("check_nosync")) {
|
||||||
if (exists(path ~ "/.nosync")) {
|
if (exists(path ~ "/.nosync")) {
|
||||||
log.vlog("Skipping item - .nosync found & --check-for-nosync enabled: ", path);
|
log.vlog("Skipping item - .nosync found & --check-for-nosync enabled: ", path);
|
||||||
return;
|
return;
|
||||||
|
@ -1497,7 +1497,7 @@ final class SyncEngine
|
||||||
|
|
||||||
if (isSymlink(path)) {
|
if (isSymlink(path)) {
|
||||||
// if config says so we skip all symlinked items
|
// if config says so we skip all symlinked items
|
||||||
if (cfg.getValue("skip_symlinks") == "true") {
|
if (cfg.getValueBool("skip_symlinks")) {
|
||||||
log.vlog("Skipping item - skip symbolic links configured: ", path);
|
log.vlog("Skipping item - skip symbolic links configured: ", path);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1532,7 +1532,7 @@ final class SyncEngine
|
||||||
if (isDir(path)) {
|
if (isDir(path)) {
|
||||||
log.vdebug("Checking path: ", path);
|
log.vdebug("Checking path: ", path);
|
||||||
// Only check path if config is != ""
|
// Only check path if config is != ""
|
||||||
if (cfg.getValue("skip_dir") != "") {
|
if (cfg.getValueString("skip_dir") != "") {
|
||||||
if (selectiveSync.isDirNameExcluded(strip(path,"./"))) {
|
if (selectiveSync.isDirNameExcluded(strip(path,"./"))) {
|
||||||
log.vlog("Skipping item - excluded by skip_dir config: ", path);
|
log.vlog("Skipping item - excluded by skip_dir config: ", path);
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue