mattermost-community-enterp.../vendor/github.com/richardlehane/msoleps/msoleps.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
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>
2025-12-17 23:59:07 +09:00

245 lines
6.5 KiB
Go

// Copyright 2014 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package msoleps implements a reader for Microsoft OLE Property Set Data structures,
// (http://msdn.microsoft.com/en-au/library/dd942421.aspx) a generic persistence format
// for simple typed metadata
// Example:
// file, _ := os.Open("test/test.doc")
// defer file.Close()
// doc, err := mscfb.NewReader(file)
// if err != nil {
// log.Fatal(err)
// }
// props := msoleps.New()
// for entry, err := doc.Next(); err == nil; entry, err = doc.Next() {
// if msoleps.IsMSOLEPS(entry.Initial) {
// if oerr := props.Reset(doc); oerr != nil {
// log.Fatal(oerr)
// }
// for _, prop := range props.Property {
// fmt.Printf("Name: %s; Type: %s; Value: %v", prop.Name, prop.Type(), prop)
// }
// }
// }
package msoleps
import (
"bytes"
"encoding/binary"
"errors"
"io"
"github.com/richardlehane/msoleps/types"
)
var (
ErrFormat = errors.New("msoleps: not a valid msoleps stream")
ErrRead = errors.New("msoleps: error reading msoleps stream")
ErrSeek = errors.New("msoleps: can't seek backwards")
)
// IsMSOLEPS checks the first uint16 character of an mscfb name to test if it is a MSOLEPS stream
func IsMSOLEPS(i uint16) bool {
return i == 0x0005
}
// Reader is a reader for MS OLE Property Set Data structures
type Reader struct {
Property []*Property
b *bytes.Buffer
buf []byte
*propertySetStream
pSets [2]*propertySet
}
func New() *Reader {
r := &Reader{}
r.b = &bytes.Buffer{}
return r
}
func (r *Reader) Reset(rdr io.Reader) error {
r.b.Reset()
return r.start(rdr)
}
func NewFrom(rdr io.Reader) (*Reader, error) {
r := &Reader{}
r.b = &bytes.Buffer{}
return r, r.start(rdr)
}
func (r *Reader) start(rdr io.Reader) error {
if _, err := r.b.ReadFrom(rdr); err != nil {
return ErrRead
}
r.buf = r.b.Bytes()
// read the header (property stream details)
pss, err := makePropertySetStream(r.buf)
if err != nil {
return err
}
// sanity checks to find obvious errors
switch {
case pss.byteOrder != 0xFFFE, pss.version > 0x0001, pss.numPropertySets > 0x00000002:
return ErrFormat
}
r.propertySetStream = pss
// identify the property identifiers and offsets
ps, err := r.getPropertySet(pss.offsetA)
if err != nil {
return err
}
plen := len(ps.idsOffs)
r.pSets[0] = ps
var psb *propertySet
if pss.numPropertySets == 2 {
psb, err = r.getPropertySet(pss.offsetB)
if err != nil {
return err
}
r.pSets[1] = psb
plen += len(psb.idsOffs)
}
r.Property = make([]*Property, plen)
dicta := make(map[uint32]string)
if propertySet, ok := propertySets[pss.fmtidA]; ok {
for k, v := range propertySet {
dicta[k] = v
}
} else if ps.dict != nil {
dicta = ps.dict
}
dicta = addDefaults(dicta)
for i, v := range ps.idsOffs {
r.Property[i] = &Property{}
r.Property[i].Name = dicta[v.id]
// don't try to evaluate dictionary property
if v.id == 0x00000000 {
r.Property[i].T = types.Null{}
continue
}
t, _ := types.Evaluate(r.buf[int(v.offset+pss.offsetA):]) // ignore errors for now as not all types implemented
if t.Type() == "CodeString" {
cs := t.(*types.CodeString)
cs.SetId(ps.code)
t = types.Type(cs)
}
r.Property[i].T = t
}
if pss.numPropertySets != 2 {
return nil
}
dictb := make(map[uint32]string)
if propertySet, ok := propertySets[pss.fmtidB]; ok {
for k, v := range propertySet {
dictb[k] = v
}
} else if psb.dict != nil {
dictb = psb.dict
}
dictb = addDefaults(dictb)
for i, v := range psb.idsOffs {
i += len(ps.idsOffs)
r.Property[i] = &Property{}
r.Property[i].Name = dictb[v.id]
// don't try to evaluate dictionary property
if v.id == 0x00000000 {
r.Property[i].T = types.Null{}
continue
}
t, _ := types.Evaluate(r.buf[int(v.offset+pss.offsetB):]) // ignore errors for now as not all types implemented
if t.Type() == "CodeString" {
cs := t.(*types.CodeString)
cs.SetId(psb.code)
t = types.Type(cs)
}
r.Property[i].T = t
}
return nil
}
func (r *Reader) getPropertySet(o uint32) (*propertySet, error) {
pSet := &propertySet{}
pSet.size = binary.LittleEndian.Uint32(r.buf[int(o) : int(o)+4])
pSet.numProperties = binary.LittleEndian.Uint32(r.buf[int(o)+4 : int(o)+8])
pSet.idsOffs = make([]propertyIDandOffset, int(pSet.numProperties))
var dictOff uint32
for i := range pSet.idsOffs {
this := i*8 + 8 + int(o)
pSet.idsOffs[i].id = binary.LittleEndian.Uint32(r.buf[this : this+4])
pSet.idsOffs[i].offset = binary.LittleEndian.Uint32(r.buf[this+4 : this+8])
switch pSet.idsOffs[i].id {
case 0x00000000:
dictOff = pSet.idsOffs[i].offset
case 0x00000001:
off := int(pSet.idsOffs[i].offset + o)
pSet.code = types.CodePageID(binary.LittleEndian.Uint16(r.buf[off+4 : off+6]))
}
}
if dictOff > 0 {
var err error
pSet.dict, err = r.getDictionary(dictOff+o, pSet.code)
if err != nil {
return nil, err
}
}
return pSet, nil
}
func (r *Reader) getDictionary(o uint32, code types.CodePageID) (map[uint32]string, error) {
b := r.buf[int(o):]
e := 4
if len(b) < e {
return nil, ErrFormat
}
num := int(binary.LittleEndian.Uint32(b[:e]))
if num == 0 {
return nil, nil
}
dict := make(map[uint32]string)
for i := 0; i < num; i++ {
if len(b[e:]) < 8 {
return nil, ErrFormat
}
id, l := binary.LittleEndian.Uint32(b[e:e+4]), binary.LittleEndian.Uint32(b[e+4:e+8])
var s types.Type
var err error
if code == 0x04B0 {
var pad int
if l%2 != 0 {
pad = 2
}
s, err = types.MakeUnicode(b[e+4:])
if err != nil {
return nil, ErrFormat
}
e = e + 8 + pad + int(l)*2
} else {
s, err = types.MakeCodeString(b[e+4:])
if err != nil {
return nil, ErrFormat
}
cs := s.(*types.CodeString)
cs.SetId((code))
s = cs
e = e + 8 + int(l)
}
dict[id] = s.String()
}
return dict, nil
}