mirror of
				https://gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud
				synced 2025-11-04 05:11:46 +00:00 
			
		
		
		
	Merge branch '6-add-webdav-api-client' into 'master'
fix #6: added gowebdav and webdav interface (TODO: webdav tests) Closes #6 See merge request partitio/Nextcloud-Partitio/gonextcloud!2
This commit is contained in:
		
							
								
								
									
										5
									
								
								auth.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								auth.go
									
									
									
									
									
								
							@@ -2,7 +2,10 @@ package gonextcloud
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	req "github.com/levigross/grequests"
 | 
			
		||||
	"github.com/studio-b12/gowebdav"
 | 
			
		||||
 | 
			
		||||
	"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -33,6 +36,8 @@ func (c *Client) Login(username string, password string) error {
 | 
			
		||||
		e := types.APIError{Message: "authentication failed"}
 | 
			
		||||
		return &e
 | 
			
		||||
	}
 | 
			
		||||
	// Create webdav client
 | 
			
		||||
	c.webdav = gowebdav.NewClient(c.baseURL.String()+"/remote.php/webdav", c.username, c.password)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								client.go
									
									
									
									
									
								
							@@ -1,9 +1,12 @@
 | 
			
		||||
package gonextcloud
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	req "github.com/levigross/grequests"
 | 
			
		||||
	"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	req "github.com/levigross/grequests"
 | 
			
		||||
	"github.com/studio-b12/gowebdav"
 | 
			
		||||
 | 
			
		||||
	"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Client is the API client that performs all operations against a Nextcloud server.
 | 
			
		||||
@@ -23,6 +26,7 @@ type Client struct {
 | 
			
		||||
	shares        *Shares
 | 
			
		||||
	users         *Users
 | 
			
		||||
	groups        *Groups
 | 
			
		||||
	webdav        *gowebdav.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClient create a new Client from the Nextcloud Instance URL
 | 
			
		||||
@@ -42,6 +46,7 @@ func NewClient(hostname string) (*Client, error) {
 | 
			
		||||
			"Accept":         "application/json",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.apps = &Apps{c}
 | 
			
		||||
	c.appsConfig = &AppsConfig{c}
 | 
			
		||||
	c.groupFolders = &GroupFolders{c}
 | 
			
		||||
@@ -49,6 +54,9 @@ func NewClient(hostname string) (*Client, error) {
 | 
			
		||||
	c.shares = &Shares{c}
 | 
			
		||||
	c.users = &Users{c}
 | 
			
		||||
	c.groups = &Groups{c}
 | 
			
		||||
	// Create empty webdav client
 | 
			
		||||
	// It will be replaced after login
 | 
			
		||||
	c.webdav = &gowebdav.Client{}
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -86,3 +94,8 @@ func (c *Client) Users() types.Users {
 | 
			
		||||
func (c *Client) Groups() types.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,6 +9,7 @@ require (
 | 
			
		||||
	github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0
 | 
			
		||||
	github.com/sirupsen/logrus v1.4.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
 | 
			
		||||
	gopkg.in/yaml.v2 v2.2.1
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							@@ -18,6 +18,8 @@ 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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 | 
			
		||||
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/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ type Client interface {
 | 
			
		||||
	Shares() Shares
 | 
			
		||||
	Users() Users
 | 
			
		||||
	Groups() Groups
 | 
			
		||||
	WebDav() WebDav
 | 
			
		||||
	Login(username string, password string) error
 | 
			
		||||
	Logout() error
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								types/webdav.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								types/webdav.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
package types
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								vendor/github.com/studio-b12/gowebdav/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/studio-b12/gowebdav/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
# 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
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/studio-b12/gowebdav/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
language: go
 | 
			
		||||
 | 
			
		||||
go:
 | 
			
		||||
  - "1.x"
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
  - go get ./...
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - go test -v --short ./...
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/github.com/studio-b12/gowebdav/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/studio-b12/gowebdav/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/studio-b12/gowebdav/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								vendor/github.com/studio-b12/gowebdav/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
# 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
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/studio-b12/gowebdav/basicAuth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								vendor/github.com/studio-b12/gowebdav/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,380 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								vendor/github.com/studio-b12/gowebdav/digestAuth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/studio-b12/gowebdav/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
// 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
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/studio-b12/gowebdav/file.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/studio-b12/gowebdav/netrc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								vendor/github.com/studio-b12/gowebdav/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,164 @@
 | 
			
		||||
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
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/github.com/studio-b12/gowebdav/utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
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,6 +19,8 @@ github.com/stretchr/objx
 | 
			
		||||
# github.com/stretchr/testify v1.2.2
 | 
			
		||||
github.com/stretchr/testify/mock
 | 
			
		||||
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/publicsuffix
 | 
			
		||||
# golang.org/x/sys v0.0.0-20190422165155-953cdadca894
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user