add initial source
This commit is contained in:
		
							parent
							
								
									a224ac9c70
								
							
						
					
					
						commit
						95f1f68142
					
				
							
								
								
									
										104
									
								
								client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | package gowebdav | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/base64" | ||||||
|  | 	"encoding/xml" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Client struct { | ||||||
|  | 	root    string | ||||||
|  | 	headers http.Header | ||||||
|  | 	c       *http.Client | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewClient(uri string, user string, pw string) *Client { | ||||||
|  | 	c := &Client{uri, make(http.Header), &http.Client{}} | ||||||
|  | 
 | ||||||
|  | 	if len(user) > 0 && len(pw) > 0 { | ||||||
|  | 		a := user + ":" + pw | ||||||
|  | 		auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a)) | ||||||
|  | 		c.headers.Add("Authorization", auth) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !strings.HasSuffix(c.root, "/") { | ||||||
|  | 		c.root += "/" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return c | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Client) Connect() error { | ||||||
|  | 	if rs, err := c.Options("/"); err == nil { | ||||||
|  | 		defer rs.Body.Close() | ||||||
|  | 
 | ||||||
|  | 		if rs.StatusCode != 200 || (rs.Header.Get("Dav") == "" && rs.Header.Get("DAV") == "") { | ||||||
|  | 			return errors.New(fmt.Sprintf("Bad Request: %d - %s", rs.StatusCode, c.root)) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// TODO check PROPFIND if path is collection | ||||||
|  | 
 | ||||||
|  | 		return nil | ||||||
|  | 	} else { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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"` | ||||||
|  | 	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.Index(prop.Status, status) != -1 { | ||||||
|  | 			return &prop | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Client) List(path string) (*[]*File, error) { | ||||||
|  | 	files := make([]*File, 0) | ||||||
|  | 	parse := func(resp interface{}) { | ||||||
|  | 		r := resp.(*response) | ||||||
|  | 		if p := getProps(r, "200"); p != nil { | ||||||
|  | 			var f File | ||||||
|  | 			if p.Type.Local == "collection" { | ||||||
|  | 				f = directory{p.Name} | ||||||
|  | 			} else { | ||||||
|  | 				f = file{p.Name, parseUint(&p.Size), parseModified(&p.Modified)} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			files = append(files, &f) | ||||||
|  | 			r.Props = nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := c.Propfind(path, false, | ||||||
|  | 		`<d:propfind xmlns:d='DAV:'> | ||||||
|  | 			<d:prop> | ||||||
|  | 				<d:displayname/> | ||||||
|  | 				<d:resourcetype/> | ||||||
|  | 				<d:getcontentlength/> | ||||||
|  | 				<d:getlastmodified/> | ||||||
|  | 			</d:prop> | ||||||
|  | 		</d:propfind>`, | ||||||
|  | 		&response{}, | ||||||
|  | 		parse) | ||||||
|  | 	return &files, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Client) Read(path string) { | ||||||
|  | 	fmt.Println("Read " + path) | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								directory.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								directory.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | package gowebdav | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Directory interface { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type directory struct { | ||||||
|  | 	name string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (d directory) Name() string { | ||||||
|  | 	return d.name | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (_ directory) Size() uint { | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (_ directory) IsDirectory() bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (_ directory) Modified() time.Time { | ||||||
|  | 	return time.Unix(0, 9) | ||||||
|  | } | ||||||
|  | func (d directory) String() string { | ||||||
|  | 	return fmt.Sprintf("DIRECTORY: %s", d.name) | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								file.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | |||||||
|  | package gowebdav | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type File interface { | ||||||
|  | 	Name() string | ||||||
|  | 	Size() uint | ||||||
|  | 	Modified() time.Time | ||||||
|  | 	IsDirectory() bool | ||||||
|  | 	String() string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type file struct { | ||||||
|  | 	name     string | ||||||
|  | 	size     uint | ||||||
|  | 	modified time.Time | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (_ file) IsDirectory() bool { | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f file) Modified() time.Time { | ||||||
|  | 	return f.modified | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f file) Name() string { | ||||||
|  | 	return f.name | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f file) Size() uint { | ||||||
|  | 	return f.size | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (f file) String() string { | ||||||
|  | 	return fmt.Sprintf("FILE: %s SIZE: %d MODIFIED: %s", f.name, f.size, f.modified.String()) | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								main/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								main/client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	d "gowebdav" | ||||||
|  | 	"flag" | ||||||
|  | 	"os" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func Fail(err interface{}) { | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println(err) | ||||||
|  | 	} else { | ||||||
|  | 		fmt.Println("Usage: client FLAGS ARGS") | ||||||
|  | 		fmt.Println("Flags:") | ||||||
|  | 		flag.PrintDefaults() | ||||||
|  | 	} | ||||||
|  | 	os.Exit(-1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  | 	root := flag.String("root", "URL", "WebDAV Endpoint") | ||||||
|  | 	usr := flag.String("user", "", "user") | ||||||
|  | 	pw := flag.String("pw", "", "password") | ||||||
|  | 	m := flag.String("X", "GET", "Method: LIST aka PROPFIND, GET") | ||||||
|  | 	flag.Parse() | ||||||
|  | 
 | ||||||
|  | 	if *root == "URL" { | ||||||
|  | 		Fail(nil) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c := d.NewClient(*root, *usr, *pw) | ||||||
|  | 	if err := c.Connect(); err != nil { | ||||||
|  | 		Fail(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(flag.Args()) > 0 { | ||||||
|  | 		path := flag.Args()[0] | ||||||
|  | 		switch *m { | ||||||
|  | 			case "LIST", "PROPFIND": | ||||||
|  | 				if files, err := c.List(path); err == nil { | ||||||
|  | 					fmt.Println(len(*files)) | ||||||
|  | 					for _, f := range *files { | ||||||
|  | 						fmt.Println(*f) | ||||||
|  | 					} | ||||||
|  | 				} else { | ||||||
|  | 					fmt.Println(err) | ||||||
|  | 				} | ||||||
|  | 			case "GET": c.Read(path) | ||||||
|  | 			default: Fail(nil) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		Fail(nil) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										75
									
								
								requests.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								requests.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | |||||||
|  | package gowebdav | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/xml" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (c *Client) req(method string, path string, body io.Reader) (req *http.Request, err error) { | ||||||
|  | 	req, err = http.NewRequest(method, Join(c.root, path), body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	for k, vals := range c.headers { | ||||||
|  | 		for _, v := range vals { | ||||||
|  | 			req.Header.Add(k, v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return req, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Client) Options(path string) (*http.Response, error) { | ||||||
|  | 	rq, err := c.req("OPTIONS", path, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rq.Header.Add("Depth", "0") | ||||||
|  | 
 | ||||||
|  | 	return c.c.Do(rq) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Client) Propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{})) error { | ||||||
|  | 	rq, err := c.req("PROPFIND", path, strings.NewReader(body)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if self { | ||||||
|  | 		rq.Header.Add("Depth", "0") | ||||||
|  | 	} else { | ||||||
|  | 		rq.Header.Add("Depth", "1") | ||||||
|  | 	} | ||||||
|  | 	rq.Header.Add("Content-Type", "text/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", "") | ||||||
|  | 
 | ||||||
|  | 	rs, err := c.c.Do(rq) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer rs.Body.Close() | ||||||
|  | 
 | ||||||
|  | 	if rs.StatusCode != 207 { | ||||||
|  | 		return errors.New(fmt.Sprintf("%s - %s %s", rs.Status, rq.Method, rq.URL.String())) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	decoder := xml.NewDecoder(rs.Body) | ||||||
|  | 	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 { | ||||||
|  | 					parse(resp) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								utils.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | package gowebdav | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"io" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func Join(path0 string, path1 string) string { | ||||||
|  | 	return strings.TrimSuffix(path0, "/") + "/" + strings.TrimPrefix(path1, "/") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func String(r io.Reader) string { | ||||||
|  | 	buf := new(bytes.Buffer) | ||||||
|  | 	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 parseModified(s *string) time.Time { | ||||||
|  | 	if t, e := time.Parse(time.RFC1123, *s); e == nil { | ||||||
|  | 		return t | ||||||
|  | 	} | ||||||
|  | 	return time.Unix(0, 0) | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user