Working auth and photo json endpoint

Signed-off-by: Kris Nóva <kris@nivenly.com>
This commit is contained in:
Kris Nóva 2021-02-09 11:17:06 -08:00
parent ef275f97f4
commit e4323b6047
2032 changed files with 821464 additions and 52 deletions

View file

View file

@ -0,0 +1,21 @@
language: go
go:
- master
- stable
- "1.14"
- "1.13"
- "1.12"
env:
- GO111MODULE=on
install:
- go get -t ./...
script:
# v1
- go test -v .
# v2
- cd v2
- go test -v .
- cd ..
after_success:
- cd v2
- curl -s https://codecov.io/bash | bash

View file

@ -0,0 +1,9 @@
MIT LICENSE
Copyright 2020 Dustin Oprea
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,8 @@
[![Build Status](https://travis-ci.org/dsoprea/go-png-image-structure.svg?branch=master)](https://travis-ci.org/dsoprea/go-png-image-structure)
[![codecov](https://codecov.io/gh/dsoprea/go-png-image-structure/branch/master/graph/badge.svg)](https://codecov.io/gh/dsoprea/go-png-image-structure)
[![Go Report Card](https://goreportcard.com/badge/github.com/dsoprea/go-png-image-structure/v2)](https://goreportcard.com/report/github.com/dsoprea/go-png-image-structure/v2)
[![GoDoc](https://godoc.org/github.com/dsoprea/go-png-image-structure/v2?status.svg)](https://godoc.org/github.com/dsoprea/go-png-image-structure/v2)
## Overview
Parse raw PNG data into individual chunks. Parse/modify EXIF data and write an updated image.

View file

@ -0,0 +1,87 @@
package pngstructure
import (
"bytes"
"fmt"
"encoding/binary"
log "github.com/dsoprea/go-logging"
)
type ChunkDecoder struct {
}
func NewChunkDecoder() *ChunkDecoder {
return new(ChunkDecoder)
}
func (cd *ChunkDecoder) Decode(c *Chunk) (decoded interface{}, err error) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.Panic(err)
}
}()
switch c.Type {
case "IHDR":
ihdr, err := cd.decodeIHDR(c)
log.PanicIf(err)
return ihdr, nil
}
// We don't decode this particular type.
return nil, nil
}
type ChunkIHDR struct {
Width uint32
Height uint32
BitDepth uint8
ColorType uint8
CompressionMethod uint8
FilterMethod uint8
InterlaceMethod uint8
}
func (ihdr *ChunkIHDR) String() string {
return fmt.Sprintf("IHDR<WIDTH=(%d) HEIGHT=(%d) DEPTH=(%d) COLOR-TYPE=(%d) COMP-METHOD=(%d) FILTER-METHOD=(%d) INTRLC-METHOD=(%d)>", ihdr.Width, ihdr.Height, ihdr.BitDepth, ihdr.ColorType, ihdr.CompressionMethod, ihdr.FilterMethod, ihdr.InterlaceMethod)
}
func (cd *ChunkDecoder) decodeIHDR(c *Chunk) (ihdr *ChunkIHDR, err error) {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.Panic(err)
}
}()
b := bytes.NewBuffer(c.Data)
ihdr = new(ChunkIHDR)
err = binary.Read(b, binary.BigEndian, &ihdr.Width)
log.PanicIf(err)
err = binary.Read(b, binary.BigEndian, &ihdr.Height)
log.PanicIf(err)
err = binary.Read(b, binary.BigEndian, &ihdr.BitDepth)
log.PanicIf(err)
err = binary.Read(b, binary.BigEndian, &ihdr.ColorType)
log.PanicIf(err)
err = binary.Read(b, binary.BigEndian, &ihdr.CompressionMethod)
log.PanicIf(err)
err = binary.Read(b, binary.BigEndian, &ihdr.FilterMethod)
log.PanicIf(err)
err = binary.Read(b, binary.BigEndian, &ihdr.InterlaceMethod)
log.PanicIf(err)
return ihdr, nil
}

View file

@ -0,0 +1,14 @@
module github.com/dsoprea/go-png-image-structure
go 1.13
// Development only
// replace github.com/dsoprea/go-utility => ../go-utility
// replace github.com/dsoprea/go-exif/v2 => ../go-exif/v2
require (
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 // indirect
)

View file

@ -0,0 +1,58 @@
github.com/dsoprea/go-exif v0.0.0-20200502203340-6aea10b45f4c h1:PoW4xOq3wUrX8ghNGiJFzem7mwd+mY/Xkgo0Z8AwcNY=
github.com/dsoprea/go-exif/v2 v2.0.0-20200113184400-90b66e3d1158 h1:DzXu3hw2xqwfd/R0QflKY/ixfrLDbMFk30D/CyJMTAM=
github.com/dsoprea/go-exif/v2 v2.0.0-20200113184400-90b66e3d1158/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v2 v2.0.0-20200113231207-0bbb7a3584f7 h1:+koSu4BOaLu+dy50WEj+ltzEjMzK5evzPawKxgIQerw=
github.com/dsoprea/go-exif/v2 v2.0.0-20200113231207-0bbb7a3584f7/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4 h1:bVaiYo8amn7Lu93sz6mTlYB3EtLG9aRcMnM1Eps8fmM=
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
github.com/dsoprea/go-exif/v2 v2.0.0-20200502203340-6aea10b45f4c h1:fQNBTLqL4u7yhl5AqW6dGG5RSxGuRhzXLnBVDR2uUuE=
github.com/dsoprea/go-exif/v2 v2.0.0-20200502203340-6aea10b45f4c/go.mod h1:YXOyDqCYjBuHHRw4JIGPgOgMit0IDvVSjjhsqOAFTYQ=
github.com/dsoprea/go-exif/v2 v2.0.0-20200517080529-c9be4b30b064 h1:V7CH/kZImE6Lf27H4DS5PG7qzBkf774GIXUuM31vVNA=
github.com/dsoprea/go-exif/v2 v2.0.0-20200517080529-c9be4b30b064/go.mod h1:YXOyDqCYjBuHHRw4JIGPgOgMit0IDvVSjjhsqOAFTYQ=
github.com/dsoprea/go-exif/v2 v2.0.0-20200517234148-29b0ff22564b h1:Uvnq5XTzlscGvQl3IwysBUVdSb1VUmqIvHrOv4x4UCE=
github.com/dsoprea/go-exif/v2 v2.0.0-20200517234148-29b0ff22564b/go.mod h1:STKu28lNwOeoO0bieAKJ3zQYkUbZ2hivI6qjjGVW0sc=
github.com/dsoprea/go-exif/v2 v2.0.0-20200518001653-d0d0f14dea03 h1:r+aCxLEe6uGDC/NJCpA3WQJ+C7WJ0chzfHKgy173fug=
github.com/dsoprea/go-exif/v2 v2.0.0-20200518001653-d0d0f14dea03/go.mod h1:STKu28lNwOeoO0bieAKJ3zQYkUbZ2hivI6qjjGVW0sc=
github.com/dsoprea/go-exif/v2 v2.0.0-20200520183328-015129a9efd5 h1:iKMxnRjFqQQYKEpdsjFDMV2+VUAncTLT4ofcCiQpDvo=
github.com/dsoprea/go-exif/v2 v2.0.0-20200520183328-015129a9efd5/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4 h1:Mg7pY7kxDQD2Bkvr1N+XW4BESSIQ7tTTR7Vv+Gi2CsM=
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 h1:VGFnZAcLwPpt1sHlAxml+pGLZz9A2s+K/s1YNhPC91Y=
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
github.com/dsoprea/go-logging v0.0.0-20200502201358-170ff607885f h1:FonKAuW3PmNtqk9tOR+Z7bnyQHytmnZBCmm5z1PQMss=
github.com/dsoprea/go-logging v0.0.0-20200502201358-170ff607885f/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d h1:F/7L5wr/fP/SKeO5HuMlNEX9Ipyx2MbH2rV9G4zJRpk=
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
github.com/dsoprea/go-utility v0.0.0-20200322055224-4dc0f716e7d0 h1:zFSboMDWXX2UX7/k/mCHBjZhHlaFMx0HmtUE37HABsA=
github.com/dsoprea/go-utility v0.0.0-20200322055224-4dc0f716e7d0/go.mod h1:xv8CVgDmI/Shx/X+EUXyXELVnH5lSRUYRija52OHq7E=
github.com/dsoprea/go-utility v0.0.0-20200322154813-27f0b0d142d7 h1:DJhSHW0odJrW5wR9MU6ry5S+PsxuRXA165KFaiB+cZo=
github.com/dsoprea/go-utility v0.0.0-20200322154813-27f0b0d142d7/go.mod h1:xv8CVgDmI/Shx/X+EUXyXELVnH5lSRUYRija52OHq7E=
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176 h1:CfXezFYb2STGOd1+n1HshvE191zVx+QX3A1nML5xxME=
github.com/dsoprea/go-utility v0.0.0-20200512094054-1abbbc781176/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.0.2 h1:xMxH9j2fNg/L4hLn/4y3M0IUsn0M6Wbu/Uh9QlOfBh4=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec h1:lJwO/92dFXWeXOZdoGXgptLmNLwynMSHUmU6besqtiw=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View file

@ -0,0 +1,106 @@
package pngstructure
import (
"bufio"
"bytes"
"io"
"os"
log "github.com/dsoprea/go-logging"
riimage "github.com/dsoprea/go-utility/image"
)
// PngMediaParser knows how to parse a PNG stream.
type PngMediaParser struct {
}
// NewPngMediaParser returns a new `PngMediaParser` struct.
func NewPngMediaParser() *PngMediaParser {
// TODO(dustin): Add test
return new(PngMediaParser)
}
// Parse parses a PNG stream given a `io.ReadSeeker`.
func (pmp *PngMediaParser) Parse(rs io.ReadSeeker, size int) (mc riimage.MediaContext, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test
ps := NewPngSplitter()
err = ps.readHeader(rs)
log.PanicIf(err)
s := bufio.NewScanner(rs)
// Since each segment can be any size, our buffer must be allowed to grow
// as large as the file.
buffer := []byte{}
s.Buffer(buffer, size)
s.Split(ps.Split)
for s.Scan() != false {
}
log.PanicIf(s.Err())
return ps.Chunks(), nil
}
// ParseFile parses a PNG stream given a file-path.
func (pmp *PngMediaParser) ParseFile(filepath string) (mc riimage.MediaContext, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
f, err := os.Open(filepath)
log.PanicIf(err)
defer f.Close()
stat, err := f.Stat()
log.PanicIf(err)
size := stat.Size()
chunks, err := pmp.Parse(f, int(size))
log.PanicIf(err)
return chunks, nil
}
// ParseBytes parses a PNG stream given a byte-slice.
func (pmp *PngMediaParser) ParseBytes(data []byte) (mc riimage.MediaContext, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// TODO(dustin): Add test
br := bytes.NewReader(data)
chunks, err := pmp.Parse(br, len(data))
log.PanicIf(err)
return chunks, nil
}
// LooksLikeFormat returns a boolean indicating whether the stream looks like a
// PNG image.
func (pmp *PngMediaParser) LooksLikeFormat(data []byte) bool {
return bytes.Compare(data[:len(PngSignature)], PngSignature[:]) == 0
}
var (
// Enforce interface conformance.
_ riimage.MediaParser = new(PngMediaParser)
)

414
vendor/github.com/dsoprea/go-png-image-structure/png.go generated vendored Normal file
View file

@ -0,0 +1,414 @@
package pngstructure
import (
"bytes"
"errors"
"fmt"
"io"
"encoding/binary"
"hash/crc32"
"github.com/dsoprea/go-exif/v2"
log "github.com/dsoprea/go-logging"
riimage "github.com/dsoprea/go-utility/image"
)
var (
PngSignature = [8]byte{137, 'P', 'N', 'G', '\r', '\n', 26, '\n'}
EXifChunkType = "eXIf"
IHDRChunkType = "IHDR"
)
var (
ErrNotPng = errors.New("not png data")
ErrNoExif = errors.New("file does not have EXIF")
ErrCrcFailure = errors.New("crc failure")
)
// ChunkSlice encapsulates a slice of chunks.
type ChunkSlice struct {
chunks []*Chunk
}
func NewChunkSlice(chunks []*Chunk) *ChunkSlice {
if len(chunks) == 0 {
log.Panicf("ChunkSlice must be initialized with at least one chunk (IHDR)")
} else if chunks[0].Type != IHDRChunkType {
log.Panicf("first chunk in any ChunkSlice must be an IHDR")
}
return &ChunkSlice{
chunks: chunks,
}
}
func NewPngChunkSlice() *ChunkSlice {
ihdrChunk := &Chunk{
Type: IHDRChunkType,
}
ihdrChunk.UpdateCrc32()
return NewChunkSlice([]*Chunk{ihdrChunk})
}
func (cs *ChunkSlice) String() string {
return fmt.Sprintf("ChunkSlize<LEN=(%d)>", len(cs.chunks))
}
// Chunks exposes the actual slice.
func (cs *ChunkSlice) Chunks() []*Chunk {
return cs.chunks
}
// Write encodes and writes all chunks.
func (cs *ChunkSlice) WriteTo(w io.Writer) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
_, err = w.Write(PngSignature[:])
log.PanicIf(err)
// TODO(dustin): !! This should respect the safe-to-copy characteristic.
for _, c := range cs.chunks {
_, err := c.WriteTo(w)
log.PanicIf(err)
}
return nil
}
// Index returns a map of chunk types to chunk slices, grouping all like chunks.
func (cs *ChunkSlice) Index() (index map[string][]*Chunk) {
index = make(map[string][]*Chunk)
for _, c := range cs.chunks {
if grouped, found := index[c.Type]; found == true {
index[c.Type] = append(grouped, c)
} else {
index[c.Type] = []*Chunk{c}
}
}
return index
}
// FindExif returns the the segment that hosts the EXIF data.
func (cs *ChunkSlice) FindExif() (chunk *Chunk, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
index := cs.Index()
if chunks, found := index[EXifChunkType]; found == true {
return chunks[0], nil
}
log.Panic(ErrNoExif)
// Never called.
return nil, nil
}
// Exif returns an `exif.Ifd` instance with the existing tags.
func (cs *ChunkSlice) Exif() (rootIfd *exif.Ifd, data []byte, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
chunk, err := cs.FindExif()
log.PanicIf(err)
im := exif.NewIfdMappingWithStandard()
ti := exif.NewTagIndex()
// TODO(dustin): Refactor and support `exif.GetExifData()`.
_, index, err := exif.Collect(im, ti, chunk.Data)
log.PanicIf(err)
return index.RootIfd, chunk.Data, nil
}
// ConstructExifBuilder returns an `exif.IfdBuilder` instance (needed for
// modifying) preloaded with all existing tags.
func (cs *ChunkSlice) ConstructExifBuilder() (rootIb *exif.IfdBuilder, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
rootIfd, _, err := cs.Exif()
log.PanicIf(err)
ib := exif.NewIfdBuilderFromExistingChain(rootIfd)
return ib, nil
}
// SetExif encodes and sets EXIF data into this segment.
func (cs *ChunkSlice) SetExif(ib *exif.IfdBuilder) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// Encode.
ibe := exif.NewIfdByteEncoder()
exifData, err := ibe.EncodeToExif(ib)
log.PanicIf(err)
// Set.
exifChunk, err := cs.FindExif()
if err == nil {
// EXIF chunk already exists.
exifChunk.Data = exifData
exifChunk.Length = uint32(len(exifData))
} else {
if log.Is(err, ErrNoExif) != true {
log.Panic(err)
}
// Add a EXIF chunk for the first time.
exifChunk = &Chunk{
Type: EXifChunkType,
Data: exifData,
Length: uint32(len(exifData)),
}
// Insert it after the IHDR chunk (it's a reliably appropriate place to
// put it).
cs.chunks = append(cs.chunks[:1], append([]*Chunk{exifChunk}, cs.chunks[1:]...)...)
}
exifChunk.UpdateCrc32()
return nil
}
// PngSplitter hosts the princpal `Split()` method uses by `bufio.Scanner`.
type PngSplitter struct {
chunks []*Chunk
currentOffset int
doCheckCrc bool
crcErrors []string
}
func (ps *PngSplitter) Chunks() *ChunkSlice {
return NewChunkSlice(ps.chunks)
}
func (ps *PngSplitter) DoCheckCrc(doCheck bool) {
ps.doCheckCrc = doCheck
}
func (ps *PngSplitter) CrcErrors() []string {
return ps.crcErrors
}
func NewPngSplitter() *PngSplitter {
return &PngSplitter{
chunks: make([]*Chunk, 0),
doCheckCrc: true,
crcErrors: make([]string, 0),
}
}
// Chunk describes a single chunk.
type Chunk struct {
Offset int
Length uint32
Type string
Data []byte
Crc uint32
}
func (c *Chunk) String() string {
return fmt.Sprintf("Chunk<OFFSET=(%d) LENGTH=(%d) TYPE=[%s] CRC=(%d)>", c.Offset, c.Length, c.Type, c.Crc)
}
func calculateCrc32(chunk *Chunk) uint32 {
c := crc32.NewIEEE()
c.Write([]byte(chunk.Type))
c.Write(chunk.Data)
return c.Sum32()
}
func (c *Chunk) UpdateCrc32() {
c.Crc = calculateCrc32(c)
}
func (c *Chunk) CheckCrc32() bool {
expected := calculateCrc32(c)
return c.Crc == expected
}
// Bytes encodes and returns the bytes for this chunk.
func (c *Chunk) Bytes() []byte {
defer func() {
if state := recover(); state != nil {
err := log.Wrap(state.(error))
log.Panic(err)
}
}()
if len(c.Data) != int(c.Length) {
log.Panicf("length of data not correct")
}
preallocated := make([]byte, 0, 4+4+c.Length+4)
b := bytes.NewBuffer(preallocated)
err := binary.Write(b, binary.BigEndian, c.Length)
log.PanicIf(err)
_, err = b.Write([]byte(c.Type))
log.PanicIf(err)
if c.Data != nil {
_, err = b.Write(c.Data)
log.PanicIf(err)
}
err = binary.Write(b, binary.BigEndian, c.Crc)
log.PanicIf(err)
return b.Bytes()
}
// Write encodes and writes the bytes for this chunk.
func (c *Chunk) WriteTo(w io.Writer) (count int, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
if len(c.Data) != int(c.Length) {
log.Panicf("length of data not correct")
}
err = binary.Write(w, binary.BigEndian, c.Length)
log.PanicIf(err)
_, err = w.Write([]byte(c.Type))
log.PanicIf(err)
_, err = w.Write(c.Data)
log.PanicIf(err)
err = binary.Write(w, binary.BigEndian, c.Crc)
log.PanicIf(err)
return 4 + len(c.Type) + len(c.Data) + 4, nil
}
// readHeader verifies that the PNG header bytes appear next.
func (ps *PngSplitter) readHeader(r io.Reader) (err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
len_ := len(PngSignature)
header := make([]byte, len_)
_, err = r.Read(header)
log.PanicIf(err)
ps.currentOffset += len_
if bytes.Compare(header, PngSignature[:]) != 0 {
log.Panic(ErrNotPng)
}
return nil
}
// Split fulfills the `bufio.SplitFunc` function definition for
// `bufio.Scanner`.
func (ps *PngSplitter) Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// We might have more than one chunk's worth, and, if `atEOF` is true, we
// won't be called again. We'll repeatedly try to read additional chunks,
// but, when we run out of the data we were given then we'll return the
// number of bytes fo rthe chunks we've already completely read. Then,
// we'll be called again from theend ofthose bytes, at which point we'll
// indicate that we don't yet have enough for another chunk, and we should
// be then called with more.
for {
len_ := len(data)
if len_ < 8 {
return advance, nil, nil
}
length := binary.BigEndian.Uint32(data[:4])
type_ := string(data[4:8])
chunkSize := (8 + int(length) + 4)
if len_ < chunkSize {
return advance, nil, nil
}
crcIndex := 8 + length
crc := binary.BigEndian.Uint32(data[crcIndex : crcIndex+4])
content := make([]byte, length)
copy(content, data[8:8+length])
c := &Chunk{
Length: length,
Type: type_,
Data: content,
Crc: crc,
Offset: ps.currentOffset,
}
ps.chunks = append(ps.chunks, c)
if c.CheckCrc32() == false {
ps.crcErrors = append(ps.crcErrors, type_)
if ps.doCheckCrc == true {
log.Panic(ErrCrcFailure)
}
}
advance += chunkSize
ps.currentOffset += chunkSize
data = data[chunkSize:]
}
return advance, nil, nil
}
var (
// Enforce interface conformance.
_ riimage.MediaContext = new(ChunkSlice)
)

View file

@ -0,0 +1,64 @@
package pngstructure
import (
"os"
"path"
log "github.com/dsoprea/go-logging"
)
var (
assetsPath = ""
)
func getModuleRootPath() string {
moduleRootPath := os.Getenv("PNG_MODULE_ROOT_PATH")
if moduleRootPath != "" {
return moduleRootPath
}
currentWd, err := os.Getwd()
log.PanicIf(err)
currentPath := currentWd
visited := make([]string, 0)
for {
tryStampFilepath := path.Join(currentPath, ".MODULE_ROOT")
_, err := os.Stat(tryStampFilepath)
if err != nil && os.IsNotExist(err) != true {
log.Panic(err)
} else if err == nil {
break
}
visited = append(visited, tryStampFilepath)
currentPath = path.Dir(currentPath)
if currentPath == "/" {
log.Panicf("could not find module-root: %v", visited)
}
}
return currentPath
}
func getTestAssetsPath() string {
if assetsPath == "" {
moduleRootPath := getModuleRootPath()
assetsPath = path.Join(moduleRootPath, "assets")
}
return assetsPath
}
func getTestBasicImageFilepath() string {
assetsPath := getTestAssetsPath()
return path.Join(assetsPath, "libpng.png")
}
func getTestExifImageFilepath() string {
assetsPath := getTestAssetsPath()
return path.Join(assetsPath, "exif.png")
}

View file

@ -0,0 +1,65 @@
package pngstructure
import (
"bytes"
"fmt"
log "github.com/dsoprea/go-logging"
)
func DumpBytes(data []byte) {
fmt.Printf("DUMP: ")
for _, x := range data {
fmt.Printf("%02x ", x)
}
fmt.Printf("\n")
}
func DumpBytesClause(data []byte) {
fmt.Printf("DUMP: ")
fmt.Printf("[]byte { ")
for i, x := range data {
fmt.Printf("0x%02x", x)
if i < len(data)-1 {
fmt.Printf(", ")
}
}
fmt.Printf(" }\n")
}
func DumpBytesToString(data []byte) string {
b := new(bytes.Buffer)
for i, x := range data {
_, err := b.WriteString(fmt.Sprintf("%02x", x))
log.PanicIf(err)
if i < len(data)-1 {
_, err := b.WriteRune(' ')
log.PanicIf(err)
}
}
return b.String()
}
func DumpBytesClauseToString(data []byte) string {
b := new(bytes.Buffer)
for i, x := range data {
_, err := b.WriteString(fmt.Sprintf("0x%02x", x))
log.PanicIf(err)
if i < len(data)-1 {
_, err := b.WriteString(", ")
log.PanicIf(err)
}
}
return b.String()
}