529 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			529 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package rest
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"github.com/eyebluecn/tank/code/core"
 | 
						|
	"github.com/eyebluecn/tank/code/tool/dav"
 | 
						|
	"github.com/eyebluecn/tank/code/tool/dav/xml"
 | 
						|
	"github.com/eyebluecn/tank/code/tool/result"
 | 
						|
	"github.com/eyebluecn/tank/code/tool/util"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"net/url"
 | 
						|
	"path"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
/**
 | 
						|
 *
 | 
						|
 * WebDav document
 | 
						|
 * https://tools.ietf.org/html/rfc4918
 | 
						|
 * refer: golang.org/x/net/webdav
 | 
						|
 * test machine: http://www.webdav.org/neon/litmus/
 | 
						|
 */
 | 
						|
//@Service
 | 
						|
type DavService struct {
 | 
						|
	BaseBean
 | 
						|
	matterDao     *MatterDao
 | 
						|
	matterService *MatterService
 | 
						|
}
 | 
						|
 | 
						|
func (this *DavService) Init() {
 | 
						|
	this.BaseBean.Init()
 | 
						|
 | 
						|
	b := core.CONTEXT.GetBean(this.matterDao)
 | 
						|
	if b, ok := b.(*MatterDao); ok {
 | 
						|
		this.matterDao = b
 | 
						|
	}
 | 
						|
 | 
						|
	b = core.CONTEXT.GetBean(this.matterService)
 | 
						|
	if b, ok := b.(*MatterService); ok {
 | 
						|
		this.matterService = b
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//get the depth in header. Not support infinity yet.
 | 
						|
func (this *DavService) ParseDepth(request *http.Request) int {
 | 
						|
 | 
						|
	depth := 1
 | 
						|
	if hdr := request.Header.Get("Depth"); hdr != "" {
 | 
						|
		switch hdr {
 | 
						|
		case "0":
 | 
						|
			return 0
 | 
						|
		case "1":
 | 
						|
			return 1
 | 
						|
		case "infinity":
 | 
						|
			return 1
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		panic(result.BadRequest("Header Depth cannot be null"))
 | 
						|
	}
 | 
						|
	return depth
 | 
						|
}
 | 
						|
 | 
						|
func (this *DavService) makePropstatResponse(href string, pstats []dav.Propstat) *dav.Response {
 | 
						|
	resp := dav.Response{
 | 
						|
		Href:     []string{(&url.URL{Path: href}).EscapedPath()},
 | 
						|
		Propstat: make([]dav.SubPropstat, 0, len(pstats)),
 | 
						|
	}
 | 
						|
	for _, p := range pstats {
 | 
						|
		var xmlErr *dav.XmlError
 | 
						|
		if p.XMLError != "" {
 | 
						|
			xmlErr = &dav.XmlError{InnerXML: []byte(p.XMLError)}
 | 
						|
		}
 | 
						|
		resp.Propstat = append(resp.Propstat, dav.SubPropstat{
 | 
						|
			Status:              fmt.Sprintf("HTTP/1.1 %d %s", p.Status, dav.StatusText(p.Status)),
 | 
						|
			Prop:                p.Props,
 | 
						|
			ResponseDescription: p.ResponseDescription,
 | 
						|
			Error:               xmlErr,
 | 
						|
		})
 | 
						|
	}
 | 
						|
	return &resp
 | 
						|
}
 | 
						|
 | 
						|
//fetch a matter's []dav.Propstat
 | 
						|
func (this *DavService) PropstatsFromXmlNames(user *User, matter *Matter, xmlNames []xml.Name) []dav.Propstat {
 | 
						|
 | 
						|
	propstats := make([]dav.Propstat, 0)
 | 
						|
 | 
						|
	var properties []dav.Property
 | 
						|
 | 
						|
	for _, xmlName := range xmlNames {
 | 
						|
		//TODO: deadprops not implement yet.
 | 
						|
 | 
						|
		// Otherwise, it must either be a live property or we don't know it.
 | 
						|
		if liveProp := LivePropMap[xmlName]; liveProp.findFn != nil && (liveProp.dir || !matter.Dir) {
 | 
						|
			innerXML := liveProp.findFn(user, matter)
 | 
						|
 | 
						|
			properties = append(properties, dav.Property{
 | 
						|
				XMLName:  xmlName,
 | 
						|
				InnerXML: []byte(innerXML),
 | 
						|
			})
 | 
						|
		} else {
 | 
						|
			this.logger.Info("%s %s cannot finish.", matter.Path, xmlName.Local)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if len(properties) == 0 {
 | 
						|
		panic(result.BadRequest("cannot parse request properties"))
 | 
						|
	}
 | 
						|
 | 
						|
	okPropstat := dav.Propstat{Status: http.StatusOK, Props: properties}
 | 
						|
 | 
						|
	propstats = append(propstats, okPropstat)
 | 
						|
 | 
						|
	return propstats
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (this *DavService) AllPropXmlNames(matter *Matter) []xml.Name {
 | 
						|
 | 
						|
	pnames := make([]xml.Name, 0)
 | 
						|
	for pn, prop := range LivePropMap {
 | 
						|
		if prop.findFn != nil && (prop.dir || !matter.Dir) {
 | 
						|
			pnames = append(pnames, pn)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return pnames
 | 
						|
}
 | 
						|
 | 
						|
func (this *DavService) Propstats(user *User, matter *Matter, propfind *dav.Propfind) []dav.Propstat {
 | 
						|
 | 
						|
	propstats := make([]dav.Propstat, 0)
 | 
						|
	if propfind.Propname != nil {
 | 
						|
		panic(result.BadRequest("TODO: propfind.Propname != nil "))
 | 
						|
	} else if propfind.Allprop != nil {
 | 
						|
 | 
						|
		//TODO: if include other things. add to it.
 | 
						|
		xmlNames := this.AllPropXmlNames(matter)
 | 
						|
 | 
						|
		propstats = this.PropstatsFromXmlNames(user, matter, xmlNames)
 | 
						|
 | 
						|
	} else {
 | 
						|
		propstats = this.PropstatsFromXmlNames(user, matter, propfind.Prop)
 | 
						|
	}
 | 
						|
 | 
						|
	return propstats
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//list the directory.
 | 
						|
func (this *DavService) HandlePropfind(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("PROPFIND %s\n", subPath)
 | 
						|
 | 
						|
	// read depth
 | 
						|
	depth := this.ParseDepth(request)
 | 
						|
 | 
						|
	propfind := dav.ReadPropfind(request.Body)
 | 
						|
 | 
						|
	//find the matter, if subPath is null, means the root directory.
 | 
						|
	matter := this.matterDao.CheckWithRootByPath(subPath, user)
 | 
						|
 | 
						|
	var matters []*Matter
 | 
						|
	if depth == 0 {
 | 
						|
		matters = []*Matter{matter}
 | 
						|
	} else {
 | 
						|
		// len(matters) == 0 means empty directory
 | 
						|
		matters = this.matterDao.FindByPuuidAndUserUuid(matter.Uuid, user.Uuid, nil)
 | 
						|
 | 
						|
		//add this matter to head.
 | 
						|
		matters = append([]*Matter{matter}, matters...)
 | 
						|
	}
 | 
						|
 | 
						|
	//prepare a multiStatusWriter.
 | 
						|
	multiStatusWriter := &dav.MultiStatusWriter{Writer: writer}
 | 
						|
 | 
						|
	for _, matter := range matters {
 | 
						|
 | 
						|
		fmt.Printf("handle Matter %s\n", matter.Path)
 | 
						|
 | 
						|
		propstats := this.Propstats(user, matter, propfind)
 | 
						|
		visitPath := fmt.Sprintf("%s%s", WEBDAV_PREFIX, matter.Path)
 | 
						|
		response := this.makePropstatResponse(visitPath, propstats)
 | 
						|
 | 
						|
		err := multiStatusWriter.Write(response)
 | 
						|
		this.PanicError(err)
 | 
						|
	}
 | 
						|
 | 
						|
	err := multiStatusWriter.Close()
 | 
						|
	this.PanicError(err)
 | 
						|
 | 
						|
	fmt.Printf("%v %v \n", subPath, propfind.Prop)
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//handle download
 | 
						|
func (this *DavService) HandleGetHeadPost(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("GET %s\n", subPath)
 | 
						|
 | 
						|
	matter := this.matterDao.CheckWithRootByPath(subPath, user)
 | 
						|
 | 
						|
	//if this is a Directory, it means Propfind
 | 
						|
	if matter.Dir {
 | 
						|
		this.HandlePropfind(writer, request, user, subPath)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	//download a file.
 | 
						|
	this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, false)
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//upload a file
 | 
						|
func (this *DavService) HandlePut(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("PUT %s\n", subPath)
 | 
						|
 | 
						|
	filename := util.GetFilenameOfPath(subPath)
 | 
						|
	dirPath := util.GetDirOfPath(subPath)
 | 
						|
 | 
						|
	dirMatter := this.matterDao.CheckWithRootByPath(dirPath, user)
 | 
						|
 | 
						|
	//if exist delete it.
 | 
						|
	srcMatter := this.matterDao.findByUserUuidAndPath(user.Uuid, subPath)
 | 
						|
	if srcMatter != nil {
 | 
						|
		this.matterService.AtomicDelete(request, srcMatter, user)
 | 
						|
	}
 | 
						|
 | 
						|
	this.matterService.Upload(request, request.Body, user, dirMatter, filename, true)
 | 
						|
 | 
						|
	//set the status code 201
 | 
						|
	writer.WriteHeader(http.StatusCreated)
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//delete file
 | 
						|
func (this *DavService) HandleDelete(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("DELETE %s\n", subPath)
 | 
						|
 | 
						|
	matter := this.matterDao.CheckWithRootByPath(subPath, user)
 | 
						|
 | 
						|
	this.matterService.AtomicDelete(request, matter, user)
 | 
						|
}
 | 
						|
 | 
						|
//crate a directory
 | 
						|
func (this *DavService) HandleMkcol(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("MKCOL %s\n", subPath)
 | 
						|
 | 
						|
	//the body of MKCOL request MUST be empty. (RFC2518:8.3.1)
 | 
						|
	bodyBytes, err := ioutil.ReadAll(request.Body)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Println("occur error when reading body: " + err.Error())
 | 
						|
	} else {
 | 
						|
		if len(bodyBytes) != 0 {
 | 
						|
			//throw conflict error
 | 
						|
			panic(result.CustomWebResult(result.UNSUPPORTED_MEDIA_TYPE, fmt.Sprintf("%s MKCOL should NO body", subPath)))
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	thisDirName := util.GetFilenameOfPath(subPath)
 | 
						|
	dirPath := util.GetDirOfPath(subPath)
 | 
						|
 | 
						|
	dirMatter := this.matterDao.FindWithRootByPath(dirPath, user)
 | 
						|
	if dirMatter == nil {
 | 
						|
		//throw conflict error
 | 
						|
		panic(result.CustomWebResult(result.CONFLICT, fmt.Sprintf("%s not exist", dirPath)))
 | 
						|
	}
 | 
						|
 | 
						|
	//check whether col exists. (RFC2518:8.3.1)
 | 
						|
	dbMatter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, TRUE, thisDirName)
 | 
						|
	if dbMatter != nil {
 | 
						|
		panic(result.CustomWebResult(result.METHOD_NOT_ALLOWED, fmt.Sprintf("%s already exists", dirPath)))
 | 
						|
	}
 | 
						|
 | 
						|
	//check whether file exists. (RFC2518:8.3.1)
 | 
						|
	fileMatter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, FALSE, thisDirName)
 | 
						|
	if fileMatter != nil {
 | 
						|
		panic(result.CustomWebResult(result.METHOD_NOT_ALLOWED, fmt.Sprintf("%s file already exists", dirPath)))
 | 
						|
	}
 | 
						|
 | 
						|
	this.matterService.AtomicCreateDirectory(request, dirMatter, thisDirName, user)
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//cors options
 | 
						|
func (this *DavService) HandleOptions(w http.ResponseWriter, r *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("OPTIONS %s\n", subPath)
 | 
						|
 | 
						|
	matter := this.matterDao.CheckWithRootByPath(subPath, user)
 | 
						|
 | 
						|
	allow := "OPTIONS, LOCK, PUT, MKCOL"
 | 
						|
	if matter.Dir {
 | 
						|
		allow = "OPTIONS, LOCK, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND"
 | 
						|
	} else {
 | 
						|
		allow = "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY, MOVE, UNLOCK, PROPFIND, PUT"
 | 
						|
	}
 | 
						|
 | 
						|
	w.Header().Set("Allow", allow)
 | 
						|
	// http://www.webdav.org/specs/rfc4918.html#dav.compliance.classes
 | 
						|
	w.Header().Set("DAV", "1, 2")
 | 
						|
	// http://msdn.microsoft.com/en-au/library/cc250217.aspx
 | 
						|
	w.Header().Set("MS-Author-Via", "DAV")
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//prepare for moving or copying
 | 
						|
func (this *DavService) prepareMoveCopy(
 | 
						|
	writer http.ResponseWriter,
 | 
						|
	request *http.Request,
 | 
						|
	user *User, subPath string) (
 | 
						|
	srcMatter *Matter,
 | 
						|
	destDirMatter *Matter,
 | 
						|
	srcDirPath string,
 | 
						|
	destinationDirPath string,
 | 
						|
	destinationName string,
 | 
						|
	overwrite bool) {
 | 
						|
 | 
						|
	//parse the destination.
 | 
						|
	destinationStr := request.Header.Get("Destination")
 | 
						|
 | 
						|
	//parse Overwrite。
 | 
						|
	overwriteStr := request.Header.Get("Overwrite")
 | 
						|
 | 
						|
	//destination path with prefix
 | 
						|
	var fullDestinationPath string
 | 
						|
	//destination path without prefix
 | 
						|
	var destinationPath string
 | 
						|
 | 
						|
	if destinationStr == "" {
 | 
						|
		panic(result.BadRequest("Header Destination cannot be null"))
 | 
						|
	}
 | 
						|
 | 
						|
	//if rename. not start with http
 | 
						|
	if strings.HasPrefix(destinationStr, WEBDAV_PREFIX) {
 | 
						|
		fullDestinationPath = destinationStr
 | 
						|
	} else {
 | 
						|
		destinationUrl, err := url.Parse(destinationStr)
 | 
						|
		this.PanicError(err)
 | 
						|
		if destinationUrl.Host != request.Host {
 | 
						|
			panic(result.BadRequest("Destination Host not the same. %s  %s != %s", destinationStr, destinationUrl.Host, request.Host))
 | 
						|
		}
 | 
						|
		fullDestinationPath = destinationUrl.Path
 | 
						|
	}
 | 
						|
 | 
						|
	//clean the relative path. eg. /a/b/../ => /a/
 | 
						|
	fullDestinationPath = path.Clean(fullDestinationPath)
 | 
						|
 | 
						|
	//clean the prefix
 | 
						|
	pattern := fmt.Sprintf(`^%s(.*)$`, WEBDAV_PREFIX)
 | 
						|
	reg := regexp.MustCompile(pattern)
 | 
						|
	strs := reg.FindStringSubmatch(fullDestinationPath)
 | 
						|
	if len(strs) == 2 {
 | 
						|
		destinationPath = strs[1]
 | 
						|
	} else {
 | 
						|
		panic(result.BadRequest("destination prefix must be %s", WEBDAV_PREFIX))
 | 
						|
	}
 | 
						|
 | 
						|
	destinationName = util.GetFilenameOfPath(destinationPath)
 | 
						|
	destinationDirPath = util.GetDirOfPath(destinationPath)
 | 
						|
	srcDirPath = util.GetDirOfPath(subPath)
 | 
						|
 | 
						|
	overwrite = false
 | 
						|
	if overwriteStr == "T" {
 | 
						|
		overwrite = true
 | 
						|
	}
 | 
						|
 | 
						|
	//if not change return.
 | 
						|
	if destinationPath == subPath {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	//source matter
 | 
						|
	srcMatter = this.matterDao.CheckWithRootByPath(subPath, user)
 | 
						|
 | 
						|
	//if source matter is root.
 | 
						|
	if srcMatter.Uuid == MATTER_ROOT {
 | 
						|
		panic(result.BadRequest("you cannot move the root directory"))
 | 
						|
	}
 | 
						|
 | 
						|
	destDirMatter = this.matterDao.FindWithRootByPath(destinationDirPath, user)
 | 
						|
	if destDirMatter == nil {
 | 
						|
		//throw conflict error
 | 
						|
		panic(result.CustomWebResult(result.CONFLICT, fmt.Sprintf("%s not exist", destinationDirPath)))
 | 
						|
	}
 | 
						|
 | 
						|
	return srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//move or rename.
 | 
						|
func (this *DavService) HandleMove(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("MOVE %s\n", subPath)
 | 
						|
 | 
						|
	srcMatter, destDirMatter, srcDirPath, destinationDirPath, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
 | 
						|
 | 
						|
	//move to the new directory
 | 
						|
	if destinationDirPath == srcDirPath {
 | 
						|
		//if destination path not change. it means rename.
 | 
						|
		this.matterService.AtomicRename(request, srcMatter, destinationName, overwrite, user)
 | 
						|
	} else {
 | 
						|
		this.matterService.AtomicMove(request, srcMatter, destDirMatter, overwrite, user)
 | 
						|
	}
 | 
						|
 | 
						|
	this.logger.Info("finish moving %s => %s", subPath, destDirMatter.Path)
 | 
						|
 | 
						|
	if overwrite {
 | 
						|
		//overwrite old. set the status code 204
 | 
						|
		writer.WriteHeader(http.StatusNoContent)
 | 
						|
	} else {
 | 
						|
		//copy new. set the status code 201
 | 
						|
		writer.WriteHeader(http.StatusCreated)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
//copy file/directory
 | 
						|
func (this *DavService) HandleCopy(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	fmt.Printf("COPY %s\n", subPath)
 | 
						|
 | 
						|
	srcMatter, destDirMatter, _, _, destinationName, overwrite := this.prepareMoveCopy(writer, request, user, subPath)
 | 
						|
 | 
						|
	//copy to the new directory
 | 
						|
	this.matterService.AtomicCopy(request, srcMatter, destDirMatter, destinationName, overwrite, user)
 | 
						|
 | 
						|
	this.logger.Info("finish copying %s => %s", subPath, destDirMatter.Path)
 | 
						|
 | 
						|
	if overwrite {
 | 
						|
		//overwrite old. set the status code 204
 | 
						|
		writer.WriteHeader(http.StatusNoContent)
 | 
						|
	} else {
 | 
						|
		//copy new. set the status code 201
 | 
						|
		writer.WriteHeader(http.StatusCreated)
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//lock.
 | 
						|
func (this *DavService) HandleLock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	panic(result.BadRequest("not support LOCK yet."))
 | 
						|
}
 | 
						|
 | 
						|
//unlock
 | 
						|
func (this *DavService) HandleUnlock(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	panic(result.BadRequest("not support UNLOCK yet."))
 | 
						|
}
 | 
						|
 | 
						|
//change the file's property
 | 
						|
func (this *DavService) HandleProppatch(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	panic(result.BadRequest("not support PROPPATCH yet."))
 | 
						|
}
 | 
						|
 | 
						|
//hanle all the request.
 | 
						|
func (this *DavService) HandleDav(writer http.ResponseWriter, request *http.Request, user *User, subPath string) {
 | 
						|
 | 
						|
	method := request.Method
 | 
						|
	if method == "OPTIONS" {
 | 
						|
 | 
						|
		//cors option
 | 
						|
		this.HandleOptions(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "GET" || method == "HEAD" || method == "POST" {
 | 
						|
 | 
						|
		//get the detail of file. download
 | 
						|
		this.HandleGetHeadPost(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "DELETE" {
 | 
						|
 | 
						|
		//delete file
 | 
						|
		this.HandleDelete(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "PUT" {
 | 
						|
 | 
						|
		//upload file
 | 
						|
		this.HandlePut(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "MKCOL" {
 | 
						|
 | 
						|
		//crate directory
 | 
						|
		this.HandleMkcol(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "COPY" {
 | 
						|
 | 
						|
		//copy file/directory
 | 
						|
		this.HandleCopy(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "MOVE" {
 | 
						|
 | 
						|
		//move/rename a file or directory
 | 
						|
		this.HandleMove(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "LOCK" {
 | 
						|
 | 
						|
		//lock
 | 
						|
		this.HandleLock(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "UNLOCK" {
 | 
						|
 | 
						|
		//unlock
 | 
						|
		this.HandleUnlock(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "PROPFIND" {
 | 
						|
 | 
						|
		//list a directory
 | 
						|
		this.HandlePropfind(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else if method == "PROPPATCH" {
 | 
						|
 | 
						|
		//change file's property.
 | 
						|
		this.HandleProppatch(writer, request, user, subPath)
 | 
						|
 | 
						|
	} else {
 | 
						|
 | 
						|
		panic(result.BadRequest("not support %s yet.", method))
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
}
 |