mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-18 16:59:53 +00:00
107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
package profilepicture
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/zitadel/exifremove/pkg/exifremove"
|
|
)
|
|
|
|
const maxRIFFSize = ^uint32(0)
|
|
|
|
// StripMetadata removes EXIF/XMP metadata from JPG, PNG and WEBP images
|
|
// Returns a *bytes.Reader so that storage backends receive a seekable reader with a known content length,
|
|
// which is required for correct checksum calculation on S3-compatible services
|
|
func StripMetadata(file io.Reader, ext string) (*bytes.Reader, error) {
|
|
data, err := io.ReadAll(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch strings.ToLower(ext) {
|
|
case "jpg", "jpeg":
|
|
stripped, err := exifremove.Remove(data)
|
|
if err == nil {
|
|
return bytes.NewReader(stripped), nil
|
|
}
|
|
return bytes.NewReader(data), nil
|
|
case "png":
|
|
stripped, err := exifremove.Remove(data)
|
|
if err == nil {
|
|
return bytes.NewReader(stripped), nil
|
|
}
|
|
return bytes.NewReader(data), nil
|
|
case "webp":
|
|
return bytes.NewReader(stripWEBPMetadata(data)), nil
|
|
default:
|
|
return bytes.NewReader(data), nil
|
|
}
|
|
}
|
|
|
|
func stripWEBPMetadata(data []byte) []byte {
|
|
// Check if the file contains the RIFF...WEBP header
|
|
if len(data) < 12 || string(data[:4]) != "RIFF" || string(data[8:12]) != "WEBP" {
|
|
return data
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
// Build the WEBP header
|
|
out.WriteString("RIFF")
|
|
out.Write([]byte{0, 0, 0, 0}) // Size will be filled at the end
|
|
out.WriteString("WEBP")
|
|
|
|
for pos := 12; pos < len(data); {
|
|
// Each RIFF chunk needs an 8 byte header
|
|
if pos+8 > len(data) {
|
|
return data
|
|
}
|
|
|
|
// Read the chunk type and payload size from the header
|
|
chunkType := string(data[pos : pos+4])
|
|
chunkSize := int(binary.LittleEndian.Uint32(data[pos+4 : pos+8]))
|
|
chunkEnd := pos + 8 + chunkSize
|
|
// Chunks with odd payload sizes include one additional byte at the end
|
|
if chunkSize%2 == 1 {
|
|
chunkEnd++
|
|
}
|
|
|
|
// End of chunk can't be more than the actual image data length
|
|
if chunkEnd > len(data) {
|
|
return data
|
|
}
|
|
|
|
// Remove chunks with the EXIF or XMP data type
|
|
if chunkType == "EXIF" || chunkType == "XMP " {
|
|
pos = chunkEnd
|
|
continue
|
|
}
|
|
|
|
// In the VP8X chunk there is a feature flag if the file contains any EXIF or XMP data
|
|
if chunkType == "VP8X" && chunkSize >= 10 {
|
|
// Copy the chunk because we don't want to modify the original one
|
|
chunk := make([]byte, chunkEnd-pos)
|
|
copy(chunk, data[pos:chunkEnd])
|
|
|
|
// Clear the Exif and XMP feature flags
|
|
chunk[8] &^= 0x0c
|
|
out.Write(chunk)
|
|
} else {
|
|
out.Write(data[pos:chunkEnd])
|
|
}
|
|
|
|
pos = chunkEnd
|
|
}
|
|
|
|
// WEBP image can max be 4GB in size
|
|
riffSize := out.Len() - 8
|
|
if riffSize < 0 || uint64(riffSize) > uint64(maxRIFFSize) {
|
|
return data
|
|
}
|
|
|
|
// Set the size in the header (byte 4-7)
|
|
binary.LittleEndian.PutUint32(out.Bytes()[4:8], uint32(riffSize))
|
|
return out.Bytes()
|
|
}
|