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