github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/office/office.go (about)

     1  package office
     2  
     3  import (
     4  	"net/http"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/cozy/cozy-stack/model/office"
    10  	"github.com/cozy/cozy-stack/model/permission"
    11  	"github.com/cozy/cozy-stack/model/sharing"
    12  	"github.com/cozy/cozy-stack/model/vfs"
    13  	"github.com/cozy/cozy-stack/pkg/consts"
    14  	"github.com/cozy/cozy-stack/pkg/jsonapi"
    15  	"github.com/cozy/cozy-stack/web/files"
    16  	"github.com/cozy/cozy-stack/web/middlewares"
    17  	"github.com/labstack/echo/v4"
    18  )
    19  
    20  // Open returns the parameters to open an office document.
    21  func Open(c echo.Context) error {
    22  	inst := middlewares.GetInstance(c)
    23  	fileID := c.Param("id")
    24  	open, err := sharing.OpenOffice(inst, fileID)
    25  	if err != nil {
    26  		return wrapError(err)
    27  	}
    28  	pdoc, err := middlewares.GetPermission(c)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	memberIndex, _ := strconv.Atoi(c.QueryParam("MemberIndex"))
    33  	readOnly := c.QueryParam("ReadOnly") == "true"
    34  
    35  	// If a directory is shared by link and contains an office document, the
    36  	// document can be opened with the same sharecode as the directory. The
    37  	// sharecode is also used to identify the member that previews a sharing.
    38  	if pdoc.Type == permission.TypeShareByLink || pdoc.Type == permission.TypeSharePreview {
    39  		code := middlewares.GetRequestToken(c)
    40  		open.AddShareByLinkCode(code)
    41  		if !readOnly {
    42  			readOnly = true
    43  			for _, perm := range pdoc.Permissions {
    44  				if perm.Type == consts.Files && !perm.Verbs.ReadOnly() {
    45  					readOnly = false
    46  				}
    47  			}
    48  		}
    49  	}
    50  
    51  	sharingID := c.QueryParam("SharingID") // Cozy to Cozy sharing
    52  	if err := open.CheckPermission(pdoc, sharingID); err != nil {
    53  		return middlewares.ErrForbidden
    54  	}
    55  
    56  	doc, err := open.GetResult(memberIndex, readOnly)
    57  	if err != nil {
    58  		return wrapError(err)
    59  	}
    60  
    61  	return jsonapi.Data(c, http.StatusOK, doc, nil)
    62  }
    63  
    64  func FileByKey(c echo.Context) error {
    65  	inst := middlewares.GetInstance(c)
    66  	file, err := office.EnsureFileForKey(inst, c.Param("key"))
    67  	if err != nil {
    68  		return wrapError(err)
    69  	}
    70  	if err := middlewares.AllowVFS(c, permission.GET, file); err != nil {
    71  		return err
    72  	}
    73  	doc := files.NewFile(file, inst)
    74  	return jsonapi.Data(c, http.StatusOK, doc, nil)
    75  }
    76  
    77  // Callback is the handler for OnlyOffice callback requests.
    78  // Cf https://api.onlyoffice.com/editors/callback
    79  func Callback(c echo.Context) error {
    80  	inst := middlewares.GetInstance(c)
    81  	var params office.CallbackParameters
    82  	if err := c.Bind(&params); err != nil {
    83  		inst.Logger().WithNamespace("office").
    84  			Warnf("Cannot bind callback parameters: %s", err)
    85  		return c.JSON(http.StatusBadRequest, echo.Map{"error": "Invalid request"})
    86  	}
    87  	header := c.Request().Header.Get(echo.HeaderAuthorization)
    88  	params.Token = strings.TrimPrefix(header, "Bearer ")
    89  
    90  	if err := office.Callback(inst, params); err != nil {
    91  		inst.Logger().WithNamespace("office").
    92  			Infof("Error on the callback: %s", err)
    93  		code := http.StatusInternalServerError
    94  		if httpError, ok := err.(*echo.HTTPError); ok {
    95  			code = httpError.Code
    96  		}
    97  		return c.JSON(code, echo.Map{"error": err.Error()})
    98  	}
    99  	return c.JSON(http.StatusOK, echo.Map{"error": 0})
   100  }
   101  
   102  // Routes sets the routing for the collaborative edition of office documents.
   103  func Routes(router *echo.Group) {
   104  	router.GET("/:id/open", Open)
   105  	router.POST("/keys/:key", FileByKey)
   106  	router.POST("/callback", Callback)
   107  }
   108  
   109  func wrapError(err error) *jsonapi.Error {
   110  	switch err {
   111  	case office.ErrNoServer, office.ErrInvalidFile, sharing.ErrCannotOpenFile:
   112  		return jsonapi.NotFound(err)
   113  	case office.ErrInternalServerError:
   114  		return jsonapi.InternalServerError(err)
   115  	case office.ErrInvalidKey:
   116  		return jsonapi.NotFound(err)
   117  	case os.ErrNotExist, vfs.ErrParentDoesNotExist, vfs.ErrParentInTrash:
   118  		return jsonapi.NotFound(err)
   119  	case sharing.ErrMemberNotFound:
   120  		return jsonapi.NotFound(err)
   121  	}
   122  	return jsonapi.InternalServerError(err)
   123  }