diff --git a/server/channels/app/user.go b/server/channels/app/user.go index e142d9bebcb..b9a7c941ec0 100644 --- a/server/channels/app/user.go +++ b/server/channels/app/user.go @@ -1026,6 +1026,12 @@ func (a *App) AdjustImage(rctx request.CTX, file io.ReadSeeker) (*bytes.Buffer, return nil, model.NewAppError("SetProfileImage", "api.user.upload_profile_user.decode.app_error", nil, "", http.StatusBadRequest).Wrap(err) } + // Decode() reads the file to EOF; seek back to beginning so GetImageOrientation + // can read the EXIF data to determine the correct orientation. + if _, seekErr := file.Seek(0, io.SeekStart); seekErr != nil { + rctx.Logger().Warn("Failed to seek image file for orientation check", mlog.Err(seekErr)) + } + orientation, err := imaging.GetImageOrientation(file, format) if err != nil { rctx.Logger().Warn("Failed to get image orientation", mlog.Err(err)) diff --git a/server/channels/app/user_test.go b/server/channels/app/user_test.go index 74553a7f225..756b17733d0 100644 --- a/server/channels/app/user_test.go +++ b/server/channels/app/user_test.go @@ -8,6 +8,8 @@ import ( "database/sql" "encoding/json" "errors" + _ "image/jpeg" + _ "image/png" "net/http" "os" "path/filepath" @@ -140,11 +142,28 @@ func TestAdjustProfileImage(t *testing.T) { // default image should not require adjustment user := th.BasicUser - image, appErr := th.App.GetDefaultProfileImage(user) + defaultImg, appErr := th.App.GetDefaultProfileImage(user) require.Nil(t, appErr) - image2, appErr := th.App.AdjustImage(th.Context, bytes.NewReader(image)) + image2, appErr := th.App.AdjustImage(th.Context, bytes.NewReader(defaultImg)) require.Nil(t, appErr) - assert.Equal(t, image, image2.Bytes()) + assert.Equal(t, defaultImg, image2.Bytes()) + + t.Run("EXIF orientation is applied for rotated images", func(t *testing.T) { + // quadrants-orientation-8.png: 128×128 color quadrants with EXIF orientation 8. + // quadrants-orientation-1.png: same visual content already rotated, EXIF orientation 1. + rotated, err := testutils.ReadTestFile("exif_samples/quadrants-orientation-8.png") + require.NoError(t, err) + normal, err := testutils.ReadTestFile("exif_samples/quadrants-orientation-1.png") + require.NoError(t, err) + + rotatedResult, appErr := th.App.AdjustImage(th.Context, bytes.NewReader(rotated)) + require.Nil(t, appErr) + normalResult, appErr := th.App.AdjustImage(th.Context, bytes.NewReader(normal)) + require.Nil(t, appErr) + + assert.Equal(t, rotatedResult.Bytes(), normalResult.Bytes(), + "EXIF-rotated image should produce the same profile picture as the normally-oriented one") + }) } func TestUpdateUserToRestrictedDomain(t *testing.T) { diff --git a/server/tests/exif_samples/quadrants-orientation-1.png b/server/tests/exif_samples/quadrants-orientation-1.png new file mode 100644 index 00000000000..18894b239ca Binary files /dev/null and b/server/tests/exif_samples/quadrants-orientation-1.png differ diff --git a/server/tests/exif_samples/quadrants-orientation-8.png b/server/tests/exif_samples/quadrants-orientation-8.png new file mode 100644 index 00000000000..a2e4341af76 Binary files /dev/null and b/server/tests/exif_samples/quadrants-orientation-8.png differ