You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

315 lines
7.4 KiB

package php_serialize
import (
"bytes"
"fmt"
"log"
"strconv"
"strings"
)
const UNSERIALIZABLE_OBJECT_MAX_LEN = 10 * 1024 * 1024 * 1024
func UnSerialize(s string) (PhpValue, error) {
decoder := NewUnSerializer(s)
decoder.SetSerializedDecodeFunc(SerializedDecodeFunc(UnSerialize))
return decoder.Decode()
}
type UnSerializer struct {
source string
r *strings.Reader
lastErr error
decodeFunc SerializedDecodeFunc
}
func NewUnSerializer(data string) *UnSerializer {
return &UnSerializer{
source: data,
}
}
func (self *UnSerializer) SetReader(r *strings.Reader) {
self.r = r
}
func (self *UnSerializer) SetSerializedDecodeFunc(f SerializedDecodeFunc) {
self.decodeFunc = f
}
func (self *UnSerializer) Decode() (PhpValue, error) {
if self.r == nil {
self.r = strings.NewReader(self.source)
}
var value PhpValue
if token, _, err := self.r.ReadRune(); err == nil {
switch token {
default:
self.saveError(fmt.Errorf("php_serialize: Unknown token %#U", token))
case TOKEN_NULL:
value = self.decodeNull()
case TOKEN_BOOL:
value = self.decodeBool()
case TOKEN_INT:
value = self.decodeNumber(false)
case TOKEN_FLOAT:
value = self.decodeNumber(true)
case TOKEN_STRING:
value = self.decodeString(DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, true)
case TOKEN_ARRAY:
value = self.decodeArray()
case TOKEN_OBJECT:
value = self.decodeObject()
case TOKEN_OBJECT_SERIALIZED:
value = self.decodeSerialized()
case TOKEN_REFERENCE, TOKEN_REFERENCE_OBJECT:
value = self.decodeReference()
case TOKEN_SPL_ARRAY:
value = self.decodeSplArray()
}
}
return value, self.lastErr
}
func (self *UnSerializer) decodeNull() PhpValue {
self.expect(SEPARATOR_VALUES)
return nil
}
func (self *UnSerializer) decodeBool() PhpValue {
var (
raw rune
err error
)
self.expect(SEPARATOR_VALUE_TYPE)
if raw, _, err = self.r.ReadRune(); err != nil {
self.saveError(fmt.Errorf("php_serialize: Error while reading bool value: %v", err))
}
self.expect(SEPARATOR_VALUES)
return raw == '1'
}
func (self *UnSerializer) decodeNumber(isFloat bool) PhpValue {
var (
raw string
err error
val PhpValue
)
self.expect(SEPARATOR_VALUE_TYPE)
if raw, err = self.readUntil(SEPARATOR_VALUES); err != nil {
self.saveError(fmt.Errorf("php_serialize: Error while reading number value: %v", err))
} else {
if isFloat {
if val, err = strconv.ParseFloat(raw, 64); err != nil {
self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to float: %v", raw, err))
}
} else {
if val, err = strconv.Atoi(raw); err != nil {
self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to int: %v", raw, err))
}
}
}
return val
}
func (self *UnSerializer) decodeString(left, right rune, isFinal bool) PhpValue {
var (
err error
val PhpValue
strLen int
readLen int
)
strLen = self.readLen()
self.expect(left)
if strLen > 0 {
buf := make([]byte, strLen)
if readLen, err = self.r.Read(buf); err != nil {
self.saveError(fmt.Errorf("php_serialize: Error while reading string value: %v", err))
} else {
if readLen != strLen {
self.saveError(fmt.Errorf("php_serialize: Unable to read string. Expected %d but have got %d bytes", strLen, readLen))
} else {
val = string(buf)
}
}
}
self.expect(right)
if isFinal {
self.expect(SEPARATOR_VALUES)
}
return val
}
func (self *UnSerializer) decodeArray() PhpValue {
var arrLen int
val := make(PhpArray)
arrLen = self.readLen()
self.expect(DELIMITER_OBJECT_LEFT)
for i := 0; i < arrLen; i++ {
k, errKey := self.Decode()
v, errVal := self.Decode()
if errKey == nil && errVal == nil {
val[k] = v
/*switch t := k.(type) {
default:
self.saveError(fmt.Errorf("php_serialize: Unexpected key type %T", t))
case string:
stringKey, _ := k.(string)
val[stringKey] = v
case int:
intKey, _ := k.(int)
val[strconv.Itoa(intKey)] = v
}*/
} else {
self.saveError(fmt.Errorf("php_serialize: Error while reading key or(and) value of array"))
}
}
self.expect(DELIMITER_OBJECT_RIGHT)
return val
}
func (self *UnSerializer) decodeObject() PhpValue {
val := &PhpObject{
className: self.readClassName(),
}
rawMembers := self.decodeArray()
val.members, _ = rawMembers.(PhpArray)
return val
}
func (self *UnSerializer) decodeSerialized() PhpValue {
val := &PhpObjectSerialized{
className: self.readClassName(),
}
rawData := self.decodeString(DELIMITER_OBJECT_LEFT, DELIMITER_OBJECT_RIGHT, false)
val.data, _ = rawData.(string)
if self.decodeFunc != nil && val.data != "" {
var err error
if val.value, err = self.decodeFunc(val.data); err != nil {
self.saveError(err)
}
}
return val
}
func (self *UnSerializer) decodeReference() PhpValue {
self.expect(SEPARATOR_VALUE_TYPE)
if _, err := self.readUntil(SEPARATOR_VALUES); err != nil {
self.saveError(fmt.Errorf("php_serialize: Error while reading reference value: %v", err))
}
return nil
}
func (self *UnSerializer) expect(expected rune) {
if token, _, err := self.r.ReadRune(); err != nil {
self.saveError(fmt.Errorf("php_serialize: Error while reading expected rune %#U: %v", expected, err))
} else if token != expected {
if debugMode {
log.Printf("php_serialize: source\n%s\n", self.source)
log.Printf("php_serialize: reader info\n%#v\n", self.r)
}
self.saveError(fmt.Errorf("php_serialize: Expected %#U but have got %#U", expected, token))
}
}
func (self *UnSerializer) readUntil(stop rune) (string, error) {
var (
token rune
err error
)
buf := bytes.NewBuffer([]byte{})
for {
if token, _, err = self.r.ReadRune(); err != nil || token == stop {
break
} else {
buf.WriteRune(token)
}
}
return buf.String(), err
}
func (self *UnSerializer) readLen() int {
var (
raw string
err error
val int
)
self.expect(SEPARATOR_VALUE_TYPE)
if raw, err = self.readUntil(SEPARATOR_VALUE_TYPE); err != nil {
self.saveError(fmt.Errorf("php_serialize: Error while reading lenght of value: %v", err))
} else {
if val, err = strconv.Atoi(raw); err != nil {
self.saveError(fmt.Errorf("php_serialize: Unable to convert %s to int: %v", raw, err))
} else if val > UNSERIALIZABLE_OBJECT_MAX_LEN {
self.saveError(fmt.Errorf("php_serialize: Unserializable object length looks too big(%d). If you are sure you wanna unserialise it, please increase UNSERIALIZABLE_OBJECT_MAX_LEN const", val))
val = 0
}
}
return val
}
func (self *UnSerializer) readClassName() (res string) {
rawClass := self.decodeString(DELIMITER_STRING_LEFT, DELIMITER_STRING_RIGHT, false)
res, _ = rawClass.(string)
return
}
func (self *UnSerializer) saveError(err error) {
if self.lastErr == nil {
self.lastErr = err
}
}
func (self *UnSerializer) decodeSplArray() PhpValue {
var err error
val := &PhpSplArray{}
self.expect(SEPARATOR_VALUE_TYPE)
self.expect(TOKEN_INT)
flags := self.decodeNumber(false)
if flags == nil {
self.saveError(fmt.Errorf("php_serialize: Unable to read flags of SplArray"))
return nil
}
val.flags = PhpValueInt(flags)
if val.array, err = self.Decode(); err != nil {
self.saveError(fmt.Errorf("php_serialize: Can't parse SplArray: %v", err))
return nil
}
self.expect(SEPARATOR_VALUES)
self.expect(TOKEN_SPL_ARRAY_MEMBERS)
self.expect(SEPARATOR_VALUE_TYPE)
if val.properties, err = self.Decode(); err != nil {
self.saveError(fmt.Errorf("php_serialize: Can't parse properties of SplArray: %v", err))
return nil
}
return val
}