mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-12-02 00:22:45 +00:00
98263a7de6
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
141 lines
3 KiB
Go
141 lines
3 KiB
Go
package exifremove
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"image/jpeg"
|
|
"image/png"
|
|
|
|
"github.com/dsoprea/go-exif"
|
|
jpegstructure "github.com/dsoprea/go-jpeg-image-structure"
|
|
pngstructure "github.com/dsoprea/go-png-image-structure"
|
|
"github.com/h2non/filetype"
|
|
)
|
|
|
|
func Remove(data []byte) ([]byte, error) {
|
|
|
|
const (
|
|
JpegMediaType = "jpeg"
|
|
PngMediaType = "png"
|
|
OtherMediaType = "other"
|
|
StartBytes = 0
|
|
EndBytes = 0
|
|
)
|
|
|
|
type MediaContext struct {
|
|
MediaType string
|
|
RootIfd *exif.Ifd
|
|
RawExif []byte
|
|
Media interface{}
|
|
}
|
|
|
|
filtered := []byte{}
|
|
|
|
head := make([]byte, 261)
|
|
_, err := bytes.NewReader(data).Read(head)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not read first 261 bytes of data: %s", err)
|
|
}
|
|
imagetype, err := filetype.Match(head)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error matching first 261 bytes of image to valid type: %s", err)
|
|
}
|
|
|
|
switch imagetype.MIME.Subtype {
|
|
case "jpeg":
|
|
jmp := jpegstructure.NewJpegMediaParser()
|
|
sl, err := jmp.ParseBytes(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, rawExif, err := sl.Exif()
|
|
if err != nil {
|
|
return data, nil
|
|
}
|
|
|
|
startExifBytes := StartBytes
|
|
endExifBytes := EndBytes
|
|
|
|
if bytes.Contains(data, rawExif) {
|
|
for i := 0; i < len(data)-len(rawExif); i++ {
|
|
if bytes.Compare(data[i:i+len(rawExif)], rawExif) == 0 {
|
|
startExifBytes = i
|
|
endExifBytes = i + len(rawExif)
|
|
break
|
|
}
|
|
}
|
|
fill := make([]byte, len(data[startExifBytes:endExifBytes]))
|
|
copy(data[startExifBytes:endExifBytes], fill)
|
|
}
|
|
|
|
filtered = data
|
|
|
|
_, err = jpeg.Decode(bytes.NewReader(filtered))
|
|
if err != nil {
|
|
return nil, errors.New("EXIF removal corrupted " + err.Error())
|
|
}
|
|
case "png":
|
|
pmp := pngstructure.NewPngMediaParser()
|
|
cs, err := pmp.ParseBytes(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, rawExif, err := cs.Exif()
|
|
if err != nil {
|
|
return data, nil
|
|
}
|
|
|
|
startExifBytes := StartBytes
|
|
endExifBytes := EndBytes
|
|
|
|
if bytes.Contains(data, rawExif) {
|
|
for i := 0; i < len(data)-len(rawExif); i++ {
|
|
if bytes.Compare(data[i:i+len(rawExif)], rawExif) == 0 {
|
|
startExifBytes = i
|
|
endExifBytes = i + len(rawExif)
|
|
break
|
|
}
|
|
}
|
|
fill := make([]byte, len(data[startExifBytes:endExifBytes]))
|
|
copy(data[startExifBytes:endExifBytes], fill)
|
|
}
|
|
|
|
filtered = data
|
|
|
|
chunks := readPNGChunks(bytes.NewReader(filtered))
|
|
|
|
for _, chunk := range chunks {
|
|
if !chunk.CRCIsValid() {
|
|
offset := int(chunk.Offset) + 8 + int(chunk.Length)
|
|
crc := chunk.CalculateCRC()
|
|
|
|
buf := new(bytes.Buffer)
|
|
binary.Write(buf, binary.BigEndian, crc)
|
|
crcBytes := buf.Bytes()
|
|
|
|
copy(filtered[offset:], crcBytes)
|
|
}
|
|
}
|
|
|
|
chunks = readPNGChunks(bytes.NewReader(filtered))
|
|
for _, chunk := range chunks {
|
|
if !chunk.CRCIsValid() {
|
|
return nil, errors.New("EXIF removal failed CRC")
|
|
}
|
|
}
|
|
|
|
_, err = png.Decode(bytes.NewReader(filtered))
|
|
if err != nil {
|
|
return nil, errors.New("EXIF removal corrupted " + err.Error())
|
|
}
|
|
default:
|
|
return nil, errors.New("filetype not recognised")
|
|
}
|
|
|
|
return filtered, nil
|
|
}
|