<commit_before>// Package files is the HTTP frontend of the vfs package. It exposes
// an HTTP api to manipulate the filesystem and offer all the
// possibilities given by the vfs.
package files

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/cozy/cozy-stack/vfs"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/labstack/echo"
)

// TagSeparator is the character separating tags
const TagSeparator = ","

// ErrDocTypeInvalid is used when the document type sent is not
// recognized
var ErrDocTypeInvalid = errors.New("Invalid document type")

// CreationHandler handle all POST requests on /files/:dir-id
// aiming at creating a new document in the FS. Given the Type
// parameter of the request, it will either upload a new file or
// create a new directory.
//
// swagger:route POST /files/:dir-id files uploadFileOrCreateDir
func CreationHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	var doc jsonapi.Object
	var err error
	switch c.QueryParam("Type") {
	case vfs.FileType:
		doc, err = createFileHandler(c, instance)
	case vfs.DirType:
		doc, err = createDirHandler(c, instance)
	default:
		err = ErrDocTypeInvalid
	}

	if err != nil {
		return wrapVfsError(err)
	}

	return jsonapi.Data(c, http.StatusCreated, doc, nil)
}

func createFileHandler(c echo.Context, vfsC vfs.Context) (doc *vfs.FileDoc, err error) {
	doc, err = fileDocFromReq(
		c,
		c.QueryParam("Name"),
		c.Param("dir-id"),
		strings.Split(c.QueryParam("Tags"), TagSeparator),
	)
	if err != nil {
		return
	}

	file, err := vfs.CreateFile(vfsC, doc, nil)
	if err != nil {
		return
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(file, c.Request().Body)
	return
}

func createDirHandler(c echo.Context, vfsC vfs.Context) (*vfs.DirDoc, error) {
	tags := strings.Split(c.QueryParam("Tags"), TagSeparator)
	path := c.QueryParam("Path")

	if path != "" {
		if c.QueryParam("Recursive") == "true" {
			return vfs.MkdirAll(vfsC, path, tags)
		}
		return vfs.Mkdir(vfsC, path, tags)
	}

	name, dirID := c.QueryParam("Name"), c.Param("dir-id")
	doc, err := vfs.NewDirDoc(name, dirID, tags, nil)
	if err != nil {
		return nil, err
	}

	if err = vfs.CreateDir(vfsC, doc); err != nil {
		return nil, err
	}

	return doc, nil
}

// OverwriteFileContentHandler handles PUT requests on /files/:file-id
// to overwrite the content of a file given its identifier.
//
// swagger:route PUT /files/:file-id files overwriteFileContent
func OverwriteFileContentHandler(c echo.Context) (err error) {
	var instance = middlewares.GetInstance(c)
	var olddoc *vfs.FileDoc
	var newdoc *vfs.FileDoc

	olddoc, err = vfs.GetFileDoc(instance, c.Param("file-id"))
	if err != nil {
		return wrapVfsError(err)
	}

	newdoc, err = fileDocFromReq(
		c,
		olddoc.Name,
		olddoc.DirID,
		olddoc.Tags,
	)
	if err != nil {
		return wrapVfsError(err)
	}

	if err = checkIfMatch(c, olddoc.Rev()); err != nil {
		return wrapVfsError(err)
	}

	file, err := vfs.CreateFile(instance, newdoc, olddoc)
	if err != nil {
		return wrapVfsError(err)
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
		if err != nil {
			wrapVfsError(err)
			return
		}
		err = jsonapi.Data(c, http.StatusOK, newdoc, nil)
	}()

	_, err = io.Copy(file, c.Request().Body)
	return
}

// ModificationHandler handles PATCH requests on /files/:file-id and
// /files/metadata.
//
// It can be used to modify the file or directory metadata, as well as
// moving and renaming it in the filesystem.<commit_msg>Missing err assignment<commit_after>// Package files is the HTTP frontend of the vfs package. It exposes
// an HTTP api to manipulate the filesystem and offer all the
// possibilities given by the vfs.
package files

import (
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/cozy/cozy-stack/vfs"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/labstack/echo"
)

// TagSeparator is the character separating tags
const TagSeparator = ","

// ErrDocTypeInvalid is used when the document type sent is not
// recognized
var ErrDocTypeInvalid = errors.New("Invalid document type")

// CreationHandler handle all POST requests on /files/:dir-id
// aiming at creating a new document in the FS. Given the Type
// parameter of the request, it will either upload a new file or
// create a new directory.
//
// swagger:route POST /files/:dir-id files uploadFileOrCreateDir
func CreationHandler(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	var doc jsonapi.Object
	var err error
	switch c.QueryParam("Type") {
	case vfs.FileType:
		doc, err = createFileHandler(c, instance)
	case vfs.DirType:
		doc, err = createDirHandler(c, instance)
	default:
		err = ErrDocTypeInvalid
	}

	if err != nil {
		return wrapVfsError(err)
	}

	return jsonapi.Data(c, http.StatusCreated, doc, nil)
}

func createFileHandler(c echo.Context, vfsC vfs.Context) (doc *vfs.FileDoc, err error) {
	doc, err = fileDocFromReq(
		c,
		c.QueryParam("Name"),
		c.Param("dir-id"),
		strings.Split(c.QueryParam("Tags"), TagSeparator),
	)
	if err != nil {
		return
	}

	file, err := vfs.CreateFile(vfsC, doc, nil)
	if err != nil {
		return
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()

	_, err = io.Copy(file, c.Request().Body)
	return
}

func createDirHandler(c echo.Context, vfsC vfs.Context) (*vfs.DirDoc, error) {
	tags := strings.Split(c.QueryParam("Tags"), TagSeparator)
	path := c.QueryParam("Path")

	if path != "" {
		if c.QueryParam("Recursive") == "true" {
			return vfs.MkdirAll(vfsC, path, tags)
		}
		return vfs.Mkdir(vfsC, path, tags)
	}

	name, dirID := c.QueryParam("Name"), c.Param("dir-id")
	doc, err := vfs.NewDirDoc(name, dirID, tags, nil)
	if err != nil {
		return nil, err
	}

	if err = vfs.CreateDir(vfsC, doc); err != nil {
		return nil, err
	}

	return doc, nil
}

// OverwriteFileContentHandler handles PUT requests on /files/:file-id
// to overwrite the content of a file given its identifier.
//
// swagger:route PUT /files/:file-id files overwriteFileContent
func OverwriteFileContentHandler(c echo.Context) (err error) {
	var instance = middlewares.GetInstance(c)
	var olddoc *vfs.FileDoc
	var newdoc *vfs.FileDoc

	olddoc, err = vfs.GetFileDoc(instance, c.Param("file-id"))
	if err != nil {
		return wrapVfsError(err)
	}

	newdoc, err = fileDocFromReq(
		c,
		olddoc.Name,
		olddoc.DirID,
		olddoc.Tags,
	)
	if err != nil {
		return wrapVfsError(err)
	}

	if err = checkIfMatch(c, olddoc.Rev()); err != nil {
		return wrapVfsError(err)
	}

	file, err := vfs.CreateFile(instance, newdoc, olddoc)
	if err != nil {
		return wrapVfsError(err)
	}

	defer func() {
		if cerr := file.Close(); cerr != nil && err == nil {
			err = cerr
		}
		if err != nil {
			err = wrapVfsError(err)
			return
		}
		err = jsonapi.Data(c, http.StatusOK, newdoc, nil)
	}()

	_, err = io.Copy(file, c.Request().Body)
	return
}

// ModificationHandler handles PATCH requests on /files/:file-id and
// /files/metadata.
//
// It can be used to modify the file or directory metadata, as well as
// moving and renaming it in the filesystem.
{'fraction_non_alphanumeric': 0.1348937897442836, 'fraction_numerical': 0.0005411987552428629, 'mean_word_length': 3.622889305816135, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3665707', 'n_tokens_mistral': 2792, 'n_tokens_neox': 2667, 'n_words': 750}