package app import ( "errors" "fmt" "io" "path/filepath" "strings" mmModel "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/focalboard/server/utils" "github.com/mattermost/mattermost-server/v6/shared/filestore" "github.com/mattermost/mattermost-server/v6/shared/mlog" ) const emptyString = "empty" var errEmptyFilename = errors.New("IsFileArchived: empty filename not allowed") var ErrFileNotFound = errors.New("file not found") func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (string, error) { // NOTE: File extension includes the dot fileExtension := strings.ToLower(filepath.Ext(filename)) if fileExtension == ".jpeg" { fileExtension = ".jpg" } createdFilename := utils.NewID(utils.IDTypeNone) fullFilename := fmt.Sprintf(`%s%s`, createdFilename, fileExtension) filePath := filepath.Join(teamID, rootID, fullFilename) fileSize, appErr := a.filesBackend.WriteFile(reader, filePath) if appErr != nil { return "", fmt.Errorf("unable to store the file in the files storage: %w", appErr) } now := utils.GetMillis() fileInfo := &mmModel.FileInfo{ Id: createdFilename[1:], CreatorId: "boards", PostId: emptyString, ChannelId: emptyString, CreateAt: now, UpdateAt: now, DeleteAt: 0, Path: emptyString, ThumbnailPath: emptyString, PreviewPath: emptyString, Name: filename, Extension: fileExtension, Size: fileSize, MimeType: emptyString, Width: 0, Height: 0, HasPreviewImage: false, MiniPreview: nil, Content: "", RemoteId: nil, } err := a.store.SaveFileInfo(fileInfo) if err != nil { return "", err } return fullFilename, nil } func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) { if len(filename) == 0 { return nil, errEmptyFilename } // filename is in the format 7. // we want to extract the part of this as this // will be the fileinfo id. parts := strings.Split(filename, ".") fileInfoID := parts[0][1:] fileInfo, err := a.store.GetFileInfo(fileInfoID) if err != nil { return nil, err } return fileInfo, nil } func (a *App) GetFileReader(teamID, rootID, filename string) (filestore.ReadCloseSeeker, error) { filePath := filepath.Join(teamID, rootID, filename) exists, err := a.filesBackend.FileExists(filePath) if err != nil { return nil, err } // FIXUP: Check the deprecated old location if teamID == "0" && !exists { oldExists, err2 := a.filesBackend.FileExists(filename) if err2 != nil { return nil, err2 } if oldExists { err2 := a.filesBackend.MoveFile(filename, filePath) if err2 != nil { a.logger.Error("ERROR moving file", mlog.String("old", filename), mlog.String("new", filePath), mlog.Err(err2), ) } else { a.logger.Debug("Moved file", mlog.String("old", filename), mlog.String("new", filePath), ) } } } else if !exists { return nil, ErrFileNotFound } reader, err := a.filesBackend.Reader(filePath) if err != nil { return nil, err } return reader, nil } func (a *App) MoveFile(channelID, teamID, boardID, filename string) error { oldPath := filepath.Join(channelID, boardID, filename) newPath := filepath.Join(teamID, boardID, filename) err := a.filesBackend.MoveFile(oldPath, newPath) if err != nil { a.logger.Error("ERROR moving file", mlog.String("old", oldPath), mlog.String("new", newPath), mlog.Err(err), ) return err } return nil }