Beispiel #1
0
// Get implements the WebDAV GET method to download a file.
func (s *svc) Get(w http.ResponseWriter, r *http.Request) {
	log := keys.MustGetLog(r)
	user := keys.MustGetUser(r)

	path := mux.Vars(r)["path"]
	info, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleGetError(err, w, r)
		return
	}
	if info.Type != entities.ObjectTypeBLOB {
		log.Warn("object is not a blob")
		w.WriteHeader(http.StatusNotImplemented)
		return
	}

	reader, err := s.dataController.DownloadBLOB(user, path)
	if err != nil {
		s.handleGetError(err, w, r)
		return
	}

	w.Header().Add("Content-Type", info.MimeType)

	if _, err := io.Copy(w, reader); err != nil {
		log.WithError(err).Error("cannot write response body")
	}
}
Beispiel #2
0
// Init retrieves the information about an object.
func (s *svc) Init(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)
	err := s.metaDataController.Init(user)
	if err != nil {
		s.handleInitError(err, w, r)
		return
	}
}
Beispiel #3
0
// DeleteObject retrieves the information about an object.
func (s *svc) DeleteObject(w http.ResponseWriter, r *http.Request) {
	path := mux.Vars(r)["path"]
	user := keys.MustGetUser(r)
	err := s.metaDataController.DeleteObject(user, path)
	if err != nil {
		s.handleDeleteObjectError(err, w, r)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
Beispiel #4
0
// CreateTree creates a tree object.
func (s *svc) CreateTree(w http.ResponseWriter, r *http.Request) {
	path := mux.Vars(r)["path"]
	user := keys.MustGetUser(r)
	err := s.metaDataController.CreateTree(user, path)
	if err != nil {
		s.handleCreateTreeError(err, w, r)
		return
	}
	w.WriteHeader(http.StatusCreated)
}
Beispiel #5
0
// Head implements the WebDAV HEAD method.
func (s *svc) Head(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)
	path := mux.Vars(r)["path"]

	info, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleHeadError(err, w, r)
		return
	}

	w.Header().Add("Content-Type", info.MimeType)
}
Beispiel #6
0
// Move implements the WebDAV MOVE method.
func (s *svc) Move(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)
	path := mux.Vars(r)["path"]

	destination := r.Header.Get("Destination")
	overwrite := r.Header.Get("Overwrite")

	if destination == "" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	destinationURL, err := url.ParseRequestURI(destination)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	overwrite = strings.ToUpper(overwrite)
	if overwrite == "" {
		overwrite = "T"
	}

	if overwrite != "T" && overwrite != "F" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// remove api base and service base to get real path
	dirs := s.conf.GetDirectives()
	toTrim := filepath.Join("/", dirs.Server.BaseURL, dirs.OCWebDAV.BaseURL) + "/remote.php/webdav/"
	destination = strings.TrimPrefix(destinationURL.Path, toTrim)

	err = s.metaDataController.MoveObject(user, path, destination)
	if err != nil {
		s.handleMoveError(err, w, r)
		return
	}

	info, err := s.metaDataController.ExamineObject(user, destination)
	if err != nil {
		s.handleMoveError(err, w, r)
		return
	}

	w.Header().Set("ETag", info.Extra.(ocsql.Extra).ETag)
	w.Header().Set("OC-FileId", info.Extra.(ocsql.Extra).ID)
	w.Header().Set("OC-ETag", info.Extra.(ocsql.Extra).ETag)

	// ownCloud want a 201 instead of 204
	w.WriteHeader(http.StatusCreated)
}
Beispiel #7
0
// ExamineObject retrieves the information about an object.
func (s *svc) ExamineObject(w http.ResponseWriter, r *http.Request) {
	log := keys.MustGetLog(r)
	user := keys.MustGetUser(r)

	path := mux.Vars(r)["path"]
	oinfo, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleExamineObjectError(err, w, r)
		return
	}
	if err := json.NewEncoder(w).Encode(oinfo); err != nil {
		log.WithError(err).Error("cannot encode")
	}
}
Beispiel #8
0
// Put uploads a blob to user tree.
func (s *svc) Put(w http.ResponseWriter, r *http.Request) {
	if r.Body == nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	user := keys.MustGetUser(r)
	log := keys.MustGetLog(r)

	if s.requestHasContentRange(r) {
		log.Warning("Content-Range header is not accepted on PUT")
		http.Error(w, http.StatusText(http.StatusNotImplemented), http.StatusNotImplemented)
		return
	}

	if s.requestSuffersFinderProblem(r) {
		s.handlerFinderRequest(w, r)
	}

	path := mux.Vars(r)["path"]
	info, err := s.metaDataController.ExamineObject(user, path)
	// if err is not found it is okay to continue
	if err != nil {
		if !s.isNotFoundError(err) {
			s.handlePutError(err, w, r)
			return
		}
	}

	if info != nil && info.Type != entities.ObjectTypeBLOB {
		log.Warn("object is not a blob")
		w.WriteHeader(http.StatusConflict)
		return
	}

	readCloser := http.MaxBytesReader(w, r.Body, int64(s.conf.GetDirectives().WebDAV.UploadMaxFileSize))
	if err := s.dataController.UploadBLOB(user, path, readCloser, ""); err != nil {
		s.handlePutError(err, w, r)
		return
	}

	// if object did not exist, http code is 201, else 204.
	if info == nil {
		w.WriteHeader(http.StatusCreated)
		return
	}
	w.WriteHeader(http.StatusNoContent)
	return
}
Beispiel #9
0
// Upload uploads a blob to user tree.
func (s *svc) Upload(w http.ResponseWriter, r *http.Request) {
	if r.Body == nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}
	user := keys.MustGetUser(r)

	path := mux.Vars(r)["path"]
	clientChecksum := s.getClientChecksum(r)
	readCloser := http.MaxBytesReader(w, r.Body, int64(s.conf.GetDirectives().Data.UploadMaxFileSize))
	if err := s.dataController.UploadBLOB(user, path, readCloser, clientChecksum); err != nil {
		s.handleUploadError(err, w, r)
		return
	}
	w.WriteHeader(http.StatusCreated)
}
Beispiel #10
0
// Get implements the WebDAV GET method to download a file.
func (s *svc) Get(w http.ResponseWriter, r *http.Request) {
	log := keys.MustGetLog(r)
	user := keys.MustGetUser(r)

	path := mux.Vars(r)["path"]
	info, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleGetError(err, w, r)
		return
	}
	if info.Type != entities.ObjectTypeBLOB {
		log.Warn("object is not a blob")
		w.WriteHeader(http.StatusNotImplemented)
		return
	}

	reader, err := s.dataController.DownloadBLOB(user, path)
	if err != nil {
		s.handleGetError(err, w, r)
		return
	}

	info, err = s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleGetError(err, w, r)
		return
	}

	w.Header().Set("Content-Type", info.MimeType)
	w.Header().Set("ETag", info.Extra.(ocsql.Extra).ETag)
	w.Header().Set("OC-FileId", info.Extra.(ocsql.Extra).ID)
	w.Header().Set("OC-ETag", info.Extra.(ocsql.Extra).ETag)
	t := time.Unix(info.ModTime/1000000000, info.ModTime%1000000000)
	lastModifiedString := t.Format(time.RFC1123)
	w.Header().Set("Last-Modified", lastModifiedString)
	w.WriteHeader(http.StatusOK)

	if info.Checksum != "" {
		w.Header().Set("OC-Checksum", info.Checksum)
	}

	if _, err := io.Copy(w, reader); err != nil {
		log.WithError(err).Error("cannot write response body")
	}
}
Beispiel #11
0
// Download streams a file to the client.
func (s *svc) Download(w http.ResponseWriter, r *http.Request) {
	log := keys.MustGetLog(r)
	user := keys.MustGetUser(r)

	path := mux.Vars(r)["path"]
	reader, err := s.dataController.DownloadBLOB(user, path)
	if err != nil {
		s.handleDownloadError(err, w, r)
		return
	}
	// add security headers
	w.Header().Add("X-Content-Type-Options", "nosniff")
	w.Header().Add("Content-Type", entities.ObjectTypeBLOBMimeType)
	w.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename='%s'", filepath.Base(path)))
	if _, err := io.Copy(w, reader); err != nil {
		log.WithError(err).Error("cannot write response body")
	}
}
Beispiel #12
0
// MoveObject retrieves the information about an object.
func (s *svc) MoveObject(w http.ResponseWriter, r *http.Request) {
	sourcePath := mux.Vars(r)["path"]
	targetPath := r.URL.Query().Get("target")

	// targetPath may be url encoded, so we decode it first.
	targetPath, err := url.QueryUnescape(targetPath)
	if err != nil {
		s.handleMoveObjectError(err, w, r)
		return
	}

	user := keys.MustGetUser(r)

	err = s.metaDataController.MoveObject(user, sourcePath, targetPath)
	if err != nil {
		s.handleMoveObjectError(err, w, r)
		return
	}
}
Beispiel #13
0
// Head implements the WebDAV HEAD method.
func (s *svc) Head(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)
	path := mux.Vars(r)["path"]

	info, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleHeadError(err, w, r)
		return
	}

	w.Header().Add("Content-Type", info.MimeType)
	w.Header().Set("ETag", info.Extra.(ocsql.Extra).ETag)
	w.Header().Set("OC-FileId", info.Extra.(ocsql.Extra).ID)
	w.Header().Set("OC-ETag", info.Extra.(ocsql.Extra).ETag)
	t := time.Unix(info.ModTime/1000000000, info.ModTime%1000000000)
	lastModifiedString := t.Format(time.RFC1123)
	w.Header().Set("Last-Modified", lastModifiedString)
	w.WriteHeader(http.StatusOK)
}
Beispiel #14
0
// Propfind implements the WebDAV PROPFIND method.
func (s *svc) Propfind(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)
	path := mux.Vars(r)["path"]

	var children bool
	depth := r.Header.Get("Depth")
	// TODO(labkode) Check default for infinity header
	if depth == "1" {
		children = true
	}

	var infos []*entities.ObjectInfo
	info, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handlePropfindError(err, w, r)
		return
	}
	infos = append(infos, info)

	if children && info.Type == entities.ObjectTypeTree {
		childrenInfos, err := s.metaDataController.ListTree(user, path)
		if err != nil {
			s.handlePropfindError(err, w, r)
			return
		}
		infos = append(infos, childrenInfos...)
	}

	infosInXML, err := s.infosToXML(infos)
	if err != nil {
		s.handlePropfindError(err, w, r)
		return
	}

	w.Header().Set("DAV", "1, 3, extended-mkcol")
	w.Header().Set("Content-Type", "application/xml; charset=utf-8")
	w.WriteHeader(207)
	w.Write([]byte(infosInXML))

}
Beispiel #15
0
// Move implements the WebDAV MOVE method.
func (s *svc) Move(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)
	path := mux.Vars(r)["path"]

	destination := r.Header.Get("Destination")
	overwrite := r.Header.Get("Overwrite")

	if destination == "" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	destinationURL, err := url.ParseRequestURI(destination)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	overwrite = strings.ToUpper(overwrite)
	if overwrite == "" {
		overwrite = "T"
	}

	if overwrite != "T" && overwrite != "F" {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// remove api base and service base to get real path
	dirs := s.conf.GetDirectives()
	toTrim := filepath.Join("/", dirs.Server.BaseURL, dirs.WebDAV.BaseURL) + "/home/"
	destination = strings.TrimPrefix(destinationURL.Path, toTrim)

	err = s.metaDataController.MoveObject(user, path, destination)
	if err != nil {
		s.handleMoveError(err, w, r)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
Beispiel #16
0
// Options implements the WebDAV GET method to download a file.
func (s *svc) Options(w http.ResponseWriter, r *http.Request) {
	user := keys.MustGetUser(r)

	path := mux.Vars(r)["path"]
	info, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handleOptionsError(err, w, r)
		return
	}

	allow := "OPTIONS, LOCK, GET, HEAD, POST, DELETE, PROPPATCH, COPY,"
	allow += " MOVE, UNLOCK, PROPFIND"
	if info.Type == entities.ObjectTypeBLOB {
		allow += ", PUT"
	}

	w.Header().Set("Allow", allow)
	w.Header().Set("DAV", "1, 2")
	w.Header().Set("MS-Author-Via", "DAV")
	w.WriteHeader(http.StatusOK)
	return
}
Beispiel #17
0
func (s *svc) PutChunked(w http.ResponseWriter, r *http.Request) {
	if r.Body == nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	user := keys.MustGetUser(r)
	log := keys.MustGetLog(r)
	path := mux.Vars(r)["path"]

	chunkInfo, err := getChunkBLOBInfo(path)
	if err != nil {
		log.WithError(err).WithField("pathspec", path).Error("cannot obtain chunk info from pathspec")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.WithField("chunk", chunkInfo).Debug("chunk info")

	chunkTempFilename, chunkTempFile, err := s.createChunkTempFile()
	if err != nil {
		log.WithError(err).Error("cannot create chunk temp file")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer chunkTempFile.Close()

	readCloser := http.MaxBytesReader(w, r.Body, int64(s.conf.GetDirectives().WebDAV.UploadMaxFileSize))
	if _, err := io.Copy(chunkTempFile, readCloser); err != nil {
		s.handlePutError(err, w, r)
		return
	}

	// force close of the file here because if it is the last chunk to
	// assemble the big file we need all the chunks closed
	if err = chunkTempFile.Close(); err != nil {
		s.handlePutError(err, w, r)
		return
	}

	chunkFolderName, err := s.getChunkFolderName(chunkInfo)
	if err != nil {
		s.handlePutError(err, w, r)
		return
	}
	log.WithField("chunkfolder", chunkFolderName).Debug("chunk folder info")

	chunkTarget := helpers.SecureJoin(chunkFolderName, fmt.Sprintf("%d", chunkInfo.currentChunk))
	if err = os.Rename(chunkTempFilename, chunkTarget); err != nil {
		s.handlePutError(err, w, r)
		return
	}
	log.WithField("chunktarget", chunkTarget).Debug("chunk target info")

	// Check that all chunks are uploaded.
	// This is very inefficient, the server has to check that it has all the
	// chunks after each uploaded chunk.
	// A two-phase upload like DropBox is better, because the server will
	// assembly the chunks when the client asks for it.
	chunkFolder, err := os.Open(chunkFolderName)
	if err != nil {
		s.handlePutError(err, w, r)
		return
	}
	defer chunkFolder.Close()

	// read all the chunks inside the chunk folder; -1 == all
	chunks, err := chunkFolder.Readdir(-1)
	if err != nil {
		s.handlePutError(err, w, r)
		return
	}
	log.WithField("chunk count", len(chunks)).Debug("current amount of chunks")

	// there is still some chunks to be uploaded so we stop here
	if len(chunks) < int(chunkInfo.totalChunks) {
		log.Debug("chunk is not final")
		w.WriteHeader(http.StatusCreated)
		return
	}

	assembledFileName, assembledFile, err := s.createChunkTempFile()
	if err != nil {
		s.handlePutError(err, w, r)
		return
	}
	defer assembledFile.Close()
	log.WithField("assembledfile", assembledFileName).Debug("assembled file info")

	// walk all chunks and append to assembled file
	for i := range chunks {
		target := helpers.SecureJoin(chunkFolderName, fmt.Sprintf("%d", i))

		chunk, err := os.Open(target)
		if err != nil {
			s.handlePutError(err, w, r)
			return
		}

		if _, err = io.Copy(assembledFile, chunk); err != nil {
			s.handlePutError(err, w, r)
			return
		}
		log.WithField("chunk", target).WithField("assembledfile", assembledFileName).Debug("chunk appended to assembled file")

		// we close the chunk here because if the assemnled file contains hundreds of chunks
		// we will end up with hundreds of open file descriptors
		if err = chunk.Close(); err != nil {
			s.handlePutError(err, w, r)
			return

		}
	}

	// at this point the assembled file is complete
	// so we free space removing the chunks folder
	defer func() {
		if err = os.RemoveAll(chunkFolderName); err != nil {
			log.WithError(err).Error("cannot remove chunk folder after recontruction")
		}
	}()

	// when writing to the assembled file the write pointer points to the end of the file
	// so we need to seek it to the beginning
	if _, err = assembledFile.Seek(0, 0); err != nil {
		s.handlePutError(err, w, r)
		return
	}

	log.WithField("pathspec", chunkInfo.pathSpec).Debug("upload chunk to final destination")
	if err = s.dataController.UploadBLOB(user, chunkInfo.pathSpec, assembledFile, ""); err != nil {
		s.handlePutError(err, w, r)
		return
	}

	info, err := s.metaDataController.ExamineObject(user, chunkInfo.pathSpec)
	if err != nil {
		s.handlePutError(err, w, r)
		return
	}

	w.Header().Add("Content-Type", info.MimeType)
	w.Header().Set("ETag", info.Extra.(ocsql.Extra).ETag)
	w.Header().Set("OC-FileId", info.Extra.(ocsql.Extra).ID)
	w.Header().Set("OC-ETag", info.Extra.(ocsql.Extra).ETag)
	t := time.Unix(info.ModTime/1000000000, info.ModTime%1000000000)
	lastModifiedString := t.Format(time.RFC1123)
	w.Header().Set("Last-Modified", lastModifiedString)
	w.Header().Set("X-OC-MTime", "accepted")

	// if object did not exist, http code is 201, else 204.
	if info == nil {
		w.WriteHeader(http.StatusCreated)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
Beispiel #18
0
// Put uploads a blob to user tree.
func (s *svc) Put(w http.ResponseWriter, r *http.Request) {
	if r.Body == nil {
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	user := keys.MustGetUser(r)
	log := keys.MustGetLog(r)
	path := mux.Vars(r)["path"]

	// if request is a chunk upload we handle it in another method
	isChunked, err := s.isChunkedUpload(path)
	if err != nil {
		log.WithError(err).WithField("pathspec", path).Error("cannot apply chunk regex")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if isChunked {
		log.Info("upload is chunked")
		s.PutChunked(w, r)
		return
	}

	if s.requestHasContentRange(r) {
		log.Warning("Content-Range header is not accepted on PUT")
		w.WriteHeader(http.StatusNotImplemented)
		return
	}

	if s.requestSuffersFinderProblem(r) {
		if err := s.handleFinderRequest(w, r); err != nil {
			return
		}
	}

	info, err := s.metaDataController.ExamineObject(user, path)
	// if err is not found it is okay to continue
	if err != nil {
		if !s.isNotFoundError(err) {
			s.handlePutError(err, w, r)
			return
		}
	}

	if info != nil && info.Type != entities.ObjectTypeBLOB {
		log.Warn("object is not a blob")
		w.WriteHeader(http.StatusConflict)
		return
	}

	// if If-Match header contains an Etag we need to check it against the ETag from the server
	// so see if they match or not. If they do not match, StatusPreconditionFailed is returned
	if info != nil {
		clientETag := r.Header.Get("If-Match")
		serverETag := info.Extra.(ocsql.Extra).ETag
		if clientETag != "" {
			if err := s.handleIfMatchHeader(clientETag, serverETag, w, r); err != nil {
				return
			}
		}
	}

	readCloser := http.MaxBytesReader(w, r.Body, int64(s.conf.GetDirectives().WebDAV.UploadMaxFileSize))
	if err := s.dataController.UploadBLOB(user, path, readCloser, ""); err != nil {
		s.handlePutError(err, w, r)
		return
	}

	newInfo, err := s.metaDataController.ExamineObject(user, path)
	if err != nil {
		s.handlePutError(err, w, r)
		return
	}

	w.Header().Add("Content-Type", newInfo.MimeType)
	w.Header().Set("ETag", newInfo.Extra.(ocsql.Extra).ETag)
	w.Header().Set("OC-FileId", newInfo.Extra.(ocsql.Extra).ID)
	w.Header().Set("OC-ETag", newInfo.Extra.(ocsql.Extra).ETag)
	t := time.Unix(newInfo.ModTime/1000000000, newInfo.ModTime%1000000000)
	lastModifiedString := t.Format(time.RFC1123)
	w.Header().Set("Last-Modified", lastModifiedString)
	w.Header().Set("X-OC-MTime", "accepted")

	// if object did not exist, http code is 201, else 204.
	if info == nil {
		w.WriteHeader(http.StatusCreated)
		return
	}
	w.WriteHeader(http.StatusNoContent)
	return
}