mirror of
https://github.com/abraunegg/onedrive
synced 2024-05-19 14:16:36 +02:00
Merge branch 'master' into fix-issue-966
This commit is contained in:
commit
c4807fd25e
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -2,6 +2,30 @@
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## 2.4.11 - 2021-4-07
|
||||
### Fixed
|
||||
* Fix support for '/*' regardless of location within sync_list file
|
||||
* Fix 429 response handling correctly check for 'retry-after' response header and use set value
|
||||
* Fix 'sync_list' path handling for sub item matching, so that items in parent are not implicitly matched when there is no wildcard present
|
||||
* Fix --get-O365-drive-id to use 'nextLink' value if present when searching for specific SharePoint site names
|
||||
* Fix OneDrive Business Shared Folder existing name conflict check
|
||||
* Fix incorrect error message 'Item cannot be deleted from OneDrive because it was not found in the local database' when item is actually present
|
||||
* Fix application crash when unable to rename folder structure due to unhandled file-system issue
|
||||
* Fix uploading documents to Shared Business Folders when the shared folder exists on a SharePoint site due to Microsoft Sharepoint 'enrichment' of files
|
||||
* Fix that a file record is kept in database when using --no-remote-delete & --remove-source-files
|
||||
|
||||
### Added
|
||||
* Added support in --get-O365-drive-id to provide the 'drive_id' for multiple 'document libraries' within a single Shared Library Site
|
||||
|
||||
### Removed
|
||||
* Removed the depreciated config option 'force_http_11' which was flagged as depreciated by PR #549 in v2.3.6 (June 2019)
|
||||
|
||||
### Updated
|
||||
* Updated error output of --get-O365-drive-id to provide more details why an error occurred if a SharePoint site lacks the details we need to perform the match
|
||||
* Updated Docker build files for Raspberry Pi to dedicated armhf & aarch64 Dockerfiles
|
||||
* Updated logging output when in --monitor mode, avoid outputting misleading logging when the new or modified item is a file, not a directory
|
||||
* Updated documentation (various)
|
||||
|
||||
## 2.4.10 - 2021-2-19
|
||||
### Fixed
|
||||
* Catch database assertion when item path cannot be calculated
|
||||
|
|
|
@ -33,7 +33,10 @@ This client is a 'fork' of the [skilion](https://github.com/skilion/onedrive) cl
|
|||
## Frequently Asked Questions
|
||||
Refer to [Frequently Asked Questions](https://github.com/abraunegg/onedrive/wiki/Frequently-Asked-Questions)
|
||||
|
||||
## Reporting issues
|
||||
## Have a question
|
||||
If you have a question or need something clarified, please raise a new disscussion post [here](https://github.com/abraunegg/onedrive/discussions)
|
||||
|
||||
## Reporting an Issue or Bug
|
||||
If you encounter any bugs you can report them here on Github. Before filing an issue be sure to:
|
||||
|
||||
1. Check the version of the application you are using `onedrive --version` and ensure that you are running either the latest [release](https://github.com/abraunegg/onedrive/releases) or built from master.
|
||||
|
|
1
config
1
config
|
@ -18,7 +18,6 @@
|
|||
# disable_notifications = "false"
|
||||
# disable_upload_validation = "false"
|
||||
# enable_logging = "false"
|
||||
# force_http_11 = "false"
|
||||
# force_http_2 = "false"
|
||||
# local_first = "false"
|
||||
# no_remote_delete = "false"
|
||||
|
|
20
configure
vendored
20
configure
vendored
|
@ -1,6 +1,6 @@
|
|||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for onedrive v2.4.11-dev.
|
||||
# Generated by GNU Autoconf 2.69 for onedrive v2.4.11.
|
||||
#
|
||||
# Report bugs to <https://github.com/abraunegg/onedrive>.
|
||||
#
|
||||
|
@ -579,8 +579,8 @@ MAKEFLAGS=
|
|||
# Identity of this package.
|
||||
PACKAGE_NAME='onedrive'
|
||||
PACKAGE_TARNAME='onedrive'
|
||||
PACKAGE_VERSION='v2.4.11-dev'
|
||||
PACKAGE_STRING='onedrive v2.4.11-dev'
|
||||
PACKAGE_VERSION='v2.4.11'
|
||||
PACKAGE_STRING='onedrive v2.4.11'
|
||||
PACKAGE_BUGREPORT='https://github.com/abraunegg/onedrive'
|
||||
PACKAGE_URL=''
|
||||
|
||||
|
@ -1219,7 +1219,7 @@ if test "$ac_init_help" = "long"; then
|
|||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures onedrive v2.4.11-dev to adapt to many kinds of systems.
|
||||
\`configure' configures onedrive v2.4.11 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
|
@ -1280,7 +1280,7 @@ fi
|
|||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of onedrive v2.4.11-dev:";;
|
||||
short | recursive ) echo "Configuration of onedrive v2.4.11:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
|
@ -1393,7 +1393,7 @@ fi
|
|||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
onedrive configure v2.4.11-dev
|
||||
onedrive configure v2.4.11
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
|
@ -1410,7 +1410,7 @@ cat >config.log <<_ACEOF
|
|||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by onedrive $as_me v2.4.11-dev, which was
|
||||
It was created by onedrive $as_me v2.4.11, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
|
@ -2162,7 +2162,7 @@ fi
|
|||
|
||||
|
||||
|
||||
PACKAGE_DATE="February 2021"
|
||||
PACKAGE_DATE="April 2021"
|
||||
|
||||
|
||||
|
||||
|
@ -3159,7 +3159,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by onedrive $as_me v2.4.11-dev, which was
|
||||
This file was extended by onedrive $as_me v2.4.11, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
|
@ -3212,7 +3212,7 @@ _ACEOF
|
|||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
onedrive config.status v2.4.11-dev
|
||||
onedrive config.status v2.4.11
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ dnl - commit the changed files (configure.ac, configure)
|
|||
dnl - tag the release
|
||||
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([onedrive],[v2.4.11-dev], [https://github.com/abraunegg/onedrive], [onedrive])
|
||||
AC_INIT([onedrive],[v2.4.11], [https://github.com/abraunegg/onedrive], [onedrive])
|
||||
AC_CONFIG_SRCDIR([src/main.d])
|
||||
|
||||
|
||||
|
|
21
contrib/docker/Dockerfile-aarch64
Normal file
21
contrib/docker/Dockerfile-aarch64
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*-Dockerfile-*-
|
||||
FROM debian:stretch
|
||||
RUN apt update && \
|
||||
apt install -y build-essential curl libcurl4-openssl-dev libsqlite3-dev pkg-config wget git
|
||||
RUN wget https://github.com/ldc-developers/ldc/releases/download/v1.16.0/ldc2-1.16.0-linux-aarch64.tar.xz && \
|
||||
tar -xvf ldc2-1.16.0-linux-aarch64.tar.xz
|
||||
COPY . /usr/src/onedrive
|
||||
RUN cd /usr/src/onedrive/ && \
|
||||
./configure DC=/ldc2-1.16.0-linux-aarch64/bin/ldmd2 && \
|
||||
make clean && \
|
||||
make && \
|
||||
make install
|
||||
|
||||
FROM debian:stretch-slim
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
RUN apt update && \
|
||||
apt install -y gosu libcurl3 libsqlite3-0 && \
|
||||
rm -rf /var/*/apt && \
|
||||
mkdir -p /onedrive/conf /onedrive/data
|
||||
COPY contrib/docker/entrypoint.sh /
|
||||
COPY --from=0 /usr/local/bin/onedrive /usr/local/bin/
|
|
@ -1,13 +1,12 @@
|
|||
# -*-Dockerfile-*-
|
||||
FROM debian:stretch
|
||||
ARG ARCH armhf # or aarch64
|
||||
RUN apt update && \
|
||||
apt install -y build-essential curl libcurl4-openssl-dev libsqlite3-dev pkg-config wget git
|
||||
RUN wget https://github.com/ldc-developers/ldc/releases/download/v1.16.0/ldc2-1.16.0-linux-${ARCH}.tar.xz && \
|
||||
tar -xvf ldc2-1.16.0-linux-${ARCH}.tar.xz
|
||||
RUN wget https://github.com/ldc-developers/ldc/releases/download/v1.16.0/ldc2-1.16.0-linux-armhf.tar.xz && \
|
||||
tar -xvf ldc2-1.16.0-linux-armhf.tar.xz
|
||||
COPY . /usr/src/onedrive
|
||||
RUN cd /usr/src/onedrive/ && \
|
||||
./configure DC=/ldc2-1.16.0-linux-${ARCH}/bin/ldmd2 && \
|
||||
./configure DC=/ldc2-1.16.0-linux-armhf/bin/ldmd2 && \
|
||||
make clean && \
|
||||
make && \
|
||||
make install
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
%endif
|
||||
|
||||
Name: onedrive
|
||||
Version: 2.4.10
|
||||
Version: 2.4.11
|
||||
Release: 1%{?dist}
|
||||
Summary: Microsoft OneDrive Client
|
||||
Group: System Environment/Network
|
||||
|
|
144
docs/Docker.md
144
docs/Docker.md
|
@ -1,56 +1,89 @@
|
|||
# onedrive docker image
|
||||
# Run the OneDrive Client for Linux under Docker
|
||||
This client can be run as a Docker container, with 3 available options for you to choose from:
|
||||
1. Container based on CentOS 7 - Docker Tag: latest
|
||||
2. Container based on Debian Stretch - Docker Tag: stretch
|
||||
3. Container based on Alpine Linux - Docker Tag: alpine
|
||||
|
||||
Thats right folks onedrive is now dockerized ;)
|
||||
These containers offer a simple monitoring-mode service for the OneDrive Client for Linux.
|
||||
|
||||
The instructions below have been validated on:
|
||||
* Red Hat Enterprise Linux 8.x
|
||||
* Ubuntu Server 20.04
|
||||
|
||||
The instructions below will utilise the 'latest' tag, however this can be substituted for 'stretch' or 'alpine' if desired.
|
||||
|
||||
This container offers simple monitoring-mode service for 'Free Client for OneDrive on Linux'.
|
||||
|
||||
## Basic Setup
|
||||
### 0. Install docker using your distribution platform's instructions
|
||||
1. Ensure that SELinux has been disabled on your system. A reboot may be required to ensure that this is correctly disabled.
|
||||
2. Install Docker as per requried for your platform
|
||||
3. Obtain your normal, non-root user UID and GID by using the `id` command
|
||||
4. As your normal, non-root user, ensure that you can run `docker run hello-world` *without* using `sudo`
|
||||
|
||||
### 0. Install docker under your own platform's instructions
|
||||
Once the above 4 steps are complete and you can successfully run `docker run hello-world` without sudo, only then proceed to 'Pulling and Running the Docker Image'
|
||||
|
||||
## Pulling and Running the Docker Image
|
||||
### 1. Pull the image
|
||||
|
||||
```bash
|
||||
docker pull driveone/onedrive:latest
|
||||
```
|
||||
|
||||
**NOTE:** SELinux context needs to be configured or disabled for Docker, to be able to write to OneDrive host directory.
|
||||
**NOTE:** SELinux context needs to be configured or disabled for Docker to be able to write to OneDrive host directory.
|
||||
|
||||
### 2. Prepare config volume
|
||||
The Docker container requries 2 Docker volumes:
|
||||
* Config Volume
|
||||
* Data Volume
|
||||
|
||||
Onedrive needs two volumes. One of them is the config volume. Create it with:
|
||||
|
||||
Create the config volume with the following command:
|
||||
```bash
|
||||
docker volume create onedrive_conf
|
||||
```
|
||||
|
||||
This will create a docker volume labeled `onedrive_conf`, where all configuration of your onedrive account will be stored. You can add a custom config file and other things later.
|
||||
|
||||
The second docker volume is for your data folder and is created in the next step. It needs the path to a folder on your filesystem that you want to keep in sync with OneDrive. Keep in mind that:
|
||||
The second docker volume is for your data folder and is created in the next step. This volume needs to be a path to a directory on your local filesystem, and this is where your data will be stored from OneDrive. Keep in mind that:
|
||||
|
||||
- The owner of your specified folder must not be root
|
||||
|
||||
- The owner of your specified folder must have permissions for its parent directory
|
||||
|
||||
### 3. First run
|
||||
|
||||
Onedrive needs to be authorized with your Microsoft account. This is achieved by running docker in interactive mode. Run the docker image with the two commands below and **make sure to change `onedriveDir` to the onedrive data directory on your filesystem (e.g. `"/home/abraunegg/OneDrive"`)**.
|
||||
Additionally, the user id and group id should be added to remove any potential user conflicts, denoted by the environment variables `${ONEDRIVE_UID}` and `${ONEDRIVE_GID}`.
|
||||
* The owner of this specified folder must not be root
|
||||
* The owner of this specified folder must have permissions for its parent directory
|
||||
|
||||
**NOTE:** Issues occur when this target folder is a mounted folder of an external system (NAS, SMB mount, USB Drive etc) as the 'mount' itself is owed by 'root'. If this is your use case, you *must* ensure your normal user can mount your desired target without having the target mounted by 'root'. If you do not fix this, your Docker container will fail to start with the following error message:
|
||||
```bash
|
||||
onedriveDir="${HOME}/OneDrive"
|
||||
docker run -it --name onedrive -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" -e "ONEDRIVE_UID:${ONEDRIVE_UID}" -e "ONEDRIVE_GID:${ONEDRIVE_GID}" driveone/onedrive:latest
|
||||
ROOT level privileges prohibited!
|
||||
```
|
||||
|
||||
- You will be asked to open a specific link using your web browser
|
||||
- Login to your Microsoft Account and give the application the permission
|
||||
- After giving the permission, you will be redirected to a blank page.
|
||||
- Copy the URI of the blank page into the application.
|
||||
### 3. First run
|
||||
The 'onedrive' client within the Docker container needs to be authorized with your Microsoft account. This is achieved by initially running docker in interactive mode.
|
||||
|
||||
The onedrive monitor is configured to start with your host system. If your onedrive is working as expected, you can detach from the container with Ctrl+p, Ctrl+q.
|
||||
Run the docker image with the commands below and make sure to change `ONEDRIVE_DATA_DIR` to the actual onedrive data directory on your filesystem that you wish to use (e.g. `"/home/abraunegg/OneDrive"`).
|
||||
```bash
|
||||
export ONEDRIVE_DATA_DIR="${HOME}/OneDrive"
|
||||
mkdir -p ${ONEDRIVE_DATA_DIR}
|
||||
docker run -it --name onedrive -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" -e "ONEDRIVE_UID:${ONEDRIVE_UID}" -e "ONEDRIVE_GID:${ONEDRIVE_GID}" driveone/onedrive:latest
|
||||
```
|
||||
**NOTE:** It is also highly advisable for you to replace `${ONEDRIVE_UID}` and `${ONEDRIVE_GID}` with your actual UID and GID as specified by your `id` command output to avoid any any potential user or group conflicts.
|
||||
|
||||
### 4. Status, stop, and restart
|
||||
**Important:** The 'target' folder of `ONEDRIVE_DATA_DIR` must exist before running the Docker container, otherwise, Docker will create the target folder, and the folder will be given 'root' permissions, which then causes the Docker container to fail upon startup with the following error message:
|
||||
```bash
|
||||
ROOT level privileges prohibited!
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```
|
||||
docker run -it --name onedrive -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" -e "ONEDRIVE_UID:1000" -e "ONEDRIVE_GID:1000" driveone/onedrive:latest
|
||||
```
|
||||
|
||||
When the Docker container successfully starts:
|
||||
* You will be asked to open a specific link using your web browser
|
||||
* Login to your Microsoft Account and give the application the permission
|
||||
* After giving the permission, you will be redirected to a blank page
|
||||
* Copy the URI of the blank page into the application prompt to authorise the application
|
||||
|
||||
Once the 'onedrive' application is authorised, the client will automatically start monitoring your `ONEDRIVE_DATA_DIR` for data changes to be uploaded to OneDrive. Files stored on OneDrive will be downloaded to this location.
|
||||
|
||||
If the client is working as expected, you can detach from the container with Ctrl+p, Ctrl+q.
|
||||
|
||||
### 4. Docker Container Status, stop, and restart
|
||||
Check if the monitor service is running
|
||||
|
||||
```bash
|
||||
|
@ -75,7 +108,7 @@ Resume monitor
|
|||
docker start onedrive
|
||||
```
|
||||
|
||||
Remove onedrive monitor
|
||||
Remove onedrive Docker container
|
||||
|
||||
```bash
|
||||
docker rm -f onedrive
|
||||
|
@ -83,9 +116,8 @@ docker rm -f onedrive
|
|||
## Advanced Setup
|
||||
|
||||
### 5. Docker-compose
|
||||
|
||||
Also supports docker-compose schemas > 3.
|
||||
In the following example it is assumed you have a `onedriveDir` environment variable and a `onedrive_conf` volume.
|
||||
In the following example it is assumed you have a `ONEDRIVE_DATA_DIR` environment variable and a `onedrive_conf` volume.
|
||||
However, you can also use bind mounts for the configuration folder, e.g. `export ONEDRIVE_CONF="${HOME}/OneDriveConfig"`.
|
||||
|
||||
```
|
||||
|
@ -99,14 +131,13 @@ services:
|
|||
- ONEDRIVE_GID=${PGID}
|
||||
volumes:
|
||||
- onedrive_conf:/onedrive/conf
|
||||
- ${onedriveDir}:/onedrive/data
|
||||
- ${ONEDRIVE_DATA_DIR}:/onedrive/data
|
||||
```
|
||||
|
||||
Note that you still have to perform step 3: First Run.
|
||||
|
||||
### 6. Edit the config
|
||||
|
||||
Onedrive should run in default configuration, however you can change your configuration by placing a custom config file in the `onedrive_conf` docker volume. First download the default config from [here](https://raw.githubusercontent.com/abraunegg/onedrive/master/config)
|
||||
The 'onedrive' client should run in default configuration, however you can change this default configuration by placing a custom config file in the `onedrive_conf` docker volume. First download the default config from [here](https://raw.githubusercontent.com/abraunegg/onedrive/master/config)
|
||||
Then put it into your onedrive_conf volume path, which can be found with:
|
||||
|
||||
```bash
|
||||
|
@ -118,34 +149,31 @@ Or you can map your own config folder to the config volume. Make sure to copy al
|
|||
The detailed document for the config can be found here: [Configuration](https://github.com/abraunegg/onedrive/blob/master/docs/USAGE.md#configuration)
|
||||
|
||||
### 7. Sync multiple accounts
|
||||
|
||||
There are many ways to do this, the easiest is probably to
|
||||
1. Create a second docker config volume (replace `Work` with your desired name): `docker volume create onedrive_conf_Work`
|
||||
2. And start a second docker monitor container (again replace `Work` with your desired name):
|
||||
```
|
||||
onedriveDirWork="/home/abraunegg/OneDriveWork"
|
||||
docker run -it --restart unless-stopped --name onedrive_Work -v onedrive_conf_Work:/onedrive/conf -v "${onedriveDirWork}:/onedrive/data" driveone/onedrive:latest
|
||||
export ONEDRIVE_DATA_DIR_WORK="/home/abraunegg/OneDriveWork"
|
||||
mkdir -p ${ONEDRIVE_DATA_DIR_WORK}
|
||||
docker run -it --restart unless-stopped --name onedrive_Work -v onedrive_conf_Work:/onedrive/conf -v "${ONEDRIVE_DATA_DIR_WORK}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
|
||||
## Run or update with one script
|
||||
|
||||
If you are experienced with docker and onedrive, you can use the following script:
|
||||
|
||||
```bash
|
||||
# Update onedriveDir with correct existing OneDrive directory path
|
||||
onedriveDir="${HOME}/OneDrive"
|
||||
# Update ONEDRIVE_DATA_DIR with correct existing OneDrive directory path
|
||||
ONEDRIVE_DATA_DIR="${HOME}/OneDrive"
|
||||
|
||||
firstRun='-d'
|
||||
docker pull driveone/onedrive:latest
|
||||
docker inspect onedrive_conf > /dev/null || { docker volume create onedrive_conf; firstRun='-it'; }
|
||||
docker inspect onedrive > /dev/null && docker rm -f onedrive
|
||||
docker run $firstRun --restart unless-stopped --name onedrive -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" driveone/onedrive:latest
|
||||
docker run $firstRun --restart unless-stopped --name onedrive -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
|
||||
|
||||
## Environment Variables
|
||||
|
||||
|
||||
| Variable | Purpose | Sample Value |
|
||||
| ---------------- | --------------------------------------------------- |:-------------:|
|
||||
| <B>ONEDRIVE_UID</B> | UserID (UID) to run as | 1000 |
|
||||
|
@ -160,24 +188,24 @@ docker run $firstRun --restart unless-stopped --name onedrive -v onedrive_conf:/
|
|||
### Usage Examples
|
||||
**Verbose Output:**
|
||||
```bash
|
||||
docker container run -e ONEDRIVE_VERBOSE=1 -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" driveone/onedrive:latest
|
||||
docker container run -e ONEDRIVE_VERBOSE=1 -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
**Debug Output:**
|
||||
```bash
|
||||
docker container run -e ONEDRIVE_DEBUG=1 -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" driveone/onedrive:latest
|
||||
docker container run -e ONEDRIVE_DEBUG=1 -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
**Perform a --resync:**
|
||||
```bash
|
||||
docker container run -e ONEDRIVE_RESYNC=1 -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" driveone/onedrive:latest
|
||||
docker container run -e ONEDRIVE_RESYNC=1 -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
**Perform a --resync and --verbose:**
|
||||
```bash
|
||||
docker container run -e ONEDRIVE_RESYNC=1 -e ONEDRIVE_VERBOSE=1 -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" driveone/onedrive:latest
|
||||
docker container run -e ONEDRIVE_RESYNC=1 -e ONEDRIVE_VERBOSE=1 -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
|
||||
**Perform a --logout and re-authenticate:**
|
||||
```bash
|
||||
docker container run -e ONEDRIVE_LOGOUT=1 -v onedrive_conf:/onedrive/conf -v "${onedriveDir}:/onedrive/data" driveone/onedrive:latest
|
||||
docker container run -e ONEDRIVE_LOGOUT=1 -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" driveone/onedrive:latest
|
||||
```
|
||||
|
||||
## Build instructions
|
||||
|
@ -185,7 +213,7 @@ docker container run -e ONEDRIVE_LOGOUT=1 -v onedrive_conf:/onedrive/conf -v "${
|
|||
* Build environment must have at least 1GB of memory & 2GB swap space
|
||||
|
||||
There are 2 ways to validate this requirement:
|
||||
* Modify the file `/etc/dphys-swapfile` and edit the `CONF_SWAPSIZE`, for example: `CONF_SWAPSIZE=2024`. A reboot is required to make this change effective.
|
||||
* Modify the file `/etc/dphys-swapfile` and edit the `CONF_SWAPSIZE`, for example: `CONF_SWAPSIZE=2048`. A reboot is required to make this change effective.
|
||||
* Dynamically allocate a swapfile for building:
|
||||
```bash
|
||||
cd /var
|
||||
|
@ -201,7 +229,7 @@ swapon -s
|
|||
free -h
|
||||
```
|
||||
|
||||
### Building the Docker image
|
||||
### Building a custom Docker image
|
||||
You can also build your own image instead of pulling the one from [hub.docker.com](https://hub.docker.com/r/driveone/onedrive):
|
||||
```bash
|
||||
git clone https://github.com/abraunegg/onedrive
|
||||
|
@ -214,18 +242,32 @@ Dockerfile-stretch or Dockerfile-alpine. These [multi-stage builder
|
|||
pattern](https://docs.docker.com/develop/develop-images/multistage-build/)
|
||||
Dockerfiles require Docker version at least 17.05.
|
||||
|
||||
#### How to build a Docker image based on Debian Stretch
|
||||
|
||||
#### How to build and run a custom Docker image based on Debian Stretch
|
||||
``` bash
|
||||
docker build . -t local-ondrive-stretch -f contrib/docker/Dockerfile-stretch
|
||||
docker container run -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" local-ondrive-stretch:latest
|
||||
```
|
||||
#### How to build a Docker image based on Alpine Linux
|
||||
|
||||
#### How to build and run a custom Docker image based on Alpine Linux
|
||||
``` bash
|
||||
docker build . -t local-ondrive-alpine -f contrib/docker/Dockerfile-alpine
|
||||
docker container run -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" local-ondrive-alpine:latest
|
||||
```
|
||||
#### How to build a Docker image for ARMHF (Raspberry Pi)
|
||||
|
||||
#### How to build and run a custom Docker image for ARMHF (Raspberry Pi)
|
||||
Compatible with:
|
||||
* Raspberry Pi
|
||||
* Raspberry Pi 2
|
||||
* Raspberry Pi Zero
|
||||
* Raspberry Pi 3
|
||||
* Raspberry Pi 4
|
||||
``` bash
|
||||
docker build . -t local-onedrive-rpi -f contrib/docker/Dockerfile-rpi
|
||||
docker container run -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" local-ondrive-rpi:latest
|
||||
```
|
||||
|
||||
#### How to build and run a custom Docker image for AARCH64 Platforms
|
||||
``` bash
|
||||
docker build . -t local-onedrive-aarch64 -f contrib/docker/Dockerfile-aarch64
|
||||
docker container run -v onedrive_conf:/onedrive/conf -v "${ONEDRIVE_DATA_DIR}:/onedrive/data" local-onedrive-aarch64:latest
|
||||
```
|
||||
|
|
|
@ -5,6 +5,7 @@ This project has been packaged for the following Linux distributions:
|
|||
|
||||
| Distribution | Package Name & Package Link | i686 | x86_64 | ARMHF | AARCH64 | Extra Details |
|
||||
|---------------------------------|------------------------------------------------------------------------------|:----:|:------:|:-----:|:-------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Alpine Linux | [onedrive](https://pkgs.alpinelinux.org/packages?name=onedrive&branch=edge) |<img src="./images/cross.gif" alt="not_supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/cross.gif" alt="not_supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|
|
||||
| Arch Linux<br><br>Manjaro Linux | [onedrive-abraunegg](https://aur.archlinux.org/packages/onedrive-abraunegg/) |<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>| Install via: `pamac build onedrive-abraunegg` from the Arch Linux User Repository (AUR)<br><br>**Note:** If asked regarding a provider for 'd-runtime' and 'd-compiler', select 'liblphobos' and 'ldc'<br><br>**Note:** System must have at least 1GB of memory & 1GB swap space
|
||||
| Debian | [onedrive](https://packages.debian.org/search?keywords=onedrive) |<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>| |
|
||||
| Fedora | [onedrive](https://koji.fedoraproject.org/koji/packageinfo?packageID=26044) |<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>|<img src="./images/tick.gif" alt="supported" width="39" height="39"/>| |
|
||||
|
@ -236,28 +237,42 @@ sudo pacman -S libnotify
|
|||
```
|
||||
|
||||
### Dependencies: Raspbian (ARMHF)
|
||||
Validated using:
|
||||
* `Linux raspberrypi 5.4.79-v7+ #1373 SMP Mon Nov 23 13:22:33 GMT 2020 armv7l GNU/Linux` (2020-12-02-raspios-buster-armhf) using Raspberry Pi 2 Model B
|
||||
* `Linux raspberrypi 5.4.83-v8+ #1379 SMP PREEMPT Mon Dec 14 13:15:14 GMT 2020 aarch64` (2021-01-11-raspios-buster-armhf) using Raspberry Pi 3 Model B+
|
||||
|
||||
**Note:** Build environment must have at least 1GB of memory & 1GB swap space. Check with `swapon`.
|
||||
|
||||
```text
|
||||
sudo apt-get install libcurl4-openssl-dev
|
||||
sudo apt-get install libsqlite3-dev
|
||||
sudo apt-get install libxml2
|
||||
sudo apt-get install pkg-config
|
||||
wget https://github.com/ldc-developers/ldc/releases/download/v1.16.0/ldc2-1.16.0-linux-armhf.tar.xz
|
||||
tar -xvf ldc2-1.16.0-linux-armhf.tar.xz
|
||||
sudo apt install build-essential
|
||||
sudo apt install libcurl4-openssl-dev
|
||||
sudo apt install libsqlite3-dev
|
||||
sudo apt install pkg-config
|
||||
sudo apt install git
|
||||
sudo apt install curl
|
||||
wget https://github.com/ldc-developers/ldc/releases/download/v1.17.0/ldc2-1.17.0-linux-armhf.tar.xz
|
||||
tar -xvf ldc2-1.17.0-linux-armhf.tar.xz
|
||||
```
|
||||
For notifications the following is also necessary:
|
||||
```text
|
||||
sudo apt install libnotify-dev
|
||||
```
|
||||
|
||||
### Dependencies: Debian (ARM64)
|
||||
### Dependencies: Ubuntu 20.x / Debian 10 (ARM64)
|
||||
Validated using:
|
||||
* `Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-1028-raspi aarch64)` (ubuntu-20.04.2-preinstalled-server-arm64+raspi) using Raspberry Pi 3 Model B+
|
||||
|
||||
**Note:** Build environment must have at least 1GB of memory & 1GB swap space. Check with `swapon`.
|
||||
|
||||
```text
|
||||
sudo apt-get install libcurl4-openssl-dev
|
||||
sudo apt-get install libsqlite3-dev
|
||||
sudo apt-get install libxml2
|
||||
sudo apt-get install pkg-config
|
||||
wget https://github.com/ldc-developers/ldc/releases/download/v1.16.0/ldc2-1.16.0-linux-aarch64.tar.xz
|
||||
tar -xvf ldc2-1.16.0-linux-aarch64.tar.xz
|
||||
sudo apt install build-essential
|
||||
sudo apt install libcurl4-openssl-dev
|
||||
sudo apt install libsqlite3-dev
|
||||
sudo apt install pkg-config
|
||||
sudo apt install git
|
||||
sudo apt install curl
|
||||
wget https://github.com/ldc-developers/ldc/releases/download/v1.25.1/ldc2-1.25.1-linux-aarch64.tar.xz
|
||||
tar -xvf ldc2-1.25.1-linux-aarch64.tar.xz
|
||||
```
|
||||
For notifications the following is also necessary:
|
||||
```text
|
||||
|
@ -337,7 +352,7 @@ as far as possible automatically, but can be overridden by passing
|
|||
```text
|
||||
git clone https://github.com/abraunegg/onedrive.git
|
||||
cd onedrive
|
||||
./configure DC=~/ldc2-1.16.0-linux-armhf/bin/ldmd2
|
||||
./configure DC=~/ldc2-1.17.0-linux-armhf/bin/ldmd2
|
||||
make clean; make
|
||||
sudo make install
|
||||
```
|
||||
|
@ -347,7 +362,7 @@ sudo make install
|
|||
```text
|
||||
git clone https://github.com/abraunegg/onedrive.git
|
||||
cd onedrive
|
||||
./configure DC=~/ldc2-1.16.0-linux-aarch64/bin/ldmd2
|
||||
./configure DC=~/ldc2-1.25.1-linux-aarch64/bin/ldmd2
|
||||
make clean; make
|
||||
sudo make install
|
||||
```
|
||||
|
|
|
@ -14,18 +14,38 @@ Syncing a OneDrive SharePoint library requires additional configuration for your
|
|||
## Query that shared library name using the client to obtain the required configuration details
|
||||
2. Run the following command using the 'onedrive' client
|
||||
```text
|
||||
onedrive --get-O365-drive-id '<your library name>'
|
||||
onedrive --get-O365-drive-id '<your site name to search>'
|
||||
```
|
||||
This will return something similar to the following:
|
||||
```text
|
||||
Configuration file successfully loaded
|
||||
Configuring Global Azure AD Endpoints
|
||||
Initializing the Synchronization Engine ...
|
||||
Office 365 Library Name Query: <your library name>
|
||||
SiteName: <your library name>
|
||||
drive_id: b!6H_y8B...xU5
|
||||
URL: <your site URL>
|
||||
Office 365 Library Name Query: <your site name to search>
|
||||
-----------------------------------------------
|
||||
Site Name: <your site name>
|
||||
Library Name: <your library name>
|
||||
drive_id: b!6H_y8B...xU5
|
||||
Library URL: <your library URL>
|
||||
-----------------------------------------------
|
||||
```
|
||||
If there are no matches to the site you are attempting to search, the following will be displayed:
|
||||
```text
|
||||
Configuration file successfully loaded
|
||||
Configuring Global Azure AD Endpoints
|
||||
Initializing the Synchronization Engine ...
|
||||
Office 365 Library Name Query: blah
|
||||
|
||||
ERROR: The requested SharePoint site could not be found. Please check it's name and your permissions to access the site.
|
||||
|
||||
The following SharePoint site names were returned:
|
||||
* <site name 1>
|
||||
* <site name 2>
|
||||
...
|
||||
* <site name X>
|
||||
```
|
||||
This list of site names can be used as a basis to search for the correct site for which you are searching
|
||||
|
||||
|
||||
## Configure the client's config file with the required 'drive_id' & 'sync_dir' options
|
||||
3. Create a new local folder to store the SharePoint Library data in
|
||||
|
@ -54,6 +74,5 @@ The OneDrive client will now be configured to sync this SharePoint shared librar
|
|||
## Sync the SharePoint Library as required
|
||||
6. Sync the SharePoint Library to your system with either `--synchronize` or `--monitor` operations
|
||||
|
||||
|
||||
# How to configure multiple OneDrive SharePoint Shared Library sync
|
||||
Refer to [./advanced-usage.md](advanced-usage.md) for configuration assistance.
|
||||
|
|
|
@ -326,7 +326,6 @@ See the [config](https://raw.githubusercontent.com/abraunegg/onedrive/master/con
|
|||
# disable_notifications = "false"
|
||||
# disable_upload_validation = "false"
|
||||
# enable_logging = "false"
|
||||
# force_http_11 = "false"
|
||||
# force_http_2 = "false"
|
||||
# local_first = "false"
|
||||
# no_remote_delete = "false"
|
||||
|
@ -698,11 +697,35 @@ systemctl --user start onedrive
|
|||
|
||||
**Note:** This will run the 'onedrive' process with a UID/GID of '0', thus, any files or folders that are created will be owned by 'root'
|
||||
|
||||
To see the logs run:
|
||||
To see the systemd application logs run:
|
||||
```text
|
||||
journalctl --user-unit=onedrive -f
|
||||
```
|
||||
|
||||
**Note:** It is a 'systemd' requirement that the XDG environment variables exist for correct enablement and operation of systemd services. If you receive this error when enabling the systemd service:
|
||||
```
|
||||
Failed to connect to bus: No such file or directory
|
||||
```
|
||||
The most likely cause is that the XDG environment variables are missing. To fix this, you must add the following to `.bashrc` or any other file which is run on user login:
|
||||
```
|
||||
export XDG_RUNTIME_DIR="/run/user/$UID"
|
||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"
|
||||
```
|
||||
|
||||
To make this change effective, you must logout of all user accounts where this change has been made.
|
||||
|
||||
**Note:** On some systems (for example - Raspbian / Ubuntu / Debian on Raspberry Pi) the above XDG fix may not be reliable after system reboots. The potential alternative to start the client via systemd as root, is to perform the following:
|
||||
1. Create a symbolic link from `/home/root/.config/onedrive` pointing to `/root/.config/onedrive/`
|
||||
2. Create a systemd service using the '@' service file: `systemctl enable onedrive@root.service`
|
||||
3. Start the root@service: `systemctl start onedrive@root.service`
|
||||
|
||||
This will ensure that the service will correctly restart on system reboot.
|
||||
|
||||
To see the systemd application logs run:
|
||||
```text
|
||||
journalctl --unit=onedrive@<username> -f
|
||||
```
|
||||
|
||||
### OneDrive service running as root user via systemd (Red Hat Enterprise Linux, CentOS Linux)
|
||||
```text
|
||||
systemctl enable onedrive
|
||||
|
@ -710,7 +733,7 @@ systemctl start onedrive
|
|||
```
|
||||
**Note:** This will run the 'onedrive' process with a UID/GID of '0', thus, any files or folders that are created will be owned by 'root'
|
||||
|
||||
To see the logs run:
|
||||
To see the systemd application logs run:
|
||||
```text
|
||||
journalctl --unit=onedrive -f
|
||||
```
|
||||
|
@ -732,7 +755,7 @@ systemctl start onedrive@<username>.service
|
|||
systemctl status onedrive@<username>.service
|
||||
```
|
||||
|
||||
To see the logs run:
|
||||
To see the systemd application logs run:
|
||||
```text
|
||||
journalctl --unit=onedrive@<username> -f
|
||||
```
|
||||
|
@ -752,7 +775,7 @@ systemctl --user enable onedrive
|
|||
systemctl --user start onedrive
|
||||
```
|
||||
|
||||
To see the logs run:
|
||||
To see the systemd application logs run:
|
||||
```text
|
||||
journalctl --user-unit=onedrive -f
|
||||
```
|
||||
|
|
|
@ -61,7 +61,7 @@ Test the configuration using '--display-config' and '--dry-run'. By doing so, th
|
|||
|
||||
#### Display the configuration
|
||||
```text
|
||||
onedrive --confdir="~/.config/my-new-config --display-config"
|
||||
onedrive --confdir="~/.config/my-new-config" --display-config
|
||||
```
|
||||
|
||||
#### Test the configuration by performing a dry-run
|
||||
|
|
|
@ -89,11 +89,6 @@ Configuration file key: \fBenable_logging\fP (default: \fBfalse\fP)
|
|||
\fB\-\-force\fP
|
||||
Force the deletion of data when a 'big delete' is detected
|
||||
.TP
|
||||
\fB\-\-force\-http\-1.1\fP
|
||||
Force the use of HTTP 1.1 for all operations (DEPRECIATED)
|
||||
.br
|
||||
Configuration file key: \fBforce_http_11\fP (default: \fBfalse\fP)
|
||||
.TP
|
||||
\fB\-\-force\-http\-2\fP
|
||||
Force the use of HTTP/2 for all operations where applicable
|
||||
.br
|
||||
|
|
|
@ -61,7 +61,6 @@ final class Config
|
|||
boolValues["disable_notifications"] = false;
|
||||
boolValues["disable_upload_validation"] = false;
|
||||
boolValues["enable_logging"] = false;
|
||||
boolValues["force_http_11"] = false;
|
||||
boolValues["force_http_2"] = false;
|
||||
boolValues["local_first"] = false;
|
||||
boolValues["no_remote_delete"] = false;
|
||||
|
@ -343,9 +342,6 @@ final class Config
|
|||
"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 (DEPRECIATED)",
|
||||
&boolValues["force_http_11"],
|
||||
"force-http-2",
|
||||
"Force the use of HTTP/2 for all operations where applicable",
|
||||
&boolValues["force_http_2"],
|
||||
|
|
|
@ -580,11 +580,6 @@ int main(string[] args)
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// If the user is still using --force-http-1.1 advise its no longer required
|
||||
if (cfg.getValueBool("force_http_11")) {
|
||||
log.log("NOTE: The use of --force-http-1.1 is depreciated");
|
||||
}
|
||||
|
||||
// Test if OneDrive service can be reached, exit if it cant be reached
|
||||
log.vdebug("Testing network to ensure network connectivity to Microsoft OneDrive Service");
|
||||
online = testNetwork();
|
||||
|
@ -1048,7 +1043,7 @@ int main(string[] args)
|
|||
log.vlog("Offline, cannot delete item!");
|
||||
} catch(SyncException e) {
|
||||
if (e.msg == "The item to delete is not in the local database") {
|
||||
log.vlog("Item cannot be deleted from OneDrive because not found in the local database");
|
||||
log.vlog("Item cannot be deleted from OneDrive because it was not found in the local database");
|
||||
} else {
|
||||
log.logAndNotify("Cannot delete remote item: ", e.msg);
|
||||
}
|
||||
|
|
|
@ -852,10 +852,15 @@ final class OneDriveApi
|
|||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/site_search?view=odsp-graph-online
|
||||
JSONValue o365SiteSearch(){
|
||||
JSONValue o365SiteSearch(const(char)[] nextLink){
|
||||
checkAccessTokenExpired();
|
||||
const(char)[] url;
|
||||
url = siteSearchUrl ~ "=*";
|
||||
// configure URL to query
|
||||
if (nextLink.empty) {
|
||||
url = siteSearchUrl ~ "=*";
|
||||
} else {
|
||||
url = nextLink;
|
||||
}
|
||||
return get(url);
|
||||
}
|
||||
|
||||
|
|
764
src/sync.d
764
src/sync.d
|
@ -661,9 +661,12 @@ final class SyncEngine
|
|||
Item databaseItem;
|
||||
foreach (searchDriveId; driveIDsArray) {
|
||||
log.vdebug("searching database for: ", searchDriveId, " ", sharedFolderName);
|
||||
if (itemdb.selectByPath(sharedFolderName, searchDriveId, databaseItem)) {
|
||||
if (itemdb.idInLocalDatabase(searchDriveId, searchResult["remoteItem"]["id"].str)){
|
||||
// Shared folder is present
|
||||
log.vdebug("Found shared folder name in database");
|
||||
itemInDatabase = true;
|
||||
// Query the DB for the details of this item
|
||||
itemdb.selectByPath(sharedFolderName, searchDriveId, databaseItem);
|
||||
log.vdebug("databaseItem: ", databaseItem);
|
||||
// Does the databaseItem.driveId == defaultDriveId?
|
||||
if (databaseItem.driveId == defaultDriveId) {
|
||||
|
@ -958,7 +961,17 @@ final class SyncEngine
|
|||
}
|
||||
|
||||
Item item;
|
||||
if (!itemdb.selectByPath(path, defaultDriveId, item)) {
|
||||
// Need to check all driveid's we know about, not just the defaultDriveId
|
||||
bool itemInDB = false;
|
||||
foreach (searchDriveId; driveIDsArray) {
|
||||
if (itemdb.selectByPath(path, searchDriveId, item)) {
|
||||
// item was found in the DB
|
||||
itemInDB = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Was the item found in the DB
|
||||
if (!itemInDB) {
|
||||
// this is odd .. this directory is not in the local database - just go delete it
|
||||
log.vlog("The requested directory to delete was not found in the local database - pushing delete request direct to OneDrive");
|
||||
uploadDeleteItem(item, path);
|
||||
|
@ -2538,7 +2551,14 @@ final class SyncEngine
|
|||
safeRename(newPath);
|
||||
}
|
||||
}
|
||||
rename(oldPath, newPath);
|
||||
// try and rename path, catch exception
|
||||
try {
|
||||
log.vdebug("Calling rename(oldPath, newPath)");
|
||||
rename(oldPath, newPath);
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
}
|
||||
}
|
||||
// handle changed content and mtime
|
||||
// HACK: use mtime+hash instead of cTag because of https://github.com/OneDrive/onedrive-api-docs/issues/765
|
||||
|
@ -2923,7 +2943,12 @@ final class SyncEngine
|
|||
}
|
||||
|
||||
// scan for changes in the path provided
|
||||
log.log("Uploading differences of ", logPath);
|
||||
if (isDir(path)) {
|
||||
// if this path is a directory, output this message.
|
||||
// if a file, potentially leads to confusion as to what the client is actually doing
|
||||
log.log("Uploading differences of ", logPath);
|
||||
}
|
||||
|
||||
Item item;
|
||||
// For each unique OneDrive driveID we know about
|
||||
foreach (driveId; driveIDsArray) {
|
||||
|
@ -2943,7 +2968,13 @@ final class SyncEngine
|
|||
}
|
||||
}
|
||||
|
||||
log.log("Uploading new items of ", logPath);
|
||||
// scan for changes in the path provided
|
||||
if (isDir(path)) {
|
||||
// if this path is a directory, output this message.
|
||||
// if a file, potentially leads to confusion as to what the client is actually doing
|
||||
log.log("Uploading new items of ", logPath);
|
||||
}
|
||||
|
||||
// Filesystem walk to find new files not uploaded
|
||||
uploadNewItems(path);
|
||||
// clean up idsToDelete only if --dry-run is set
|
||||
|
@ -3505,10 +3536,18 @@ final class SyncEngine
|
|||
itemdb.deleteById(item.driveId, item.id);
|
||||
return;
|
||||
} else {
|
||||
// For logging consistency
|
||||
writeln("");
|
||||
// normal session upload
|
||||
response = session.upload(path, item.driveId, item.parentId, baseName(path), item.eTag);
|
||||
if ((!syncBusinessFolders) || (item.driveId == defaultDriveId)) {
|
||||
// For logging consistency
|
||||
writeln("");
|
||||
// If we are not syncing Shared Business Folders, or this change is going to the 'users' default drive, handle normally
|
||||
// Perform a normal session upload
|
||||
response = session.upload(path, item.driveId, item.parentId, baseName(path), item.eTag);
|
||||
} else {
|
||||
// If we are uploading to a shared business folder, there are a couple of corner cases here:
|
||||
// 1. Shared Folder is a 'users' folder
|
||||
// 2. Shared Folder is a 'SharePoint Library' folder, meaning we get hit by this stupidity: https://github.com/OneDrive/onedrive-api-docs/issues/935
|
||||
response = handleSharePointMetadataAdditionBug(item, path);
|
||||
}
|
||||
}
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
|
@ -3542,12 +3581,17 @@ final class SyncEngine
|
|||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
// upload done without error
|
||||
writeln("done.");
|
||||
|
||||
// As the session.upload includes the last modified time, save the response
|
||||
// Is the response a valid JSON object - validation checking done in saveItem
|
||||
saveItem(response);
|
||||
// Did the upload fail?
|
||||
if (!uploadFailed){
|
||||
// upload done without error or failure
|
||||
writeln("done.");
|
||||
// As the session.upload includes the last modified time, save the response
|
||||
// Is the response a valid JSON object - validation checking done in saveItem
|
||||
saveItem(response);
|
||||
} else {
|
||||
// uploadFailed, return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OneDrive documentLibrary
|
||||
|
@ -3565,59 +3609,19 @@ final class SyncEngine
|
|||
itemdb.deleteById(item.driveId, item.id);
|
||||
return;
|
||||
} else {
|
||||
// Handle certain file types differently
|
||||
if ((extension(path) == ".txt") || (extension(path) == ".csv")) {
|
||||
// .txt and .csv are unaffected by https://github.com/OneDrive/onedrive-api-docs/issues/935
|
||||
// For logging consistency
|
||||
writeln("");
|
||||
try {
|
||||
response = session.upload(path, item.driveId, item.parentId, baseName(path), item.eTag);
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
// Resolve https://github.com/abraunegg/onedrive/issues/36
|
||||
if ((e.httpStatusCode == 409) || (e.httpStatusCode == 423)) {
|
||||
// The file is currently checked out or locked for editing by another user
|
||||
// We cant upload this file at this time
|
||||
writeln("skipped.");
|
||||
log.fileOnly("Uploading modified file ", path, " ... skipped.");
|
||||
writeln("", path, " is currently checked out or locked for editing by another user.");
|
||||
log.fileOnly(path, " is currently checked out or locked for editing by another user.");
|
||||
uploadFailed = true;
|
||||
return;
|
||||
} else {
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
// upload done without error
|
||||
// Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint.
|
||||
// This means, as a session upload, on 'completion' the file is 'moved' and generates a 404 ......
|
||||
response = handleSharePointMetadataAdditionBug(item, path);
|
||||
|
||||
// Did the upload fail?
|
||||
if (!uploadFailed){
|
||||
// upload done without error or failure
|
||||
writeln("done.");
|
||||
// As the session.upload includes the last modified time, save the response
|
||||
// Is the response a valid JSON object - validation checking done in saveItem
|
||||
saveItem(response);
|
||||
} else {
|
||||
// Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint.
|
||||
// This means, as a session upload, on 'completion' the file is 'moved' and generates a 404 ......
|
||||
writeln("skipped.");
|
||||
log.fileOnly("Uploading modified file ", path, " ... skipped.");
|
||||
log.vlog("Skip Reason: Microsoft Sharepoint 'enrichment' after upload issue");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
// Delete record from the local database - file will be uploaded as a new file
|
||||
itemdb.deleteById(item.driveId, item.id);
|
||||
} else {
|
||||
// uploadFailed, return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -3732,6 +3736,68 @@ final class SyncEngine
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JSONValue handleSharePointMetadataAdditionBug(const ref Item item, const(string) path)
|
||||
{
|
||||
// Explicit function for handling https://github.com/OneDrive/onedrive-api-docs/issues/935
|
||||
JSONValue response;
|
||||
// Handle certain file types differently
|
||||
if ((extension(path) == ".txt") || (extension(path) == ".csv")) {
|
||||
// .txt and .csv are unaffected by https://github.com/OneDrive/onedrive-api-docs/issues/935
|
||||
// For logging consistency
|
||||
writeln("");
|
||||
try {
|
||||
response = session.upload(path, item.driveId, item.parentId, baseName(path), item.eTag);
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
// Resolve https://github.com/abraunegg/onedrive/issues/36
|
||||
if ((e.httpStatusCode == 409) || (e.httpStatusCode == 423)) {
|
||||
// The file is currently checked out or locked for editing by another user
|
||||
// We cant upload this file at this time
|
||||
writeln("skipped.");
|
||||
log.fileOnly("Uploading modified file ", path, " ... skipped.");
|
||||
writeln("", path, " is currently checked out or locked for editing by another user.");
|
||||
log.fileOnly(path, " is currently checked out or locked for editing by another user.");
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
} else {
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
// upload done without error
|
||||
writeln("done.");
|
||||
} else {
|
||||
// Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint.
|
||||
// This means, as a session upload, on 'completion' the file is 'moved' and generates a 404 ......
|
||||
writeln("skipped.");
|
||||
log.fileOnly("Uploading modified file ", path, " ... skipped.");
|
||||
log.vlog("Skip Reason: Microsoft Sharepoint 'enrichment' after upload issue");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
// Delete record from the local database - file will be uploaded as a new file
|
||||
itemdb.deleteById(item.driveId, item.id);
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
|
||||
// return a JSON response so that it can be used and saved
|
||||
return response;
|
||||
}
|
||||
|
||||
// upload new items to OneDrive
|
||||
private void uploadNewItems(const(string) path)
|
||||
|
@ -3973,18 +4039,15 @@ final class SyncEngine
|
|||
if (!uploadFailed) {
|
||||
// Upload did not fail
|
||||
// Issue #763 - Delete local files after sync handling
|
||||
// are we in an --upload-only scenario?
|
||||
if (uploadOnly) {
|
||||
// are we in a delete local file after upload?
|
||||
if (localDeleteAfterUpload) {
|
||||
// Log that we are deleting a local item
|
||||
log.log("Removing local file as --upload-only & --remove-source-files configured");
|
||||
// are we in a --dry-run scenario?
|
||||
if (!dryRun) {
|
||||
// No --dry-run ... process local file delete
|
||||
log.vdebug("Removing local file: ", path);
|
||||
safeRemove(path);
|
||||
}
|
||||
// are we in an --upload-only & --remove-source-files scenario?
|
||||
if ((uploadOnly) && (localDeleteAfterUpload)) {
|
||||
// Log that we are deleting a local item
|
||||
log.log("Removing local file as --upload-only & --remove-source-files configured");
|
||||
// are we in a --dry-run scenario?
|
||||
if (!dryRun) {
|
||||
// No --dry-run ... process local file delete
|
||||
log.vdebug("Removing local file: ", path);
|
||||
safeRemove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4551,9 +4614,20 @@ final class SyncEngine
|
|||
// This has been seen with PNG / JPG files mainly, which then contributes to generating a 412 error when we attempt to update the metadata
|
||||
// Validate here that the file uploaded, at least in size, matches in the response to what the size is on disk
|
||||
if (thisFileSize != uploadFileSize){
|
||||
if(disableUploadValidation){
|
||||
// Print a warning message
|
||||
// Upload size did not match local size
|
||||
// There are 2 scenarios where this happens:
|
||||
// 1. Failed Transfer
|
||||
// 2. Upload file is going to a SharePoint Site, where Microsoft enriches the file with additional metadata with no way to disable
|
||||
// For this client:
|
||||
// - If a SharePoint Library, disableUploadValidation gets flagged as True
|
||||
// - If we are syncing a business shared folder, this folder could reside on a Users Path (there should be no upload issue) or SharePoint (upload issue)
|
||||
if ((disableUploadValidation)|| (syncBusinessFolders && (parent.driveId != defaultDriveId))){
|
||||
// Print a warning message - should only be triggered if:
|
||||
// - disableUploadValidation gets flagged (documentLibrary account type)
|
||||
// - syncBusinessFolders is being used & parent.driveId != defaultDriveId
|
||||
log.log("WARNING: Uploaded file size does not match local file - skipping upload validation");
|
||||
log.vlog("WARNING: Due to Microsoft Sharepoint 'enrichment' of files, this file is now technically different to your local copy");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
} else {
|
||||
// OK .. the uploaded file does not match and we did not disable this validation
|
||||
log.log("Uploaded file size does not match local file - upload failure - retrying");
|
||||
|
@ -4820,53 +4894,71 @@ final class SyncEngine
|
|||
} else {
|
||||
// OneDrive Business account modified file upload handling
|
||||
if (accountType == "business"){
|
||||
// OneDrive Business Account - always use a session to upload
|
||||
writeln("");
|
||||
try {
|
||||
response = session.upload(path, parent.driveId, parent.id, baseName(path), fileDetailsFromOneDrive["eTag"].str);
|
||||
} catch (OneDriveException e) {
|
||||
log.vdebug("response = session.upload(path, parent.driveId, parent.id, baseName(path), fileDetailsFromOneDrive['eTag'].str); generated a OneDriveException");
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
// OneDrive Business Account
|
||||
if ((!syncBusinessFolders) || (parent.driveId == defaultDriveId)) {
|
||||
// If we are not syncing Shared Business Folders, or this change is going to the 'users' default drive, handle normally
|
||||
// For logging consistency
|
||||
writeln("");
|
||||
try {
|
||||
response = session.upload(path, parent.driveId, parent.id, baseName(path), fileDetailsFromOneDrive["eTag"].str);
|
||||
} catch (OneDriveException e) {
|
||||
log.vdebug("response = session.upload(path, parent.driveId, parent.id, baseName(path), fileDetailsFromOneDrive['eTag'].str); generated a OneDriveException");
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
if (e.httpStatusCode == 429) {
|
||||
// HTTP request returned status code 429 (Too Many Requests). We need to leverage the response Retry-After HTTP header to ensure minimum delay until the throttle is removed.
|
||||
handleOneDriveThrottleRequest();
|
||||
// Retry original request by calling function again to avoid replicating any further error handling
|
||||
log.vdebug("Retrying original request that generated the OneDrive HTTP 429 Response Code (Too Many Requests) - calling uploadNewFile(path);");
|
||||
uploadNewFile(path);
|
||||
// return back to original call
|
||||
return;
|
||||
}
|
||||
if (e.httpStatusCode == 504) {
|
||||
// HTTP request returned status code 504 (Gateway Timeout)
|
||||
log.log("OneDrive returned a 'HTTP 504 - Gateway Timeout' - retrying upload request");
|
||||
// Retry original request by calling function again to avoid replicating any further error handling
|
||||
uploadNewFile(path);
|
||||
// return back to original call
|
||||
return;
|
||||
} else {
|
||||
// error uploading file
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
if (e.httpStatusCode == 429) {
|
||||
// HTTP request returned status code 429 (Too Many Requests). We need to leverage the response Retry-After HTTP header to ensure minimum delay until the throttle is removed.
|
||||
handleOneDriveThrottleRequest();
|
||||
// Retry original request by calling function again to avoid replicating any further error handling
|
||||
log.vdebug("Retrying original request that generated the OneDrive HTTP 429 Response Code (Too Many Requests) - calling uploadNewFile(path);");
|
||||
uploadNewFile(path);
|
||||
// return back to original call
|
||||
return;
|
||||
}
|
||||
if (e.httpStatusCode == 504) {
|
||||
// HTTP request returned status code 504 (Gateway Timeout)
|
||||
log.log("OneDrive returned a 'HTTP 504 - Gateway Timeout' - retrying upload request");
|
||||
// Retry original request by calling function again to avoid replicating any further error handling
|
||||
uploadNewFile(path);
|
||||
// return back to original call
|
||||
return;
|
||||
// upload complete
|
||||
writeln("done.");
|
||||
saveItem(response);
|
||||
} else {
|
||||
// If we are uploading to a shared business folder, there are a couple of corner cases here:
|
||||
// 1. Shared Folder is a 'users' folder
|
||||
// 2. Shared Folder is a 'SharePoint Library' folder, meaning we get hit by this stupidity: https://github.com/OneDrive/onedrive-api-docs/issues/935
|
||||
|
||||
// Need try{} & catch (OneDriveException e) { & catch (FileException e) { handler for this query
|
||||
response = handleSharePointMetadataAdditionBugReplaceFile(fileDetailsFromOneDrive, parent, path);
|
||||
if (!uploadFailed){
|
||||
// Is the response a valid JSON object - validation checking done in saveItem
|
||||
saveItem(response);
|
||||
} else {
|
||||
// error uploading file
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
// uploadFailed, return
|
||||
return;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
// upload complete
|
||||
writeln("done.");
|
||||
saveItem(response);
|
||||
}
|
||||
|
||||
// OneDrive SharePoint account modified file upload handling
|
||||
|
@ -4875,93 +4967,15 @@ final class SyncEngine
|
|||
// as if too large, the following error will be generated by OneDrive:
|
||||
// HTTP request returned status code 413 (Request Entity Too Large)
|
||||
// We also cant use a session to upload the file, we have to use simpleUploadReplace
|
||||
|
||||
// Calculate existing hash for this file
|
||||
string existingFileHash = computeQuickXorHash(path);
|
||||
|
||||
if (getSize(path) <= thresholdFileSize) {
|
||||
// Upload file via simpleUploadReplace as below threshold size
|
||||
try {
|
||||
response = onedrive.simpleUploadReplace(path, fileDetailsFromOneDrive["parentReference"]["driveId"].str, fileDetailsFromOneDrive["id"].str, fileDetailsFromOneDrive["eTag"].str);
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return;
|
||||
} else {
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Need try{} & catch (OneDriveException e) { & catch (FileException e) { handler for this query
|
||||
response = handleSharePointMetadataAdditionBugReplaceFile(fileDetailsFromOneDrive, parent, path);
|
||||
if (!uploadFailed){
|
||||
// Is the response a valid JSON object - validation checking done in saveItem
|
||||
saveItem(response);
|
||||
} else {
|
||||
// Have to upload via a session, however we have to delete the file first otherwise this will generate a 404 error post session upload
|
||||
// Remove the existing file
|
||||
onedrive.deleteById(fileDetailsFromOneDrive["parentReference"]["driveId"].str, fileDetailsFromOneDrive["id"].str, fileDetailsFromOneDrive["eTag"].str);
|
||||
// Upload as a session, as a new file
|
||||
writeln("");
|
||||
try {
|
||||
response = session.upload(path, parent.driveId, parent.id, baseName(path));
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return;
|
||||
} else {
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
writeln(" done.");
|
||||
// Is the response a valid JSON object - validation checking done in saveItem
|
||||
saveItem(response);
|
||||
|
||||
// Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint.
|
||||
// So - now the 'local' and 'remote' file is technically DIFFERENT ... thanks Microsoft .. NO way to disable this stupidity
|
||||
string uploadNewFileHash;
|
||||
if (hasQuickXorHash(response)) {
|
||||
// use the response json hash detail to compare
|
||||
uploadNewFileHash = response["file"]["hashes"]["quickXorHash"].str;
|
||||
}
|
||||
|
||||
if (existingFileHash != uploadNewFileHash) {
|
||||
// file was modified by Microsoft post upload to SharePoint site
|
||||
log.vdebug("Existing Local File Hash: ", existingFileHash);
|
||||
log.vdebug("New Remote File Hash: ", uploadNewFileHash);
|
||||
|
||||
if(!uploadOnly){
|
||||
// Download the Microsoft 'modified' file so 'local' is now in sync
|
||||
log.vlog("Due to Microsoft Sharepoint 'enrichment' of files, downloading 'enriched' file to ensure local file is in-sync");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
auto fileSize = response["size"].integer;
|
||||
onedrive.downloadById(response["parentReference"]["driveId"].str, response["id"].str, path, fileSize);
|
||||
} else {
|
||||
// we are not downloading a file, warn that file differences will exist
|
||||
log.vlog("WARNING: Due to Microsoft Sharepoint 'enrichment' of files, this file is now technically different to your local copy");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
}
|
||||
// uploadFailed, return
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5038,6 +5052,106 @@ final class SyncEngine
|
|||
}
|
||||
}
|
||||
|
||||
private JSONValue handleSharePointMetadataAdditionBugReplaceFile(JSONValue fileDetailsFromOneDrive, const ref Item parent, const(string) path)
|
||||
{
|
||||
// Explicit function for handling https://github.com/OneDrive/onedrive-api-docs/issues/935
|
||||
// Replace existing file
|
||||
JSONValue response;
|
||||
|
||||
// Depending on the file size, this will depend on how best to handle the modified local file
|
||||
// as if too large, the following error will be generated by OneDrive:
|
||||
// HTTP request returned status code 413 (Request Entity Too Large)
|
||||
// We also cant use a session to upload the file, we have to use simpleUploadReplace
|
||||
|
||||
// Calculate existing hash for this file
|
||||
string existingFileHash = computeQuickXorHash(path);
|
||||
|
||||
if (getSize(path) <= thresholdFileSize) {
|
||||
// Upload file via simpleUploadReplace as below threshold size
|
||||
try {
|
||||
response = onedrive.simpleUploadReplace(path, fileDetailsFromOneDrive["parentReference"]["driveId"].str, fileDetailsFromOneDrive["id"].str, fileDetailsFromOneDrive["eTag"].str);
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
} else {
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
} else {
|
||||
// Have to upload via a session, however we have to delete the file first otherwise this will generate a 404 error post session upload
|
||||
// Remove the existing file
|
||||
onedrive.deleteById(fileDetailsFromOneDrive["parentReference"]["driveId"].str, fileDetailsFromOneDrive["id"].str, fileDetailsFromOneDrive["eTag"].str);
|
||||
// Upload as a session, as a new file
|
||||
writeln("");
|
||||
try {
|
||||
response = session.upload(path, parent.driveId, parent.id, baseName(path));
|
||||
} catch (OneDriveException e) {
|
||||
if (e.httpStatusCode == 401) {
|
||||
// OneDrive returned a 'HTTP/1.1 401 Unauthorized Error' - file failed to be uploaded
|
||||
writeln("skipped.");
|
||||
log.vlog("OneDrive returned a 'HTTP 401 - Unauthorized' - gracefully handling error");
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
} else {
|
||||
// display what the error is
|
||||
writeln("skipped.");
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
} catch (FileException e) {
|
||||
// display the error message
|
||||
writeln("skipped.");
|
||||
displayFileSystemErrorMessage(e.msg, getFunctionName!({}));
|
||||
uploadFailed = true;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
writeln("done.");
|
||||
// Due to https://github.com/OneDrive/onedrive-api-docs/issues/935 Microsoft modifies all PDF, MS Office & HTML files with added XML content. It is a 'feature' of SharePoint.
|
||||
// So - now the 'local' and 'remote' file is technically DIFFERENT ... thanks Microsoft .. NO way to disable this stupidity
|
||||
string uploadNewFileHash;
|
||||
if (hasQuickXorHash(response)) {
|
||||
// use the response json hash detail to compare
|
||||
uploadNewFileHash = response["file"]["hashes"]["quickXorHash"].str;
|
||||
}
|
||||
|
||||
if (existingFileHash != uploadNewFileHash) {
|
||||
// file was modified by Microsoft post upload to SharePoint site
|
||||
log.vdebug("Existing Local File Hash: ", existingFileHash);
|
||||
log.vdebug("New Remote File Hash: ", uploadNewFileHash);
|
||||
|
||||
if(!uploadOnly){
|
||||
// Download the Microsoft 'modified' file so 'local' is now in sync
|
||||
log.vlog("Due to Microsoft Sharepoint 'enrichment' of files, downloading 'enriched' file to ensure local file is in-sync");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
auto fileSize = response["size"].integer;
|
||||
onedrive.downloadById(response["parentReference"]["driveId"].str, response["id"].str, path, fileSize);
|
||||
} else {
|
||||
// we are not downloading a file, warn that file differences will exist
|
||||
log.vlog("WARNING: Due to Microsoft Sharepoint 'enrichment' of files, this file is now technically different to your local copy");
|
||||
log.vlog("See: https://github.com/OneDrive/onedrive-api-docs/issues/935 for further details");
|
||||
}
|
||||
}
|
||||
|
||||
// return a JSON response so that it can be used and saved
|
||||
return response;
|
||||
}
|
||||
|
||||
// delete an item on OneDrive
|
||||
private void uploadDeleteItem(Item item, const(string) path)
|
||||
{
|
||||
|
@ -5212,11 +5326,18 @@ final class SyncEngine
|
|||
if (jsonItem.type() == JSONType.object){
|
||||
// Check if the response JSON has an 'id', otherwise makeItem() fails with 'Key not found: id'
|
||||
if (hasId(jsonItem)) {
|
||||
// Takes a JSON input and formats to an item which can be used by the database
|
||||
Item item = makeItem(jsonItem);
|
||||
// Add to the local database
|
||||
log.vdebug("Adding to database: ", item);
|
||||
itemdb.upsert(item);
|
||||
// Are we in a --upload-only & --remove-source-files scenario?
|
||||
// We do not want to add the item to the database in this situation as there is no local reference to the file post file deletion
|
||||
if ((uploadOnly) && (localDeleteAfterUpload)) {
|
||||
// Log that we are deleting a local item
|
||||
log.vdebug("Skipping adding to database as --upload-only & --remove-source-files configured");
|
||||
} else {
|
||||
// Takes a JSON input and formats to an item which can be used by the database
|
||||
Item item = makeItem(jsonItem);
|
||||
// Add to the local database
|
||||
log.vdebug("Adding to database: ", item);
|
||||
itemdb.upsert(item);
|
||||
}
|
||||
} else {
|
||||
// log error
|
||||
log.error("ERROR: OneDrive response missing required 'id' element");
|
||||
|
@ -5356,9 +5477,19 @@ final class SyncEngine
|
|||
void deleteByPath(const(string) path)
|
||||
{
|
||||
Item item;
|
||||
if (!itemdb.selectByPath(path, defaultDriveId, item)) {
|
||||
// Need to check all driveid's we know about, not just the defaultDriveId
|
||||
bool itemInDB = false;
|
||||
foreach (searchDriveId; driveIDsArray) {
|
||||
if (itemdb.selectByPath(path, searchDriveId, item)) {
|
||||
// item was found in the DB
|
||||
itemInDB = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!itemInDB) {
|
||||
throw new SyncException("The item to delete is not in the local database");
|
||||
}
|
||||
|
||||
if (item.parentId == null) {
|
||||
// the item is a remote folder, need to do the operation on the parent
|
||||
enforce(itemdb.selectByPathWithoutRemote(path, defaultDriveId, item));
|
||||
|
@ -5418,93 +5549,158 @@ final class SyncEngine
|
|||
|
||||
string site_id;
|
||||
string drive_id;
|
||||
string webUrl;
|
||||
bool found = false;
|
||||
JSONValue siteQuery;
|
||||
JSONValue siteQuery;
|
||||
string nextLink;
|
||||
string[] siteSearchResults;
|
||||
|
||||
log.log("Office 365 Library Name Query: ", o365SharedLibraryName);
|
||||
|
||||
try {
|
||||
siteQuery = onedrive.o365SiteSearch();
|
||||
} catch (OneDriveException e) {
|
||||
log.error("ERROR: Query of OneDrive for Office 365 Library Name failed");
|
||||
if (e.httpStatusCode == 403) {
|
||||
// Forbidden - most likely authentication scope needs to be updated
|
||||
log.error("ERROR: Authentication scope needs to be updated. Use --logout and re-authenticate client.");
|
||||
return;
|
||||
} else {
|
||||
// display what the error is
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// is siteQuery a valid JSON object & contain data we can use?
|
||||
if ((siteQuery.type() == JSONType.object) && ("value" in siteQuery)) {
|
||||
// valid JSON object
|
||||
log.vdebug("O365 Query Response: ", siteQuery);
|
||||
|
||||
foreach (searchResult; siteQuery["value"].array) {
|
||||
// Need an 'exclusive' match here with o365SharedLibraryName as entered
|
||||
log.vdebug("Found O365 Site: ", searchResult);
|
||||
|
||||
// 'displayName', 'id' and 'webUrl' have to be present in the search result record
|
||||
if (("displayName" in searchResult) && ("id" in searchResult) && ("webUrl" in searchResult)) {
|
||||
if (o365SharedLibraryName == searchResult["displayName"].str){
|
||||
// 'displayName' matches search request
|
||||
site_id = searchResult["id"].str;
|
||||
webUrl = searchResult["webUrl"].str;
|
||||
JSONValue siteDriveQuery;
|
||||
|
||||
try {
|
||||
siteDriveQuery = onedrive.o365SiteDrives(site_id);
|
||||
} catch (OneDriveException e) {
|
||||
log.error("ERROR: Query of OneDrive for Office Site ID failed");
|
||||
// display what the error is
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
return;
|
||||
}
|
||||
|
||||
// is siteDriveQuery a valid JSON object & contain data we can use?
|
||||
if ((siteDriveQuery.type() == JSONType.object) && ("value" in siteDriveQuery)) {
|
||||
// valid JSON object
|
||||
foreach (driveResult; siteDriveQuery["value"].array) {
|
||||
// Display results
|
||||
found = true;
|
||||
writeln("SiteName: ", searchResult["displayName"].str);
|
||||
writeln("drive_id: ", driveResult["id"].str);
|
||||
writeln("URL: ", webUrl);
|
||||
}
|
||||
} else {
|
||||
// not a valid JSON object
|
||||
log.error("ERROR: There was an error performing this operation on OneDrive");
|
||||
log.error("ERROR: Increase logging verbosity to assist determining why.");
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
try {
|
||||
siteQuery = onedrive.o365SiteSearch(nextLink);
|
||||
} catch (OneDriveException e) {
|
||||
log.error("ERROR: Query of OneDrive for Office 365 Library Name failed");
|
||||
if (e.httpStatusCode == 403) {
|
||||
// Forbidden - most likely authentication scope needs to be updated
|
||||
log.error("ERROR: Authentication scope needs to be updated. Use --logout and re-authenticate client.");
|
||||
return;
|
||||
}
|
||||
// HTTP request returned status code 429 (Too Many Requests)
|
||||
if (e.httpStatusCode == 429) {
|
||||
// HTTP request returned status code 429 (Too Many Requests). We need to leverage the response Retry-After HTTP header to ensure minimum delay until the throttle is removed.
|
||||
handleOneDriveThrottleRequest();
|
||||
log.vdebug("Retrying original request that generated the OneDrive HTTP 429 Response Code (Too Many Requests) - attempting to query OneDrive drive children");
|
||||
}
|
||||
// HTTP request returned status code 504 (Gateway Timeout) or 429 retry
|
||||
if ((e.httpStatusCode == 429) || (e.httpStatusCode == 504)) {
|
||||
// re-try the specific changes queries
|
||||
if (e.httpStatusCode == 504) {
|
||||
log.log("OneDrive returned a 'HTTP 504 - Gateway Timeout' when attempting to query Sharepoint Sites - retrying applicable request");
|
||||
log.vdebug("siteQuery = onedrive.o365SiteSearch(nextLink) previously threw an error - retrying");
|
||||
// The server, while acting as a proxy, did not receive a timely response from the upstream server it needed to access in attempting to complete the request.
|
||||
log.vdebug("Thread sleeping for 30 seconds as the server did not receive a timely response from the upstream server it needed to access in attempting to complete the request");
|
||||
Thread.sleep(dur!"seconds"(30));
|
||||
}
|
||||
// re-try original request - retried for 429 and 504
|
||||
try {
|
||||
log.vdebug("Retrying Query: siteQuery = onedrive.o365SiteSearch(nextLink)");
|
||||
siteQuery = onedrive.o365SiteSearch(nextLink);
|
||||
log.vdebug("Query 'siteQuery = onedrive.o365SiteSearch(nextLink)' performed successfully on re-try");
|
||||
} catch (OneDriveException e) {
|
||||
// display what the error is
|
||||
log.vdebug("Query Error: siteQuery = onedrive.o365SiteSearch(nextLink) on re-try after delay");
|
||||
// error was not a 504 this time
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 'displayName' not present in JSON results
|
||||
log.error("ERROR: The results returned from OneDrive API do not contain the required items to match. Please check your permissions with your site administrator.");
|
||||
log.error("ERROR: Your site security settings is preventing the following details from being accessed: 'displayName', 'id' and 'webUrl'");
|
||||
log.error("ERROR: To debug this further, please use --verbose --verbose to provide insight as to what details are actually returned.");
|
||||
// display what the error is
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
log.error("ERROR: The requested SharePoint site could not be found. Please check it's name and your permissions to access the site.");
|
||||
// List all sites returned to assist user
|
||||
log.log("\nThe following SharePoint site names were returned:");
|
||||
// is siteQuery a valid JSON object & contain data we can use?
|
||||
if ((siteQuery.type() == JSONType.object) && ("value" in siteQuery)) {
|
||||
// valid JSON object
|
||||
log.vdebug("O365 Query Response: ", siteQuery);
|
||||
|
||||
foreach (searchResult; siteQuery["value"].array) {
|
||||
// list the display name that we use to match against the user query
|
||||
log.log(" * ", searchResult["displayName"].str);
|
||||
// Need an 'exclusive' match here with o365SharedLibraryName as entered
|
||||
log.vdebug("Found O365 Site: ", searchResult);
|
||||
|
||||
// 'displayName' and 'id' have to be present in the search result record in order to query the site
|
||||
if (("displayName" in searchResult) && ("id" in searchResult)) {
|
||||
if (o365SharedLibraryName == searchResult["displayName"].str){
|
||||
// 'displayName' matches search request
|
||||
site_id = searchResult["id"].str;
|
||||
JSONValue siteDriveQuery;
|
||||
|
||||
try {
|
||||
siteDriveQuery = onedrive.o365SiteDrives(site_id);
|
||||
} catch (OneDriveException e) {
|
||||
log.error("ERROR: Query of OneDrive for Office Site ID failed");
|
||||
// display what the error is
|
||||
displayOneDriveErrorMessage(e.msg, getFunctionName!({}));
|
||||
return;
|
||||
}
|
||||
|
||||
// is siteDriveQuery a valid JSON object & contain data we can use?
|
||||
if ((siteDriveQuery.type() == JSONType.object) && ("value" in siteDriveQuery)) {
|
||||
// valid JSON object
|
||||
foreach (driveResult; siteDriveQuery["value"].array) {
|
||||
// Display results
|
||||
writeln("-----------------------------------------------");
|
||||
log.vdebug("Site Details: ", driveResult);
|
||||
found = true;
|
||||
writeln("Site Name: ", searchResult["displayName"].str);
|
||||
writeln("Library Name: ", driveResult["name"].str);
|
||||
writeln("drive_id: ", driveResult["id"].str);
|
||||
writeln("Library URL: ", driveResult["webUrl"].str);
|
||||
}
|
||||
// closeout
|
||||
writeln("-----------------------------------------------");
|
||||
} else {
|
||||
// not a valid JSON object
|
||||
log.error("ERROR: There was an error performing this operation on OneDrive");
|
||||
log.error("ERROR: Increase logging verbosity to assist determining why.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 'displayName', 'id' or ''webUrl' not present in JSON results for a specific site
|
||||
string siteNameAvailable = "Site 'name' was restricted by OneDrive API permissions";
|
||||
bool displayNameAvailable = false;
|
||||
bool idAvailable = false;
|
||||
if ("name" in searchResult) siteNameAvailable = searchResult["name"].str;
|
||||
if ("displayName" in searchResult) displayNameAvailable = true;
|
||||
if ("id" in searchResult) idAvailable = true;
|
||||
|
||||
// Display error details for this site data
|
||||
log.error("\nERROR: SharePoint Site details not provided for: ", siteNameAvailable);
|
||||
log.error("ERROR: The SharePoint Site results returned from OneDrive API do not contain the required items to match. Please check your permissions with your site administrator.");
|
||||
log.error("ERROR: Your site security settings is preventing the following details from being accessed: 'displayName' or 'id'");
|
||||
log.vlog(" - Is 'displayName' available = ", displayNameAvailable);
|
||||
log.vlog(" - Is 'id' available = ", idAvailable);
|
||||
log.error("ERROR: To debug this further, please increase verbosity (--verbose or --verbose --verbose) to provide further insight as to what details are actually being returned.");
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
// The SharePoint site we are searching for was not found in this bundle set
|
||||
// Add to siteSearchResults so we can display what we did find
|
||||
string siteSearchResultsEntry;
|
||||
foreach (searchResult; siteQuery["value"].array) {
|
||||
siteSearchResultsEntry = " * " ~ searchResult["displayName"].str;
|
||||
siteSearchResults ~= siteSearchResultsEntry;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// not a valid JSON object
|
||||
log.error("ERROR: There was an error performing this operation on OneDrive");
|
||||
log.error("ERROR: Increase logging verbosity to assist determining why.");
|
||||
return;
|
||||
}
|
||||
|
||||
// If a collection exceeds the default page size (200 items), the @odata.nextLink property is returned in the response
|
||||
// to indicate more items are available and provide the request URL for the next page of items.
|
||||
if ("@odata.nextLink" in siteQuery) {
|
||||
// Update nextLink to next set of SharePoint library names
|
||||
nextLink = siteQuery["@odata.nextLink"].str;
|
||||
log.vdebug("Setting nextLink to (@odata.nextLink): ", nextLink);
|
||||
} else break;
|
||||
}
|
||||
|
||||
// Was the intended target found?
|
||||
if(!found) {
|
||||
log.error("\nERROR: The requested SharePoint site could not be found. Please check it's name and your permissions to access the site.");
|
||||
// List all sites returned to assist user
|
||||
log.log("\nThe following SharePoint site names were returned:");
|
||||
foreach (searchResultEntry; siteSearchResults) {
|
||||
// list the display name that we use to match against the user query
|
||||
log.log(searchResultEntry);
|
||||
}
|
||||
} else {
|
||||
// not a valid JSON object
|
||||
log.error("ERROR: There was an error performing this operation on OneDrive");
|
||||
log.error("ERROR: Increase logging verbosity to assist determining why.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6209,8 +6405,8 @@ final class SyncEngine
|
|||
// to indicate more items are available and provide the request URL for the next page of items.
|
||||
if ("@odata.nextLink" in thisLevelChildren) {
|
||||
// Update nextLink to next changeSet bundle
|
||||
log.vdebug("Setting nextLink to (@odata.nextLink): ", nextLink);
|
||||
nextLink = thisLevelChildren["@odata.nextLink"].str;
|
||||
log.vdebug("Setting nextLink to (@odata.nextLink): ", nextLink);
|
||||
} else break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue