mirror of
https://gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud
synced 2025-12-24 18:57:43 +00:00
Compare commits
3 Commits
v1.2.1
...
7-move-to-
| Author | SHA1 | Date | |
|---|---|---|---|
| f564979da5 | |||
| 359e45d9d5 | |||
| 4f981e786a |
@@ -4,9 +4,14 @@ stages:
|
|||||||
- test
|
- test
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
- go get -u golang.org/x/lint/golint
|
||||||
|
- go get -u github.com/kardianos/govendor
|
||||||
- mkdir -p /go/src/gitlab.bertha.cloud/partitio/Nextcloud-Partitio
|
- mkdir -p /go/src/gitlab.bertha.cloud/partitio/Nextcloud-Partitio
|
||||||
- cp -r $CI_PROJECT_DIR /go/src/gitlab.bertha.cloud/partitio/Nextcloud-Partitio
|
- cp -r $CI_PROJECT_DIR /go/src/gitlab.bertha.cloud/partitio/Nextcloud-Partitio
|
||||||
- cd /go/src/gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud
|
- cd /go/src/gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud
|
||||||
|
#- sed -i -e 's/$NEXTCLOUD_URL/'${NEXTCLOUD_URL//\//\\/}'/g' config.yml
|
||||||
|
#- sed -i -e 's/$NEXTCLOUD_PASSWORD/'${NEXTCLOUD_PASSWORD}'/g' config.yml
|
||||||
|
#- sed -i -e 's/$NEXTCLOUD_EMAIL/'${NEXTCLOUD_EMAIL}'/g' config.yml
|
||||||
- make dep
|
- make dep
|
||||||
|
|
||||||
unit_tests:
|
unit_tests:
|
||||||
@@ -35,5 +40,4 @@ lint_code:
|
|||||||
tags:
|
tags:
|
||||||
- docker
|
- docker
|
||||||
script:
|
script:
|
||||||
- go get -u golang.org/x/lint/golint
|
|
||||||
- make lint
|
- make lint
|
||||||
|
|||||||
4
auth.go
4
auth.go
@@ -2,9 +2,7 @@ package gonextcloud
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
req "github.com/levigross/grequests"
|
req "github.com/levigross/grequests"
|
||||||
|
|
||||||
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
|
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,8 +33,6 @@ func (c *Client) Login(username string, password string) error {
|
|||||||
e := types.APIError{Message: "authentication failed"}
|
e := types.APIError{Message: "authentication failed"}
|
||||||
return &e
|
return &e
|
||||||
}
|
}
|
||||||
// Create webdav client
|
|
||||||
c.webdav = newWebDav(c.baseURL.String()+"/remote.php/webdav", c.username, c.password)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
client.go
15
client.go
@@ -1,12 +1,9 @@
|
|||||||
package gonextcloud
|
package gonextcloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
|
||||||
|
|
||||||
req "github.com/levigross/grequests"
|
req "github.com/levigross/grequests"
|
||||||
"github.com/studio-b12/gowebdav"
|
|
||||||
|
|
||||||
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
|
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
|
||||||
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is the API client that performs all operations against a Nextcloud server.
|
// Client is the API client that performs all operations against a Nextcloud server.
|
||||||
@@ -26,7 +23,6 @@ type Client struct {
|
|||||||
shares *Shares
|
shares *Shares
|
||||||
users *Users
|
users *Users
|
||||||
groups *Groups
|
groups *Groups
|
||||||
webdav *webDav
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient create a new Client from the Nextcloud Instance URL
|
// NewClient create a new Client from the Nextcloud Instance URL
|
||||||
@@ -46,7 +42,6 @@ func NewClient(hostname string) (*Client, error) {
|
|||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
c.apps = &Apps{c}
|
c.apps = &Apps{c}
|
||||||
c.appsConfig = &AppsConfig{c}
|
c.appsConfig = &AppsConfig{c}
|
||||||
c.groupFolders = &GroupFolders{c}
|
c.groupFolders = &GroupFolders{c}
|
||||||
@@ -54,9 +49,6 @@ func NewClient(hostname string) (*Client, error) {
|
|||||||
c.shares = &Shares{c}
|
c.shares = &Shares{c}
|
||||||
c.users = &Users{c}
|
c.users = &Users{c}
|
||||||
c.groups = &Groups{c}
|
c.groups = &Groups{c}
|
||||||
// Create empty webdav client
|
|
||||||
// It will be replaced after login
|
|
||||||
c.webdav = &webDav{Client: &gowebdav.Client{}}
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +86,3 @@ func (c *Client) Users() types.Users {
|
|||||||
func (c *Client) Groups() types.Groups {
|
func (c *Client) Groups() types.Groups {
|
||||||
return c.groups
|
return c.groups
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebDav return the WebDav client Interface
|
|
||||||
func (c *Client) WebDav() types.WebDav {
|
|
||||||
return c.webdav
|
|
||||||
}
|
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -9,7 +9,6 @@ require (
|
|||||||
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0
|
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/stretchr/testify v1.2.2
|
github.com/stretchr/testify v1.2.2
|
||||||
github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac
|
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 // indirect
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.1
|
gopkg.in/yaml.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -18,8 +18,6 @@ github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac h1:xQ9gCVzqb939vjhxuES4IXYe4AlHB4Q71/K06aazQmQ=
|
|
||||||
github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s=
|
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ type Client interface {
|
|||||||
Shares() Shares
|
Shares() Shares
|
||||||
Users() Users
|
Users() Users
|
||||||
Groups() Groups
|
Groups() Groups
|
||||||
WebDav() WebDav
|
|
||||||
Login(username string, password string) error
|
Login(username string, password string) error
|
||||||
Logout() error
|
Logout() error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WebDav available methods
|
|
||||||
type WebDav interface {
|
|
||||||
// ReadDir reads the contents of a remote directory
|
|
||||||
ReadDir(path string) ([]os.FileInfo, error)
|
|
||||||
// Stat returns the file stats for a specified path
|
|
||||||
Stat(path string) (os.FileInfo, error)
|
|
||||||
// Remove removes a remote file
|
|
||||||
Remove(path string) error
|
|
||||||
// RemoveAll removes remote files
|
|
||||||
RemoveAll(path string) error
|
|
||||||
// Mkdir makes a directory
|
|
||||||
Mkdir(path string, _ os.FileMode) error
|
|
||||||
// MkdirAll like mkdir -p, but for webdav
|
|
||||||
MkdirAll(path string, _ os.FileMode) error
|
|
||||||
// Rename moves a file from A to B
|
|
||||||
Rename(oldpath, newpath string, overwrite bool) error
|
|
||||||
// Copy copies a file from A to B
|
|
||||||
Copy(oldpath, newpath string, overwrite bool) error
|
|
||||||
// Read reads the contents of a remote file
|
|
||||||
Read(path string) ([]byte, error)
|
|
||||||
// ReadStream reads the stream for a given path
|
|
||||||
ReadStream(path string) (io.ReadCloser, error)
|
|
||||||
// Write writes data to a given path
|
|
||||||
Write(path string, data []byte, _ os.FileMode) error
|
|
||||||
// WriteStream writes a stream
|
|
||||||
WriteStream(path string, stream io.Reader, _ os.FileMode) error
|
|
||||||
|
|
||||||
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
|
||||||
// directory in the tree, including root.
|
|
||||||
Walk(path string, walkFunc filepath.WalkFunc) error
|
|
||||||
}
|
|
||||||
19
vendor/github.com/studio-b12/gowebdav/.gitignore
generated
vendored
19
vendor/github.com/studio-b12/gowebdav/.gitignore
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
# Folders to ignore
|
|
||||||
/src
|
|
||||||
/bin
|
|
||||||
/pkg
|
|
||||||
/gowebdav
|
|
||||||
/.idea
|
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test binary, build with `go test -c`
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
|
||||||
10
vendor/github.com/studio-b12/gowebdav/.travis.yml
generated
vendored
10
vendor/github.com/studio-b12/gowebdav/.travis.yml
generated
vendored
@@ -1,10 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- "1.x"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v --short ./...
|
|
||||||
27
vendor/github.com/studio-b12/gowebdav/LICENSE
generated
vendored
27
vendor/github.com/studio-b12/gowebdav/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2014, Studio B12 GmbH
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its contributors
|
|
||||||
may be used to endorse or promote products derived from this software without
|
|
||||||
specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
33
vendor/github.com/studio-b12/gowebdav/Makefile
generated
vendored
33
vendor/github.com/studio-b12/gowebdav/Makefile
generated
vendored
@@ -1,33 +0,0 @@
|
|||||||
BIN := gowebdav
|
|
||||||
SRC := $(wildcard *.go) cmd/gowebdav/main.go
|
|
||||||
|
|
||||||
all: test cmd
|
|
||||||
|
|
||||||
cmd: ${BIN}
|
|
||||||
|
|
||||||
${BIN}: ${SRC}
|
|
||||||
go build -o $@ ./cmd/gowebdav
|
|
||||||
|
|
||||||
test:
|
|
||||||
go test -v --short ./...
|
|
||||||
|
|
||||||
api:
|
|
||||||
@sed '/^## API$$/,$$d' -i README.md
|
|
||||||
@echo '## API' >> README.md
|
|
||||||
@godoc2md github.com/studio-b12/gowebdav | sed '/^$$/N;/^\n$$/D' |\
|
|
||||||
sed '2d' |\
|
|
||||||
sed 's/\/src\/github.com\/studio-b12\/gowebdav\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\
|
|
||||||
sed 's/\/src\/target\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\
|
|
||||||
sed 's/^#/##/g' >> README.md
|
|
||||||
|
|
||||||
check:
|
|
||||||
gofmt -w -s $(SRC)
|
|
||||||
@echo
|
|
||||||
gocyclo -over 15 .
|
|
||||||
@echo
|
|
||||||
golint ./...
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@rm -f ${BIN}
|
|
||||||
|
|
||||||
.PHONY: all cmd clean test api check
|
|
||||||
147
vendor/github.com/studio-b12/gowebdav/README.md
generated
vendored
147
vendor/github.com/studio-b12/gowebdav/README.md
generated
vendored
@@ -1,147 +0,0 @@
|
|||||||
# GoWebDAV
|
|
||||||
|
|
||||||
[](https://travis-ci.org/studio-b12/gowebdav)
|
|
||||||
[](https://godoc.org/github.com/studio-b12/gowebdav)
|
|
||||||
[](https://goreportcard.com/report/github.com/studio-b12/gowebdav)
|
|
||||||
|
|
||||||
A golang WebDAV client library.
|
|
||||||
|
|
||||||
## Main features
|
|
||||||
`gowebdav` library allows to perform following actions on the remote WebDAV server:
|
|
||||||
* [create path](#create-path-on-a-webdav-server)
|
|
||||||
* [get files list](#get-files-list)
|
|
||||||
* [download file](#download-file-to-byte-array)
|
|
||||||
* [upload file](#upload-file-from-byte-array)
|
|
||||||
* [get information about specified file/folder](#get-information-about-specified-filefolder)
|
|
||||||
* [move file to another location](#move-file-to-another-location)
|
|
||||||
* [copy file to another location](#copy-file-to-another-location)
|
|
||||||
* [delete file](#delete-file)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
First of all you should create `Client` instance using `NewClient()` function:
|
|
||||||
|
|
||||||
```go
|
|
||||||
root := "https://webdav.mydomain.me"
|
|
||||||
user := "user"
|
|
||||||
password := "password"
|
|
||||||
|
|
||||||
c := gowebdav.NewClient(root, user, password)
|
|
||||||
```
|
|
||||||
|
|
||||||
After you can use this `Client` to perform actions, described below.
|
|
||||||
|
|
||||||
**NOTICE:** we will not check errors in examples, to focus you on the `gowebdav` library's code, but you should do it in your code!
|
|
||||||
|
|
||||||
### Create path on a WebDAV server
|
|
||||||
```go
|
|
||||||
err := c.Mkdir("folder", 0644)
|
|
||||||
```
|
|
||||||
In case you want to create several folders you can use `c.MkdirAll()`:
|
|
||||||
```go
|
|
||||||
err := c.MkdirAll("folder/subfolder/subfolder2", 0644)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get files list
|
|
||||||
```go
|
|
||||||
files, _ := c.ReadDir("folder/subfolder")
|
|
||||||
for _, file := range files {
|
|
||||||
//notice that [file] has os.FileInfo type
|
|
||||||
fmt.Println(file.Name())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Download file to byte array
|
|
||||||
```go
|
|
||||||
webdavFilePath := "folder/subfolder/file.txt"
|
|
||||||
localFilePath := "/tmp/webdav/file.txt"
|
|
||||||
|
|
||||||
bytes, _ := c.Read(webdavFilePath)
|
|
||||||
ioutil.WriteFile(localFilePath, bytes, 0644)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Download file via reader
|
|
||||||
Also you can use `c.ReadStream()` method:
|
|
||||||
```go
|
|
||||||
webdavFilePath := "folder/subfolder/file.txt"
|
|
||||||
localFilePath := "/tmp/webdav/file.txt"
|
|
||||||
|
|
||||||
reader, _ := c.ReadStream(webdavFilePath)
|
|
||||||
|
|
||||||
file, _ := os.Create(localFilePath)
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
io.Copy(file, reader)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Upload file from byte array
|
|
||||||
```go
|
|
||||||
webdavFilePath := "folder/subfolder/file.txt"
|
|
||||||
localFilePath := "/tmp/webdav/file.txt"
|
|
||||||
|
|
||||||
bytes, _ := ioutil.ReadFile(localFilePath)
|
|
||||||
|
|
||||||
c.Write(webdavFilePath, bytes, 0644)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Upload file via writer
|
|
||||||
```go
|
|
||||||
webdavFilePath := "folder/subfolder/file.txt"
|
|
||||||
localFilePath := "/tmp/webdav/file.txt"
|
|
||||||
|
|
||||||
file, _ := os.Open(localFilePath)
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
c.WriteStream(webdavFilePath, file, 0644)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get information about specified file/folder
|
|
||||||
```go
|
|
||||||
webdavFilePath := "folder/subfolder/file.txt"
|
|
||||||
|
|
||||||
info := c.Stat(webdavFilePath)
|
|
||||||
//notice that [info] has os.FileInfo type
|
|
||||||
fmt.Println(info)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Move file to another location
|
|
||||||
```go
|
|
||||||
oldPath := "folder/subfolder/file.txt"
|
|
||||||
newPath := "folder/subfolder/moved.txt"
|
|
||||||
isOverwrite := true
|
|
||||||
|
|
||||||
c.Rename(oldPath, newPath, isOverwrite)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Copy file to another location
|
|
||||||
```go
|
|
||||||
oldPath := "folder/subfolder/file.txt"
|
|
||||||
newPath := "folder/subfolder/file-copy.txt"
|
|
||||||
isOverwrite := true
|
|
||||||
|
|
||||||
c.Copy(oldPath, newPath, isOverwrite)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Delete file
|
|
||||||
```go
|
|
||||||
webdavFilePath := "folder/subfolder/file.txt"
|
|
||||||
|
|
||||||
c.Remove(webdavFilePath)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
More details about WebDAV server you can read from following resources:
|
|
||||||
|
|
||||||
* [RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)](https://tools.ietf.org/html/rfc4918)
|
|
||||||
* [RFC 5689 - Extended MKCOL for Web Distributed Authoring and Versioning (WebDAV)](https://tools.ietf.org/html/rfc5689)
|
|
||||||
* [RFC 2616 - HTTP/1.1 Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html "HTTP/1.1 Status Code Definitions")
|
|
||||||
* [WebDav: Next Generation Collaborative Web Authoring By Lisa Dusseaul](https://books.google.de/books?isbn=0130652083 "WebDav: Next Generation Collaborative Web Authoring By Lisa Dusseault")
|
|
||||||
|
|
||||||
**NOTICE**: RFC 2518 is obsoleted by RFC 4918 in June 2007
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
All contributing are welcome. If you have any suggestions or find some bug - please create an Issue to let us make this project better. We appreciate your help!
|
|
||||||
|
|
||||||
## License
|
|
||||||
This library is distributed under the BSD 3-Clause license found in the [LICENSE](https://github.com/studio-b12/gowebdav/blob/master/LICENSE) file.
|
|
||||||
33
vendor/github.com/studio-b12/gowebdav/basicAuth.go
generated
vendored
33
vendor/github.com/studio-b12/gowebdav/basicAuth.go
generated
vendored
@@ -1,33 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BasicAuth structure holds our credentials
|
|
||||||
type BasicAuth struct {
|
|
||||||
user string
|
|
||||||
pw string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type identifies the BasicAuthenticator
|
|
||||||
func (b *BasicAuth) Type() string {
|
|
||||||
return "BasicAuth"
|
|
||||||
}
|
|
||||||
|
|
||||||
// User holds the BasicAuth username
|
|
||||||
func (b *BasicAuth) User() string {
|
|
||||||
return b.user
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass holds the BasicAuth password
|
|
||||||
func (b *BasicAuth) Pass() string {
|
|
||||||
return b.pw
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorize the current request
|
|
||||||
func (b *BasicAuth) Authorize(c *Client, method string, path string) {
|
|
||||||
a := b.user + ":" + b.pw
|
|
||||||
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a))
|
|
||||||
c.headers.Set("Authorization", auth)
|
|
||||||
}
|
|
||||||
380
vendor/github.com/studio-b12/gowebdav/client.go
generated
vendored
380
vendor/github.com/studio-b12/gowebdav/client.go
generated
vendored
@@ -1,380 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
pathpkg "path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client defines our structure
|
|
||||||
type Client struct {
|
|
||||||
root string
|
|
||||||
headers http.Header
|
|
||||||
c *http.Client
|
|
||||||
auth Authenticator
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticator stub
|
|
||||||
type Authenticator interface {
|
|
||||||
Type() string
|
|
||||||
User() string
|
|
||||||
Pass() string
|
|
||||||
Authorize(*Client, string, string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoAuth structure holds our credentials
|
|
||||||
type NoAuth struct {
|
|
||||||
user string
|
|
||||||
pw string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type identifies the authenticator
|
|
||||||
func (n *NoAuth) Type() string {
|
|
||||||
return "NoAuth"
|
|
||||||
}
|
|
||||||
|
|
||||||
// User returns the current user
|
|
||||||
func (n *NoAuth) User() string {
|
|
||||||
return n.user
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass returns the current password
|
|
||||||
func (n *NoAuth) Pass() string {
|
|
||||||
return n.pw
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorize the current request
|
|
||||||
func (n *NoAuth) Authorize(c *Client, method string, path string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient creates a new instance of client
|
|
||||||
func NewClient(uri, user, pw string) *Client {
|
|
||||||
return &Client{FixSlash(uri), make(http.Header), &http.Client{}, &NoAuth{user, pw}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeader lets us set arbitrary headers for a given client
|
|
||||||
func (c *Client) SetHeader(key, value string) {
|
|
||||||
c.headers.Add(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTimeout exposes the ability to set a time limit for requests
|
|
||||||
func (c *Client) SetTimeout(timeout time.Duration) {
|
|
||||||
c.c.Timeout = timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTransport exposes the ability to define custom transports
|
|
||||||
func (c *Client) SetTransport(transport http.RoundTripper) {
|
|
||||||
c.c.Transport = transport
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect connects to our dav server
|
|
||||||
func (c *Client) Connect() error {
|
|
||||||
rs, err := c.options("/")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rs.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.StatusCode != 200 {
|
|
||||||
return newPathError("Connect", c.root, rs.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type props struct {
|
|
||||||
Status string `xml:"DAV: status"`
|
|
||||||
Name string `xml:"DAV: prop>displayname,omitempty"`
|
|
||||||
Type xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"`
|
|
||||||
Size string `xml:"DAV: prop>getcontentlength,omitempty"`
|
|
||||||
ContentType string `xml:"DAV: prop>getcontenttype,omitempty"`
|
|
||||||
ETag string `xml:"DAV: prop>getetag,omitempty"`
|
|
||||||
Modified string `xml:"DAV: prop>getlastmodified,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type response struct {
|
|
||||||
Href string `xml:"DAV: href"`
|
|
||||||
Props []props `xml:"DAV: propstat"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProps(r *response, status string) *props {
|
|
||||||
for _, prop := range r.Props {
|
|
||||||
if strings.Contains(prop.Status, status) {
|
|
||||||
return &prop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadDir reads the contents of a remote directory
|
|
||||||
func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
|
|
||||||
path = FixSlashes(path)
|
|
||||||
files := make([]os.FileInfo, 0)
|
|
||||||
skipSelf := true
|
|
||||||
parse := func(resp interface{}) error {
|
|
||||||
r := resp.(*response)
|
|
||||||
|
|
||||||
if skipSelf {
|
|
||||||
skipSelf = false
|
|
||||||
if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" {
|
|
||||||
r.Props = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return newPathError("ReadDir", path, 405)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p := getProps(r, "200"); p != nil {
|
|
||||||
f := new(File)
|
|
||||||
if ps, err := url.QueryUnescape(r.Href); err == nil {
|
|
||||||
f.name = pathpkg.Base(ps)
|
|
||||||
} else {
|
|
||||||
f.name = p.Name
|
|
||||||
}
|
|
||||||
f.path = path + f.name
|
|
||||||
f.modified = parseModified(&p.Modified)
|
|
||||||
f.etag = p.ETag
|
|
||||||
f.contentType = p.ContentType
|
|
||||||
|
|
||||||
if p.Type.Local == "collection" {
|
|
||||||
f.path += "/"
|
|
||||||
f.size = 0
|
|
||||||
f.isdir = true
|
|
||||||
} else {
|
|
||||||
f.size = parseInt64(&p.Size)
|
|
||||||
f.isdir = false
|
|
||||||
}
|
|
||||||
|
|
||||||
files = append(files, *f)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Props = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := c.propfind(path, false,
|
|
||||||
`<d:propfind xmlns:d='DAV:'>
|
|
||||||
<d:prop>
|
|
||||||
<d:displayname/>
|
|
||||||
<d:resourcetype/>
|
|
||||||
<d:getcontentlength/>
|
|
||||||
<d:getcontenttype/>
|
|
||||||
<d:getetag/>
|
|
||||||
<d:getlastmodified/>
|
|
||||||
</d:prop>
|
|
||||||
</d:propfind>`,
|
|
||||||
&response{},
|
|
||||||
parse)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(*os.PathError); !ok {
|
|
||||||
err = newPathErrorErr("ReadDir", path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat returns the file stats for a specified path
|
|
||||||
func (c *Client) Stat(path string) (os.FileInfo, error) {
|
|
||||||
var f *File
|
|
||||||
parse := func(resp interface{}) error {
|
|
||||||
r := resp.(*response)
|
|
||||||
if p := getProps(r, "200"); p != nil && f == nil {
|
|
||||||
f = new(File)
|
|
||||||
f.name = p.Name
|
|
||||||
f.path = path
|
|
||||||
f.etag = p.ETag
|
|
||||||
f.contentType = p.ContentType
|
|
||||||
|
|
||||||
if p.Type.Local == "collection" {
|
|
||||||
if !strings.HasSuffix(f.path, "/") {
|
|
||||||
f.path += "/"
|
|
||||||
}
|
|
||||||
f.size = 0
|
|
||||||
f.modified = time.Unix(0, 0)
|
|
||||||
f.isdir = true
|
|
||||||
} else {
|
|
||||||
f.size = parseInt64(&p.Size)
|
|
||||||
f.modified = parseModified(&p.Modified)
|
|
||||||
f.isdir = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Props = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := c.propfind(path, true,
|
|
||||||
`<d:propfind xmlns:d='DAV:'>
|
|
||||||
<d:prop>
|
|
||||||
<d:displayname/>
|
|
||||||
<d:resourcetype/>
|
|
||||||
<d:getcontentlength/>
|
|
||||||
<d:getcontenttype/>
|
|
||||||
<d:getetag/>
|
|
||||||
<d:getlastmodified/>
|
|
||||||
</d:prop>
|
|
||||||
</d:propfind>`,
|
|
||||||
&response{},
|
|
||||||
parse)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(*os.PathError); !ok {
|
|
||||||
err = newPathErrorErr("ReadDir", path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes a remote file
|
|
||||||
func (c *Client) Remove(path string) error {
|
|
||||||
return c.RemoveAll(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveAll removes remote files
|
|
||||||
func (c *Client) RemoveAll(path string) error {
|
|
||||||
rs, err := c.req("DELETE", path, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return newPathError("Remove", path, 400)
|
|
||||||
}
|
|
||||||
err = rs.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.StatusCode == 200 || rs.StatusCode == 204 || rs.StatusCode == 404 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPathError("Remove", path, rs.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mkdir makes a directory
|
|
||||||
func (c *Client) Mkdir(path string, _ os.FileMode) error {
|
|
||||||
path = FixSlashes(path)
|
|
||||||
status := c.mkcol(path)
|
|
||||||
if status == 201 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPathError("Mkdir", path, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MkdirAll like mkdir -p, but for webdav
|
|
||||||
func (c *Client) MkdirAll(path string, _ os.FileMode) error {
|
|
||||||
path = FixSlashes(path)
|
|
||||||
status := c.mkcol(path)
|
|
||||||
if status == 201 {
|
|
||||||
return nil
|
|
||||||
} else if status == 409 {
|
|
||||||
paths := strings.Split(path, "/")
|
|
||||||
sub := "/"
|
|
||||||
for _, e := range paths {
|
|
||||||
if e == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sub += e + "/"
|
|
||||||
status = c.mkcol(sub)
|
|
||||||
if status != 201 {
|
|
||||||
return newPathError("MkdirAll", sub, status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPathError("MkdirAll", path, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rename moves a file from A to B
|
|
||||||
func (c *Client) Rename(oldpath, newpath string, overwrite bool) error {
|
|
||||||
return c.copymove("MOVE", oldpath, newpath, overwrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy copies a file from A to B
|
|
||||||
func (c *Client) Copy(oldpath, newpath string, overwrite bool) error {
|
|
||||||
return c.copymove("COPY", oldpath, newpath, overwrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads the contents of a remote file
|
|
||||||
func (c *Client) Read(path string) ([]byte, error) {
|
|
||||||
var stream io.ReadCloser
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if stream, err = c.ReadStream(path); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer stream.Close()
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
_, err = buf.ReadFrom(stream)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadStream reads the stream for a given path
|
|
||||||
func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
|
|
||||||
rs, err := c.req("GET", path, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, newPathErrorErr("ReadStream", path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.StatusCode == 200 {
|
|
||||||
return rs.Body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rs.Body.Close()
|
|
||||||
return nil, newPathError("ReadStream", path, rs.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes data to a given path
|
|
||||||
func (c *Client) Write(path string, data []byte, _ os.FileMode) error {
|
|
||||||
s := c.put(path, bytes.NewReader(data))
|
|
||||||
switch s {
|
|
||||||
|
|
||||||
case 200, 201, 204:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case 409:
|
|
||||||
err := c.createParentCollection(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s = c.put(path, bytes.NewReader(data))
|
|
||||||
if s == 200 || s == 201 || s == 204 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPathError("Write", path, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteStream writes a stream
|
|
||||||
func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) error {
|
|
||||||
|
|
||||||
err := c.createParentCollection(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := c.put(path, stream)
|
|
||||||
|
|
||||||
switch s {
|
|
||||||
case 200, 201, 204:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return newPathError("WriteStream", path, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
146
vendor/github.com/studio-b12/gowebdav/digestAuth.go
generated
vendored
146
vendor/github.com/studio-b12/gowebdav/digestAuth.go
generated
vendored
@@ -1,146 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DigestAuth structure holds our credentials
|
|
||||||
type DigestAuth struct {
|
|
||||||
user string
|
|
||||||
pw string
|
|
||||||
digestParts map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type identifies the DigestAuthenticator
|
|
||||||
func (d *DigestAuth) Type() string {
|
|
||||||
return "DigestAuth"
|
|
||||||
}
|
|
||||||
|
|
||||||
// User holds the DigestAuth username
|
|
||||||
func (d *DigestAuth) User() string {
|
|
||||||
return d.user
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass holds the DigestAuth password
|
|
||||||
func (d *DigestAuth) Pass() string {
|
|
||||||
return d.pw
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorize the current request
|
|
||||||
func (d *DigestAuth) Authorize(c *Client, method string, path string) {
|
|
||||||
d.digestParts["uri"] = path
|
|
||||||
d.digestParts["method"] = method
|
|
||||||
d.digestParts["username"] = d.user
|
|
||||||
d.digestParts["password"] = d.pw
|
|
||||||
c.headers.Set("Authorization", getDigestAuthorization(d.digestParts))
|
|
||||||
}
|
|
||||||
|
|
||||||
func digestParts(resp *http.Response) map[string]string {
|
|
||||||
result := map[string]string{}
|
|
||||||
if len(resp.Header["Www-Authenticate"]) > 0 {
|
|
||||||
wantedHeaders := []string{"nonce", "realm", "qop", "opaque", "algorithm", "entityBody"}
|
|
||||||
responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",")
|
|
||||||
for _, r := range responseHeaders {
|
|
||||||
for _, w := range wantedHeaders {
|
|
||||||
if strings.Contains(r, w) {
|
|
||||||
result[w] = strings.Trim(
|
|
||||||
strings.SplitN(r, `=`, 2)[1],
|
|
||||||
`"`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMD5(text string) string {
|
|
||||||
hasher := md5.New()
|
|
||||||
hasher.Write([]byte(text))
|
|
||||||
return hex.EncodeToString(hasher.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCnonce() string {
|
|
||||||
b := make([]byte, 8)
|
|
||||||
io.ReadFull(rand.Reader, b)
|
|
||||||
return fmt.Sprintf("%x", b)[:16]
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDigestAuthorization(digestParts map[string]string) string {
|
|
||||||
d := digestParts
|
|
||||||
// These are the correct ha1 and ha2 for qop=auth. We should probably check for other types of qop.
|
|
||||||
|
|
||||||
var (
|
|
||||||
ha1 string
|
|
||||||
ha2 string
|
|
||||||
nonceCount = 00000001
|
|
||||||
cnonce = getCnonce()
|
|
||||||
response string
|
|
||||||
)
|
|
||||||
|
|
||||||
// 'ha1' value depends on value of "algorithm" field
|
|
||||||
switch d["algorithm"] {
|
|
||||||
case "MD5", "":
|
|
||||||
ha1 = getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
|
|
||||||
case "MD5-sess":
|
|
||||||
ha1 = getMD5(
|
|
||||||
fmt.Sprintf("%s:%v:%s",
|
|
||||||
getMD5(d["username"]+":"+d["realm"]+":"+d["password"]),
|
|
||||||
nonceCount,
|
|
||||||
cnonce,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'ha2' value depends on value of "qop" field
|
|
||||||
switch d["qop"] {
|
|
||||||
case "auth", "":
|
|
||||||
ha2 = getMD5(d["method"] + ":" + d["uri"])
|
|
||||||
case "auth-int":
|
|
||||||
if d["entityBody"] != "" {
|
|
||||||
ha2 = getMD5(d["method"] + ":" + d["uri"] + ":" + getMD5(d["entityBody"]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'response' value depends on value of "qop" field
|
|
||||||
switch d["qop"] {
|
|
||||||
case "":
|
|
||||||
response = getMD5(
|
|
||||||
fmt.Sprintf("%s:%s:%s",
|
|
||||||
ha1,
|
|
||||||
d["nonce"],
|
|
||||||
ha2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
case "auth", "auth-int":
|
|
||||||
response = getMD5(
|
|
||||||
fmt.Sprintf("%s:%s:%v:%s:%s:%s",
|
|
||||||
ha1,
|
|
||||||
d["nonce"],
|
|
||||||
nonceCount,
|
|
||||||
cnonce,
|
|
||||||
d["qop"],
|
|
||||||
ha2,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", nc=%v, cnonce="%s", response="%s"`,
|
|
||||||
d["username"], d["realm"], d["nonce"], d["uri"], nonceCount, cnonce, response)
|
|
||||||
|
|
||||||
if d["qop"] != "" {
|
|
||||||
authorization += fmt.Sprintf(`, qop=%s`, d["qop"])
|
|
||||||
}
|
|
||||||
|
|
||||||
if d["opaque"] != "" {
|
|
||||||
authorization += fmt.Sprintf(`, opaque="%s"`, d["opaque"])
|
|
||||||
}
|
|
||||||
|
|
||||||
return authorization
|
|
||||||
}
|
|
||||||
3
vendor/github.com/studio-b12/gowebdav/doc.go
generated
vendored
3
vendor/github.com/studio-b12/gowebdav/doc.go
generated
vendored
@@ -1,3 +0,0 @@
|
|||||||
// Package gowebdav is a WebDAV client library with a command line tool
|
|
||||||
// included.
|
|
||||||
package gowebdav
|
|
||||||
72
vendor/github.com/studio-b12/gowebdav/file.go
generated
vendored
72
vendor/github.com/studio-b12/gowebdav/file.go
generated
vendored
@@ -1,72 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// File is our structure for a given file
|
|
||||||
type File struct {
|
|
||||||
path string
|
|
||||||
name string
|
|
||||||
contentType string
|
|
||||||
size int64
|
|
||||||
modified time.Time
|
|
||||||
etag string
|
|
||||||
isdir bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of a file
|
|
||||||
func (f File) Name() string {
|
|
||||||
return f.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContentType returns the content type of a file
|
|
||||||
func (f File) ContentType() string {
|
|
||||||
return f.contentType
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the size of a file
|
|
||||||
func (f File) Size() int64 {
|
|
||||||
return f.size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mode will return the mode of a given file
|
|
||||||
func (f File) Mode() os.FileMode {
|
|
||||||
// TODO check webdav perms
|
|
||||||
if f.isdir {
|
|
||||||
return 0775 | os.ModeDir
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0664
|
|
||||||
}
|
|
||||||
|
|
||||||
// ModTime returns the modified time of a file
|
|
||||||
func (f File) ModTime() time.Time {
|
|
||||||
return f.modified
|
|
||||||
}
|
|
||||||
|
|
||||||
// ETag returns the ETag of a file
|
|
||||||
func (f File) ETag() string {
|
|
||||||
return f.etag
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDir let us see if a given file is a directory or not
|
|
||||||
func (f File) IsDir() bool {
|
|
||||||
return f.isdir
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sys ????
|
|
||||||
func (f File) Sys() interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String lets us see file information
|
|
||||||
func (f File) String() string {
|
|
||||||
if f.isdir {
|
|
||||||
return fmt.Sprintf("Dir : '%s' - '%s'", f.path, f.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("File: '%s' SIZE: %d MODIFIED: %s ETAG: %s CTYPE: %s", f.path, f.size, f.modified.String(), f.etag, f.contentType)
|
|
||||||
}
|
|
||||||
54
vendor/github.com/studio-b12/gowebdav/netrc.go
generated
vendored
54
vendor/github.com/studio-b12/gowebdav/netrc.go
generated
vendored
@@ -1,54 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseLine(s string) (login, pass string) {
|
|
||||||
fields := strings.Fields(s)
|
|
||||||
for i, f := range fields {
|
|
||||||
if f == "login" {
|
|
||||||
login = fields[i+1]
|
|
||||||
}
|
|
||||||
if f == "password" {
|
|
||||||
pass = fields[i+1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return login, pass
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadConfig reads login and password configuration from ~/.netrc
|
|
||||||
// machine foo.com login username password 123456
|
|
||||||
func ReadConfig(uri, netrc string) (string, string) {
|
|
||||||
u, err := url.Parse(uri)
|
|
||||||
if err != nil {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.Open(netrc)
|
|
||||||
if err != nil {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
re := fmt.Sprintf(`^.*machine %s.*$`, u.Host)
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
s := scanner.Text()
|
|
||||||
|
|
||||||
matched, err := regexp.MatchString(re, s)
|
|
||||||
if err != nil {
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
if matched {
|
|
||||||
return parseLine(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", ""
|
|
||||||
}
|
|
||||||
164
vendor/github.com/studio-b12/gowebdav/requests.go
generated
vendored
164
vendor/github.com/studio-b12/gowebdav/requests.go
generated
vendored
@@ -1,164 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) {
|
|
||||||
// Tee the body, because if authorization fails we will need to read from it again.
|
|
||||||
var r *http.Request
|
|
||||||
var ba bytes.Buffer
|
|
||||||
bb := io.TeeReader(body, &ba)
|
|
||||||
|
|
||||||
if body == nil {
|
|
||||||
r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), nil)
|
|
||||||
} else {
|
|
||||||
r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), bb)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.auth.Authorize(c, method, path)
|
|
||||||
|
|
||||||
for k, vals := range c.headers {
|
|
||||||
for _, v := range vals {
|
|
||||||
r.Header.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if intercept != nil {
|
|
||||||
intercept(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
rs, err := c.c.Do(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.StatusCode == 401 && c.auth.Type() == "NoAuth" {
|
|
||||||
if strings.Index(rs.Header.Get("Www-Authenticate"), "Digest") > -1 {
|
|
||||||
c.auth = &DigestAuth{c.auth.User(), c.auth.Pass(), digestParts(rs)}
|
|
||||||
} else if strings.Index(rs.Header.Get("Www-Authenticate"), "Basic") > -1 {
|
|
||||||
c.auth = &BasicAuth{c.auth.User(), c.auth.Pass()}
|
|
||||||
} else {
|
|
||||||
return rs, newPathError("Authorize", c.root, rs.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if body == nil {
|
|
||||||
return c.req(method, path, nil, intercept)
|
|
||||||
} else {
|
|
||||||
return c.req(method, path, &ba, intercept)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if rs.StatusCode == 401 {
|
|
||||||
return rs, newPathError("Authorize", c.root, rs.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) mkcol(path string) int {
|
|
||||||
rs, err := c.req("MKCOL", path, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return 400
|
|
||||||
}
|
|
||||||
defer rs.Body.Close()
|
|
||||||
|
|
||||||
if rs.StatusCode == 201 || rs.StatusCode == 405 {
|
|
||||||
return 201
|
|
||||||
}
|
|
||||||
|
|
||||||
return rs.StatusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) options(path string) (*http.Response, error) {
|
|
||||||
return c.req("OPTIONS", path, nil, func(rq *http.Request) {
|
|
||||||
rq.Header.Add("Depth", "0")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error {
|
|
||||||
rs, err := c.req("PROPFIND", path, strings.NewReader(body), func(rq *http.Request) {
|
|
||||||
if self {
|
|
||||||
rq.Header.Add("Depth", "0")
|
|
||||||
} else {
|
|
||||||
rq.Header.Add("Depth", "1")
|
|
||||||
}
|
|
||||||
rq.Header.Add("Content-Type", "application/xml;charset=UTF-8")
|
|
||||||
rq.Header.Add("Accept", "application/xml,text/xml")
|
|
||||||
rq.Header.Add("Accept-Charset", "utf-8")
|
|
||||||
// TODO add support for 'gzip,deflate;q=0.8,q=0.7'
|
|
||||||
rq.Header.Add("Accept-Encoding", "")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rs.Body.Close()
|
|
||||||
|
|
||||||
if rs.StatusCode != 207 {
|
|
||||||
return fmt.Errorf("%s - %s %s", rs.Status, "PROPFIND", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseXML(rs.Body, resp, parse)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) doCopyMove(method string, oldpath string, newpath string, overwrite bool) (int, io.ReadCloser) {
|
|
||||||
rs, err := c.req(method, oldpath, nil, func(rq *http.Request) {
|
|
||||||
rq.Header.Add("Destination", Join(c.root, newpath))
|
|
||||||
if overwrite {
|
|
||||||
rq.Header.Add("Overwrite", "T")
|
|
||||||
} else {
|
|
||||||
rq.Header.Add("Overwrite", "F")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return 400, nil
|
|
||||||
}
|
|
||||||
return rs.StatusCode, rs.Body
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) copymove(method string, oldpath string, newpath string, overwrite bool) error {
|
|
||||||
s, data := c.doCopyMove(method, oldpath, newpath, overwrite)
|
|
||||||
defer data.Close()
|
|
||||||
|
|
||||||
switch s {
|
|
||||||
case 201, 204:
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case 207:
|
|
||||||
// TODO handle multistat errors, worst case ...
|
|
||||||
log(fmt.Sprintf(" TODO handle %s - %s multistatus result %s", method, oldpath, String(data)))
|
|
||||||
|
|
||||||
case 409:
|
|
||||||
err := c.createParentCollection(newpath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.copymove(method, oldpath, newpath, overwrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPathError(method, oldpath, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) put(path string, stream io.Reader) int {
|
|
||||||
rs, err := c.req("PUT", path, stream, nil)
|
|
||||||
if err != nil {
|
|
||||||
return 400
|
|
||||||
}
|
|
||||||
defer rs.Body.Close()
|
|
||||||
|
|
||||||
return rs.StatusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) createParentCollection(itemPath string) (err error) {
|
|
||||||
parentPath := path.Dir(itemPath)
|
|
||||||
return c.MkdirAll(parentPath, 0755)
|
|
||||||
}
|
|
||||||
109
vendor/github.com/studio-b12/gowebdav/utils.go
generated
vendored
109
vendor/github.com/studio-b12/gowebdav/utils.go
generated
vendored
@@ -1,109 +0,0 @@
|
|||||||
package gowebdav
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func log(msg interface{}) {
|
|
||||||
fmt.Println(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPathError(op string, path string, statusCode int) error {
|
|
||||||
return &os.PathError{
|
|
||||||
Op: op,
|
|
||||||
Path: path,
|
|
||||||
Err: fmt.Errorf("%d", statusCode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPathErrorErr(op string, path string, err error) error {
|
|
||||||
return &os.PathError{
|
|
||||||
Op: op,
|
|
||||||
Path: path,
|
|
||||||
Err: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathEscape escapes all segemnts of a given path
|
|
||||||
func PathEscape(path string) string {
|
|
||||||
s := strings.Split(path, "/")
|
|
||||||
for i, e := range s {
|
|
||||||
s[i] = url.PathEscape(e)
|
|
||||||
}
|
|
||||||
return strings.Join(s, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixSlash appends a trailing / to our string
|
|
||||||
func FixSlash(s string) string {
|
|
||||||
if !strings.HasSuffix(s, "/") {
|
|
||||||
s += "/"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// FixSlashes appends and prepends a / if they are missing
|
|
||||||
func FixSlashes(s string) string {
|
|
||||||
if s[0] != '/' {
|
|
||||||
s = "/" + s
|
|
||||||
}
|
|
||||||
return FixSlash(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join joins two paths
|
|
||||||
func Join(path0 string, path1 string) string {
|
|
||||||
return strings.TrimSuffix(path0, "/") + "/" + strings.TrimPrefix(path1, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// String pulls a string out of our io.Reader
|
|
||||||
func String(r io.Reader) string {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
// TODO - make String return an error as well
|
|
||||||
_, _ = buf.ReadFrom(r)
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseUint(s *string) uint {
|
|
||||||
if n, e := strconv.ParseUint(*s, 10, 32); e == nil {
|
|
||||||
return uint(n)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInt64(s *string) int64 {
|
|
||||||
if n, e := strconv.ParseInt(*s, 10, 64); e == nil {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseModified(s *string) time.Time {
|
|
||||||
if t, e := time.Parse(time.RFC1123, *s); e == nil {
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
return time.Unix(0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error {
|
|
||||||
decoder := xml.NewDecoder(data)
|
|
||||||
for t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() {
|
|
||||||
switch se := t.(type) {
|
|
||||||
case xml.StartElement:
|
|
||||||
if se.Name.Local == "response" {
|
|
||||||
if e := decoder.DecodeElement(resp, &se); e == nil {
|
|
||||||
if err := parse(resp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -19,8 +19,6 @@ github.com/stretchr/objx
|
|||||||
# github.com/stretchr/testify v1.2.2
|
# github.com/stretchr/testify v1.2.2
|
||||||
github.com/stretchr/testify/mock
|
github.com/stretchr/testify/mock
|
||||||
github.com/stretchr/testify/assert
|
github.com/stretchr/testify/assert
|
||||||
# github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac
|
|
||||||
github.com/studio-b12/gowebdav
|
|
||||||
# golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76
|
# golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76
|
||||||
golang.org/x/net/publicsuffix
|
golang.org/x/net/publicsuffix
|
||||||
# golang.org/x/sys v0.0.0-20190422165155-953cdadca894
|
# golang.org/x/sys v0.0.0-20190422165155-953cdadca894
|
||||||
|
|||||||
93
webdav.go
93
webdav.go
@@ -1,93 +0,0 @@
|
|||||||
package gonextcloud
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/studio-b12/gowebdav"
|
|
||||||
)
|
|
||||||
|
|
||||||
type webDav struct {
|
|
||||||
*gowebdav.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWebDav(url string, user string, password string) *webDav {
|
|
||||||
wb := gowebdav.NewClient(url, user, password)
|
|
||||||
return &webDav{Client: wb}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementation adapted from filepath.Walk
|
|
||||||
|
|
||||||
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
|
||||||
// directory in the tree, including root. All errors that arise visiting files
|
|
||||||
// and directories are filtered by walkFn. The files are walked in lexical
|
|
||||||
// order, which makes the output deterministic but means that for very
|
|
||||||
// large directories Walk can be inefficient.
|
|
||||||
// Walk does not follow symbolic links.
|
|
||||||
func (wd *webDav) Walk(root string, walkFn filepath.WalkFunc) error {
|
|
||||||
info, err := wd.Stat(root)
|
|
||||||
if err != nil {
|
|
||||||
err = walkFn(root, nil, err)
|
|
||||||
} else {
|
|
||||||
err = wd.walk(root, info, walkFn)
|
|
||||||
}
|
|
||||||
if err == filepath.SkipDir {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk recursively descends path, calling walkFn.
|
|
||||||
func (wd *webDav) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
|
||||||
if !info.IsDir() {
|
|
||||||
return walkFn(path, info, nil)
|
|
||||||
}
|
|
||||||
names, err := wd.readDirNames(path)
|
|
||||||
err1 := walkFn(path, info, err)
|
|
||||||
// If err != nil, walk can't walk into this directory.
|
|
||||||
// err1 != nil means walkFn want walk to skip this directory or stop walking.
|
|
||||||
// Therefore, if one of err and err1 isn't nil, walk will return.
|
|
||||||
if err != nil || err1 != nil {
|
|
||||||
// The caller's behavior is controlled by the return value, which is decided
|
|
||||||
// by walkFn. walkFn may ignore err and return nil.
|
|
||||||
// If walkFn returns SkipDir, it will be handled by the caller.
|
|
||||||
// So walk should return whatever walkFn returns.
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name := range names {
|
|
||||||
filename := filepath.Join(path, name)
|
|
||||||
fileInfo, err := wd.Stat(filename)
|
|
||||||
if err != nil {
|
|
||||||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = wd.walk(filename, fileInfo, walkFn)
|
|
||||||
if err != nil {
|
|
||||||
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readDirNames reads the directory named by dirname and returns
|
|
||||||
// a sorted list of directory entries.
|
|
||||||
func (wd *webDav) readDirNames(dirname string) ([]string, error) {
|
|
||||||
fs, err := wd.ReadDir(dirname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var names []string
|
|
||||||
for _, i := range fs {
|
|
||||||
if i.IsDir() {
|
|
||||||
names = append(names, i.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names, nil
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user