diff --git a/server/.golangci.yml b/server/.golangci.yml index b7db5d489b..f6af94764c 100644 --- a/server/.golangci.yml +++ b/server/.golangci.yml @@ -83,7 +83,6 @@ issues: channels/app/bot_test.go|\ channels/app/brand.go|\ channels/app/config_test.go|\ - channels/app/export.go|\ channels/app/file.go|\ channels/app/file_bench_test.go|\ channels/app/file_test.go|\ diff --git a/server/channels/app/export.go b/server/channels/app/export.go index e12937912f..92707cc103 100644 --- a/server/channels/app/export.go +++ b/server/channels/app/export.go @@ -113,7 +113,11 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job if opts.CreateArchive { var err error zipWr = zip.NewWriter(writer) - defer zipWr.Close() + defer func() { + if err = zipWr.Close(); err != nil { + ctx.Logger().Error("Error closing zip writer", mlog.Err(err)) + } + }() writer, err = zipWr.Create("import.jsonl") if err != nil { return model.NewAppError("BulkExport", "app.export.zip_create.error", @@ -202,7 +206,7 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job emojisLen := len(emojiPaths) ctx.Logger().Info("Bulk export: exporting custom emojis") for _, emojiPath := range emojiPaths { - if err := a.exportFile(outPath, emojiPath, zipWr); err != nil { + if err := a.exportFile(ctx, outPath, emojiPath, zipWr); err != nil { return err } totalExportedEmojis++ @@ -217,7 +221,7 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job if opts.IncludeProfilePictures { ctx.Logger().Info("Bulk export: exporting profile pictures") for _, profilePicture := range profilePictures { - if err := a.exportFile(outPath, profilePicture, zipWr); err != nil { + if err := a.exportFile(ctx, outPath, profilePicture, zipWr); err != nil { ctx.Logger().Warn("Unable to export profile picture", mlog.String("profile_picture", profilePicture), mlog.Err(err)) } } @@ -231,7 +235,7 @@ func (a *App) exportAttachments(ctx request.CTX, attachments []imports.Attachmen totalExportedFiles := 0 attachmentsLen := len(attachments) for _, attachment := range attachments { - if err := a.exportFile(outPath, *attachment.Path, zipWr); err != nil { + if err := a.exportFile(ctx, outPath, *attachment.Path, zipWr); err != nil { return err } totalExportedFiles++ @@ -847,12 +851,13 @@ func (a *App) buildPostAttachments(postID string) ([]imports.AttachmentImportDat return attachments, nil } -func (a *App) exportCustomEmoji(c request.CTX, job *model.Job, writer io.Writer, outPath, exportDir string, exportFiles bool) ([]string, *model.AppError) { +func (a *App) exportCustomEmoji(rctx request.CTX, job *model.Job, writer io.Writer, outPath, exportDir string, exportFiles bool) ([]string, *model.AppError) { var emojiPaths []string pageNumber := 0 cnt := 0 for { - customEmojiList, err := a.GetEmojiList(c, pageNumber, 100, model.EmojiSortByName) + customEmojiList, err := a.GetEmojiList(rctx, pageNumber, 100, model.EmojiSortByName) + if err != nil { return nil, err } @@ -861,7 +866,7 @@ func (a *App) exportCustomEmoji(c request.CTX, job *model.Job, writer io.Writer, break } cnt += len(customEmojiList) - updateJobProgress(c.Logger(), a.Srv().Store(), job, "emojis_exported", cnt) + updateJobProgress(rctx.Logger(), a.Srv().Store(), job, "emojis_exported", cnt) pageNumber++ @@ -869,40 +874,45 @@ func (a *App) exportCustomEmoji(c request.CTX, job *model.Job, writer io.Writer, pathToDir := filepath.Join(outPath, exportDir) if exportFiles { if _, err := os.Stat(pathToDir); os.IsNotExist(err) { - os.Mkdir(pathToDir, os.ModePerm) - } - } - - for _, emoji := range customEmojiList { - emojiImagePath := filepath.Join(emojiPath, emoji.Id, "image") - filePath := filepath.Join(exportDir, emoji.Id, "image") - if exportFiles { - err := a.copyEmojiImages(emoji.Id, emojiImagePath, pathToDir) - if err != nil { - return nil, model.NewAppError("BulkExport", "app.export.export_custom_emoji.copy_emoji_images.error", nil, "err="+err.Error(), http.StatusBadRequest) + if err := os.Mkdir(pathToDir, os.ModePerm); err != nil { + return nil, model.NewAppError("BulkExport", "app.export.export_custom_emoji.mkdir.error", nil, "err="+err.Error(), http.StatusBadRequest) } - } else { - filePath = filepath.Join("emoji", emoji.Id, "image") - emojiPaths = append(emojiPaths, filePath) } - emojiImportObject := importLineFromEmoji(emoji, filePath) - if err := a.exportWriteLine(writer, emojiImportObject); err != nil { - return nil, err + for _, emoji := range customEmojiList { + emojiImagePath := filepath.Join(emojiPath, emoji.Id, "image") + filePath := filepath.Join(exportDir, emoji.Id, "image") + if exportFiles { + err := a.copyEmojiImages(rctx, emoji.Id, emojiImagePath, pathToDir) + if err != nil { + return nil, model.NewAppError("BulkExport", "app.export.export_custom_emoji.copy_emoji_images.error", nil, "err="+err.Error(), http.StatusBadRequest) + } + } else { + filePath = filepath.Join("emoji", emoji.Id, "image") + emojiPaths = append(emojiPaths, filePath) + } + + emojiImportObject := importLineFromEmoji(emoji, filePath) + if err := a.exportWriteLine(writer, emojiImportObject); err != nil { + return nil, err + } } } } - return emojiPaths, nil } // Copies emoji files from 'data/emoji' dir to 'exported_emoji' dir -func (a *App) copyEmojiImages(emojiId string, emojiImagePath string, pathToDir string) error { +func (a *App) copyEmojiImages(rctx request.CTX, emojiId string, emojiImagePath string, pathToDir string) error { fromPath, err := os.Open(emojiImagePath) if fromPath == nil || err != nil { - return errors.New("Error reading " + emojiImagePath + "file") + return errors.New("Error reading " + emojiImagePath + " file") } - defer fromPath.Close() + defer func() { + if err = fromPath.Close(); err != nil { + rctx.Logger().Error("Error closing source file", mlog.String("path", emojiImagePath), mlog.Err(err)) + } + }() emojiDir := pathToDir + "/" + emojiId @@ -920,8 +930,11 @@ func (a *App) copyEmojiImages(emojiId string, emojiImagePath string, pathToDir s if err != nil { return errors.New("Error creating the image file " + err.Error()) } - defer toPath.Close() - + defer func() { + if err = toPath.Close(); err != nil { + rctx.Logger().Error("Error closing destination file", mlog.String("path", emojiDir+"/image"), mlog.Err(err)) + } + }() _, err = io.Copy(toPath, fromPath) if err != nil { return errors.New("Error copying emojis " + err.Error()) @@ -1159,17 +1172,19 @@ func (a *App) exportAllDirectPosts(ctx request.CTX, job *model.Job, writer io.Wr return attachments, nil } -func (a *App) exportFile(outPath, filePath string, zipWr *zip.Writer) *model.AppError { - var wr io.Writer - var err error +func (a *App) exportFile(rctx request.CTX, outPath, filePath string, zipWr *zip.Writer) *model.AppError { rd, appErr := a.FileReader(filePath) if appErr != nil { return appErr } - defer rd.Close() + defer func() { + if err := rd.Close(); err != nil { + rctx.Logger().Error("Error closing file", mlog.Err(err)) + } + }() if zipWr != nil { - wr, err = zipWr.CreateHeader(&zip.FileHeader{ + wr, err := zipWr.CreateHeader(&zip.FileHeader{ Name: filepath.Join(model.ExportDataDir, filePath), Method: zip.Store, }) @@ -1177,24 +1192,33 @@ func (a *App) exportFile(outPath, filePath string, zipWr *zip.Writer) *model.App return model.NewAppError("exportFileAttachment", "app.export.export_attachment.zip_create_header.error", nil, "err="+err.Error(), http.StatusInternalServerError) } + + if _, err = io.Copy(wr, rd); err != nil { + return model.NewAppError("exportFileAttachment", "app.export.export_attachment.copy_file.error", + nil, "err="+err.Error(), http.StatusInternalServerError) + } } else { filePath = filepath.Join(outPath, model.ExportDataDir, filePath) - if err = os.MkdirAll(filepath.Dir(filePath), 0700); err != nil { + if err := os.MkdirAll(filepath.Dir(filePath), 0700); err != nil { return model.NewAppError("exportFileAttachment", "app.export.export_attachment.mkdirall.error", nil, "err="+err.Error(), http.StatusInternalServerError) } - wr, err = os.Create(filePath) + file, err := os.Create(filePath) if err != nil { return model.NewAppError("exportFileAttachment", "app.export.export_attachment.create_file.error", nil, "err="+err.Error(), http.StatusInternalServerError) } - defer wr.(*os.File).Close() - } + defer func() { + if err = file.Close(); err != nil { + rctx.Logger().Error("Error closing file", mlog.Err(err)) + } + }() - if _, err := io.Copy(wr, rd); err != nil { - return model.NewAppError("exportFileAttachment", "app.export.export_attachment.copy_file.error", - nil, "err="+err.Error(), http.StatusInternalServerError) + if _, err = io.Copy(file, rd); err != nil { + return model.NewAppError("exportFileAttachment", "app.export.export_attachment.copy_file.error", + nil, "err="+err.Error(), http.StatusInternalServerError) + } } return nil diff --git a/server/channels/app/export_test.go b/server/channels/app/export_test.go index cca72fc87a..bee76b43e6 100644 --- a/server/channels/app/export_test.go +++ b/server/channels/app/export_test.go @@ -160,7 +160,7 @@ func TestCopyEmojiImages(t *testing.T) { require.NoError(t, err) defer os.RemoveAll(filePath) - copyError := th.App.copyEmojiImages(emoji.Id, emojiImagePath, pathToDir) + copyError := th.App.copyEmojiImages(th.Context, emoji.Id, emojiImagePath, pathToDir) require.NoError(t, copyError) _, err = os.Stat(pathToDir + "/" + emoji.Id + "/image") diff --git a/server/i18n/en.json b/server/i18n/en.json index e292190f97..9653bf53b7 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -5114,6 +5114,10 @@ "id": "app.export.export_custom_emoji.copy_emoji_images.error", "translation": "Unable to copy custom emoji images" }, + { + "id": "app.export.export_custom_emoji.mkdir.error", + "translation": "Unable to create a directory for custom emoji images" + }, { "id": "app.export.export_write_line.io_writer.error", "translation": "An error occurred writing the export data."