2019-03-10 17:43:47 +00:00
|
|
|
package ipp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
sizeInteger = int16(4)
|
|
|
|
sizeBoolean = int16(1)
|
|
|
|
)
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// ipp attribute encoder
|
|
|
|
// encodes attribute to a io.Writer
|
2019-03-10 17:43:47 +00:00
|
|
|
type AttributeEncoder struct {
|
|
|
|
writer io.Writer
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// create a new attribute encoder from a writer
|
2019-03-10 17:43:47 +00:00
|
|
|
func NewAttributeEncoder(w io.Writer) *AttributeEncoder {
|
|
|
|
return &AttributeEncoder{w}
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// encodes a attribute and its value to a io.Writer
|
|
|
|
// the tag is determined by the AttributeTagMapping map
|
2019-03-10 17:43:47 +00:00
|
|
|
func (e *AttributeEncoder) Encode(attribute string, value interface{}) error {
|
|
|
|
tag, ok := AttributeTagMapping[attribute]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("cannot get tag of attribute %s", attribute)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch value.(type) {
|
2019-03-13 21:54:08 +00:00
|
|
|
case int:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
if err := e.encodeInteger(int32(value.(int))); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-03-13 21:54:08 +00:00
|
|
|
case int16:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(int32(value.(int16))); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case int8:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(int32(value.(int8))); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case int32:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(value.(int32)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case int64:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(int32(value.(int64))); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case []int:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for index, val := range value.([]int) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
if err := e.encodeInteger(int32(val)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case []int16:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for index, val := range value.([]int16) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(int32(val)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case []int8:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for index, val := range value.([]int8) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(int32(val)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case []int32:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for index, val := range value.([]int32) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 17:43:47 +00:00
|
|
|
if err := e.encodeInteger(val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-03-13 21:54:08 +00:00
|
|
|
case []int64:
|
2019-05-29 02:03:47 +00:00
|
|
|
if tag != TagInteger && tag != TagEnum {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-13 21:54:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for index, val := range value.([]int64) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeInteger(int32(val)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-03-10 17:43:47 +00:00
|
|
|
case bool:
|
|
|
|
if tag != TagBoolean {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeBoolean(value.(bool)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case []bool:
|
|
|
|
if tag != TagBoolean {
|
2019-05-29 02:08:57 +00:00
|
|
|
return fmt.Errorf("tag for attribute %s does not match with value type", attribute)
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for index, val := range value.([]bool) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeBoolean(val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case string:
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(value.(string)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case []string:
|
|
|
|
for index, val := range value.([]string) {
|
|
|
|
if err := e.encodeTag(tag); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index == 0 {
|
|
|
|
if err := e.encodeString(attribute); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := e.writeNullByte(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := e.encodeString(val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("type %T is not supportet", value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *AttributeEncoder) encodeString(s string) error {
|
|
|
|
if err := binary.Write(e.writer, binary.BigEndian, int16(len(s))); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := e.writer.Write([]byte(s))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
func (e *AttributeEncoder) encodeInteger(i int32) error {
|
2019-03-10 17:43:47 +00:00
|
|
|
if err := binary.Write(e.writer, binary.BigEndian, sizeInteger); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return binary.Write(e.writer, binary.BigEndian, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *AttributeEncoder) encodeBoolean(b bool) error {
|
|
|
|
if err := binary.Write(e.writer, binary.BigEndian, sizeBoolean); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return binary.Write(e.writer, binary.BigEndian, b)
|
|
|
|
}
|
|
|
|
|
2020-03-09 11:47:23 +00:00
|
|
|
func (e *AttributeEncoder) encodeTag(t int8) error {
|
2020-03-09 11:21:57 +00:00
|
|
|
return binary.Write(e.writer, binary.BigEndian, t)
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *AttributeEncoder) writeNullByte() error {
|
|
|
|
return binary.Write(e.writer, binary.BigEndian, int16(0))
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// representation of a ipp attribute
|
|
|
|
// a attribute contains a tag, witch identifies the type, the name of the attribute a the value
|
2019-03-10 17:43:47 +00:00
|
|
|
type Attribute struct {
|
2020-03-09 11:47:23 +00:00
|
|
|
Tag int8
|
2019-03-13 21:54:08 +00:00
|
|
|
Name string
|
2019-03-10 17:43:47 +00:00
|
|
|
Value interface{}
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// ipp attribute decoder
|
|
|
|
// reads from a io.Reader an decode the data into an attribute struct
|
2019-03-10 17:43:47 +00:00
|
|
|
type AttributeDecoder struct {
|
|
|
|
reader io.Reader
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// create a new attribute decoder from a reader
|
2019-03-10 17:43:47 +00:00
|
|
|
func NewAttributeDecoder(r io.Reader) *AttributeDecoder {
|
|
|
|
return &AttributeDecoder{r}
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// reads from a io.Reader and decode the attribute
|
|
|
|
// the type is identified by a tag passed as an argument
|
2020-03-09 11:47:23 +00:00
|
|
|
func (d *AttributeDecoder) Decode(tag int8) (*Attribute, error) {
|
2019-03-13 21:54:08 +00:00
|
|
|
attr := Attribute{Tag: tag}
|
2019-03-10 17:43:47 +00:00
|
|
|
|
|
|
|
name, err := d.decodeString()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Name = name
|
|
|
|
|
|
|
|
switch attr.Tag {
|
|
|
|
case TagEnum, TagInteger:
|
|
|
|
val, err := d.decodeInteger()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Value = val
|
|
|
|
case TagBoolean:
|
|
|
|
val, err := d.decodeBool()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Value = val
|
|
|
|
case TagDate:
|
|
|
|
val, err := d.decodeDate()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Value = val
|
|
|
|
case TagRange:
|
|
|
|
val, err := d.decodeRange()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Value = val
|
|
|
|
case TagResolution:
|
|
|
|
val, err := d.decodeResolution()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Value = val
|
|
|
|
default:
|
|
|
|
val, err := d.decodeString()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
attr.Value = val
|
|
|
|
}
|
|
|
|
|
|
|
|
return &attr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *AttributeDecoder) decodeBool() (b bool, err error) {
|
|
|
|
if _, err = d.readValueLength(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &b); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *AttributeDecoder) decodeInteger() (i int, err error) {
|
|
|
|
if _, err = d.readValueLength(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
var reti int32
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &reti); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
return int(reti), nil
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
func (d *AttributeDecoder) decodeString() (string, error) {
|
2019-03-10 17:43:47 +00:00
|
|
|
length, err := d.readValueLength()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if length == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
bs := make([]byte, length)
|
|
|
|
if _, err := d.reader.Read(bs); err != nil {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(bs), nil
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
func (d *AttributeDecoder) decodeDate() ([]int, error) {
|
2019-03-10 17:43:47 +00:00
|
|
|
length, err := d.readValueLength()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
is := make([]int, length)
|
2020-03-09 11:58:54 +00:00
|
|
|
var ti int8
|
2019-03-10 17:43:47 +00:00
|
|
|
|
|
|
|
for i := int16(0); i < length; i++ {
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
is[i] = int(ti)
|
|
|
|
}
|
|
|
|
|
|
|
|
return is, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *AttributeDecoder) decodeRange() ([]int, error) {
|
|
|
|
length, err := d.readValueLength()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize range element count (c) and range slice (r)
|
|
|
|
c := length / 4
|
|
|
|
r := make([]int, c)
|
|
|
|
|
|
|
|
for i := int16(0); i < c; i++ {
|
|
|
|
var ti int
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &ti); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
r[i] = ti
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2020-03-27 22:18:52 +00:00
|
|
|
// represents the data of the resolution attribute
|
2019-03-10 17:43:47 +00:00
|
|
|
type Resolution struct {
|
|
|
|
Height int
|
2019-03-13 21:54:08 +00:00
|
|
|
Width int
|
2020-03-09 11:58:54 +00:00
|
|
|
Depth int8
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *AttributeDecoder) decodeResolution() (res Resolution, err error) {
|
|
|
|
_, err = d.readValueLength()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &res.Height); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &res.Width); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = binary.Read(d.reader, binary.BigEndian, &res.Depth); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *AttributeDecoder) readValueLength() (length int16, err error) {
|
|
|
|
err = binary.Read(d.reader, binary.BigEndian, &length)
|
|
|
|
return
|
|
|
|
}
|