Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
302 lines
5.8 KiB
Go
302 lines
5.8 KiB
Go
// Copyright 2024 Bjørn Erik Pedersen
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package imagemeta
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
"sync"
|
|
)
|
|
|
|
type bytesAndReader struct {
|
|
b []byte
|
|
r *bytes.Reader
|
|
}
|
|
|
|
var bytesAndReaderPool = &sync.Pool{
|
|
New: func() any {
|
|
return &bytesAndReader{
|
|
b: make([]byte, 1024),
|
|
r: bytes.NewReader(nil),
|
|
}
|
|
},
|
|
}
|
|
|
|
func getBytesAndReader(length int) *bytesAndReader {
|
|
b := bytesAndReaderPool.Get().(*bytesAndReader)
|
|
if length > cap(b.b) {
|
|
b.b = make([]byte, length)
|
|
}
|
|
b.b = b.b[:length]
|
|
return b
|
|
}
|
|
|
|
func putBytesAndReader(br *bytesAndReader) {
|
|
br.b = br.b[:0]
|
|
bytesAndReaderPool.Put(br)
|
|
}
|
|
|
|
var errShortRead = errors.New("short read")
|
|
|
|
func newStreamReader(r io.Reader, byteOrder binary.ByteOrder) *streamReader {
|
|
return &streamReader{
|
|
r: r.(io.ReadSeeker),
|
|
byteOrder: byteOrder,
|
|
}
|
|
}
|
|
|
|
type closerFunc func() error
|
|
|
|
func (f closerFunc) Close() error {
|
|
return f()
|
|
}
|
|
|
|
type decoder interface {
|
|
decode() error
|
|
}
|
|
|
|
type fourCC [4]byte
|
|
|
|
type readerCloser interface {
|
|
io.ReadSeeker
|
|
io.Closer
|
|
}
|
|
|
|
// streamReader is a wrapper around a Reader that provides methods to read binary data.
|
|
// Note that this is not thread safe.
|
|
type streamReader struct {
|
|
// The current Reader.
|
|
r io.ReadSeeker
|
|
byteOrder binary.ByteOrder
|
|
|
|
buf []byte
|
|
|
|
isEOF bool
|
|
readErr error
|
|
readerOffset int64
|
|
}
|
|
|
|
var noopCloser closerFunc = func() error {
|
|
return nil
|
|
}
|
|
|
|
func (e *streamReader) otherByteOrder() binary.ByteOrder {
|
|
if e.byteOrder == binary.BigEndian {
|
|
return binary.LittleEndian
|
|
}
|
|
return binary.BigEndian
|
|
}
|
|
|
|
// 10 MB should be plenty for image metadata.
|
|
const maxBufSize = 10 * 1024 * 1024
|
|
|
|
// bufferedReader reads length bytes from the stream and returns a ReaderCloser.
|
|
// It's important to call Close on the ReaderCloser when done.
|
|
func (e *streamReader) bufferedReader(length int64) (readerCloser, error) {
|
|
if length > maxBufSize {
|
|
return nil, newInvalidFormatErrorf("length %d exceeds max %d", length, maxBufSize)
|
|
}
|
|
if length == 0 {
|
|
return struct {
|
|
io.ReadSeeker
|
|
io.Closer
|
|
}{
|
|
bytes.NewReader(nil),
|
|
noopCloser,
|
|
}, nil
|
|
}
|
|
|
|
if length < 0 {
|
|
return nil, newInvalidFormatErrorf("negative length")
|
|
}
|
|
|
|
br := getBytesAndReader(int(length))
|
|
|
|
_, err := io.ReadFull(e.r, br.b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var closer closerFunc = func() error {
|
|
putBytesAndReader(br)
|
|
return nil
|
|
}
|
|
|
|
br.r.Reset(br.b)
|
|
|
|
return struct {
|
|
io.ReadSeeker
|
|
io.Closer
|
|
}{
|
|
br.r,
|
|
closer,
|
|
}, nil
|
|
}
|
|
|
|
func (e *streamReader) allocateBuf(length int) {
|
|
if length > cap(e.buf) {
|
|
e.buf = make([]byte, length)
|
|
}
|
|
}
|
|
|
|
func (e *streamReader) pos() int64 {
|
|
n, _ := e.r.Seek(0, 1)
|
|
return n
|
|
}
|
|
|
|
func (e *streamReader) read1() uint8 {
|
|
return e.read1r(e.r)
|
|
}
|
|
|
|
func (e *streamReader) read1r(r io.Reader) uint8 {
|
|
const n = 1
|
|
e.readNFromRIntoBuf(n, r)
|
|
return e.buf[0]
|
|
}
|
|
|
|
func (e *streamReader) read2() uint16 {
|
|
return e.read2r(e.r)
|
|
}
|
|
|
|
func (e *streamReader) read2E() (uint16, error) {
|
|
const n = 2
|
|
if err := e.readNIntoBufE(n); err != nil {
|
|
return 0, err
|
|
}
|
|
return e.byteOrder.Uint16(e.buf[:n]), nil
|
|
}
|
|
|
|
func (e *streamReader) read2r(r io.Reader) uint16 {
|
|
const n = 2
|
|
e.readNFromRIntoBuf(n, r)
|
|
return e.byteOrder.Uint16(e.buf[:n])
|
|
}
|
|
|
|
func (e *streamReader) read4() uint32 {
|
|
const n = 4
|
|
e.readNIntoBuf(n)
|
|
return e.byteOrder.Uint32(e.buf[:n])
|
|
}
|
|
|
|
func (e *streamReader) read4r(r io.Reader) uint32 {
|
|
const n = 4
|
|
e.readNFromRIntoBuf(n, r)
|
|
return e.byteOrder.Uint32(e.buf[:n])
|
|
}
|
|
|
|
func (e *streamReader) read4sr(r io.Reader) int32 {
|
|
const n = 4
|
|
e.readNFromRIntoBuf(n, r)
|
|
return int32(e.byteOrder.Uint32(e.buf[:n]))
|
|
}
|
|
|
|
func (e *streamReader) read8r(r io.Reader) uint64 {
|
|
const n = 8
|
|
e.readNFromRIntoBuf(n, r)
|
|
return e.byteOrder.Uint64(e.buf[:n])
|
|
}
|
|
|
|
func (e *streamReader) readBytes(b []byte) error {
|
|
if _, err := io.ReadFull(e.r, b); err != nil {
|
|
e.stop(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// readNullTerminatedBytes reads a slice of bytes from the stream
|
|
// until a null byte is encountered.
|
|
// It returns the slice of bytes read and the number of bytes read.
|
|
// Note that max is the maximum number of bytes to read including the null byte.
|
|
func (e *streamReader) readNullTerminatedBytes(max int) ([]byte, int64) {
|
|
var b []byte
|
|
var n int64
|
|
for i := range max {
|
|
b = append(b, e.read1())
|
|
n++
|
|
if b[i] == 0 {
|
|
return b[:i], n
|
|
}
|
|
}
|
|
return b, n
|
|
}
|
|
|
|
// readBytesVolatile reads a slice of bytes from the stream
|
|
// which is not guaranteed to be valid after the next read.
|
|
func (e *streamReader) readBytesVolatile(n int) []byte {
|
|
e.readNIntoBuf(n)
|
|
return e.buf[:n]
|
|
}
|
|
|
|
func (e *streamReader) readBytesVolatileE(n int) ([]byte, error) {
|
|
err := e.readNIntoBufE(n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return e.buf[:n], nil
|
|
}
|
|
|
|
func (e *streamReader) readBytesFromRVolatile(n int, r io.Reader) []byte {
|
|
e.readNFromRIntoBuf(n, r)
|
|
return e.buf[:n]
|
|
}
|
|
|
|
func (e *streamReader) readNFromRIntoBuf(n int, r io.Reader) {
|
|
if err := e.readNFromRIntoBufE(n, r); err != nil {
|
|
e.stop(err)
|
|
}
|
|
}
|
|
|
|
func (e *streamReader) readNFromRIntoBufE(n int, r io.Reader) error {
|
|
e.allocateBuf(n)
|
|
n2, err := io.ReadFull(r, e.buf[:n])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != n2 {
|
|
return errShortRead
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *streamReader) readNIntoBuf(n int) {
|
|
e.readNFromRIntoBuf(n, e.r)
|
|
}
|
|
|
|
func (e *streamReader) readNIntoBufE(n int) error {
|
|
return e.readNFromRIntoBufE(n, e.r)
|
|
}
|
|
|
|
func (e *streamReader) preservePos(f func() error) error {
|
|
pos := e.pos()
|
|
err := f()
|
|
e.seek(pos)
|
|
return err
|
|
}
|
|
|
|
func (e *streamReader) seek(pos int64) {
|
|
_, err := e.r.Seek(pos, io.SeekStart)
|
|
if err != nil {
|
|
e.stop(err)
|
|
}
|
|
}
|
|
|
|
func (e *streamReader) skip(n int64) {
|
|
e.r.Seek(n, io.SeekCurrent)
|
|
}
|
|
|
|
func (e *streamReader) stop(err error) {
|
|
// Alow one silent EOF.
|
|
// This allows the client to not having to check for EOF on every read.
|
|
if err == io.EOF && !e.isEOF {
|
|
e.isEOF = true
|
|
return
|
|
}
|
|
if err != nil {
|
|
e.readErr = err
|
|
}
|
|
panic(errStop)
|
|
}
|