diff --git a/src/duplicacy_oneclient.go b/src/duplicacy_oneclient.go index 9bdeb6f1..0f25bb91 100644 --- a/src/duplicacy_oneclient.go +++ b/src/duplicacy_oneclient.go @@ -51,7 +51,7 @@ type OneDriveClient struct { APIURL string } -func NewOneDriveClient(tokenFile string, isBusiness bool, client_id string, client_secret string) (*OneDriveClient, error) { +func NewOneDriveClient(tokenFile string, isBusiness bool, client_id string, client_secret string, drive_id string) (*OneDriveClient, error) { description, err := ioutil.ReadFile(tokenFile) if err != nil { @@ -88,10 +88,13 @@ func NewOneDriveClient(tokenFile string, isBusiness bool, client_id string, clie if isBusiness { client.RefreshTokenURL = "https://duplicacy.com/odb_refresh" - client.APIURL = "https://graph.microsoft.com/v1.0/me" + client.APIURL = "https://graph.microsoft.com/v1.0/me/drive" + if drive_id != "" { + client.APIURL = "https://graph.microsoft.com/v1.0/drives/"+drive_id + } } else { client.RefreshTokenURL = "https://duplicacy.com/one_refresh" - client.APIURL = "https://api.onedrive.com/v1.0" + client.APIURL = "https://api.onedrive.com/v1.0/drive" } client.RefreshToken(false) @@ -285,9 +288,9 @@ func (client *OneDriveClient) ListEntries(path string) ([]OneDriveEntry, error) entries := []OneDriveEntry{} - url := client.APIURL + "/drive/root:/" + path + ":/children" + url := client.APIURL + "/root:/" + path + ":/children" if path == "" { - url = client.APIURL + "/drive/root/children" + url = client.APIURL + "/root/children" } if client.TestMode { url += "?top=8" @@ -323,7 +326,8 @@ func (client *OneDriveClient) ListEntries(path string) ([]OneDriveEntry, error) func (client *OneDriveClient) GetFileInfo(path string) (string, bool, int64, error) { - url := client.APIURL + "/drive/root:/" + path + url := client.APIURL + "/root:/" + path + if path == "" { url = client.APIURL + "/root" } url += "?select=id,name,size,folder" readCloser, _, err := client.call(url, "GET", 0, "") @@ -348,7 +352,7 @@ func (client *OneDriveClient) GetFileInfo(path string) (string, bool, int64, err func (client *OneDriveClient) DownloadFile(path string) (io.ReadCloser, int64, error) { - url := client.APIURL + "/drive/items/root:/" + path + ":/content" + url := client.APIURL + "/items/root:/" + path + ":/content" return client.call(url, "GET", 0, "") } @@ -358,7 +362,7 @@ func (client *OneDriveClient) UploadFile(path string, content []byte, rateLimit // Upload file using the simple method; this is only possible for OneDrive Personal or if the file // is smaller than 4MB for OneDrive Business if !client.IsBusiness || (client.TestMode && rand.Int() % 2 == 0) { - url := client.APIURL + "/drive/root:/" + path + ":/content" + url := client.APIURL + "/root:/" + path + ":/content" readCloser, _, err := client.call(url, "PUT", CreateRateLimitedReader(content, rateLimit), "application/octet-stream") if err != nil { @@ -392,7 +396,7 @@ func (client *OneDriveClient) CreateUploadSession(path string) (uploadURL string }, } - readCloser, _, err := client.call(client.APIURL + "/drive/root:/" + path + ":/createUploadSession", "POST", input, "application/json") + readCloser, _, err := client.call(client.APIURL + "/root:/" + path + ":/createUploadSession", "POST", input, "application/json") if err != nil { return "", err } @@ -436,7 +440,7 @@ func (client *OneDriveClient) UploadFileSession(uploadURL string, content []byte func (client *OneDriveClient) DeleteFile(path string) error { - url := client.APIURL + "/drive/root:/" + path + url := client.APIURL + "/root:/" + path readCloser, _, err := client.call(url, "DELETE", 0, "") if err != nil { @@ -449,10 +453,10 @@ func (client *OneDriveClient) DeleteFile(path string) error { func (client *OneDriveClient) MoveFile(path string, parent string) error { - url := client.APIURL + "/drive/root:/" + path + url := client.APIURL + "/root:/" + path parentReference := make(map[string]string) - parentReference["path"] = "/drive/root:/" + parent + parentReference["path"] = "/root:/" + parent parameters := make(map[string]interface{}) parameters["parentReference"] = parentReference @@ -504,7 +508,7 @@ func (client *OneDriveClient) CreateDirectory(path string, name string) error { return fmt.Errorf("The path '%s' is not a directory", path) } - url = client.APIURL + "/drive/root:/" + path + ":/children" + url = client.APIURL + "/root:/" + path + ":/children" } parameters := make(map[string]interface{}) diff --git a/src/duplicacy_onestorage.go b/src/duplicacy_onestorage.go index 2e24333c..fb0fc804 100644 --- a/src/duplicacy_onestorage.go +++ b/src/duplicacy_onestorage.go @@ -19,13 +19,13 @@ type OneDriveStorage struct { } // CreateOneDriveStorage creates an OneDrive storage object. -func CreateOneDriveStorage(tokenFile string, isBusiness bool, storagePath string, threads int, client_id string, client_secret string) (storage *OneDriveStorage, err error) { +func CreateOneDriveStorage(tokenFile string, isBusiness bool, storagePath string, threads int, client_id string, client_secret string, drive_id string) (storage *OneDriveStorage, err error) { for len(storagePath) > 0 && storagePath[len(storagePath)-1] == '/' { storagePath = storagePath[:len(storagePath)-1] } - client, err := NewOneDriveClient(tokenFile, isBusiness, client_id, client_secret) + client, err := NewOneDriveClient(tokenFile, isBusiness, client_id, client_secret, drive_id) if err != nil { return nil, err } diff --git a/src/duplicacy_storage.go b/src/duplicacy_storage.go index 99059712..0ad6891d 100644 --- a/src/duplicacy_storage.go +++ b/src/duplicacy_storage.go @@ -261,7 +261,8 @@ func CreateStorage(preference Preference, resetPassword bool, threads int) (stor return fileStorage } - urlRegex := regexp.MustCompile(`^([\w-]+)://([\w\-@\.]+@)?([^/]+)(/(.+))?`) + // Added \! to matched[2] because OneDrive drive ids contain ! (e.g. "b!xxx") + urlRegex := regexp.MustCompile(`^([\w-]+)://([\w\-@\.\!]+@)?([^/]+)(/(.+))?`) matched := urlRegex.FindStringSubmatch(storageURL) @@ -644,6 +645,15 @@ func CreateStorage(preference Preference, resetPassword bool, threads int) (stor SavePassword(preference, "gcd_token", tokenFile) return gcdStorage } else if matched[1] == "one" || matched[1] == "odb" { + // Handle writing directly to the root of the drive + // For odb://drive_id@/, drive_id@ is match[3] not match[2] + if matched[2] == "" && strings.HasSuffix(matched[3], "@") { + matched[2], matched[3] = matched[3], matched[2] + } + drive_id := matched[2] + if len(drive_id) > 0 { + drive_id = drive_id[:len(drive_id)-1] + } storagePath := matched[3] + matched[4] prompt := fmt.Sprintf("Enter the path of the OneDrive token file (downloadable from https://duplicacy.com/one_start):") tokenFile := GetPassword(preference, matched[1] + "_token", prompt, true, resetPassword) @@ -659,7 +669,7 @@ func CreateStorage(preference Preference, resetPassword bool, threads int) (stor client_secret = GetPassword(preference, matched[1] + "_client_secret", prompt, true, resetPassword) } - oneDriveStorage, err := CreateOneDriveStorage(tokenFile, matched[1] == "odb", storagePath, threads, client_id, client_secret) + oneDriveStorage, err := CreateOneDriveStorage(tokenFile, matched[1] == "odb", storagePath, threads, client_id, client_secret, drive_id) if err != nil { LOG_ERROR("STORAGE_CREATE", "Failed to load the OneDrive storage at %s: %v", storageURL, err) return nil