mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 20:59:03 +00:00
Validate incoming GPS data (#951)
* Validate incoming GPS data and throw an error if it is incorrect, storing Null values * Extracted GPS data processing to function in external parser; optimized IF in internal parser; removed unnecessary comments; set exact values for positive test * Add the migration for removing existing invalid GPS data and its test; added better errors to asserts in the GPS validation test * Install FFmpeg and ExifTool on the API unit-test environment * Fix 'stripped.jpg', 'IncorrectGPS.jpg', and 'CorrectGPS.jpg' tests for the external parser * Optimized data validation in the external parser, returned error by the internal parser for invalid data, updated test to expect errors and handle them * Switched from error to log entry in case of incorrect GPS data, as error handling is not so transparent in the internal parser --------- Co-authored-by: Konstantin Koval
This commit is contained in:
@@ -63,6 +63,31 @@ func sanitizeEXIF(exif *models.MediaEXIF) {
|
||||
}
|
||||
}
|
||||
|
||||
func extractValidGpsData(fileInfo *exiftool.FileMetadata, media_path string) (*float64, *float64) {
|
||||
var GPSLat, GPSLong *float64
|
||||
|
||||
// GPS coordinates - longitude
|
||||
longitudeRaw, err := fileInfo.GetFloat("GPSLongitude")
|
||||
if err == nil {
|
||||
GPSLong = &longitudeRaw
|
||||
}
|
||||
|
||||
// GPS coordinates - latitude
|
||||
latitudeRaw, err := fileInfo.GetFloat("GPSLatitude")
|
||||
if err == nil {
|
||||
GPSLat = &latitudeRaw
|
||||
}
|
||||
|
||||
// GPS data validation
|
||||
if (GPSLat != nil && math.Abs(*GPSLat) > 90) || (GPSLong != nil && math.Abs(*GPSLong) > 90) {
|
||||
log.Printf(
|
||||
"Incorrect GPS data in the %s Exif data: %f, %f, while expected values between '-90' and '90'. Ignoring GPS data.",
|
||||
media_path, *GPSLat, *GPSLong)
|
||||
return nil, nil
|
||||
}
|
||||
return GPSLat, GPSLong
|
||||
}
|
||||
|
||||
func (p *externalExifParser) ParseExif(media_path string) (returnExif *models.MediaEXIF, returnErr error) {
|
||||
// ExifTool - No print conversion mode
|
||||
if p.et == nil {
|
||||
@@ -182,18 +207,10 @@ func (p *externalExifParser) ParseExif(media_path string) (returnExif *models.Me
|
||||
newExif.ExposureProgram = &expProgram
|
||||
}
|
||||
|
||||
// GPS coordinates - longitude
|
||||
longitudeRaw, err := fileInfo.GetFloat("GPSLongitude")
|
||||
if err == nil {
|
||||
// Get GPS data
|
||||
newExif.GPSLatitude, newExif.GPSLongitude = extractValidGpsData(&fileInfo, media_path)
|
||||
if (newExif.GPSLatitude != nil) && (newExif.GPSLongitude != nil) {
|
||||
found_exif = true
|
||||
newExif.GPSLongitude = &longitudeRaw
|
||||
}
|
||||
|
||||
// GPS coordinates - latitude
|
||||
latitudeRaw, err := fileInfo.GetFloat("GPSLatitude")
|
||||
if err == nil {
|
||||
found_exif = true
|
||||
newExif.GPSLatitude = &latitudeRaw
|
||||
}
|
||||
|
||||
if !found_exif {
|
||||
|
||||
@@ -3,6 +3,7 @@ package exif
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
@@ -142,8 +143,16 @@ func (p internalExifParser) ParseExif(media_path string) (returnExif *models.Med
|
||||
|
||||
lat, long, err := exifTags.LatLong()
|
||||
if err == nil {
|
||||
newExif.GPSLatitude = &lat
|
||||
newExif.GPSLongitude = &long
|
||||
if math.Abs(lat) > 90 || math.Abs(long) > 90 {
|
||||
returnExif = &newExif
|
||||
log.Printf(
|
||||
"Incorrect GPS data in the %s Exif data: %f, %f, while expected values between '-90' and '90'. Ignoring GPS data.",
|
||||
media_path, long, lat)
|
||||
return
|
||||
} else {
|
||||
newExif.GPSLatitude = &lat
|
||||
newExif.GPSLongitude = &long
|
||||
}
|
||||
}
|
||||
|
||||
returnExif = &newExif
|
||||
|
||||
@@ -43,11 +43,12 @@ func TestExifParsers(t *testing.T) {
|
||||
|
||||
images := []struct {
|
||||
path string
|
||||
assert func(t *testing.T, exif *models.MediaEXIF)
|
||||
assert func(t *testing.T, exif *models.MediaEXIF, err error)
|
||||
}{
|
||||
{
|
||||
path: "./test_data/bird.jpg",
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF) {
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, *exif.Description, "Photo of a Bird")
|
||||
assert.WithinDuration(t, *exif.DateShot, time.Unix(1336318784, 0).UTC(), time.Minute)
|
||||
assert.EqualValues(t, *exif.Camera, "Canon EOS 600D")
|
||||
@@ -65,16 +66,61 @@ func TestExifParsers(t *testing.T) {
|
||||
},
|
||||
{
|
||||
path: "./test_data/stripped.jpg",
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF) {
|
||||
assert.Nil(t, exif)
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF, err error) {
|
||||
assert.NoError(t, err)
|
||||
if exif == nil {
|
||||
assert.Nil(t, exif)
|
||||
} else {
|
||||
assert.Equal(t, 0, exif.ID)
|
||||
assert.True(t, exif.CreatedAt.IsZero())
|
||||
assert.True(t, exif.UpdatedAt.IsZero())
|
||||
assert.Nil(t, exif.Description)
|
||||
assert.Nil(t, exif.Camera)
|
||||
assert.Nil(t, exif.Maker)
|
||||
assert.Nil(t, exif.Lens)
|
||||
assert.Nil(t, exif.Exposure)
|
||||
assert.Nil(t, exif.Aperture)
|
||||
assert.Nil(t, exif.Iso)
|
||||
assert.Nil(t, exif.FocalLength)
|
||||
assert.Nil(t, exif.Flash)
|
||||
assert.Nil(t, exif.Orientation)
|
||||
assert.Nil(t, exif.ExposureProgram)
|
||||
assert.Nil(t, exif.GPSLatitude)
|
||||
assert.Nil(t, exif.GPSLongitude)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "./test_data/bad-exif.jpg",
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF) {
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, exif.Exposure)
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "./test_data/IncorrectGPS.jpg",
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF, err error) {
|
||||
assert.Nil(t, exif.GPSLatitude,
|
||||
"GPSLatitude expected to be NULL for an incorrect input data: %+v", exif.GPSLatitude)
|
||||
assert.Nil(t, exif.GPSLongitude,
|
||||
"GPSLongitude expected to be NULL for an incorrect input data: %+v", exif.GPSLongitude)
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "./test_data/CorrectGPS.jpg",
|
||||
assert: func(t *testing.T, exif *models.MediaEXIF, err error) {
|
||||
const precision = 1e-7
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, exif.GPSLatitude,
|
||||
"GPSLatitude expected to be Not-NULL for a correct input data: %+v", exif.GPSLatitude)
|
||||
assert.NotNil(t, exif.GPSLongitude,
|
||||
"GPSLongitude expected to be Not-NULL for a correct input data: %+v", exif.GPSLongitude)
|
||||
assert.InDelta(t, *exif.GPSLatitude, 44.478997222222226, precision,
|
||||
"The exact value from input data is expected: %+v", exif.GPSLatitude)
|
||||
assert.InDelta(t, *exif.GPSLongitude, 11.297922222222223, precision,
|
||||
"The exact value from input data is expected: %+v", exif.GPSLongitude)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, p := range parsers {
|
||||
@@ -90,9 +136,7 @@ func TestExifParsers(t *testing.T) {
|
||||
|
||||
exif, err := p.parser.ParseExif(img.path)
|
||||
|
||||
if assert.NoError(t, err) {
|
||||
img.assert(t, exif)
|
||||
}
|
||||
img.assert(t, exif, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
BIN
api/scanner/exif/test_data/CorrectGPS.jpg
Normal file
BIN
api/scanner/exif/test_data/CorrectGPS.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
api/scanner/exif/test_data/IncorrectGPS.jpg
Executable file
BIN
api/scanner/exif/test_data/IncorrectGPS.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 MiB |
Reference in New Issue
Block a user