File goipp-1.2.0.obscpio of Package goipp
07070100000000000081A400000000000000000000000167F56AFB00000013000000000000000000000000000000000000001700000000goipp-1.2.0/.gitignoreipp-usb
tags
*.swp
07070100000001000081A400000000000000000000000167F56AFB0000052F000000000000000000000000000000000000001400000000goipp-1.2.0/LICENSEBSD 2-Clause License
Copyright (c) 2020, Alexander Pevzner
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
07070100000002000081A400000000000000000000000167F56AFB00000034000000000000000000000000000000000000001500000000goipp-1.2.0/Makefileall:
-gotags -R . > tags
go build
test:
go test
07070100000003000081A400000000000000000000000167F56AFB0000035A000000000000000000000000000000000000001600000000goipp-1.2.0/README.md# goipp
[](http://godoc.org/github.com/OpenPrinting/goipp)

[](https://goreportcard.com/report/github.com/OpenPrinting/goipp)
The goipp library is fairly complete implementation of IPP core protocol in
pure Go. Essentially, it is IPP messages parser/composer. Transport is
not implemented here, because Go standard library has an excellent built-in
HTTP client, and it doesn't make a lot of sense to wrap it here.
High-level requests, like "print a file" are also not implemented, only the
low-level stuff.
All documentation is on godoc.org -- follow the link above. Pull requests
are welcomed, assuming they don't break existing API.
07070100000004000081A400000000000000000000000167F56AFB0000001D000000000000000000000000000000000000001800000000goipp-1.2.0/_config.ymltheme: jekyll-theme-architect07070100000005000081A400000000000000000000000167F56AFB00001285000000000000000000000000000000000000001400000000goipp-1.2.0/attr.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Message attributes
*/
package goipp
import (
"fmt"
"sort"
)
// Attributes represents a slice of attributes
type Attributes []Attribute
// Add Attribute to Attributes
func (attrs *Attributes) Add(attr Attribute) {
*attrs = append(*attrs, attr)
}
// Clone creates a shallow copy of Attributes.
// For nil input it returns nil output.
func (attrs Attributes) Clone() Attributes {
var attrs2 Attributes
if attrs != nil {
attrs2 = make(Attributes, len(attrs))
copy(attrs2, attrs)
}
return attrs2
}
// DeepCopy creates a deep copy of Attributes.
// For nil input it returns nil output.
func (attrs Attributes) DeepCopy() Attributes {
var attrs2 Attributes
if attrs != nil {
attrs2 = make(Attributes, len(attrs))
for i := range attrs {
attrs2[i] = attrs[i].DeepCopy()
}
}
return attrs2
}
// Equal checks that attrs and attrs2 are equal
//
// Note, Attributes(nil) and Attributes{} are not Equal but Similar.
func (attrs Attributes) Equal(attrs2 Attributes) bool {
if len(attrs) != len(attrs2) {
return false
}
if (attrs == nil) != (attrs2 == nil) {
return false
}
for i, attr := range attrs {
attr2 := attrs2[i]
if !attr.Equal(attr2) {
return false
}
}
return true
}
// Similar checks that attrs and attrs2 are **logically** equal,
// which means the following:
// - attrs and addrs2 contain the same set of attributes,
// but may be differently ordered
// - Values of attributes of the same name within attrs and
// attrs2 are similar
//
// Note, Attributes(nil) and Attributes{} are not Equal but Similar.
func (attrs Attributes) Similar(attrs2 Attributes) bool {
// Fast check: if lengths are not the same, attributes
// are definitely not equal
if len(attrs) != len(attrs2) {
return false
}
// Sort attrs and attrs2 by name
sorted1 := attrs.Clone()
sort.SliceStable(sorted1, func(i, j int) bool {
return sorted1[i].Name < sorted1[j].Name
})
sorted2 := attrs2.Clone()
sort.SliceStable(sorted2, func(i, j int) bool {
return sorted2[i].Name < sorted2[j].Name
})
// And now compare sorted slices
for i, attr1 := range sorted1 {
attr2 := sorted2[i]
if !attr1.Similar(attr2) {
return false
}
}
return true
}
// Attribute represents a single attribute, which consist of
// the Name and one or more Values
type Attribute struct {
Name string // Attribute name
Values Values // Slice of values
}
// MakeAttribute makes Attribute with single value.
//
// Deprecated. Use [MakeAttr] instead.
func MakeAttribute(name string, tag Tag, value Value) Attribute {
attr := Attribute{Name: name}
attr.Values.Add(tag, value)
return attr
}
// MakeAttr makes Attribute with one or more values.
func MakeAttr(name string, tag Tag, val1 Value, values ...Value) Attribute {
attr := Attribute{Name: name}
attr.Values.Add(tag, val1)
for _, val := range values {
attr.Values.Add(tag, val)
}
return attr
}
// MakeAttrCollection makes [Attribute] with [Collection] value.
func MakeAttrCollection(name string,
member1 Attribute, members ...Attribute) Attribute {
col := make(Collection, len(members)+1)
col[0] = member1
copy(col[1:], members)
return MakeAttribute(name, TagBeginCollection, col)
}
// Equal checks that Attribute is equal to another Attribute
// (i.e., names are the same and values are equal)
func (a Attribute) Equal(a2 Attribute) bool {
return a.Name == a2.Name && a.Values.Equal(a2.Values)
}
// Similar checks that Attribute is **logically** equal to another
// Attribute (i.e., names are the same and values are similar)
func (a Attribute) Similar(a2 Attribute) bool {
return a.Name == a2.Name && a.Values.Similar(a2.Values)
}
// DeepCopy creates a deep copy of the Attribute
func (a Attribute) DeepCopy() Attribute {
a2 := a
a2.Values = a2.Values.DeepCopy()
return a2
}
// Unpack attribute value from its wire representation
func (a *Attribute) unpack(tag Tag, value []byte) error {
var err error
var val Value
switch tag.Type() {
case TypeVoid, TypeCollection:
val = Void{}
case TypeInteger:
val = Integer(0)
case TypeBoolean:
val = Boolean(false)
case TypeString:
val = String("")
case TypeDateTime:
val = Time{}
case TypeResolution:
val = Resolution{}
case TypeRange:
val = Range{}
case TypeTextWithLang:
val = TextWithLang{}
case TypeBinary:
val = Binary(nil)
default:
panic(fmt.Sprintf("(Attribute) uppack(): tag=%s type=%s", tag, tag.Type()))
}
val, err = val.decode(value)
if err == nil {
a.Values.Add(tag, val)
} else {
err = fmt.Errorf("%s: %s", tag, err)
}
return err
}
07070100000006000081A400000000000000000000000167F56AFB00001D10000000000000000000000000000000000000001900000000goipp-1.2.0/attr_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Message attributes tests
*/
package goipp
import (
"errors"
"testing"
"time"
"unsafe"
)
// TestAttributesEqualSimilar tests Attributes.Equal and
// Attributes.Similar methods
func TestAttributesEqualSimilar(t *testing.T) {
type testData struct {
a1, a2 Attributes // A pair of Attributes slice
equal bool // Expected a1.Equal(a2) output
similar bool // Expected a2.Similar(a2) output
}
tests := []testData{
{
// nil Attributes are equal and similar
a1: nil,
a2: nil,
equal: true,
similar: true,
},
{
// Empty Attributes are equal and similar
a1: Attributes{},
a2: Attributes{},
equal: true,
similar: true,
},
{
// Attributes(nil) vs Attributes{} are similar but not equal
a1: Attributes{},
a2: nil,
equal: false,
similar: true,
},
{
// Attributes of different length are neither equal nor similar
a1: Attributes{
MakeAttr("attr1", TagInteger, Integer(0)),
},
a2: Attributes{},
equal: false,
similar: false,
},
{
// Same Attributes are equal and similar
a1: Attributes{
MakeAttr("attr1", TagInteger, Integer(0)),
},
a2: Attributes{
MakeAttr("attr1", TagInteger, Integer(0)),
},
equal: true,
similar: true,
},
{
// Same tag, different value: not equal or similar
a1: Attributes{
MakeAttr("attr1", TagInteger, Integer(0)),
},
a2: Attributes{
MakeAttr("attr1", TagInteger, Integer(1)),
},
equal: false,
similar: false,
},
{
// Same value, tag value: not equal or similar
a1: Attributes{
MakeAttr("attr1", TagInteger, Integer(0)),
},
a2: Attributes{
MakeAttr("attr1", TagEnum, Integer(0)),
},
equal: false,
similar: false,
},
{
// Different but similar Value types:
// Attributes are not equal but similar
a1: Attributes{
MakeAttr("attr1", TagString, Binary("hello")),
MakeAttr("attr2", TagString, String("world")),
},
a2: Attributes{
MakeAttr("attr1", TagString, String("hello")),
MakeAttr("attr2", TagString, Binary("world")),
},
equal: false,
similar: true,
},
{
// Different order: not equal but similar
a1: Attributes{
MakeAttr("attr1", TagString, String("hello")),
MakeAttr("attr2", TagString, String("world")),
},
a2: Attributes{
MakeAttr("attr2", TagString, String("world")),
MakeAttr("attr1", TagString, String("hello")),
},
equal: false,
similar: true,
},
}
for _, test := range tests {
equal := test.a1.Equal(test.a2)
similar := test.a1.Similar(test.a2)
if equal != test.equal {
t.Errorf("testing Attributes.Equal:\n"+
"attrs 1: %s\n"+
"attrs 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.a1, test.a2,
test.equal, equal,
)
}
if similar != test.similar {
t.Errorf("testing Attributes.Similar:\n"+
"attrs 1: %s\n"+
"attrs 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.a1, test.a2,
test.similar, similar,
)
}
}
}
// TestAttributesConstructors tests Attributes.Add and MakeAttr
func TestAttributesConstructors(t *testing.T) {
attrs1 := Attributes{
Attribute{
Name: "attr1",
Values: Values{
{TagString, String("hello")},
{TagString, String("world")},
},
},
Attribute{
Name: "attr2",
Values: Values{
{TagInteger, Integer(1)},
{TagInteger, Integer(2)},
{TagInteger, Integer(3)},
},
},
}
attrs2 := Attributes{}
attrs2.Add(MakeAttr("attr1", TagString, String("hello"), String("world")))
attrs2.Add(MakeAttr("attr2", TagInteger, Integer(1), Integer(2), Integer(3)))
if !attrs1.Equal(attrs2) {
t.Errorf("Attributes constructors test failed")
}
}
// TestMakeAttribute tests MakeAttribute function
func TestMakeAttribute(t *testing.T) {
a1 := Attribute{
Name: "attr",
Values: Values{{TagInteger, Integer(1)}},
}
a2 := MakeAttribute("attr", TagInteger, Integer(1))
if !a1.Equal(a2) {
t.Errorf("MakeAttribute test failed")
}
}
// TestAttributesCopy tests Attributes.Clone and Attributes.DeepCopy
func TestAttributesCopy(t *testing.T) {
type testData struct {
attrs Attributes
}
tests := []testData{
{nil},
{Attributes{}},
{
Attributes{
MakeAttr("attr1", TagString, String("hello"), String("world")),
MakeAttr("attr2", TagInteger, Integer(1), Integer(2), Integer(3)),
MakeAttr("attr2", TagBoolean, Boolean(true), Boolean(false)),
},
},
}
for _, test := range tests {
clone := test.attrs.Clone()
if !test.attrs.Equal(clone) {
t.Errorf("testing Attributes.Clone\n"+
"expected: %#v\n"+
"present: %#v\n",
test.attrs,
clone,
)
}
copy := test.attrs.DeepCopy()
if !test.attrs.Equal(copy) {
t.Errorf("testing Attributes.DeepCopy\n"+
"expected: %#v\n"+
"present: %#v\n",
test.attrs,
copy,
)
}
}
}
// TestAttributeUnpack tests Attribute.unpack method for all kinds
// of Value types
func TestAttributeUnpack(t *testing.T) {
loc := time.FixedZone("UTC+3:30", 3*3600+1800)
tm, _ := time.ParseInLocation(
time.RFC3339, "2025-03-29T16:48:53+03:30", loc)
values := Values{
{TagBoolean, Boolean(true)},
{TagExtension, Binary{}},
{TagString, Binary{1, 2, 3}},
{TagInteger, Integer(123)},
{TagEnum, Integer(-321)},
{TagRange, Range{-100, 200}},
{TagRange, Range{-100, -50}},
{TagResolution, Resolution{150, 300, UnitsDpi}},
{TagResolution, Resolution{100, 200, UnitsDpcm}},
{TagResolution, Resolution{75, 150, 10}},
{TagName, String("hello")},
{TagTextLang, TextWithLang{"en-US", "hello"}},
{TagDateTime, Time{tm}},
{TagNoValue, Void{}},
}
for _, v := range values {
expected := Attribute{Name: "attr", Values: Values{v}}
present := Attribute{Name: "attr"}
data, _ := v.V.encode()
present.unpack(v.T, data)
if !expected.Equal(present) {
t.Errorf("%x %d", data, unsafe.Sizeof(int(5)))
t.Errorf("testing Attribute.unpack:\n"+
"expected: %#v\n"+
"present: %#v\n",
expected, present,
)
}
}
}
// TestAttributeUnpackError tests that Attribute.unpack properly
// handles errors from the Value.decode
func TestAttributeUnpackError(t *testing.T) {
noError := errors.New("")
type testData struct {
t Tag // Input value tag
data []byte // Input data
err string // Expected error
}
tests := []testData{
{
t: TagInteger,
data: []byte{},
err: "integer: value must be 4 bytes",
},
{
t: TagBoolean,
data: []byte{},
err: "boolean: value must be 1 byte",
},
}
for _, test := range tests {
attr := Attribute{Name: "attr"}
err := attr.unpack(test.t, test.data)
if err == nil {
err = noError
}
if err.Error() != test.err {
t.Errorf("testing Attribute.unpack:\n"+
"input tag: %s\n"+
"input data: %x\n"+
"error expected: %s\n"+
"error present: %s\n",
test.t, test.data,
test.err, err,
)
}
}
}
// TestAttributeUnpackPanic tests that Attribute.unpack panics
// on invalid Tag
func TestAttributeUnpackPanic(t *testing.T) {
defer func() {
recover()
}()
attr := Attribute{Name: "attr"}
attr.unpack(TagOperationGroup, []byte{})
t.Errorf("Attribute.unpack must panic on the invalid Tag")
}
07070100000007000081A400000000000000000000000167F56AFB0000019D000000000000000000000000000000000000001500000000goipp-1.2.0/const.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Various constants
*/
package goipp
const (
// ContentType is the HTTP content type for IPP messages
ContentType = "application/ipp"
// msgPrintIndent used for indentation by message pretty-printer
msgPrintIndent = " "
)
07070100000008000081A400000000000000000000000167F56AFB000025E4000000000000000000000000000000000000001700000000goipp-1.2.0/decoder.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Message decoder
*/
package goipp
import (
"encoding/binary"
"errors"
"fmt"
"io"
)
// DecoderOptions represents message decoder options
type DecoderOptions struct {
// EnableWorkarounds, if set to true, enables various workarounds
// for decoding IPP messages that violate IPP protocol specification
//
// Currently it includes the following workarounds:
// * Pantum M7300FDW violates collection encoding rules.
// Instead of using TagMemberName, it uses named attributes
// within the collection
//
// The list of implemented workarounds may grow in the
// future
EnableWorkarounds bool
}
// messageDecoder represents Message decoder
type messageDecoder struct {
in io.Reader // Input stream
off int // Offset of last read
cnt int // Count of read bytes
opt DecoderOptions // Options
}
// Decode the message
func (md *messageDecoder) decode(m *Message) error {
// Wire format:
//
// 2 bytes: Version
// 2 bytes: Code (Operation or Status)
// 4 bytes: RequestID
// variable: attributes
// 1 byte: TagEnd
// Parse message header
var err error
m.Version, err = md.decodeVersion()
if err == nil {
m.Code, err = md.decodeCode()
}
if err == nil {
m.RequestID, err = md.decodeU32()
}
// Now parse attributes
done := false
var group *Attributes
var attr Attribute
var prev *Attribute
for err == nil && !done {
var tag Tag
tag, err = md.decodeTag()
if err != nil {
break
}
if tag.IsDelimiter() {
prev = nil
}
if tag.IsGroup() {
m.Groups.Add(Group{tag, nil})
}
switch tag {
case TagZero:
err = errors.New("Invalid tag 0")
case TagEnd:
done = true
case TagOperationGroup:
group = &m.Operation
case TagJobGroup:
group = &m.Job
case TagPrinterGroup:
group = &m.Printer
case TagUnsupportedGroup:
group = &m.Unsupported
case TagSubscriptionGroup:
group = &m.Subscription
case TagEventNotificationGroup:
group = &m.EventNotification
case TagResourceGroup:
group = &m.Resource
case TagDocumentGroup:
group = &m.Document
case TagSystemGroup:
group = &m.System
case TagFuture11Group:
group = &m.Future11
case TagFuture12Group:
group = &m.Future12
case TagFuture13Group:
group = &m.Future13
case TagFuture14Group:
group = &m.Future14
case TagFuture15Group:
group = &m.Future15
default:
// Decode attribute
if tag == TagMemberName || tag == TagEndCollection {
err = fmt.Errorf("Unexpected tag %s", tag)
} else {
attr, err = md.decodeAttribute(tag)
}
if err == nil && tag == TagBeginCollection {
attr.Values[0].V, err = md.decodeCollection()
}
// If everything is OK, save attribute
switch {
case err != nil:
case attr.Name == "":
if prev != nil {
prev.Values.Add(attr.Values[0].T, attr.Values[0].V)
// Append value to the last Attribute of the
// last Group in the m.Groups
//
// Note, if we are here, this last Attribute definitely exists,
// because:
// * prev != nil
// * prev is set when new named attribute is added
// * prev is reset when delimiter tag is encountered
gLast := &m.Groups[len(m.Groups)-1]
aLast := &gLast.Attrs[len(gLast.Attrs)-1]
aLast.Values.Add(attr.Values[0].T, attr.Values[0].V)
} else {
err = errors.New("Additional value without preceding attribute")
}
case group != nil:
group.Add(attr)
prev = &(*group)[len(*group)-1]
m.Groups[len(m.Groups)-1].Add(attr)
default:
err = errors.New("Attribute without a group")
}
}
}
if err != nil {
err = fmt.Errorf("%s at 0x%x", err, md.off)
}
return err
}
// Decode a Collection
//
// Collection is like a nested object - an attribute which value is a sequence
// of named attributes. Collections can be nested.
//
// Wire format:
//
// ATTR: Tag = TagBeginCollection, - the outer attribute that
// Name = "name", value - ignored contains the collection
//
// ATTR: Tag = TagMemberName, name = "", - member name \
// value - string, name of the next |
// member | repeated for
// | each member
// ATTR: Tag = any attribute tag, name = "", - repeated for |
// value = member value multi-value /
// members
//
// ATTR: Tag = TagEndCollection, name = "",
// value - ignored
//
// The format looks a bit baroque, but please note that it was added
// in the IPP 2.0. For IPP 1.x collection looks like a single multi-value
// TagBeginCollection attribute (attributes without names considered
// next value for the previously defined named attributes) and so
// 1.x parser silently ignores collections and doesn't get confused
// with them.
func (md *messageDecoder) decodeCollection() (Collection, error) {
collection := make(Collection, 0)
memberName := ""
for {
tag, err := md.decodeTag()
if err != nil {
return nil, err
}
// Delimiter cannot be inside a collection
if tag.IsDelimiter() {
err = fmt.Errorf("Collection: unexpected tag %s", tag)
return nil, err
}
// Check for TagMemberName without the subsequent value attribute
if (tag == TagMemberName || tag == TagEndCollection) && memberName != "" {
err = fmt.Errorf("Collection: unexpected %s, expected value tag", tag)
return nil, err
}
// Fetch next attribute
attr, err := md.decodeAttribute(tag)
if err != nil {
return nil, err
}
// Process next attribute
switch tag {
case TagEndCollection:
return collection, nil
case TagMemberName:
memberName = string(attr.Values[0].V.(String))
if memberName == "" {
err = fmt.Errorf("Collection: %s value is empty", tag)
return nil, err
}
case TagBeginCollection:
// Decode nested collection
attr.Values[0].V, err = md.decodeCollection()
if err != nil {
return nil, err
}
fallthrough
default:
if md.opt.EnableWorkarounds &&
memberName == "" && attr.Name != "" {
// Workaround for: Pantum M7300FDW
//
// This device violates collection encoding rules.
// Instead of using TagMemberName, it uses named
// attributes within the collection
memberName = attr.Name
}
if memberName != "" {
attr.Name = memberName
collection = append(collection, attr)
memberName = ""
} else if len(collection) > 0 {
l := len(collection)
collection[l-1].Values.Add(tag, attr.Values[0].V)
} else {
// We've got a value without preceding TagMemberName
err = fmt.Errorf("Collection: unexpected %s, expected %s", tag, TagMemberName)
return nil, err
}
}
}
}
// Decode a tag
func (md *messageDecoder) decodeTag() (Tag, error) {
t, err := md.decodeU8()
return Tag(t), err
}
// Decode a Version
func (md *messageDecoder) decodeVersion() (Version, error) {
code, err := md.decodeU16()
return Version(code), err
}
// Decode a Code
func (md *messageDecoder) decodeCode() (Code, error) {
code, err := md.decodeU16()
return Code(code), err
}
// Decode a single attribute
//
// Wire format:
//
// 1 byte: Tag
// 2+N bytes: Name length (2 bytes) + name string
// 2+N bytes: Value length (2 bytes) + value bytes
//
// For the extended tag format, Tag is encoded as TagExtension and
// 4 bytes of the actual tag value prepended to the value bytes
func (md *messageDecoder) decodeAttribute(tag Tag) (Attribute, error) {
var attr Attribute
var value []byte
var err error
// Obtain attribute name and raw value
attr.Name, err = md.decodeString()
if err != nil {
goto ERROR
}
value, err = md.decodeBytes()
if err != nil {
goto ERROR
}
// Handle TagExtension
if tag == TagExtension {
if len(value) < 4 {
err = errors.New("Extension tag truncated")
goto ERROR
}
t := binary.BigEndian.Uint32(value[:4])
if t > 0x7fffffff {
err = fmt.Errorf(
"Extension tag 0x%8.8x out of range", t)
goto ERROR
}
}
// Unpack value
err = attr.unpack(tag, value)
if err != nil {
goto ERROR
}
return attr, nil
// Return a error
ERROR:
return Attribute{}, err
}
// Decode a 8-bit integer
func (md *messageDecoder) decodeU8() (uint8, error) {
buf := make([]byte, 1)
err := md.read(buf)
return buf[0], err
}
// Decode a 16-bit integer
func (md *messageDecoder) decodeU16() (uint16, error) {
buf := make([]byte, 2)
err := md.read(buf)
return binary.BigEndian.Uint16(buf[:]), err
}
// Decode a 32-bit integer
func (md *messageDecoder) decodeU32() (uint32, error) {
buf := make([]byte, 4)
err := md.read(buf)
return binary.BigEndian.Uint32(buf[:]), err
}
// Decode sequence of bytes
func (md *messageDecoder) decodeBytes() ([]byte, error) {
length, err := md.decodeU16()
if err != nil {
return nil, err
}
data := make([]byte, length)
err = md.read(data)
if err != nil {
return nil, err
}
return data, nil
}
// Decode string
func (md *messageDecoder) decodeString() (string, error) {
data, err := md.decodeBytes()
if err != nil {
return "", err
}
return string(data), nil
}
// Read a piece of raw data from input stream
func (md *messageDecoder) read(data []byte) error {
md.off = md.cnt
for len(data) > 0 {
n, err := md.in.Read(data)
if n > 0 {
md.cnt += n
data = data[n:]
} else {
md.off = md.cnt
if err == nil || err == io.EOF {
err = errors.New("Message truncated")
}
return err
}
}
return nil
}
07070100000009000081A400000000000000000000000167F56AFB000010A3000000000000000000000000000000000000001300000000goipp-1.2.0/doc.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Package documentation
*/
/*
Package goipp implements IPP core protocol, as defined by RFC 8010
It doesn't implement high-level operations, such as "print a document",
"cancel print job" and so on. It's scope is limited to proper generation
and parsing of IPP requests and responses.
IPP protocol uses the following simple model:
1. Send a request
2. Receive a response
Request and response both has a similar format, represented here
by type Message, with the only difference, that Code field of
that Message is the Operation code in request and Status code
in response. So most of operations are common for request and
response messages
# Example (Get-Printer-Attributes):
package main
import (
"bytes"
"net/http"
"os"
"github.com/OpenPrinting/goipp"
)
const uri = "http://192.168.1.102:631"
// Build IPP OpGetPrinterAttributes request
func makeRequest() ([]byte, error) {
m := goipp.NewRequest(goipp.DefaultVersion, goipp.OpGetPrinterAttributes, 1)
m.Operation.Add(goipp.MakeAttribute("attributes-charset",
goipp.TagCharset, goipp.String("utf-8")))
m.Operation.Add(goipp.MakeAttribute("attributes-natural-language",
goipp.TagLanguage, goipp.String("en-US")))
m.Operation.Add(goipp.MakeAttribute("printer-uri",
goipp.TagURI, goipp.String(uri)))
m.Operation.Add(goipp.MakeAttribute("requested-attributes",
goipp.TagKeyword, goipp.String("all")))
return m.EncodeBytes()
}
// Check that there is no error
func check(err error) {
if err != nil {
panic(err)
}
}
func main() {
request, err := makeRequest()
check(err)
resp, err := http.Post(uri, goipp.ContentType, bytes.NewBuffer(request))
check(err)
var respMsg goipp.Message
err = respMsg.Decode(resp.Body)
check(err)
respMsg.Print(os.Stdout, false)
}
# Example (Print PDF file):
package main
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"os"
"github.com/OpenPrinting/goipp"
)
const (
PrinterURL = "ipp://192.168.1.102:631/ipp/print"
TestPage = "onepage-a4.pdf"
)
// checkErr checks for an error. If err != nil, it prints error
// message and exits
func checkErr(err error, format string, args ...interface{}) {
if err != nil {
msg := fmt.Sprintf(format, args...)
fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err)
os.Exit(1)
}
}
// ExamplePrintPDF demo
func main() {
// Build and encode IPP request
req := goipp.NewRequest(goipp.DefaultVersion, goipp.OpPrintJob, 1)
req.Operation.Add(goipp.MakeAttribute("attributes-charset",
goipp.TagCharset, goipp.String("utf-8")))
req.Operation.Add(goipp.MakeAttribute("attributes-natural-language",
goipp.TagLanguage, goipp.String("en-US")))
req.Operation.Add(goipp.MakeAttribute("printer-uri",
goipp.TagURI, goipp.String(PrinterURL)))
req.Operation.Add(goipp.MakeAttribute("requesting-user-name",
goipp.TagName, goipp.String("John Doe")))
req.Operation.Add(goipp.MakeAttribute("job-name",
goipp.TagName, goipp.String("job name")))
req.Operation.Add(goipp.MakeAttribute("document-format",
goipp.TagMimeType, goipp.String("application/pdf")))
payload, err := req.EncodeBytes()
checkErr(err, "IPP encode")
// Open document file
file, err := os.Open(TestPage)
checkErr(err, "Open document file")
defer file.Close()
// Build HTTP request
body := io.MultiReader(bytes.NewBuffer(payload), file)
httpReq, err := http.NewRequest(http.MethodPost, PrinterURL, body)
checkErr(err, "HTTP")
httpReq.Header.Set("content-type", goipp.ContentType)
httpReq.Header.Set("accept", goipp.ContentType)
// Execute HTTP request
httpRsp, err := http.DefaultClient.Do(httpReq)
if httpRsp != nil {
defer httpRsp.Body.Close()
}
checkErr(err, "HTTP")
if httpRsp.StatusCode/100 != 2 {
checkErr(errors.New(httpRsp.Status), "HTTP")
}
// Decode IPP response
rsp := &goipp.Message{}
err = rsp.Decode(httpRsp.Body)
checkErr(err, "IPP decode")
if goipp.Status(rsp.Code) != goipp.StatusOk {
err = errors.New(goipp.Status(rsp.Code).String())
checkErr(err, "IPP")
}
}
*/
package goipp
0707010000000A000081A400000000000000000000000167F56AFB00020AEF000000000000000000000000000000000000001B00000000goipp-1.2.0/encdec_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Encoder/decoder test
*/
package goipp
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
"testing"
"time"
)
// errWriter implements io.Writer interface.
//
// It accepts some first bytes and after that always returns an error.
// It is used to test I/O error return from the Message.Encode function.
type errWriter struct{ skip int }
var _ = io.Writer(&errWriter{})
func (ewr *errWriter) Write(data []byte) (int, error) {
if len(data) <= ewr.skip {
ewr.skip -= len(data)
return len(data), nil
}
n := ewr.skip
ewr.skip = 0
return n, errors.New("I/O error")
}
// errValue implements Value interface and returns
// error from its encode() and decode() methods
type errValue struct{}
var _ = Value(errValue{})
func (errValue) String() string { return "" }
func (errValue) Type() Type { return TypeInteger }
func (errValue) DeepCopy() Value { return errValue{} }
func (errValue) encode() ([]byte, error) {
return nil, errors.New("encode error")
}
func (errValue) decode([]byte) (Value, error) {
return nil, errors.New("decode error")
}
// Check that err == nil
func assertNoError(t *testing.T, err error) {
if err != nil {
t.Errorf("%s", err)
}
}
// Check that err != nil
func assertWithError(t *testing.T, err error) {
if err == nil {
t.Errorf("Error expected")
}
}
// Check that err != nil and contains expected test
func assertErrorIs(t *testing.T, err error, s string) {
if err == nil {
if s != "" {
t.Errorf("Error expected")
}
return
}
if !strings.HasPrefix(err.Error(), s) {
t.Errorf("Error is %q, expected %q", err, s)
}
}
// parseTime parses time given in time.Layout format.
// this function panics if time cannot be parsed
func parseTime(s string) Time {
const layout = "01/02 03:04:05PM '06 -0700" // Missed in go 1.11
t, err := time.Parse(layout, s)
if err != nil {
panic(fmt.Sprintf("parseTime(%q): %s", s, err))
}
return Time{t}
}
// testEncodeDecodeMessage creates a quite complex message
// for Encode/Decode test
func testEncodeDecodeMessage() *Message {
m := &Message{
Version: DefaultVersion,
Code: 0x1234,
RequestID: 0x87654321,
}
// Populate all groups
m.Operation.Add(MakeAttribute("grp_operation", TagInteger,
Integer(1)))
m.Job.Add(MakeAttribute("grp_job", TagInteger,
Integer(2)))
m.Printer.Add(MakeAttribute("grp_printer", TagInteger,
Integer(3)))
m.Unsupported.Add(MakeAttribute("grp_unsupported", TagInteger,
Integer(4)))
m.Subscription.Add(MakeAttribute("grp_subscription", TagInteger,
Integer(5)))
m.EventNotification.Add(MakeAttribute("grp_eventnotification", TagInteger,
Integer(6)))
m.Resource.Add(MakeAttribute("grp_resource", TagInteger,
Integer(7)))
m.Document.Add(MakeAttribute("grp_document", TagInteger,
Integer(8)))
m.System.Add(MakeAttribute("grp_system", TagInteger,
Integer(9)))
m.Future11.Add(MakeAttribute("grp_future11", TagInteger,
Integer(10)))
m.Future12.Add(MakeAttribute("grp_future12", TagInteger,
Integer(11)))
m.Future13.Add(MakeAttribute("grp_future13", TagInteger,
Integer(12)))
m.Future14.Add(MakeAttribute("grp_future14", TagInteger,
Integer(13)))
m.Future15.Add(MakeAttribute("grp_future15", TagInteger,
Integer(14)))
// Use all possible attribute types
m.Operation.Add(MakeAttribute("type_integer", TagInteger, Integer(123)))
m.Operation.Add(MakeAttribute("type_boolean_t", TagBoolean, Boolean(true)))
m.Operation.Add(MakeAttribute("type_boolean_f", TagBoolean, Boolean(false)))
m.Operation.Add(MakeAttribute("type_void", TagUnsupportedValue, Void{}))
m.Operation.Add(MakeAttribute("type_string_1", TagText, String("hello")))
m.Operation.Add(MakeAttribute("type_string_2", TagText, String("")))
m.Operation.Add(MakeAttribute("type_time_1", TagDateTime,
parseTime("01/02 03:04:05PM '06 -0700")))
m.Operation.Add(MakeAttribute("type_time_2", TagDateTime,
parseTime("01/02 03:04:05PM '06 +0700")))
m.Operation.Add(MakeAttribute("type_time_3", TagDateTime,
parseTime("01/02 03:04:05PM '06 -0730")))
m.Operation.Add(MakeAttribute("type_resolution_1", TagResolution,
Resolution{123, 456, UnitsDpi}))
m.Operation.Add(MakeAttribute("type_resolution_2", TagResolution,
Resolution{78, 90, UnitsDpcm}))
m.Operation.Add(MakeAttribute("type_range", TagRange,
Range{100, 1000}))
m.Operation.Add(MakeAttribute("type_textlang_1", TagTextLang,
TextWithLang{"hello", "en"}))
m.Operation.Add(MakeAttribute("type_textlang_1", TagTextLang,
TextWithLang{"привет", "ru"}))
return m
}
// Test Message Encode, then Decode
func TestEncodeDecode(t *testing.T) {
m1 := testEncodeDecodeMessage()
data, err := m1.EncodeBytes()
assertNoError(t, err)
m2 := &Message{}
err = m2.DecodeBytes(data)
assertNoError(t, err)
}
// Test encode errors
func TestEncodeErrors(t *testing.T) {
// Attribute without name
m := NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a := MakeAttribute("attr", TagInteger, Integer(123))
a.Name = ""
m.Operation.Add(a)
err := m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute without name")
// Attribute without value
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, Integer(123))
a.Values = nil
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute without value")
// Attribute name exceeds...
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, Integer(123))
a.Name = strings.Repeat("x", 32767)
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertNoError(t, err)
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, Integer(123))
a.Name = strings.Repeat("x", 32767+1)
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute name exceeds 32767 bytes")
// Attribute value exceeds...
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagText, String(strings.Repeat("x", 32767)))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertNoError(t, err)
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagText, String(strings.Repeat("x", 32767+1)))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Attribute value exceeds 32767 bytes")
// Tag XXX cannot be used with value
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagJobGroup, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag job-attributes-tag cannot be used with value")
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagMemberName, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag memberAttrName cannot be used with value")
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagEndCollection, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag endCollection cannot be used with value")
// Collection member without name
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagBeginCollection, Collection{
MakeAttribute("", TagInteger, Integer(123)),
})
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Collection member without name")
// Tag XXX: YYY value required, ZZZ present
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagText, Integer(123))
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "Tag textWithoutLanguage: String value required, Integer present")
// I/O error
m = testEncodeDecodeMessage()
data, err := m.EncodeBytes()
assertNoError(t, err)
for skip := 0; skip < len(data); skip++ {
err = m.Encode(&errWriter{skip})
assertErrorIs(t, err, "I/O error")
}
// encode error
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagInteger, errValue{})
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "encode error")
m = NewRequest(DefaultVersion, OpGetPrinterAttributes, 0x12345678)
a = MakeAttribute("attr", TagBeginCollection, Collection{
MakeAttribute("attr", TagInteger, errValue{}),
})
m.Operation.Add(a)
err = m.Encode(ioutil.Discard)
assertErrorIs(t, err, "encode error")
}
// Test decode errors
func TestDecodeErrors(t *testing.T) {
var d []byte
var err error
var m = &Message{}
hdr := []byte{
0x01, 0x01, // IPP version
0x00, 0x02, // Print-Job operation
0x01, 0x02, 0x03, 0x04, // Request ID
}
body := []byte{}
// Message truncated
body = []byte{
uint8(TagJobGroup),
uint8(TagInteger),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Message truncated at")
d, err = testEncodeDecodeMessage().EncodeBytes()
assertNoError(t, err)
for i := 0; i < len(d); i++ {
err = m.DecodeBytes(d[:i])
assertErrorIs(t, err, "Message truncated at")
}
d = goodMessage1
for i := 0; i < len(d); i++ {
err = m.DecodeBytes(d[:i])
assertErrorIs(t, err, "Message truncated at")
}
// Invalid tag 0
d = append(hdr, 0)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Invalid tag 0 at 0x8")
// Attribute without a group
body = []byte{
uint8(TagInteger),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, uint8(TagJobGroup))
d = append(d, body...)
err = m.DecodeBytes(d)
assertNoError(t, err)
err = m.DecodeBytes(append(hdr, body...))
assertErrorIs(t, err, "Attribute without a group at")
// Additional value without preceding attribute
body = []byte{
uint8(TagJobGroup),
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagEnd),
}
err = m.DecodeBytes(append(hdr, body...))
assertErrorIs(t, err, "Additional value without preceding attribute")
// "Unexpected tag XXX"
for _, tag := range []Tag{TagMemberName, TagEndCollection} {
body = []byte{
uint8(TagJobGroup),
uint8(TagInteger),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
}
d = append(hdr, body...)
d = append(d, uint8(tag))
d = append(d, uint8(TagEnd))
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Unexpected tag")
}
// Collection: unexpected tag XXX
for tag := TagZero; tag.IsDelimiter(); tag++ {
body = []byte{
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x0a, // Name length + name
'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x06, // Value length + value
'm', 'e', 'm', 'b', 'e', 'r',
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(tag),
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
}
d = append(hdr, body...)
d = append(d, uint8(TagEnd))
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Collection: unexpected tag")
}
// Collection: unexpected endCollection, expected value tag
body = []byte{
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x0a, // Name length + name
'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x06, // Value length + value
'm', 'e', 'm', 'b', 'e', 'r',
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Collection: unexpected endCollection, expected value tag")
// Collection: unexpected memberAttrName, expected value tag
body = []byte{
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x0a, // Name length + name
'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x07, // Value length + value
'm', 'e', 'm', 'b', 'e', 'r', '1',
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x07, // Value length + value
'm', 'e', 'm', 'b', 'e', 'r', '2',
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Collection: unexpected memberAttrName, expected value tag")
// Collection: memberAttrName value is empty
body = []byte{
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x0a, // Name length + name
'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Collection: memberAttrName value is empty")
// Collection: unexpected integer, expected memberAttrName
body = []byte{
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x0a, // Name length + name
'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n',
0x00, 0x00, // No value
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Collection: unexpected integer, expected memberAttrName")
}
// Test errors in decoding values
func TestDecodeValueErrors(t *testing.T) {
var d []byte
var err error
var m = &Message{}
hdr := []byte{
0x01, 0x01, // IPP version
0x00, 0x02, // Print-Job operation
0x01, 0x02, 0x03, 0x04, // Request ID
}
body := []byte{}
// integer: value must be 4 bytes
body = []byte{
uint8(TagJobGroup),
uint8(TagInteger),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length + value
0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "integer: value must be 4 bytes")
// boolean: value must be 1 byte
body = []byte{
uint8(TagJobGroup),
uint8(TagBoolean),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length + value
0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "boolean: value must be 1 byte")
// dateTime: value must be 11 bytes
body = []byte{
uint8(TagJobGroup),
uint8(TagDateTime),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length + value
0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "dateTime: value must be 11 bytes")
// dateTime: bad XXX
var datetest = []struct {
in []byte
err string
}{
// year month day hour min sec s/10
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
""},
{[]byte{0x07, 0xe7, 0xff, 0x15, 0x11, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
"dateTime: bad month 255"},
{[]byte{0x07, 0xe7, 0x02, 0xff, 0x11, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
"dateTime: bad day 255"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0xff, 0x23, 0x32, 0x05, '+', 0x04, 0x00},
"dateTime: bad hours 255"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0xff, 0x32, 0x05, '+', 0x04, 0x00},
"dateTime: bad minutes 255"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0xff, 0x05, '+', 0x04, 0x00},
"dateTime: bad seconds 255"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0xff, '+', 0x04, 0x00},
"dateTime: bad deciseconds 255"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '?', 0x04, 0x00},
"dateTime: bad UTC sign"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '-', 0xff, 0x00},
"dateTime: bad UTC hours 255"},
{[]byte{0x07, 0xe7, 0x02, 0x15, 0x11, 0x23, 0x32, 0x05, '-', 0x04, 0xff},
"dateTime: bad UTC minutes 255"},
}
for _, test := range datetest {
body = []byte{
uint8(TagJobGroup),
uint8(TagDateTime),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x0b, // Value length + value
}
d = append(hdr, body...)
d = append(d, test.in...)
d = append(d, uint8(TagEnd))
err = m.DecodeBytes(d)
assertErrorIs(t, err, test.err)
}
// resolution: value must be 9 bytes
body = []byte{
uint8(TagJobGroup),
uint8(TagResolution),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length + value
0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "resolution: value must be 9 bytes ")
// rangeOfInteger: value must be 8 bytes
body = []byte{
uint8(TagJobGroup),
uint8(TagRange),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length + value
0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "rangeOfInteger: value must be 8 bytes")
// textWithLanguage: truncated language length
body = []byte{
uint8(TagJobGroup),
uint8(TagTextLang),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x01, // Value length
0x00,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "textWithLanguage: truncated language length")
// textWithLanguage: truncated language name
body = []byte{
uint8(TagJobGroup),
uint8(TagTextLang),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length
0x00, 0x02, // Language length
'e',
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "textWithLanguage: truncated language name")
// textWithLanguage: truncated text length
body = []byte{
uint8(TagJobGroup),
uint8(TagTextLang),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x05, // Value length
0x00, 0x02, // Language length
'e', 'n', // Language name
0x00, // Text length
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "textWithLanguage: truncated text length")
// textWithLanguage: truncated text string
body = []byte{
uint8(TagJobGroup),
uint8(TagTextLang),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x09, // Value length
0x00, 0x02, // Language length
'e', 'n', // Language name
0x00, 0x05, // Text length
'h', 'e', 'l',
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "textWithLanguage: truncated text string")
// textWithLanguage: extra 2 bytes at the end of value
body = []byte{
uint8(TagJobGroup),
uint8(TagTextLang),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x0d, // Value length
0x00, 0x02, // Language length
'e', 'n', // Language name
0x00, 0x05, // Text length
'h', 'e', 'l', 'l', 'o', // Test string
'?', '?', // Extra 2 bytes
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "textWithLanguage: extra 2 bytes at the end of value")
}
// Test TagExtension
func TestTagExtension(t *testing.T) {
// Normal tag cannot exceed 0xff and there is no automatic
// conversion of the high-value tags into extended.
m1 := NewResponse(DefaultVersion, StatusOk, 0x12345678)
m1.Operation.Add(MakeAttribute("attr", 0x100, Binary{1, 2, 3, 4, 5}))
_, err := m1.EncodeBytes()
assertErrorIs(t, err, "Tag 0x00000100 out of range")
// Ensure extension tag encodes and decodes well
m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
m1.Operation.Add(MakeAttribute("attr", TagExtension,
Binary{1, 2, 3, 4, 5}))
data, err := m1.EncodeBytes()
assertNoError(t, err)
m2 := Message{}
err = m2.DecodeBytes(data)
assertNoError(t, err)
if !m1.Equal(m2) {
t.Errorf("Message is not the same after encoding and decoding")
}
// Extension tag requires Binary payload
m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
m1.Operation.Add(MakeAttribute("attr", TagExtension,
String("hello")))
_, err = m1.EncodeBytes()
assertErrorIs(t, err, "Tag extension: Binary value required, String present")
// Extension tag requires at least 4 bytes of payload
m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
m1.Operation.Add(MakeAttribute("attr", TagExtension,
Binary{1, 2, 3}))
_, err = m1.EncodeBytes()
assertErrorIs(t, err, "Extension tag truncated")
// Extension tag can't exceed 0x7fffffff, check that encoder
// validates it.
m1 = NewResponse(DefaultVersion, StatusOk, 0x12345678)
m1.Operation.Add(MakeAttribute("attr", TagExtension,
Binary{0x80, 1, 2, 3, 4, 5}))
_, err = m1.EncodeBytes()
assertErrorIs(t, err, "Extension tag 0x80010203 out of range")
// Now prepare to decoder tests
var d []byte
var m = &Message{}
hdr := []byte{
0x01, 0x01, // IPP version
0x00, 0x02, // Print-Job operation
0x01, 0x02, 0x03, 0x04, // Request ID
}
body := []byte{}
// Extension tag truncated
body = []byte{
uint8(TagJobGroup),
uint8(TagExtension),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x03, // Value length + value
0x00, 0x54, 0x56,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Extension tag truncated")
// Extension tag out of range
body = []byte{
uint8(TagJobGroup),
uint8(TagExtension),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x08, // Value length + value
0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertErrorIs(t, err, "Extension tag 0xffffffff out of range")
// Extension tag with the small value is OK
body = []byte{
uint8(TagJobGroup),
uint8(TagExtension),
0x00, 0x04, // Name length + name
'a', 't', 't', 'r',
0x00, 0x08, // Value length + value
0x00, 0x00, 0x00, 0x01, 0, 0, 0, 0,
uint8(TagEnd),
}
d = append(hdr, body...)
err = m.DecodeBytes(d)
assertNoError(t, err)
}
// Test message decoding
func testDecode(t *testing.T, data []byte, opt DecoderOptions,
mustFail, mustEncode bool) {
var m Message
err := m.DecodeBytesEx(data, opt)
if mustFail {
assertWithError(t, err)
} else {
assertNoError(t, err)
}
if err != nil {
return
}
//m.Print(os.Stdout, true)
if !m.Equal(m) {
t.Errorf("Message is not equal to itself")
}
if mustEncode {
buf, err := m.EncodeBytes()
assertNoError(t, err)
if !bytes.Equal(buf, data) {
t.Errorf("Message is not the same after decoding and encoding")
}
}
// We can't test a lot of (*Message) Print(), so lets test
// at least that it doesn't hand
m.Print(ioutil.Discard, true)
}
func TestDecodeGoodMessage1(t *testing.T) {
testDecode(t, goodMessage1, DecoderOptions{}, false, true)
}
func TestDecodeGoodMessage2(t *testing.T) {
testDecode(t, goodMessage2, DecoderOptions{}, false, true)
}
func TestDecodeBadMessage1(t *testing.T) {
testDecode(t, badMessage1, DecoderOptions{}, true, false)
testDecode(t, badMessage1,
DecoderOptions{EnableWorkarounds: true}, false, false)
}
func TestDecodeHPOfficeJetPro8730(t *testing.T) {
testDecode(t, attrsHPOfficeJetPro8730, DecoderOptions{}, false, true)
}
func TestDecodePantumM7300FDW(t *testing.T) {
testDecode(t, attrsPantumM7300FDW,
DecoderOptions{EnableWorkarounds: false}, true, false)
testDecode(t, attrsPantumM7300FDW,
DecoderOptions{EnableWorkarounds: true}, false, false)
}
// ------------------------ Test Data ------------------------
// The good message - 1
var goodMessage1 = []byte{
0x01, 0x01, // IPP version
0x00, 0x02, // Print-Job operation
0x00, 0x00, 0x00, 0x01, // Request ID
uint8(TagOperationGroup),
uint8(TagCharset),
0x00, 0x12, // Name length + name
'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
'c', 'h', 'a', 'r', 's', 'e', 't',
0x00, 0x05, // Value length + value
'u', 't', 'f', '-', '8',
uint8(TagLanguage),
0x00, 0x1b, // Name length + name
'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
'n', 'a', 't', 'u', 'r', 'a', 'l', '-', 'l', 'a', 'n',
'g', 'u', 'a', 'g', 'e',
0x00, 0x02, // Value length + value
'e', 'n',
uint8(TagURI),
0x00, 0x0b, // Name length + name
'p', 'r', 'i', 'n', 't', 'e', 'r', '-', 'u', 'r', 'i',
0x00, 0x1c, // Value length + value
'i', 'p', 'p', ':', '/', '/', 'l', 'o', 'c', 'a', 'l',
'h', 'o', 's', 't', '/', 'p', 'r', 'i', 'n', 't', 'e',
'r', 's', '/', 'f', 'o', 'o',
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x09, // Name length + name
'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0a, // Value length + value
'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
uint8(TagBeginCollection),
0x00, 0x00, // Name length + name
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0b, // Value length + value
'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0b, // Value length + value
'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x6d, 0x24,
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0b, // Value length + value
'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',
uint8(TagKeyword),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
'b', 'l', 'u', 'e',
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0a, // Value length + value
'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',
uint8(TagKeyword),
0x00, 0x00, // No name
0x00, 0x05, // Value length + value
'p', 'l', 'a', 'i', 'n',
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagBeginCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0a, // Value length + value
'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
uint8(TagBeginCollection),
0x00, 0x00, // Name length + name
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0b, // Value length + value
'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x52, 0x08,
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0b, // Value length + value
'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
uint8(TagInteger),
0x00, 0x00, // No name
0x00, 0x04, // Value length + value
0x00, 0x00, 0x74, 0x04,
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0b, // Value length + value
'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',
uint8(TagKeyword),
0x00, 0x00, // No name
0x00, 0x05, // Value length + value
'p', 'l', 'a', 'i', 'd',
uint8(TagMemberName),
0x00, 0x00, // No name
0x00, 0x0a, // Value length + value
'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',
uint8(TagKeyword),
0x00, 0x00, // No name
0x00, 0x06, // Value length + value
'g', 'l', 'o', 's', 's', 'y',
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEnd),
}
// The good message - 2
var goodMessage2 = []byte{
0x01, 0x01, // IPP version
0x00, 0x02, // Print-Job operation
0x00, 0x00, 0x00, 0x01, // Request ID
uint8(TagOperationGroup),
uint8(TagInteger),
0x00, 0x1f, // Name length + name
'n', 'o', 't', 'i', 'f', 'y', '-', 'l', 'e', 'a', 's', 'e',
'-', 'd', 'u', 'r', 'a', 't', 'i', 'o', 'n', '-', 's', 'u',
'p', 'p', 'o', 'r', 't', 'e', 'd',
0x00, 0x04, // Value length + value
0x00, 0x00, 0x00, 0x01,
uint8(TagRange),
0x00, 0x00, // No name
0x00, 0x08, // Value length + value
0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x20,
uint8(TagEnd),
}
// The bad message - 1
//
// This message violates IPP encoding rules: instead of
// using TagMemberName it uses named attributes
//
// It must not decode normally, but must decode with workarounds
var badMessage1 = []byte{
0x01, 0x01, // IPP version */
0x00, 0x02, // Print-Job operation */
0x00, 0x00, 0x00, 0x01, // Request ID */
uint8(TagOperationGroup),
uint8(TagCharset),
0x00, 0x12, // Name length + name
'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
'c', 'h', 'a', 'r', 's', 'e', 't',
0x00, 0x05, // Value length + value
'u', 't', 'f', '-', '8',
uint8(TagLanguage),
0x00, 0x1b, // Name length + name
'a', 't', 't', 'r', 'i', 'b', 'u', 't', 'e', 's', '-',
'n', 'a', 't', 'u', 'r', 'a', 'l', '-', 'l', 'a', 'n',
'g', 'u', 'a', 'g', 'e',
0x00, 0x02, // Value length + value
'e', 'n',
uint8(TagURI),
0x00, 0x0b, // Name length + name
'p', 'r', 'i', 'n', 't', 'e', 'r', '-', 'u', 'r', 'i',
0x00, 0x1c, // Value length + value
'i', 'p', 'p', ':', '/', '/', 'l', 'o', 'c', 'a', 'l',
'h', 'o', 's', 't', '/', 'p', 'r', 'i', 'n', 't', 'e',
'r', 's', '/', 'f', 'o', 'o',
uint8(TagJobGroup),
uint8(TagBeginCollection),
0x00, 0x09, // Name length + name
'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
0x00, 0x00, // No value
uint8(TagBeginCollection),
0x00, 0x0a, // Name length + name
'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
0x00, 0x00, // No value
uint8(TagInteger),
0x00, 0x0b, // Name length + name
'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
0x00, 0x04, // Value length + value
0x00, 0x00, 0x54, 0x56,
uint8(TagInteger),
0x00, 0x0b, // Name length + name
'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
0x00, 0x04, // Value length + value
0x00, 0x00, 0x6d, 0x24,
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEndCollection),
0x00, 0x00, // No name
0x00, 0x00, // No value
uint8(TagEnd),
}
// The big real example, Get-Printer-Attributes output
// from the HP OfficeJet Pro 8730
var attrsHPOfficeJetPro8730 = []byte{
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x47, 0x00, 0x12, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05,
0x75, 0x74, 0x66, 0x2d, 0x38, 0x48, 0x00, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x2d, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75,
0x61, 0x67, 0x65, 0x00, 0x02, 0x65, 0x6e, 0x04, 0x45, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x75, 0x72, 0x69, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x19, 0x69, 0x70, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73,
0x74, 0x2f, 0x69, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x45, 0x00, 0x00, 0x00, 0x1e,
0x69, 0x70, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
0x3a, 0x34, 0x34, 0x33, 0x2f, 0x69, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x44, 0x00,
0x16, 0x75, 0x72, 0x69, 0x2d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2d, 0x73, 0x75,
0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x44, 0x00, 0x00,
0x00, 0x03, 0x74, 0x6c, 0x73, 0x44, 0x00, 0x1c, 0x75, 0x72, 0x69, 0x2d, 0x61, 0x75, 0x74, 0x68,
0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x00, 0x14, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x14, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6e,
0x61, 0x6d, 0x65, 0x44, 0x00, 0x25, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x65,
0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x11, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d, 0x73, 0x73, 0x69, 0x64, 0x44, 0x00,
0x00, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d,
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x42, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d, 0x73, 0x73, 0x69, 0x64, 0x00, 0x09, 0x41, 0x31,
0x2d, 0x35, 0x45, 0x46, 0x33, 0x37, 0x31, 0x23, 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x2d, 0x77, 0x69, 0x66, 0x69, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x00, 0x04, 0x00, 0x00,
0x00, 0x04, 0x42, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d,
0x65, 0x00, 0x08, 0x48, 0x50, 0x30, 0x38, 0x43, 0x32, 0x32, 0x39, 0x41, 0x00, 0x10, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
0x45, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x6f, 0x72, 0x65, 0x2d,
0x69, 0x6e, 0x66, 0x6f, 0x00, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x23, 0x68, 0x49, 0x64, 0x2d, 0x70, 0x67, 0x41, 0x69,
0x72, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x41, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x1e, 0x48, 0x50, 0x20, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65,
0x4a, 0x65, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37, 0x33, 0x30, 0x20, 0x5b, 0x30, 0x38,
0x43, 0x32, 0x32, 0x39, 0x5d, 0x42, 0x00, 0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
0x64, 0x6e, 0x73, 0x2d, 0x73, 0x64, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x1e, 0x48, 0x50, 0x20,
0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x4a, 0x65, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37,
0x33, 0x30, 0x20, 0x5b, 0x30, 0x38, 0x43, 0x32, 0x32, 0x39, 0x5d, 0x41, 0x00, 0x16, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d,
0x6f, 0x64, 0x65, 0x6c, 0x00, 0x15, 0x48, 0x50, 0x20, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x4a,
0x65, 0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37, 0x33, 0x30, 0x23, 0x00, 0x0d, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00,
0x03, 0x44, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74,
0x65, 0x2d, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x00, 0x1a, 0x77, 0x69, 0x66, 0x69, 0x2d,
0x6e, 0x6f, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x2d, 0x72,
0x65, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00, 0x44,
0x00, 0x16, 0x69, 0x70, 0x70, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2e, 0x30, 0x44, 0x00, 0x00,
0x00, 0x03, 0x31, 0x2e, 0x31, 0x44, 0x00, 0x00, 0x00, 0x03, 0x32, 0x2e, 0x30, 0x23, 0x00, 0x14,
0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x23, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x39, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x23, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x06, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x23, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x40, 0x29, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x2a, 0x23, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x23,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3b, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x3c, 0x47, 0x00, 0x12, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x47, 0x00, 0x11,
0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x48, 0x00, 0x1b, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x61, 0x6c, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x00, 0x02, 0x65, 0x6e, 0x48, 0x00, 0x24, 0x67, 0x65, 0x6e,
0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x2d, 0x6c,
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x02, 0x65, 0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x66, 0x72, 0x48, 0x00, 0x00, 0x00,
0x02, 0x64, 0x65, 0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x73, 0x48, 0x00, 0x00, 0x00, 0x02, 0x69,
0x74, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73, 0x76, 0x48, 0x00, 0x00, 0x00, 0x02, 0x64, 0x61, 0x48,
0x00, 0x00, 0x00, 0x02, 0x6e, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6e, 0x6c, 0x48, 0x00, 0x00,
0x00, 0x02, 0x66, 0x69, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6a, 0x61, 0x48, 0x00, 0x00, 0x00, 0x02,
0x70, 0x74, 0x48, 0x00, 0x00, 0x00, 0x02, 0x70, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02, 0x74, 0x72,
0x48, 0x00, 0x00, 0x00, 0x05, 0x7a, 0x68, 0x2d, 0x74, 0x77, 0x48, 0x00, 0x00, 0x00, 0x05, 0x7a,
0x68, 0x2d, 0x63, 0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02,
0x63, 0x73, 0x48, 0x00, 0x00, 0x00, 0x02, 0x68, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6b, 0x6f,
0x48, 0x00, 0x00, 0x00, 0x02, 0x68, 0x65, 0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x6c, 0x48, 0x00,
0x00, 0x00, 0x02, 0x61, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x62, 0x67, 0x48, 0x00, 0x00, 0x00,
0x02, 0x68, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73,
0x6b, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73, 0x6c, 0x48, 0x00, 0x23, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75,
0x61, 0x67, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x02,
0x65, 0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x66, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x64, 0x65,
0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x73, 0x48, 0x00, 0x00, 0x00, 0x02, 0x69, 0x74, 0x48, 0x00,
0x00, 0x00, 0x02, 0x73, 0x76, 0x48, 0x00, 0x00, 0x00, 0x02, 0x64, 0x61, 0x48, 0x00, 0x00, 0x00,
0x02, 0x6e, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6e, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02, 0x66,
0x69, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6a, 0x61, 0x48, 0x00, 0x00, 0x00, 0x02, 0x70, 0x74, 0x48,
0x00, 0x00, 0x00, 0x02, 0x70, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02, 0x74, 0x72, 0x48, 0x00, 0x00,
0x00, 0x05, 0x7a, 0x68, 0x2d, 0x74, 0x77, 0x48, 0x00, 0x00, 0x00, 0x05, 0x7a, 0x68, 0x2d, 0x63,
0x6e, 0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02, 0x63, 0x73, 0x48,
0x00, 0x00, 0x00, 0x02, 0x68, 0x75, 0x48, 0x00, 0x00, 0x00, 0x02, 0x6b, 0x6f, 0x48, 0x00, 0x00,
0x00, 0x02, 0x68, 0x65, 0x48, 0x00, 0x00, 0x00, 0x02, 0x65, 0x6c, 0x48, 0x00, 0x00, 0x00, 0x02,
0x61, 0x72, 0x48, 0x00, 0x00, 0x00, 0x02, 0x62, 0x67, 0x48, 0x00, 0x00, 0x00, 0x02, 0x68, 0x72,
0x48, 0x00, 0x00, 0x00, 0x02, 0x72, 0x6f, 0x48, 0x00, 0x00, 0x00, 0x02, 0x73, 0x6b, 0x48, 0x00,
0x00, 0x00, 0x02, 0x73, 0x6c, 0x45, 0x00, 0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x75, 0x72, 0x69, 0x00, 0x25, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x69, 0x70,
0x70, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x65, 0x6e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x73, 0x49, 0x00, 0x17, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f,
0x72, 0x6d, 0x61, 0x74, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x18, 0x61, 0x70,
0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d,
0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x00, 0x19, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
0x65, 0x64, 0x00, 0x16, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f,
0x76, 0x6e, 0x64, 0x2e, 0x68, 0x70, 0x2d, 0x50, 0x43, 0x4c, 0x49, 0x00, 0x00, 0x00, 0x18, 0x61,
0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x68,
0x70, 0x2d, 0x50, 0x43, 0x4c, 0x58, 0x4c, 0x49, 0x00, 0x00, 0x00, 0x16, 0x61, 0x70, 0x70, 0x6c,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x6f, 0x73, 0x74, 0x73, 0x63, 0x72, 0x69,
0x70, 0x74, 0x49, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x2f, 0x70, 0x64, 0x66, 0x49, 0x00, 0x00, 0x00, 0x0a, 0x69, 0x6d, 0x61, 0x67, 0x65,
0x2f, 0x6a, 0x70, 0x65, 0x67, 0x49, 0x00, 0x00, 0x00, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x50, 0x43, 0x4c, 0x6d, 0x49, 0x00, 0x00, 0x00, 0x09, 0x69,
0x6d, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x72, 0x66, 0x49, 0x00, 0x00, 0x00, 0x10, 0x69, 0x6d, 0x61,
0x67, 0x65, 0x2f, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73, 0x74, 0x65, 0x72, 0x49, 0x00, 0x00,
0x00, 0x18, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63,
0x74, 0x65, 0x74, 0x2d, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x00, 0x21, 0x64, 0x6f, 0x63,
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2d, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x05,
0x50, 0x43, 0x4c, 0x35, 0x63, 0x41, 0x00, 0x00, 0x00, 0x05, 0x50, 0x43, 0x4c, 0x58, 0x4c, 0x41,
0x00, 0x00, 0x00, 0x04, 0x50, 0x53, 0x2f, 0x33, 0x41, 0x00, 0x00, 0x00, 0x0c, 0x4e, 0x41, 0x54,
0x49, 0x56, 0x45, 0x4f, 0x46, 0x46, 0x49, 0x43, 0x45, 0x41, 0x00, 0x00, 0x00, 0x07, 0x50, 0x44,
0x46, 0x2f, 0x31, 0x2e, 0x37, 0x41, 0x00, 0x00, 0x00, 0x07, 0x50, 0x43, 0x4c, 0x33, 0x47, 0x55,
0x49, 0x41, 0x00, 0x00, 0x00, 0x04, 0x50, 0x43, 0x4c, 0x33, 0x41, 0x00, 0x00, 0x00, 0x03, 0x50,
0x4a, 0x4c, 0x41, 0x00, 0x00, 0x00, 0x09, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63,
0x41, 0x00, 0x00, 0x00, 0x04, 0x4a, 0x50, 0x45, 0x47, 0x41, 0x00, 0x00, 0x00, 0x04, 0x50, 0x43,
0x4c, 0x4d, 0x41, 0x00, 0x00, 0x00, 0x0b, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x52, 0x61, 0x73, 0x74,
0x65, 0x72, 0x41, 0x00, 0x00, 0x00, 0x09, 0x50, 0x57, 0x47, 0x52, 0x61, 0x73, 0x74, 0x65, 0x72,
0x22, 0x00, 0x19, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x73, 0x2d, 0x61, 0x63,
0x63, 0x65, 0x70, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x6a, 0x6f, 0x62, 0x73, 0x00, 0x01, 0x01, 0x21,
0x00, 0x10, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x2d, 0x6a, 0x6f, 0x62, 0x2d, 0x63, 0x6f, 0x75,
0x6e, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x16, 0x70, 0x64, 0x6c, 0x2d, 0x6f,
0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x09, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x21, 0x00, 0x0f, 0x70,
0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x70, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x04,
0x00, 0x00, 0x07, 0xdf, 0x31, 0x00, 0x14, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x63,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x0b, 0x07, 0xe4, 0x01,
0x13, 0x0b, 0x17, 0x17, 0x00, 0x2b, 0x00, 0x00, 0x44, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x70, 0x72,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x44, 0x00, 0x00, 0x00, 0x07, 0x64, 0x65, 0x66, 0x6c, 0x61,
0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x22, 0x00, 0x0f, 0x63, 0x6f,
0x6c, 0x6f, 0x72, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x01,
0x44, 0x00, 0x21, 0x6a, 0x6f, 0x62, 0x2d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x00, 0x06, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00,
0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x44, 0x00, 0x00, 0x00, 0x0e,
0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x44, 0x00,
0x00, 0x00, 0x11, 0x6a, 0x6f, 0x62, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x70, 0x65, 0x72,
0x2d, 0x73, 0x65, 0x74, 0x44, 0x00, 0x00, 0x00, 0x05, 0x73, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00,
0x00, 0x00, 0x15, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x44, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64,
0x69, 0x61, 0x44, 0x00, 0x00, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x71, 0x75, 0x61,
0x6c, 0x69, 0x74, 0x79, 0x44, 0x00, 0x00, 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0a,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x62, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x09, 0x6d,
0x65, 0x64, 0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x44, 0x00, 0x00, 0x00, 0x0b, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x44, 0x00, 0x00, 0x00, 0x16, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d,
0x69, 0x7a, 0x65, 0x44, 0x00, 0x00, 0x00, 0x16, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x00,
0x00, 0x00, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d, 0x6d,
0x6f, 0x64, 0x65, 0x44, 0x00, 0x00, 0x00, 0x16, 0x69, 0x70, 0x70, 0x2d, 0x61, 0x74, 0x74, 0x72,
0x69, 0x62, 0x75, 0x74, 0x65, 0x2d, 0x66, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x74, 0x79, 0x44, 0x00,
0x00, 0x00, 0x08, 0x6a, 0x6f, 0x62, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0b,
0x70, 0x61, 0x67, 0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x1a,
0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
0x74, 0x2d, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6a,
0x6f, 0x62, 0x2d, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2d, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x09, 0x6f, 0x76, 0x65, 0x72,
0x72, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x16, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d,
0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74,
0x44, 0x00, 0x00, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x61, 0x6c, 0x69,
0x6e, 0x67, 0x46, 0x00, 0x1f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2d, 0x75,
0x72, 0x69, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x68, 0x74, 0x74, 0x70, 0x46, 0x00, 0x00, 0x00, 0x05, 0x68,
0x74, 0x74, 0x70, 0x73, 0x41, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x64,
0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x01, 0x99, 0x4d, 0x46, 0x47, 0x3a, 0x48, 0x50,
0x3b, 0x4d, 0x44, 0x4c, 0x3a, 0x48, 0x50, 0x20, 0x4f, 0x66, 0x66, 0x69, 0x63, 0x65, 0x4a, 0x65,
0x74, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x38, 0x37, 0x33, 0x30, 0x3b, 0x43, 0x4d, 0x44, 0x3a, 0x50,
0x43, 0x4c, 0x35, 0x63, 0x2c, 0x50, 0x43, 0x4c, 0x58, 0x4c, 0x2c, 0x50, 0x4f, 0x53, 0x54, 0x53,
0x43, 0x52, 0x49, 0x50, 0x54, 0x2c, 0x4e, 0x41, 0x54, 0x49, 0x56, 0x45, 0x4f, 0x46, 0x46, 0x49,
0x43, 0x45, 0x2c, 0x50, 0x44, 0x46, 0x2c, 0x50, 0x43, 0x4c, 0x33, 0x47, 0x55, 0x49, 0x2c, 0x50,
0x43, 0x4c, 0x33, 0x2c, 0x50, 0x4a, 0x4c, 0x2c, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69,
0x63, 0x2c, 0x4a, 0x50, 0x45, 0x47, 0x2c, 0x50, 0x43, 0x4c, 0x4d, 0x2c, 0x41, 0x70, 0x70, 0x6c,
0x65, 0x52, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2c, 0x50, 0x57, 0x47, 0x52, 0x61, 0x73, 0x74, 0x65,
0x72, 0x2c, 0x38, 0x30, 0x32, 0x2e, 0x31, 0x31, 0x2c, 0x38, 0x30, 0x32, 0x2e, 0x33, 0x2c, 0x44,
0x45, 0x53, 0x4b, 0x4a, 0x45, 0x54, 0x2c, 0x44, 0x59, 0x4e, 0x3b, 0x43, 0x4c, 0x53, 0x3a, 0x50,
0x52, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x3b, 0x44, 0x45, 0x53, 0x3a, 0x44, 0x39, 0x4c, 0x32, 0x30,
0x41, 0x3b, 0x43, 0x49, 0x44, 0x3a, 0x48, 0x50, 0x49, 0x4a, 0x56, 0x49, 0x50, 0x41, 0x56, 0x39,
0x3b, 0x4c, 0x45, 0x44, 0x4d, 0x44, 0x49, 0x53, 0x3a, 0x55, 0x53, 0x42, 0x23, 0x46, 0x46, 0x23,
0x43, 0x43, 0x23, 0x30, 0x30, 0x2c, 0x55, 0x53, 0x42, 0x23, 0x46, 0x46, 0x23, 0x30, 0x34, 0x23,
0x30, 0x31, 0x3b, 0x4d, 0x43, 0x54, 0x3a, 0x4d, 0x46, 0x3b, 0x4d, 0x43, 0x4c, 0x3a, 0x44, 0x49,
0x3b, 0x4d, 0x43, 0x56, 0x3a, 0x33, 0x2e, 0x30, 0x3b, 0x53, 0x4e, 0x3a, 0x43, 0x4e, 0x37, 0x38,
0x33, 0x46, 0x36, 0x30, 0x57, 0x31, 0x3b, 0x53, 0x3a, 0x30, 0x33, 0x38, 0x30, 0x38, 0x30, 0x43,
0x34, 0x38, 0x34, 0x31, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x35, 0x38, 0x30, 0x30, 0x38,
0x30, 0x30, 0x30, 0x30, 0x34, 0x34, 0x31, 0x38, 0x30, 0x30, 0x33, 0x63, 0x34, 0x35, 0x31, 0x38,
0x30, 0x30, 0x34, 0x36, 0x34, 0x36, 0x31, 0x38, 0x30, 0x30, 0x33, 0x63, 0x34, 0x31, 0x31, 0x38,
0x30, 0x30, 0x34, 0x36, 0x3b, 0x5a, 0x3a, 0x30, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x30,
0x30, 0x30, 0x30, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30,
0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x39, 0x2c, 0x31, 0x32, 0x30, 0x30, 0x30, 0x2c, 0x31, 0x37,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2c, 0x31,
0x38, 0x31, 0x3b, 0x30, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x61, 0x6c,
0x65, 0x72, 0x74, 0x00, 0x27, 0x63, 0x6f, 0x64, 0x65, 0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77,
0x6e, 0x3b, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72,
0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x30, 0x00, 0x00, 0x00,
0x27, 0x63, 0x6f, 0x64, 0x65, 0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x73, 0x65,
0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x3b, 0x67, 0x72, 0x6f,
0x75, 0x70, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x30, 0x00, 0x00, 0x00, 0x27, 0x63, 0x6f, 0x64,
0x65, 0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69,
0x74, 0x79, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x3d, 0x6f,
0x74, 0x68, 0x65, 0x72, 0x30, 0x00, 0x00, 0x00, 0x27, 0x63, 0x6f, 0x64, 0x65, 0x3d, 0x75, 0x6e,
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x3d, 0x6f,
0x74, 0x68, 0x65, 0x72, 0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x3d, 0x6f, 0x74, 0x68, 0x65, 0x72,
0x41, 0x00, 0x19, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x61, 0x6c, 0x65, 0x72, 0x74,
0x2d, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x09, 0x67, 0x65,
0x6e, 0x75, 0x69, 0x6e, 0x65, 0x48, 0x50, 0x41, 0x00, 0x00, 0x00, 0x09, 0x67, 0x65, 0x6e, 0x75,
0x69, 0x6e, 0x65, 0x48, 0x50, 0x41, 0x00, 0x00, 0x00, 0x09, 0x67, 0x65, 0x6e, 0x75, 0x69, 0x6e,
0x65, 0x48, 0x50, 0x41, 0x00, 0x00, 0x00, 0x09, 0x67, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x48,
0x50, 0x45, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x75, 0x69, 0x64,
0x00, 0x2d, 0x75, 0x72, 0x6e, 0x3a, 0x75, 0x75, 0x69, 0x64, 0x3a, 0x64, 0x62, 0x63, 0x63, 0x34,
0x62, 0x35, 0x38, 0x2d, 0x66, 0x63, 0x34, 0x63, 0x2d, 0x66, 0x36, 0x66, 0x64, 0x2d, 0x62, 0x34,
0x64, 0x36, 0x2d, 0x32, 0x62, 0x30, 0x30, 0x64, 0x35, 0x35, 0x34, 0x61, 0x64, 0x34, 0x37, 0x23,
0x00, 0x29, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2d, 0x6f, 0x72, 0x69, 0x65,
0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,
0x64, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00,
0x05, 0x34, 0x00, 0x19, 0x6a, 0x6f, 0x62, 0x2d, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69,
0x6e, 0x74, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x00, 0x4a,
0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d,
0x65, 0x42, 0x00, 0x00, 0x00, 0x0f, 0x66, 0x75, 0x6c, 0x6c, 0x62, 0x6c, 0x65, 0x65, 0x64, 0x2d,
0x73, 0x69, 0x7a, 0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f,
0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c,
0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69,
0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44, 0x00, 0x00,
0x00, 0x14, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x35, 0x2e, 0x35,
0x78, 0x38, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x2d, 0x35, 0x78, 0x38, 0x5f, 0x35, 0x78, 0x38, 0x69, 0x6e, 0x44, 0x00, 0x00,
0x00, 0x14, 0x6e, 0x61, 0x5f, 0x6f, 0x66, 0x69, 0x63, 0x69, 0x6f, 0x5f, 0x38, 0x2e, 0x35, 0x78,
0x31, 0x33, 0x2e, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x6f, 0x6d, 0x5f, 0x31, 0x36,
0x6b, 0x5f, 0x31, 0x39, 0x35, 0x78, 0x32, 0x37, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10,
0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x38, 0x34, 0x78, 0x32, 0x36, 0x30, 0x6d, 0x6d,
0x44, 0x00, 0x00, 0x00, 0x14, 0x72, 0x6f, 0x63, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x37, 0x2e, 0x37,
0x35, 0x78, 0x31, 0x30, 0x2e, 0x37, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6a, 0x70,
0x6e, 0x5f, 0x6f, 0x75, 0x66, 0x75, 0x6b, 0x75, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x30, 0x30,
0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
0x2d, 0x31, 0x30, 0x5f, 0x34, 0x2e, 0x31, 0x32, 0x35, 0x78, 0x39, 0x2e, 0x35, 0x69, 0x6e, 0x44,
0x00, 0x00, 0x00, 0x16, 0x6e, 0x61, 0x5f, 0x6d, 0x6f, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x33,
0x2e, 0x38, 0x37, 0x35, 0x78, 0x37, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69,
0x73, 0x6f, 0x5f, 0x62, 0x35, 0x5f, 0x31, 0x37, 0x36, 0x78, 0x32, 0x35, 0x30, 0x6d, 0x6d, 0x44,
0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x35, 0x5f, 0x31, 0x36, 0x32, 0x78, 0x32,
0x32, 0x39, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x36, 0x5f,
0x31, 0x31, 0x34, 0x78, 0x31, 0x36, 0x32, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73,
0x6f, 0x5f, 0x64, 0x6c, 0x5f, 0x31, 0x31, 0x30, 0x78, 0x32, 0x32, 0x30, 0x6d, 0x6d, 0x44, 0x00,
0x00, 0x00, 0x13, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f, 0x75, 0x33, 0x5f, 0x31, 0x32, 0x30,
0x78, 0x32, 0x33, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6a, 0x70, 0x6e, 0x5f, 0x63,
0x68, 0x6f, 0x75, 0x34, 0x5f, 0x39, 0x30, 0x78, 0x32, 0x30, 0x35, 0x6d, 0x6d, 0x37, 0x00, 0x00,
0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f,
0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x75,
0x70, 0x6c, 0x65, 0x78, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x73,
0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64,
0x65, 0x64, 0x2d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00, 0x00,
0x00, 0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d, 0x6c, 0x6f, 0x6e, 0x67,
0x2d, 0x65, 0x64, 0x67, 0x65, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44,
0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x38, 0x2e,
0x35, 0x78, 0x31, 0x31, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x11, 0x6e, 0x61, 0x5f, 0x6c, 0x65,
0x67, 0x61, 0x6c, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00,
0x18, 0x6e, 0x61, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x37, 0x2e,
0x32, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6e, 0x61,
0x5f, 0x66, 0x6f, 0x6f, 0x6c, 0x73, 0x63, 0x61, 0x70, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x33,
0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x34, 0x5f, 0x32, 0x31,
0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x6a, 0x69, 0x73, 0x5f,
0x62, 0x35, 0x5f, 0x31, 0x38, 0x32, 0x78, 0x32, 0x35, 0x37, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00,
0x14, 0x6e, 0x61, 0x5f, 0x6f, 0x66, 0x69, 0x63, 0x69, 0x6f, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31,
0x33, 0x2e, 0x34, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e,
0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x2d, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x44, 0x00,
0x00, 0x00, 0x09, 0x63, 0x61, 0x72, 0x64, 0x73, 0x74, 0x6f, 0x63, 0x6b, 0x37, 0x00, 0x00, 0x00,
0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c,
0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x75, 0x70,
0x6c, 0x65, 0x78, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x73, 0x69,
0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65,
0x64, 0x2d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00, 0x00, 0x00,
0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d, 0x6c, 0x6f, 0x6e, 0x67, 0x2d,
0x65, 0x64, 0x67, 0x65, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44, 0x00,
0x00, 0x00, 0x14, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63, 0x65, 0x5f, 0x35, 0x2e,
0x35, 0x78, 0x38, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69,
0x6e, 0x64, 0x65, 0x78, 0x2d, 0x33, 0x78, 0x35, 0x5f, 0x33, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00,
0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x34, 0x78, 0x36, 0x5f,
0x34, 0x78, 0x36, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x61, 0x5f, 0x35, 0x78, 0x37,
0x5f, 0x35, 0x78, 0x37, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e,
0x64, 0x65, 0x78, 0x2d, 0x35, 0x78, 0x38, 0x5f, 0x35, 0x78, 0x38, 0x69, 0x6e, 0x44, 0x00, 0x00,
0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x35, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x31, 0x30,
0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x36, 0x5f, 0x31, 0x30,
0x35, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6f, 0x65, 0x5f, 0x70,
0x68, 0x6f, 0x74, 0x6f, 0x2d, 0x6c, 0x5f, 0x33, 0x2e, 0x35, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00,
0x00, 0x00, 0x18, 0x6f, 0x6d, 0x5f, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x2d, 0x70, 0x68, 0x6f, 0x74,
0x6f, 0x5f, 0x31, 0x30, 0x30, 0x78, 0x31, 0x35, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10,
0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x39, 0x35, 0x78, 0x32, 0x37, 0x30, 0x6d, 0x6d,
0x44, 0x00, 0x00, 0x00, 0x10, 0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x38, 0x34, 0x78,
0x32, 0x36, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x72, 0x6f, 0x63, 0x5f, 0x31, 0x36,
0x6b, 0x5f, 0x37, 0x2e, 0x37, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x37, 0x35, 0x69, 0x6e, 0x44, 0x00,
0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f, 0x68, 0x61, 0x67, 0x61, 0x6b, 0x69, 0x5f, 0x31, 0x30,
0x30, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f,
0x6f, 0x75, 0x66, 0x75, 0x6b, 0x75, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x30, 0x30, 0x6d, 0x6d,
0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d, 0x31,
0x30, 0x5f, 0x34, 0x2e, 0x31, 0x32, 0x35, 0x78, 0x39, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00,
0x00, 0x16, 0x6e, 0x61, 0x5f, 0x6d, 0x6f, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x33, 0x2e, 0x38,
0x37, 0x35, 0x78, 0x37, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f,
0x5f, 0x62, 0x35, 0x5f, 0x31, 0x37, 0x36, 0x78, 0x32, 0x35, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00,
0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x35, 0x5f, 0x31, 0x36, 0x32, 0x78, 0x32, 0x32, 0x39,
0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x36, 0x5f, 0x31, 0x31,
0x34, 0x78, 0x31, 0x36, 0x32, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f,
0x64, 0x6c, 0x5f, 0x31, 0x31, 0x30, 0x78, 0x32, 0x32, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00,
0x13, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f, 0x75, 0x33, 0x5f, 0x31, 0x32, 0x30, 0x78, 0x32,
0x33, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f,
0x75, 0x34, 0x5f, 0x39, 0x30, 0x78, 0x32, 0x30, 0x35, 0x6d, 0x6d, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x17, 0x6a, 0x6f, 0x62, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x73,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00,
0x00, 0x00, 0x0f, 0x66, 0x75, 0x6c, 0x6c, 0x62, 0x6c, 0x65, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x7a,
0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70,
0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f,
0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01,
0x28, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74,
0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74,
0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0d, 0x72,
0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x00, 0x00, 0x00,
0x0c, 0x64, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x4a, 0x00, 0x00,
0x00, 0x05, 0x73, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x09, 0x6f, 0x6e, 0x65, 0x2d,
0x73, 0x69, 0x64, 0x65, 0x64, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a,
0x00, 0x00, 0x00, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d,
0x65, 0x42, 0x00, 0x00, 0x00, 0x0c, 0x64, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x2d, 0x73, 0x69, 0x7a,
0x65, 0x73, 0x4a, 0x00, 0x00, 0x00, 0x05, 0x73, 0x69, 0x64, 0x65, 0x73, 0x44, 0x00, 0x00, 0x00,
0x09, 0x6f, 0x6e, 0x65, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x37, 0x00, 0x00, 0x00, 0x00, 0x44,
0x00, 0x16, 0x69, 0x70, 0x70, 0x2d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2d, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x0c, 0x61, 0x69, 0x72, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x2d, 0x31, 0x2e, 0x35, 0x44, 0x00, 0x00, 0x00, 0x0e, 0x69, 0x70, 0x70, 0x2d, 0x65,
0x76, 0x65, 0x72, 0x79, 0x77, 0x68, 0x65, 0x72, 0x65, 0x44, 0x00, 0x14, 0x77, 0x68, 0x69, 0x63,
0x68, 0x2d, 0x6a, 0x6f, 0x62, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x44, 0x00, 0x00, 0x00, 0x0d,
0x6e, 0x6f, 0x74, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x44, 0x00, 0x00,
0x00, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x00, 0x11, 0x6a, 0x6f, 0x62, 0x2d, 0x69, 0x64, 0x73, 0x2d,
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x01, 0x22, 0x00, 0x1d, 0x72,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x75,
0x72, 0x69, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x01, 0x44,
0x00, 0x22, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x2d, 0x6f, 0x75, 0x74, 0x2d, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0b, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x6a, 0x6f,
0x62, 0x12, 0x00, 0x14, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x67, 0x65, 0x6f, 0x2d,
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x41, 0x00, 0x10, 0x6d, 0x6f, 0x70,
0x72, 0x69, 0x61, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64, 0x00, 0x03, 0x31,
0x2e, 0x33, 0x22, 0x00, 0x1e, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x2d, 0x61,
0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
0x74, 0x65, 0x64, 0x00, 0x01, 0x00, 0x31, 0x00, 0x1f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x64,
0x61, 0x74, 0x65, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x0b, 0x07, 0xe4, 0x01, 0x13, 0x0a, 0x31,
0x10, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x00, 0x1a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x74, 0x69,
0x6d, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x1e, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d,
0x64, 0x61, 0x74, 0x65, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x0b, 0x07, 0xe4, 0x01, 0x13, 0x0a,
0x31, 0x10, 0x00, 0x2b, 0x00, 0x00, 0x21, 0x00, 0x19, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d, 0x74, 0x69,
0x6d, 0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x1b, 0x64, 0x6f, 0x63, 0x75, 0x6d,
0x65, 0x6e, 0x74, 0x2d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x2d, 0x73, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0c, 0x70,
0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6b, 0x69, 0x6e, 0x64, 0x00, 0x08, 0x64, 0x6f, 0x63,
0x75, 0x6d, 0x65, 0x6e, 0x74, 0x44, 0x00, 0x00, 0x00, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00,
0x08, 0x70, 0x6f, 0x73, 0x74, 0x63, 0x61, 0x72, 0x64, 0x44, 0x00, 0x0f, 0x6d, 0x65, 0x64, 0x69,
0x61, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x12, 0x6e, 0x61, 0x5f,
0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x31, 0x69, 0x6e, 0x44,
0x00, 0x00, 0x00, 0x11, 0x6e, 0x61, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x5f, 0x38, 0x2e, 0x35,
0x78, 0x31, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x65, 0x78, 0x65,
0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x37, 0x2e, 0x32, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x35,
0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x76, 0x6f, 0x69, 0x63,
0x65, 0x5f, 0x35, 0x2e, 0x35, 0x78, 0x38, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x14,
0x6e, 0x61, 0x5f, 0x66, 0x6f, 0x6f, 0x6c, 0x73, 0x63, 0x61, 0x70, 0x5f, 0x38, 0x2e, 0x35, 0x78,
0x31, 0x33, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65,
0x78, 0x2d, 0x33, 0x78, 0x35, 0x5f, 0x33, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12,
0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x34, 0x78, 0x36, 0x5f, 0x34, 0x78, 0x36,
0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x6e, 0x61, 0x5f, 0x35, 0x78, 0x37, 0x5f, 0x35, 0x78,
0x37, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6e, 0x61, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
0x2d, 0x35, 0x78, 0x38, 0x5f, 0x35, 0x78, 0x38, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69,
0x73, 0x6f, 0x5f, 0x61, 0x34, 0x5f, 0x32, 0x31, 0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x44,
0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x35, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32,
0x31, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x36, 0x5f,
0x31, 0x30, 0x35, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x6a, 0x69,
0x73, 0x5f, 0x62, 0x35, 0x5f, 0x31, 0x38, 0x32, 0x78, 0x32, 0x35, 0x37, 0x6d, 0x6d, 0x44, 0x00,
0x00, 0x00, 0x12, 0x6f, 0x65, 0x5f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x2d, 0x6c, 0x5f, 0x33, 0x2e,
0x35, 0x78, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x18, 0x6f, 0x6d, 0x5f, 0x73, 0x6d, 0x61,
0x6c, 0x6c, 0x2d, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x5f, 0x31, 0x30, 0x30, 0x78, 0x31, 0x35, 0x30,
0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6e, 0x61, 0x5f, 0x6f, 0x66, 0x69, 0x63, 0x69, 0x6f,
0x5f, 0x38, 0x2e, 0x35, 0x78, 0x31, 0x33, 0x2e, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10,
0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x39, 0x35, 0x78, 0x32, 0x37, 0x30, 0x6d, 0x6d,
0x44, 0x00, 0x00, 0x00, 0x10, 0x6f, 0x6d, 0x5f, 0x31, 0x36, 0x6b, 0x5f, 0x31, 0x38, 0x34, 0x78,
0x32, 0x36, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x72, 0x6f, 0x63, 0x5f, 0x31, 0x36,
0x6b, 0x5f, 0x37, 0x2e, 0x37, 0x35, 0x78, 0x31, 0x30, 0x2e, 0x37, 0x35, 0x69, 0x6e, 0x44, 0x00,
0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f, 0x68, 0x61, 0x67, 0x61, 0x6b, 0x69, 0x5f, 0x31, 0x30,
0x30, 0x78, 0x31, 0x34, 0x38, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x14, 0x6a, 0x70, 0x6e, 0x5f,
0x6f, 0x75, 0x66, 0x75, 0x6b, 0x75, 0x5f, 0x31, 0x34, 0x38, 0x78, 0x32, 0x30, 0x30, 0x6d, 0x6d,
0x44, 0x00, 0x00, 0x00, 0x18, 0x6e, 0x61, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d, 0x31,
0x30, 0x5f, 0x34, 0x2e, 0x31, 0x32, 0x35, 0x78, 0x39, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00,
0x00, 0x16, 0x6e, 0x61, 0x5f, 0x6d, 0x6f, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x33, 0x2e, 0x38,
0x37, 0x35, 0x78, 0x37, 0x2e, 0x35, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f,
0x5f, 0x62, 0x35, 0x5f, 0x31, 0x37, 0x36, 0x78, 0x32, 0x35, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00,
0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x35, 0x5f, 0x31, 0x36, 0x32, 0x78, 0x32, 0x32, 0x39,
0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x63, 0x36, 0x5f, 0x31, 0x31,
0x34, 0x78, 0x31, 0x36, 0x32, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f,
0x64, 0x6c, 0x5f, 0x31, 0x31, 0x30, 0x78, 0x32, 0x32, 0x30, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00,
0x13, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f, 0x75, 0x33, 0x5f, 0x31, 0x32, 0x30, 0x78, 0x32,
0x33, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6a, 0x70, 0x6e, 0x5f, 0x63, 0x68, 0x6f,
0x75, 0x34, 0x5f, 0x39, 0x30, 0x78, 0x32, 0x30, 0x35, 0x6d, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x10,
0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x33, 0x78, 0x35, 0x69, 0x6e,
0x44, 0x00, 0x00, 0x00, 0x13, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6d, 0x61, 0x78, 0x5f,
0x38, 0x2e, 0x35, 0x78, 0x31, 0x34, 0x69, 0x6e, 0x44, 0x00, 0x0d, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f, 0x61, 0x34,
0x5f, 0x32, 0x31, 0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x44, 0x00, 0x13, 0x6d, 0x65, 0x64,
0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00, 0x00,
0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x44, 0x00, 0x00, 0x00, 0x10,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x44, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d,
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x44, 0x00, 0x00,
0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d,
0x61, 0x72, 0x67, 0x69, 0x6e, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0f, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x34, 0x00, 0x11, 0x6d, 0x65, 0x64,
0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00,
0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e,
0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x10,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d,
0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x6d,
0x61, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79,
0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x79, 0x37, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x17, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69,
0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65,
0x00, 0x00, 0x21, 0x00, 0x1b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d,
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21,
0x00, 0x1c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61,
0x72, 0x67, 0x69, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04,
0x00, 0x00, 0x01, 0x28, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x1a,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x1d, 0x6d, 0x65, 0x64, 0x69,
0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x2d,
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x21,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x16, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x00, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
0x74, 0x79, 0x70, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x0a,
0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x44, 0x00, 0x00, 0x00, 0x14, 0x63,
0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x65, 0x63, 0x6f, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x2d, 0x6c,
0x69, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x70,
0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x2d, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x25, 0x63, 0x6f,
0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x74, 0x72, 0x69, 0x66, 0x6f, 0x6c, 0x64, 0x2d, 0x62, 0x72, 0x6f,
0x63, 0x68, 0x75, 0x72, 0x65, 0x2d, 0x67, 0x6c, 0x6f, 0x73, 0x73, 0x79, 0x2d, 0x31, 0x38, 0x30,
0x67, 0x73, 0x6d, 0x44, 0x00, 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x62,
0x72, 0x6f, 0x63, 0x68, 0x75, 0x72, 0x65, 0x2d, 0x6d, 0x61, 0x74, 0x74, 0x65, 0x44, 0x00, 0x00,
0x00, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x62, 0x72, 0x6f, 0x63, 0x68, 0x75, 0x72,
0x65, 0x2d, 0x67, 0x6c, 0x6f, 0x73, 0x73, 0x79, 0x44, 0x00, 0x00, 0x00, 0x15, 0x63, 0x6f, 0x6d,
0x2e, 0x68, 0x70, 0x2e, 0x61, 0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x2d, 0x70, 0x68, 0x6f,
0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x16, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x79, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x44, 0x00, 0x00,
0x00, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x70, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x09, 0x63, 0x61, 0x72, 0x64, 0x73, 0x74,
0x6f, 0x63, 0x6b, 0x44, 0x00, 0x00, 0x00, 0x16, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65,
0x72, 0x79, 0x2d, 0x68, 0x65, 0x61, 0x76, 0x79, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x44, 0x00,
0x00, 0x00, 0x15, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x2d, 0x6c, 0x65,
0x74, 0x74, 0x65, 0x72, 0x68, 0x65, 0x61, 0x64, 0x34, 0x00, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x73, 0x69, 0x7a, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x6d, 0x24, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x8a, 0xe8, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x47, 0xef, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x68, 0x2e, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x36, 0x92, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x80, 0xfc, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x1d, 0xc4, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x27, 0xb0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3b, 0x88, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x45, 0x74, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x4f, 0x60, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x39, 0xd0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x29, 0x04, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x47, 0x18, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x64, 0x64, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x22, 0xba, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x31, 0x9c, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x27, 0x10, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3a, 0x98, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x84, 0xf4, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x4c, 0x2c, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x69, 0x78, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x47, 0xe0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x65, 0x90, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x4c, 0xe5, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x6a, 0xa9, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x27, 0x10, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x4e, 0x20, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x28, 0xed, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x5e, 0x42, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x26, 0x72, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x4a, 0x6a, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x44, 0xc0, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x61, 0xa8, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3f, 0x48, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x59, 0x74, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x2c, 0x88, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x3f, 0x48, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x2a, 0xf8, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x55, 0xf0, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x2e, 0xe0, 0x4a, 0x00,
0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x5b, 0xcc, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x23, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x50, 0x14, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x33, 0x00, 0x00,
0x00, 0x08, 0x00, 0x00, 0x1d, 0xc4, 0x00, 0x00, 0x54, 0x56, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x33, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x31, 0x9c, 0x00, 0x00, 0x8a, 0xe8, 0x37, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0b, 0x6d,
0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x00, 0x10, 0x69, 0x73, 0x6f, 0x5f,
0x61, 0x34, 0x5f, 0x32, 0x31, 0x30, 0x78, 0x32, 0x39, 0x37, 0x6d, 0x6d, 0x34, 0x00, 0x0f, 0x6d,
0x65, 0x64, 0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x72, 0x65, 0x61, 0x64, 0x79, 0x00, 0x00,
0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e,
0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00,
0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x10,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69,
0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d,
0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x6d,
0x61, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79,
0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72,
0x79, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0a,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a,
0x00, 0x00, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64,
0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x74,
0x04, 0x37, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x74, 0x6f, 0x70, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62,
0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d,
0x6c, 0x65, 0x66, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72,
0x69, 0x67, 0x68, 0x74, 0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x00, 0x00, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x00,
0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00,
0x00, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x37, 0x00, 0x00, 0x00,
0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61,
0x2d, 0x73, 0x69, 0x7a, 0x65, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x78,
0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x52, 0x08, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00, 0x00,
0x00, 0x4a, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x74, 0x6f, 0x70, 0x2d,
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a,
0x00, 0x00, 0x00, 0x13, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d,
0x2d, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28,
0x4a, 0x00, 0x00, 0x00, 0x11, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x6c, 0x65, 0x66, 0x74, 0x2d,
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a,
0x00, 0x00, 0x00, 0x12, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d,
0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x28, 0x4a,
0x00, 0x00, 0x00, 0x0c, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x44, 0x00, 0x00, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x00, 0x00, 0x00, 0x0a, 0x6d, 0x65,
0x64, 0x69, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x79, 0x37, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x14, 0x66,
0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x72, 0x65,
0x61, 0x64, 0x79, 0x00, 0x00, 0x44, 0x00, 0x18, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e,
0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x12, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x6d, 0x70,
0x6c, 0x61, 0x74, 0x65, 0x30, 0x00, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66,
0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x72, 0x00, 0x00, 0x30, 0x00, 0x1c, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x21, 0x00, 0x10, 0x70, 0x61, 0x67,
0x65, 0x73, 0x2d, 0x70, 0x65, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x00, 0x04, 0x00,
0x00, 0x00, 0x18, 0x21, 0x00, 0x16, 0x70, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x70, 0x65, 0x72, 0x2d,
0x6d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x04, 0x00, 0x00,
0x00, 0x14, 0x33, 0x00, 0x17, 0x6a, 0x70, 0x65, 0x67, 0x2d, 0x6b, 0x2d, 0x6f, 0x63, 0x74, 0x65,
0x74, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x33, 0x00, 0x1a, 0x6a, 0x70, 0x65, 0x67, 0x2d, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x33, 0x00, 0x1a,
0x6a, 0x70, 0x65, 0x67, 0x2d, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x40, 0x00, 0x33, 0x00, 0x16, 0x70, 0x64, 0x66, 0x2d, 0x6b, 0x2d, 0x6f, 0x63, 0x74,
0x65, 0x74, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x33, 0x00, 0x14, 0x70, 0x64, 0x66, 0x2d, 0x73, 0x69,
0x7a, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x00, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x22, 0x00, 0x17, 0x70, 0x64, 0x66, 0x2d, 0x66,
0x69, 0x74, 0x2d, 0x74, 0x6f, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x00, 0x01, 0x00, 0x22, 0x00, 0x19, 0x70, 0x64, 0x66, 0x2d, 0x66, 0x69, 0x74, 0x2d,
0x74, 0x6f, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x01, 0x00, 0x44, 0x00, 0x16, 0x70, 0x64, 0x66, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x10, 0x69,
0x73, 0x6f, 0x2d, 0x33, 0x32, 0x30, 0x30, 0x30, 0x2d, 0x31, 0x5f, 0x32, 0x30, 0x30, 0x38, 0x44,
0x00, 0x00, 0x00, 0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x37, 0x44, 0x00, 0x00,
0x00, 0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x36, 0x44, 0x00, 0x00, 0x00, 0x09,
0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x35, 0x44, 0x00, 0x00, 0x00, 0x10, 0x69, 0x73,
0x6f, 0x2d, 0x31, 0x39, 0x30, 0x30, 0x35, 0x2d, 0x31, 0x5f, 0x32, 0x30, 0x30, 0x35, 0x44, 0x00,
0x00, 0x00, 0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x34, 0x44, 0x00, 0x00, 0x00,
0x09, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x33, 0x44, 0x00, 0x00, 0x00, 0x09, 0x61,
0x64, 0x6f, 0x62, 0x65, 0x2d, 0x31, 0x2e, 0x32, 0x44, 0x00, 0x0d, 0x75, 0x72, 0x66, 0x2d, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x03, 0x43, 0x50, 0x31, 0x44, 0x00, 0x00,
0x00, 0x0f, 0x4d, 0x54, 0x31, 0x2d, 0x32, 0x2d, 0x38, 0x2d, 0x39, 0x2d, 0x31, 0x30, 0x2d, 0x31,
0x31, 0x44, 0x00, 0x00, 0x00, 0x07, 0x50, 0x51, 0x33, 0x2d, 0x34, 0x2d, 0x35, 0x44, 0x00, 0x00,
0x00, 0x09, 0x52, 0x53, 0x33, 0x30, 0x30, 0x2d, 0x36, 0x30, 0x30, 0x44, 0x00, 0x00, 0x00, 0x06,
0x53, 0x52, 0x47, 0x42, 0x32, 0x34, 0x44, 0x00, 0x00, 0x00, 0x04, 0x4f, 0x42, 0x31, 0x30, 0x44,
0x00, 0x00, 0x00, 0x02, 0x57, 0x38, 0x44, 0x00, 0x00, 0x00, 0x05, 0x44, 0x45, 0x56, 0x57, 0x38,
0x44, 0x00, 0x00, 0x00, 0x08, 0x44, 0x45, 0x56, 0x52, 0x47, 0x42, 0x32, 0x34, 0x44, 0x00, 0x00,
0x00, 0x0a, 0x41, 0x44, 0x4f, 0x42, 0x45, 0x52, 0x47, 0x42, 0x32, 0x34, 0x44, 0x00, 0x00, 0x00,
0x03, 0x44, 0x4d, 0x33, 0x44, 0x00, 0x00, 0x00, 0x03, 0x46, 0x4e, 0x33, 0x44, 0x00, 0x00, 0x00,
0x05, 0x49, 0x53, 0x31, 0x2d, 0x32, 0x44, 0x00, 0x00, 0x00, 0x04, 0x56, 0x31, 0x2e, 0x34, 0x42,
0x00, 0x0c, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x00, 0x08,
0x63, 0x79, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x00, 0x00, 0x0b, 0x6d, 0x61, 0x67,
0x65, 0x6e, 0x74, 0x61, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x00, 0x00, 0x0a, 0x79, 0x65, 0x6c,
0x6c, 0x6f, 0x77, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x00, 0x00, 0x09, 0x62, 0x6c, 0x61, 0x63,
0x6b, 0x20, 0x69, 0x6e, 0x6b, 0x42, 0x00, 0x0d, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x63,
0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x00, 0x07, 0x23, 0x30, 0x30, 0x46, 0x46, 0x46, 0x46, 0x42, 0x00,
0x00, 0x00, 0x07, 0x23, 0x46, 0x46, 0x30, 0x30, 0x46, 0x46, 0x42, 0x00, 0x00, 0x00, 0x07, 0x23,
0x46, 0x46, 0x46, 0x46, 0x30, 0x30, 0x42, 0x00, 0x00, 0x00, 0x07, 0x23, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x44, 0x00, 0x0c, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x74, 0x79, 0x70, 0x65,
0x73, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65,
0x44, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64,
0x67, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x72,
0x69, 0x64, 0x67, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x61, 0x72,
0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x21, 0x00, 0x11, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d,
0x6c, 0x6f, 0x77, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x01, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00, 0x12, 0x6d, 0x61,
0x72, 0x6b, 0x65, 0x72, 0x2d, 0x68, 0x69, 0x67, 0x68, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x73,
0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x21,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x64, 0x21, 0x00, 0x0d, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c,
0x73, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x46,
0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x46, 0x30, 0x00, 0x0e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x75, 0x70,
0x70, 0x6c, 0x79, 0x00, 0x65, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x72,
0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69,
0x74, 0x79, 0x3d, 0x31, 0x30, 0x30, 0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x36, 0x30, 0x3b,
0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x54, 0x68, 0x61, 0x74,
0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x3b, 0x75, 0x6e, 0x69, 0x74, 0x3d,
0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x61, 0x6e, 0x74,
0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x63, 0x79, 0x61, 0x6e, 0x3b, 0x30, 0x00, 0x00, 0x00, 0x68, 0x74,
0x79, 0x70, 0x65, 0x3d, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65,
0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3d, 0x31, 0x30, 0x30,
0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x37, 0x30, 0x3b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d,
0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x54, 0x68, 0x61, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73,
0x75, 0x6d, 0x65, 0x64, 0x3b, 0x75, 0x6e, 0x69, 0x74, 0x3d, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e,
0x74, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x6d,
0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x3b, 0x30, 0x00, 0x00, 0x00, 0x67, 0x74, 0x79, 0x70, 0x65,
0x3d, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x3b, 0x6d, 0x61,
0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3d, 0x31, 0x30, 0x30, 0x3b, 0x6c, 0x65,
0x76, 0x65, 0x6c, 0x3d, 0x36, 0x30, 0x3b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x73, 0x75, 0x70,
0x70, 0x6c, 0x79, 0x54, 0x68, 0x61, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
0x64, 0x3b, 0x75, 0x6e, 0x69, 0x74, 0x3d, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x3b, 0x63,
0x6f, 0x6c, 0x6f, 0x72, 0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x79, 0x65, 0x6c, 0x6c,
0x6f, 0x77, 0x3b, 0x30, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x69, 0x6e, 0x6b,
0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70,
0x61, 0x63, 0x69, 0x74, 0x79, 0x3d, 0x31, 0x30, 0x30, 0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d,
0x37, 0x30, 0x3b, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x3d, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x54,
0x68, 0x61, 0x74, 0x49, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x3b, 0x75, 0x6e,
0x69, 0x74, 0x3d, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x3b, 0x63, 0x6f, 0x6c, 0x6f, 0x72,
0x61, 0x6e, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x3b, 0x41, 0x00,
0x1a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x2d,
0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x0e, 0x43, 0x79, 0x61,
0x6e, 0x20, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x41, 0x00, 0x00, 0x00, 0x11,
0x4d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x20, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67,
0x65, 0x41, 0x00, 0x00, 0x00, 0x10, 0x59, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x43, 0x61, 0x72,
0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x41, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x6c, 0x61, 0x63, 0x6b,
0x20, 0x43, 0x61, 0x72, 0x74, 0x72, 0x69, 0x64, 0x67, 0x65, 0x42, 0x00, 0x15, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x6e, 0x61,
0x6d, 0x65, 0x00, 0x16, 0x57, 0x45, 0x42, 0x50, 0x44, 0x4c, 0x50, 0x50, 0x31, 0x4e, 0x30, 0x30,
0x31, 0x2e, 0x31, 0x39, 0x31, 0x39, 0x41, 0x2e, 0x30, 0x30, 0x41, 0x00, 0x1f, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x16, 0x57, 0x45,
0x42, 0x50, 0x44, 0x4c, 0x50, 0x50, 0x31, 0x4e, 0x30, 0x30, 0x31, 0x2e, 0x31, 0x39, 0x31, 0x39,
0x41, 0x2e, 0x30, 0x30, 0x30, 0x00, 0x18, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x66,
0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00,
0x16, 0x57, 0x45, 0x42, 0x50, 0x44, 0x4c, 0x50, 0x50, 0x31, 0x4e, 0x30, 0x30, 0x31, 0x2e, 0x31,
0x39, 0x31, 0x39, 0x41, 0x2e, 0x30, 0x30, 0x30, 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x2d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x2d, 0x74, 0x72, 0x61, 0x79, 0x00, 0x86, 0x74, 0x79,
0x70, 0x65, 0x3d, 0x73, 0x68, 0x65, 0x65, 0x74, 0x46, 0x65, 0x65, 0x64, 0x41, 0x75, 0x74, 0x6f,
0x4e, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x3b, 0x64, 0x69, 0x6d,
0x75, 0x6e, 0x69, 0x74, 0x3d, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
0x3b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x66, 0x65, 0x65, 0x64, 0x3d, 0x32, 0x39, 0x37, 0x30, 0x30,
0x30, 0x3b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x78, 0x66, 0x65, 0x65, 0x64, 0x3d, 0x32, 0x31, 0x30,
0x30, 0x30, 0x30, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3d,
0x2d, 0x32, 0x3b, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x3d, 0x2d, 0x32, 0x3b, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0x3d, 0x30, 0x3b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x54,
0x72, 0x61, 0x79, 0x31, 0x30, 0x00, 0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x74, 0x72, 0x61, 0x79, 0x00, 0x6e, 0x74, 0x79, 0x70, 0x65,
0x3d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x3b, 0x6d, 0x61, 0x78, 0x63, 0x61, 0x70, 0x61,
0x63, 0x69, 0x74, 0x79, 0x3d, 0x2d, 0x32, 0x3b, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e,
0x67, 0x3d, 0x2d, 0x32, 0x3b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3d, 0x35, 0x3b, 0x73, 0x74,
0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3d, 0x75, 0x6e, 0x6b, 0x6e,
0x6f, 0x77, 0x6e, 0x3b, 0x70, 0x61, 0x67, 0x65, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79,
0x3d, 0x66, 0x61, 0x63, 0x65, 0x44, 0x6f, 0x77, 0x6e, 0x3b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x4f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x72, 0x61, 0x79, 0x31, 0x21, 0x00, 0x0e, 0x63, 0x6f, 0x70,
0x69, 0x65, 0x73, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00,
0x01, 0x23, 0x00, 0x12, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x64,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x13, 0x00, 0x16, 0x66,
0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x2d, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x23, 0x00, 0x1d, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d,
0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x15,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2d, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x32, 0x00, 0x1a, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00,
0x02, 0x58, 0x03, 0x44, 0x00, 0x0d, 0x73, 0x69, 0x64, 0x65, 0x73, 0x2d, 0x64, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x00, 0x09, 0x6f, 0x6e, 0x65, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x44, 0x00,
0x12, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x62, 0x69, 0x6e, 0x2d, 0x64, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x00, 0x09, 0x66, 0x61, 0x63, 0x65, 0x2d, 0x64, 0x6f, 0x77, 0x6e, 0x44, 0x00,
0x13, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x64, 0x65, 0x66,
0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x18, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x64, 0x65,
0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x22, 0x6d, 0x75,
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x00, 0x24, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d,
0x65, 0x6e, 0x74, 0x73, 0x2d, 0x75, 0x6e, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x2d,
0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x21, 0x00, 0x11, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d,
0x75, 0x70, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
0x44, 0x00, 0x28, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
0x2d, 0x75, 0x70, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x10, 0x74, 0x6f, 0x72,
0x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x6f, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x44, 0x00, 0x1e,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2d,
0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04,
0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x15, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x61,
0x6c, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75,
0x74, 0x6f, 0x33, 0x00, 0x10, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70,
0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x23,
0x00, 0x14, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x2d, 0x73, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x33, 0x00, 0x1b, 0x6a,
0x6f, 0x62, 0x2d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x2d, 0x70, 0x65, 0x72, 0x2d, 0x73, 0x65, 0x74,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff, 0x23, 0x00, 0x1f, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x2d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x73, 0x75, 0x70,
0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x23, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07,
0x23, 0x00, 0x17, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03,
0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
0x00, 0x05, 0x32, 0x00, 0x1c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x72, 0x65, 0x73,
0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x09, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x32, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58, 0x03, 0x32, 0x00, 0x00, 0x00, 0x09, 0x00,
0x00, 0x04, 0xb0, 0x00, 0x00, 0x04, 0xb0, 0x03, 0x44, 0x00, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x73,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x6f, 0x6e, 0x65, 0x2d,
0x73, 0x69, 0x64, 0x65, 0x64, 0x44, 0x00, 0x00, 0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69,
0x64, 0x65, 0x64, 0x2d, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00,
0x00, 0x00, 0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d, 0x6c, 0x6f, 0x6e,
0x67, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00, 0x14, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x2d,
0x62, 0x69, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x66,
0x61, 0x63, 0x65, 0x2d, 0x64, 0x6f, 0x77, 0x6e, 0x44, 0x00, 0x15, 0x6f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x2d,
0x6d, 0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x6d,
0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x05, 0x63, 0x6f,
0x6c, 0x6f, 0x72, 0x44, 0x00, 0x1a, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6c, 0x6f,
0x72, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x75, 0x74, 0x6f, 0x2d,
0x6d, 0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x6d,
0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x44, 0x00, 0x00, 0x00, 0x05, 0x63, 0x6f,
0x6c, 0x6f, 0x72, 0x44, 0x00, 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x2d,
0x6d, 0x6f, 0x6e, 0x6f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x22, 0x00, 0x15, 0x70, 0x61, 0x67,
0x65, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
0x65, 0x64, 0x00, 0x01, 0x01, 0x44, 0x00, 0x24, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x69,
0x6e, 0x67, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x24, 0x73, 0x65,
0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73,
0x2d, 0x75, 0x6e, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x2d, 0x63, 0x6f, 0x70, 0x69,
0x65, 0x73, 0x44, 0x00, 0x00, 0x00, 0x22, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x2d,
0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2d, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74,
0x65, 0x64, 0x2d, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x21, 0x00, 0x13, 0x6e, 0x75, 0x6d, 0x62,
0x65, 0x72, 0x2d, 0x75, 0x70, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00,
0x04, 0x00, 0x00, 0x00, 0x01, 0x44, 0x00, 0x2a, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0x2d, 0x75, 0x70, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
0x65, 0x64, 0x00, 0x10, 0x74, 0x6f, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x74, 0x6f, 0x62, 0x6f,
0x74, 0x74, 0x6f, 0x6d, 0x44, 0x00, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x05, 0x70, 0x61, 0x67, 0x65,
0x73, 0x44, 0x00, 0x00, 0x00, 0x05, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x44, 0x00, 0x00, 0x00, 0x09,
0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x63, 0x6f, 0x6c, 0x44, 0x00, 0x20, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x2d, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x69, 0x6e, 0x74, 0x65,
0x6e, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x61, 0x75,
0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x75, 0x61,
0x6c, 0x44, 0x00, 0x17, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e,
0x67, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x61, 0x75, 0x74,
0x6f, 0x44, 0x00, 0x00, 0x00, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x2d, 0x66, 0x69, 0x74, 0x44, 0x00,
0x00, 0x00, 0x04, 0x66, 0x69, 0x6c, 0x6c, 0x44, 0x00, 0x00, 0x00, 0x03, 0x66, 0x69, 0x74, 0x44,
0x00, 0x00, 0x00, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x45, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x69, 0x63, 0x6f, 0x6e, 0x73, 0x00, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x77, 0x65, 0x62, 0x41, 0x70,
0x70, 0x73, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x2d, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x2e, 0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00, 0x2b,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
0x2f, 0x77, 0x65, 0x62, 0x41, 0x70, 0x70, 0x73, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00, 0x31,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
0x2f, 0x77, 0x65, 0x62, 0x41, 0x70, 0x70, 0x73, 0x2f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x2e, 0x70, 0x6e,
0x67, 0x45, 0x00, 0x17, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x75, 0x70, 0x70,
0x6c, 0x79, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x75, 0x72, 0x69, 0x00, 0x26, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x2f, 0x23, 0x68,
0x49, 0x64, 0x2d, 0x70, 0x67, 0x49, 0x6e, 0x6b, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x61, 0x62,
0x6c, 0x65, 0x73, 0x44, 0x00, 0x1e, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74,
0x65, 0x6e, 0x74, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x2d, 0x64, 0x65, 0x66,
0x61, 0x75, 0x6c, 0x74, 0x00, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x44, 0x00, 0x20, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6f, 0x70, 0x74, 0x69, 0x6d,
0x69, 0x7a, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x61,
0x75, 0x74, 0x6f, 0x44, 0x00, 0x00, 0x00, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x44, 0x00, 0x00,
0x00, 0x08, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x44, 0x00, 0x00, 0x00, 0x04, 0x74,
0x65, 0x78, 0x74, 0x44, 0x00, 0x00, 0x00, 0x11, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x61, 0x6e, 0x64,
0x2d, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x44, 0x00, 0x1e, 0x70, 0x77, 0x67, 0x2d,
0x72, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
0x73, 0x68, 0x65, 0x65, 0x74, 0x2d, 0x62, 0x61, 0x63, 0x6b, 0x00, 0x07, 0x72, 0x6f, 0x74, 0x61,
0x74, 0x65, 0x64, 0x44, 0x00, 0x22, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73, 0x74, 0x65, 0x72,
0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x2d, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x07, 0x73, 0x67, 0x72, 0x61, 0x79, 0x5f,
0x38, 0x44, 0x00, 0x00, 0x00, 0x06, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x38, 0x44, 0x00, 0x00, 0x00,
0x0b, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2d, 0x72, 0x67, 0x62, 0x5f, 0x38, 0x44, 0x00, 0x00, 0x00,
0x05, 0x72, 0x67, 0x62, 0x5f, 0x38, 0x32, 0x00, 0x28, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73,
0x74, 0x65, 0x72, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x65, 0x73,
0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x09, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00, 0x01, 0x2c, 0x03, 0x32, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58, 0x03, 0x41, 0x00, 0x16, 0x65, 0x70, 0x63,
0x6c, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
0x74, 0x65, 0x64, 0x00, 0x03, 0x31, 0x2e, 0x30, 0x22, 0x00, 0x17, 0x6d, 0x61, 0x6e, 0x75, 0x61,
0x6c, 0x2d, 0x64, 0x75, 0x70, 0x6c, 0x65, 0x78, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
0x65, 0x64, 0x00, 0x01, 0x00, 0x32, 0x00, 0x20, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x2d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x00,
0x01, 0x2c, 0x03, 0x32, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58,
0x03, 0x32, 0x00, 0x1e, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d,
0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x00, 0x09, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x02, 0x58, 0x03, 0x21, 0x00, 0x1b,
0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x70, 0x2d, 0x68, 0x65, 0x69, 0x67, 0x68,
0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x21, 0x00, 0x1b, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x70, 0x2d, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x2d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x00,
0x04, 0x00, 0x00, 0x00, 0x20, 0x44, 0x00, 0x15, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x72, 0x61, 0x73,
0x74, 0x65, 0x72, 0x2d, 0x62, 0x61, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x00, 0x07, 0x72,
0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x00, 0x21, 0x70, 0x63, 0x6c, 0x6d, 0x2d, 0x63, 0x6f,
0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x2d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x00, 0x04, 0x6a, 0x70, 0x65, 0x67,
0x44, 0x00, 0x00, 0x00, 0x05, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x00, 0x00, 0x00, 0x03, 0x72,
0x6c, 0x65, 0x44, 0x00, 0x22, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f,
0x72, 0x6d, 0x61, 0x74, 0x2d, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x2d, 0x61, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x06, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x44,
0x00, 0x00, 0x00, 0x15, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d,
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x44, 0x00, 0x20, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x2d, 0x67, 0x65, 0x74, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x0f, 0x64, 0x6f,
0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x41, 0x00, 0x14,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x41, 0x00, 0x1b, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x2d, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2d,
0x75, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x44, 0x00, 0x18, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
0x79, 0x2d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
0x74, 0x00, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x44, 0x00, 0x1a, 0x69, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x66, 0x79, 0x2d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75,
0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
0x44, 0x00, 0x00, 0x00, 0x05, 0x73, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x00, 0x00, 0x00, 0x03, 0x70,
0x69, 0x6e, 0x23, 0x00, 0x1a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00,
0x04, 0x00, 0x00, 0x00, 0x0a, 0x21, 0x00, 0x1b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65,
0x2d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x74, 0x69, 0x6d, 0x65, 0x2d,
0x6f, 0x75, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78, 0x22, 0x00, 0x20, 0x6d, 0x75, 0x6c, 0x74,
0x69, 0x70, 0x6c, 0x65, 0x2d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d, 0x6a, 0x6f,
0x62, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x00, 0x03,
}
// Get-Printer-Attributes output from Pantum M7300FDW
var attrsPantumM7300FDW = []byte{
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x47, 0x00, 0x12,
0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2d, 0x63,
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x05, 0x75, 0x74, 0x66, 0x2d,
0x38, 0x48, 0x00, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x2d, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x2d, 0x6c,
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x05, 0x65, 0x6e, 0x2d,
0x55, 0x53, 0x04, 0x22, 0x00, 0x0f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2d,
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x01, 0x00,
0x49, 0x00, 0x19, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2d,
0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x00, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
0x75, 0x72, 0x66, 0x49, 0x00, 0x00, 0x00, 0x10, 0x69, 0x6d, 0x61, 0x67,
0x65, 0x2f, 0x70, 0x77, 0x67, 0x2d, 0x72, 0x61, 0x73, 0x74, 0x65, 0x72,
0x49, 0x00, 0x00, 0x00, 0x18, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6f, 0x63, 0x74, 0x65, 0x74, 0x2d, 0x73,
0x74, 0x72, 0x65, 0x61, 0x6d, 0x41, 0x00, 0x10, 0x6d, 0x6f, 0x70, 0x72,
0x69, 0x61, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x65, 0x64,
0x00, 0x03, 0x31, 0x2e, 0x33, 0x41, 0x00, 0x10, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x00, 0x0e, 0x70, 0x61, 0x6e, 0x74, 0x75, 0x6d, 0x20, 0x70, 0x72, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x41, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x12, 0x50, 0x61, 0x6e,
0x74, 0x75, 0x6d, 0x20, 0x49, 0x50, 0x50, 0x20, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x45, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x2d, 0x6d, 0x6f, 0x72, 0x65, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x00,
0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x36, 0x30, 0x30, 0x30, 0x30, 0x2f,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x2d, 0x6a, 0x75, 0x6d, 0x70, 0x2e, 0x68,
0x74, 0x6d, 0x6c, 0x41, 0x00, 0x16, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d,
0x6f, 0x64, 0x65, 0x6c, 0x00, 0x16, 0x50, 0x61, 0x6e, 0x74, 0x75, 0x6d,
0x20, 0x4d, 0x37, 0x33, 0x30, 0x30, 0x46, 0x44, 0x57, 0x20, 0x53, 0x65,
0x72, 0x69, 0x65, 0x73, 0x41, 0x00, 0x11, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x69, 0x64,
0x00, 0x45, 0x4d, 0x46, 0x47, 0x3a, 0x50, 0x61, 0x6e, 0x74, 0x75, 0x6d,
0x3b, 0x43, 0x4d, 0x44, 0x3a, 0x3a, 0x44, 0x57, 0x2d, 0x50, 0x53, 0x2c,
0x44, 0x57, 0x2d, 0x50, 0x43, 0x4c, 0x2c, 0x55, 0x52, 0x46, 0x3b, 0x4d,
0x44, 0x4c, 0x3a, 0x2d, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x3b,
0x43, 0x4c, 0x53, 0x3a, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x3b,
0x44, 0x45, 0x53, 0x3a, 0x50, 0x61, 0x6e, 0x74, 0x75, 0x6d, 0x3b, 0x45,
0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x75,
0x69, 0x64, 0x00, 0x2d, 0x75, 0x72, 0x6e, 0x3a, 0x75, 0x75, 0x69, 0x64,
0x3a, 0x30, 0x37, 0x62, 0x63, 0x35, 0x61, 0x34, 0x37, 0x2d, 0x65, 0x64,
0x66, 0x32, 0x2d, 0x34, 0x36, 0x36, 0x31, 0x2d, 0x38, 0x37, 0x34, 0x38,
0x2d, 0x38, 0x36, 0x32, 0x30, 0x66, 0x37, 0x39, 0x64, 0x34, 0x61, 0x63,
0x34, 0x45, 0x00, 0x0d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d,
0x69, 0x63, 0x6f, 0x6e, 0x73, 0x00, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a,
0x36, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x68,
0x69, 0x70, 0x34, 0x38, 0x2e, 0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00,
0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x68, 0x6f, 0x73, 0x74, 0x3a, 0x36, 0x30, 0x30, 0x30, 0x30, 0x2f,
0x66, 0x6c, 0x61, 0x67, 0x73, 0x68, 0x69, 0x70, 0x31, 0x32, 0x38, 0x2e,
0x70, 0x6e, 0x67, 0x45, 0x00, 0x00, 0x00, 0x26, 0x68, 0x74, 0x74, 0x70,
0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
0x3a, 0x36, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x66, 0x6c, 0x61, 0x67, 0x73,
0x68, 0x69, 0x70, 0x35, 0x31, 0x32, 0x2e, 0x70, 0x6e, 0x67, 0x42, 0x00,
0x13, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x64, 0x6e, 0x73,
0x2d, 0x73, 0x64, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x1d, 0x50, 0x61,
0x6e, 0x74, 0x75, 0x6d, 0x20, 0x4d, 0x37, 0x33, 0x30, 0x30, 0x46, 0x44,
0x57, 0x20, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x31, 0x43, 0x36,
0x46, 0x43, 0x44, 0x44, 0x00, 0x0d, 0x75, 0x72, 0x66, 0x2d, 0x73, 0x75,
0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x04, 0x56, 0x31, 0x2e,
0x34, 0x44, 0x00, 0x00, 0x00, 0x02, 0x57, 0x38, 0x44, 0x00, 0x00, 0x00,
0x03, 0x49, 0x53, 0x31, 0x44, 0x00, 0x00, 0x00, 0x04, 0x43, 0x50, 0x39,
0x39, 0x44, 0x00, 0x00, 0x00, 0x03, 0x50, 0x51, 0x34, 0x44, 0x00, 0x00,
0x00, 0x04, 0x4f, 0x42, 0x31, 0x30, 0x44, 0x00, 0x00, 0x00, 0x05, 0x52,
0x53, 0x36, 0x30, 0x30, 0x44, 0x00, 0x00, 0x00, 0x03, 0x44, 0x4d, 0x31,
0x44, 0x00, 0x0c, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6b,
0x69, 0x6e, 0x64, 0x00, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
0x74, 0x44, 0x00, 0x00, 0x00, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
0x70, 0x65, 0x34, 0x00, 0x14, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x2d, 0x73,
0x69, 0x7a, 0x65, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
0x64, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x54, 0x56, 0x21,
0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x00, 0x04, 0x00, 0x00, 0x6d, 0x24, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69,
0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x52,
0x08, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x74, 0x04, 0x37, 0x00, 0x00,
0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00,
0x00, 0x39, 0xd0, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x52, 0x08, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b,
0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00,
0x04, 0x00, 0x00, 0x29, 0x04, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69,
0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x39,
0xd0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x00, 0x04, 0x00, 0x00, 0x2c, 0x88, 0x21, 0x00, 0x0b, 0x79, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00,
0x00, 0x3f, 0x48, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x2a, 0xf8, 0x21, 0x00, 0x0b,
0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00,
0x04, 0x00, 0x00, 0x55, 0xf0, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00,
0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x27, 0x10, 0x21,
0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x00, 0x04, 0x00, 0x00, 0x39, 0xd0, 0x37, 0x00, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69,
0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x2e,
0xe0, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x5b, 0xcc, 0x37, 0x00, 0x00,
0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b, 0x78, 0x2d,
0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00,
0x00, 0x28, 0xed, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x5e, 0x42, 0x37,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x0b,
0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00,
0x04, 0x00, 0x00, 0x54, 0x56, 0x21, 0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69,
0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x04, 0x00, 0x00, 0x8a,
0xe8, 0x37, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x33,
0x00, 0x0b, 0x78, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x00, 0x08, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x39, 0xd0, 0x33,
0x00, 0x0b, 0x79, 0x2d, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x00, 0x08, 0x00, 0x00, 0x54, 0x60, 0x00, 0x00, 0x8b, 0x10, 0x37,
0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x0f, 0x73, 0x69, 0x64, 0x65, 0x73,
0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x09,
0x6f, 0x6e, 0x65, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x44, 0x00, 0x00,
0x00, 0x14, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64, 0x2d,
0x73, 0x68, 0x6f, 0x72, 0x74, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x44, 0x00,
0x00, 0x00, 0x13, 0x74, 0x77, 0x6f, 0x2d, 0x73, 0x69, 0x64, 0x65, 0x64,
0x2d, 0x6c, 0x6f, 0x6e, 0x67, 0x2d, 0x65, 0x64, 0x67, 0x65, 0x03,
}
0707010000000B000081A400000000000000000000000167F56AFB00001300000000000000000000000000000000000000001700000000goipp-1.2.0/encoder.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Message encoder
*/
package goipp
import (
"encoding/binary"
"errors"
"fmt"
"io"
"math"
)
// Type messageEncoder represents Message encoder
type messageEncoder struct {
out io.Writer // Output stream
}
// Encode the message
func (me *messageEncoder) encode(m *Message) error {
// Wire format:
//
// 2 bytes: Version
// 2 bytes: Code (Operation or Status)
// 4 bytes: RequestID
// variable: attributes
// 1 byte: TagEnd
// Encode message header
var err error
err = me.encodeU16(uint16(m.Version))
if err == nil {
err = me.encodeU16(uint16(m.Code))
}
if err == nil {
err = me.encodeU32(uint32(m.RequestID))
}
// Encode attributes
for _, grp := range m.AttrGroups() {
err = me.encodeTag(grp.Tag)
if err == nil {
for _, attr := range grp.Attrs {
if attr.Name == "" {
err = errors.New("Attribute without name")
} else {
err = me.encodeAttr(attr, true)
}
}
}
if err != nil {
break
}
}
if err == nil {
err = me.encodeTag(TagEnd)
}
return err
}
// Encode attribute
func (me *messageEncoder) encodeAttr(attr Attribute, checkTag bool) error {
// Wire format
// 1 byte: Tag
// 2 bytes: len(Name)
// variable: name
// 2 bytes: len(Value)
// variable Value
//
// And each additional value comes as attribute
// without name
if len(attr.Values) == 0 {
return errors.New("Attribute without value")
}
name := attr.Name
for _, val := range attr.Values {
tag := val.T
if checkTag {
if tag.IsDelimiter() || tag == TagMemberName || tag == TagEndCollection {
return fmt.Errorf("Tag %s cannot be used with value", tag)
}
if uint(tag) >= 0x100 {
return fmt.Errorf("Tag %s out of range", tag)
}
}
err := me.encodeTag(tag)
if err != nil {
return err
}
err = me.encodeName(name)
if err != nil {
return err
}
err = me.encodeValue(val.T, val.V)
if err != nil {
return err
}
name = "" // Each additional value comes without name
}
return nil
}
// Encode 8-bit integer
func (me *messageEncoder) encodeU8(v uint8) error {
return me.write([]byte{v})
}
// Encode 16-bit integer
func (me *messageEncoder) encodeU16(v uint16) error {
return me.write([]byte{byte(v >> 8), byte(v)})
}
// Encode 32-bit integer
func (me *messageEncoder) encodeU32(v uint32) error {
return me.write([]byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)})
}
// Encode Tag
func (me *messageEncoder) encodeTag(tag Tag) error {
return me.encodeU8(byte(tag))
}
// Encode Attribute name
func (me *messageEncoder) encodeName(name string) error {
if len(name) > math.MaxInt16 {
return fmt.Errorf("Attribute name exceeds %d bytes",
math.MaxInt16)
}
err := me.encodeU16(uint16(len(name)))
if err == nil {
err = me.write([]byte(name))
}
return err
}
// Encode Attribute value
func (me *messageEncoder) encodeValue(tag Tag, v Value) error {
// Check Value type vs the Tag
tagType := tag.Type()
if tagType == TypeVoid {
v = Void{} // Ignore supplied value
} else if tagType != v.Type() {
return fmt.Errorf("Tag %s: %s value required, %s present",
tag, tagType, v.Type())
}
// Convert Value to bytes in wire representation.
data, err := v.encode()
if err != nil {
return err
}
if len(data) > math.MaxInt16 {
return fmt.Errorf("Attribute value exceeds %d bytes",
math.MaxInt16)
}
// TagExtension encoding rules enforcement.
if tag == TagExtension {
if len(data) < 4 {
return fmt.Errorf(
"Extension tag truncated (%d bytes)", len(data))
}
t := binary.BigEndian.Uint32(data)
if t > 0x7fffffff {
return fmt.Errorf(
"Extension tag 0x%8.8x out of range", t)
}
}
// Encode the value
err = me.encodeU16(uint16(len(data)))
if err == nil {
err = me.write(data)
}
// Handle collection
if collection, ok := v.(Collection); ok {
return me.encodeCollection(tag, collection)
}
return err
}
// Encode collection
func (me *messageEncoder) encodeCollection(tag Tag, collection Collection) error {
for _, attr := range collection {
if attr.Name == "" {
return errors.New("Collection member without name")
}
attrName := MakeAttribute("", TagMemberName, String(attr.Name))
err := me.encodeAttr(attrName, false)
if err == nil {
err = me.encodeAttr(
Attribute{Name: "", Values: attr.Values}, true)
}
if err != nil {
return err
}
}
return me.encodeAttr(MakeAttribute("", TagEndCollection, Void{}), false)
}
// Write a piece of raw data to output stream
func (me *messageEncoder) write(data []byte) error {
for len(data) > 0 {
n, err := me.out.Write(data)
if err != nil {
return err
}
data = data[n:]
}
return nil
}
0707010000000C000081A400000000000000000000000167F56AFB00001402000000000000000000000000000000000000001900000000goipp-1.2.0/formatter.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP formatter (pretty-printer)
*/
package goipp
import (
"bytes"
"fmt"
"io"
"strings"
)
// Formatter parameters:
const (
// FormatterIndentShift is the indentation shift, number of space
// characters per indentation level.
FormatterIndentShift = 4
)
// Formatter formats IPP messages, attributes, groups etc
// for pretty-printing.
//
// It supersedes [Message.Print] method which is now considered
// deprecated.
type Formatter struct {
indent int // Indentation level
userIndent int // User-settable indent
buf bytes.Buffer // Output buffer
}
// NewFormatter returns a new Formatter.
func NewFormatter() *Formatter {
return &Formatter{}
}
// Reset resets the formatter.
func (f *Formatter) Reset() {
f.buf.Reset()
f.indent = 0
}
// SetIndent configures indentation. If parameter is greater that
// zero, the specified amount of white space will prepended to each
// non-empty output line.
func (f *Formatter) SetIndent(n int) {
f.userIndent = 0
if n > 0 {
f.userIndent = n
}
}
// Bytes returns formatted text as a byte slice.
func (f *Formatter) Bytes() []byte {
return f.buf.Bytes()
}
// String returns formatted text as a string.
func (f *Formatter) String() string {
return f.buf.String()
}
// WriteTo writes formatted text to w.
// It implements [io.WriterTo] interface.
func (f *Formatter) WriteTo(w io.Writer) (int64, error) {
return f.buf.WriteTo(w)
}
// Printf writes formatted line into the [Formatter], automatically
// indented and with added newline at the end.
//
// It returns the number of bytes written and nil as an error (for
// consistency with other printf-like functions).
func (f *Formatter) Printf(format string, args ...interface{}) (int, error) {
s := fmt.Sprintf(format, args...)
lines := strings.Split(s, "\n")
cnt := 0
for _, line := range lines {
if line != "" {
cnt += f.doIndent()
}
f.buf.WriteString(line)
f.buf.WriteByte('\n')
cnt += len(line) + 1
}
return cnt, nil
}
// FmtRequest formats a request [Message].
func (f *Formatter) FmtRequest(msg *Message) {
f.fmtMessage(msg, true)
}
// FmtResponse formats a response [Message].
func (f *Formatter) FmtResponse(msg *Message) {
f.fmtMessage(msg, false)
}
// fmtMessage formats a request or response Message.
func (f *Formatter) fmtMessage(msg *Message, request bool) {
f.Printf("{")
f.indent++
f.Printf("REQUEST-ID %d", msg.RequestID)
f.Printf("VERSION %s", msg.Version)
if request {
f.Printf("OPERATION %s", Op(msg.Code))
} else {
f.Printf("STATUS %s", Status(msg.Code))
}
if groups := msg.AttrGroups(); len(groups) != 0 {
f.Printf("")
f.FmtGroups(groups)
}
f.indent--
f.Printf("}")
}
// FmtGroups formats a [Groups] slice.
func (f *Formatter) FmtGroups(groups Groups) {
for i, g := range groups {
if i != 0 {
f.Printf("")
}
f.FmtGroup(g)
}
}
// FmtGroup formats a single [Group].
func (f *Formatter) FmtGroup(g Group) {
f.Printf("GROUP %s", g.Tag)
f.FmtAttributes(g.Attrs)
}
// FmtAttributes formats a [Attributes] slice.
func (f *Formatter) FmtAttributes(attrs Attributes) {
for _, attr := range attrs {
f.FmtAttribute(attr)
}
}
// FmtAttribute formats a single [Attribute].
func (f *Formatter) FmtAttribute(attr Attribute) {
f.fmtAttributeOrMember(attr, false)
}
// FmtAttributes formats a single [Attribute] or collection member.
func (f *Formatter) fmtAttributeOrMember(attr Attribute, member bool) {
buf := &f.buf
f.doIndent()
if member {
fmt.Fprintf(buf, "MEMBER %q", attr.Name)
} else {
fmt.Fprintf(buf, "ATTR %q", attr.Name)
}
tag := TagZero
for _, val := range attr.Values {
if val.T != tag {
fmt.Fprintf(buf, " %s:", val.T)
tag = val.T
}
if collection, ok := val.V.(Collection); ok {
if f.onNL() {
f.Printf("{")
} else {
buf.Write([]byte(" {\n"))
}
f.indent++
for _, attr2 := range collection {
f.fmtAttributeOrMember(attr2, true)
}
f.indent--
f.Printf("}")
} else {
fmt.Fprintf(buf, " %s", val.V)
}
}
f.forceNL()
}
// onNL returns true if Formatter is at the beginning of new line.
func (f *Formatter) onNL() bool {
b := f.buf.Bytes()
return len(b) == 0 || b[len(b)-1] == '\n'
}
// forceNL inserts newline character if Formatter is not at the.
// beginning of new line
func (f *Formatter) forceNL() {
if !f.onNL() {
f.buf.WriteByte('\n')
}
}
// doIndent outputs indentation space.
// It returns number of characters written.
func (f *Formatter) doIndent() int {
cnt := FormatterIndentShift * f.indent
cnt += f.userIndent
n := cnt
for n > len(formatterSomeSpace) {
f.buf.Write([]byte(formatterSomeSpace[:]))
n -= len(formatterSomeSpace)
}
f.buf.Write([]byte(formatterSomeSpace[:n]))
return cnt
}
// formatterSomeSpace contains some space characters for
// fast output of indentation space.
var formatterSomeSpace [64]byte
func init() {
for i := range formatterSomeSpace {
formatterSomeSpace[i] = ' '
}
}
0707010000000D000081A400000000000000000000000167F56AFB00001B0C000000000000000000000000000000000000001E00000000goipp-1.2.0/formatter_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP formatter test
*/
package goipp
import (
"strings"
"testing"
)
// TestFmtAttribute runs Formatter.FmtAttribute tests
func TestFmtAttribute(t *testing.T) {
type testData struct {
attr Attribute // Inpur attribute
out []string // Expected output
indent int // Indentation
}
tests := []testData{
// Simple test
{
attr: MakeAttr(
"attributes-charset",
TagCharset,
String("utf-8")),
out: []string{
`ATTR "attributes-charset" charset: utf-8`,
},
},
// Simple test with indentation
{
attr: MakeAttr(
"attributes-charset",
TagCharset,
String("utf-8")),
indent: 2,
out: []string{
` ATTR "attributes-charset" charset: utf-8`,
},
},
// Simple test with huge indentation
{
attr: MakeAttr(
"attributes-charset",
TagCharset,
String("utf-8")),
indent: 123,
out: []string{
strings.Repeat(" ", 123) +
`ATTR "attributes-charset" charset: utf-8`,
},
},
// Collection
{
attr: MakeAttrCollection("media-col",
MakeAttrCollection("media-size",
MakeAttribute("x-dimension",
TagInteger, Integer(10160)),
MakeAttribute("y-dimension",
TagInteger, Integer(15240)),
),
MakeAttribute("media-left-margin",
TagInteger, Integer(0)),
MakeAttribute("media-right-margin",
TagInteger, Integer(0)),
MakeAttribute("media-top-margin",
TagInteger, Integer(0)),
MakeAttribute("media-bottom-margin",
TagInteger, Integer(0)),
),
out: []string{
`ATTR "media-col" collection: {`,
` MEMBER "media-size" collection: {`,
` MEMBER "x-dimension" integer: 10160`,
` MEMBER "y-dimension" integer: 15240`,
` }`,
` MEMBER "media-left-margin" integer: 0`,
` MEMBER "media-right-margin" integer: 0`,
` MEMBER "media-top-margin" integer: 0`,
` MEMBER "media-bottom-margin" integer: 0`,
`}`,
},
},
// 1SetOf Collection
{
attr: MakeAttr("media-size-supported",
TagBeginCollection,
Collection{
MakeAttribute("x-dimension",
TagInteger, Integer(20990)),
MakeAttribute("y-dimension",
TagInteger, Integer(29704)),
},
Collection{
MakeAttribute("x-dimension",
TagInteger, Integer(14852)),
MakeAttribute("y-dimension",
TagInteger, Integer(20990)),
},
),
indent: 2,
out: []string{
` ATTR "media-size-supported" collection: {`,
` MEMBER "x-dimension" integer: 20990`,
` MEMBER "y-dimension" integer: 29704`,
` }`,
` {`,
` MEMBER "x-dimension" integer: 14852`,
` MEMBER "y-dimension" integer: 20990`,
` }`,
},
},
// Multiple values
{
attr: MakeAttr("page-delivery-supported",
TagKeyword,
String("reverse-order"),
String("same-order")),
out: []string{
`ATTR "page-delivery-supported" keyword: reverse-order same-order`,
},
},
// Values of mixed type
{
attr: Attribute{
Name: "page-ranges",
Values: Values{
{TagInteger, Integer(1)},
{TagInteger, Integer(2)},
{TagInteger, Integer(3)},
{TagRange, Range{5, 7}},
},
},
out: []string{
`ATTR "page-ranges" integer: 1 2 3 rangeOfInteger: 5-7`,
},
},
}
f := NewFormatter()
for _, test := range tests {
f.Reset()
f.SetIndent(test.indent)
expected := strings.Join(test.out, "\n") + "\n"
f.FmtAttribute(test.attr)
out := f.String()
if out != expected {
t.Errorf("output mismatch\n"+
"expected:\n%s"+
"present:\n%s",
expected, out)
}
}
}
// TestFmtRequestResponse runs Formatter.FmtRequest and
// Formatter.FmtResponse tests
func TestFmtRequestResponse(t *testing.T) {
type testData struct {
msg *Message // Input message
rq bool // This is request
out []string // Expected output
}
tests := []testData{
{
msg: &Message{
Version: MakeVersion(2, 0),
Code: Code(OpGetPrinterAttributes),
RequestID: 1,
Operation: []Attribute{
MakeAttribute(
"attributes-charset",
TagCharset,
String("utf-8")),
MakeAttribute(
"attributes-natural-language",
TagLanguage,
String("en-us")),
MakeAttribute(
"requested-attributes",
TagKeyword,
String("printer-name")),
},
},
rq: true,
out: []string{
`{`,
` REQUEST-ID 1`,
` VERSION 2.0`,
` OPERATION Get-Printer-Attributes`,
``,
` GROUP operation-attributes-tag`,
` ATTR "attributes-charset" charset: utf-8`,
` ATTR "attributes-natural-language" naturalLanguage: en-us`,
` ATTR "requested-attributes" keyword: printer-name`,
`}`,
},
},
{
msg: &Message{
Version: MakeVersion(2, 0),
Code: Code(StatusOk),
RequestID: 1,
Operation: []Attribute{
MakeAttribute(
"attributes-charset",
TagCharset,
String("utf-8")),
MakeAttribute(
"attributes-natural-language",
TagLanguage,
String("en-us")),
},
Printer: []Attribute{
MakeAttribute(
"printer-name",
TagName,
String("Kyocera_ECOSYS_M2040dn")),
},
},
rq: false,
out: []string{
`{`,
` REQUEST-ID 1`,
` VERSION 2.0`,
` STATUS successful-ok`,
``,
` GROUP operation-attributes-tag`,
` ATTR "attributes-charset" charset: utf-8`,
` ATTR "attributes-natural-language" naturalLanguage: en-us`,
``,
` GROUP printer-attributes-tag`,
` ATTR "printer-name" nameWithoutLanguage: Kyocera_ECOSYS_M2040dn`,
`}`,
},
},
}
f := NewFormatter()
for _, test := range tests {
f.Reset()
if test.rq {
f.FmtRequest(test.msg)
} else {
f.FmtResponse(test.msg)
}
out := f.String()
expected := strings.Join(test.out, "\n") + "\n"
if out != expected {
t.Errorf("output mismatch\n"+
"expected:\n%s"+
"present:\n%s",
expected, out)
}
}
}
// TestFmtBytes tests Formatter.Bytes function
func TestFmtBytes(t *testing.T) {
msg := &Message{
Version: MakeVersion(2, 0),
Code: Code(OpGetPrinterAttributes),
RequestID: 1,
Operation: []Attribute{
MakeAttribute(
"attributes-charset",
TagCharset,
String("utf-8")),
MakeAttribute(
"attributes-natural-language",
TagLanguage,
String("en-us")),
MakeAttribute(
"requested-attributes",
TagKeyword,
String("printer-name")),
},
}
f := NewFormatter()
f.FmtRequest(msg)
s := f.String()
b := f.Bytes()
// Note, we've already tested f.String() function in the previous
// tests, so f.Bytes() is tested against it
if string(b) != s {
t.Errorf("Formatter.Bytes test failed:\n"+
"expected: %s\n"+
"present: %s\n",
s,
b,
)
}
}
0707010000000E000081A400000000000000000000000167F56AFB0000002E000000000000000000000000000000000000001300000000goipp-1.2.0/go.modmodule github.com/OpenPrinting/goipp
go 1.11
0707010000000F000081A400000000000000000000000167F56AFB00000CE0000000000000000000000000000000000000001500000000goipp-1.2.0/group.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Groups of attributes
*/
package goipp
import "sort"
// Group represents a group of attributes.
//
// Since 1.1.0
type Group struct {
Tag Tag // Group tag
Attrs Attributes // Group attributes
}
// Groups represents a sequence of groups
//
// The primary purpose of this type is to represent
// messages with repeated groups with the same group tag
//
// # See Message type documentation for more details
//
// Since 1.1.0
type Groups []Group
// Add Attribute to the Group
func (g *Group) Add(attr Attribute) {
g.Attrs.Add(attr)
}
// Equal checks that groups g and g2 are equal
func (g Group) Equal(g2 Group) bool {
return g.Tag == g2.Tag && g.Attrs.Equal(g2.Attrs)
}
// Similar checks that groups g and g2 are **logically** equal.
func (g Group) Similar(g2 Group) bool {
return g.Tag == g2.Tag && g.Attrs.Similar(g2.Attrs)
}
// Clone creates a shallow copy of the Group
func (g Group) Clone() Group {
g2 := g
g2.Attrs = g.Attrs.Clone()
return g2
}
// DeepCopy creates a deep copy of the Group
func (g Group) DeepCopy() Group {
g2 := g
g2.Attrs = g.Attrs.DeepCopy()
return g2
}
// Add Group to Groups
func (groups *Groups) Add(g Group) {
*groups = append(*groups, g)
}
// Clone creates a shallow copy of Groups.
// For nil input it returns nil output.
func (groups Groups) Clone() Groups {
var groups2 Groups
if groups != nil {
groups2 = make(Groups, len(groups))
copy(groups2, groups)
}
return groups2
}
// DeepCopy creates a deep copy of Groups.
// For nil input it returns nil output.
func (groups Groups) DeepCopy() Groups {
var groups2 Groups
if groups != nil {
groups2 = make(Groups, len(groups))
for i := range groups {
groups2[i] = groups[i].DeepCopy()
}
}
return groups2
}
// Equal checks that groups and groups2 are equal.
//
// Note, Groups(nil) and Groups{} are not equal but similar.
func (groups Groups) Equal(groups2 Groups) bool {
if len(groups) != len(groups2) {
return false
}
if (groups == nil) != (groups2 == nil) {
return false
}
for i, g := range groups {
g2 := groups2[i]
if !g.Equal(g2) {
return false
}
}
return true
}
// Similar checks that groups and groups2 are **logically** equal,
// which means the following:
// - groups and groups2 contain the same set of
// groups, but groups with different tags may
// be reordered between each other.
// - groups with the same tag cannot be reordered.
// - attributes of corresponding groups are similar.
//
// Note, Groups(nil) and Groups{} are not equal but similar.
func (groups Groups) Similar(groups2 Groups) bool {
// Fast check: if lengths are not the same, groups
// are definitely not equal
if len(groups) != len(groups2) {
return false
}
// Sort groups by tag
groups = groups.Clone()
groups2 = groups2.Clone()
sort.SliceStable(groups, func(i, j int) bool {
return groups[i].Tag < groups[j].Tag
})
sort.SliceStable(groups2, func(i, j int) bool {
return groups2[i].Tag < groups2[j].Tag
})
// Now compare, group by group
for i := range groups {
if !groups[i].Similar(groups2[i]) {
return false
}
}
return true
}
07070100000010000081A400000000000000000000000167F56AFB00001BA5000000000000000000000000000000000000001A00000000goipp-1.2.0/group_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Tests fop groups of attributes
*/
package goipp
import "testing"
// TestGroupEqualSimilar tests Group.Equal and Group.Similar
func TestGroupEqualSimilar(t *testing.T) {
type testData struct {
g1, g2 Group // A pair of Attributes slice
equal bool // Expected g1.Equal(g2) output
similar bool // Expected g2.Similar(g2) output
}
attrs1 := Attributes{
MakeAttr("attr1", TagInteger, Integer(1)),
MakeAttr("attr2", TagInteger, Integer(2)),
MakeAttr("attr3", TagInteger, Integer(3)),
}
attrs2 := Attributes{
MakeAttr("attr3", TagInteger, Integer(3)),
MakeAttr("attr2", TagInteger, Integer(2)),
MakeAttr("attr1", TagInteger, Integer(1)),
}
tests := []testData{
{
g1: Group{TagJobGroup, nil},
g2: Group{TagJobGroup, nil},
equal: true,
similar: true,
},
{
g1: Group{TagJobGroup, Attributes{}},
g2: Group{TagJobGroup, Attributes{}},
equal: true,
similar: true,
},
{
g1: Group{TagJobGroup, Attributes{}},
g2: Group{TagJobGroup, nil},
equal: false,
similar: true,
},
{
g1: Group{TagJobGroup, attrs1},
g2: Group{TagJobGroup, attrs1},
equal: true,
similar: true,
},
{
g1: Group{TagJobGroup, attrs1},
g2: Group{TagJobGroup, attrs2},
equal: false,
similar: true,
},
}
for _, test := range tests {
equal := test.g1.Equal(test.g2)
similar := test.g1.Similar(test.g2)
if equal != test.equal {
t.Errorf("testing Group.Equal:\n"+
"attrs 1: %s\n"+
"attrs 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.g1, test.g2,
test.equal, equal,
)
}
if similar != test.similar {
t.Errorf("testing Group.Similar:\n"+
"attrs 1: %s\n"+
"attrs 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.g1, test.g2,
test.similar, similar,
)
}
}
}
// TestGroupAdd tests Group.Add
func TestGroupAdd(t *testing.T) {
g1 := Group{
TagJobGroup,
Attributes{
MakeAttr("attr1", TagInteger, Integer(1)),
MakeAttr("attr2", TagInteger, Integer(2)),
MakeAttr("attr3", TagInteger, Integer(3)),
},
}
g2 := Group{Tag: TagJobGroup}
for _, attr := range g1.Attrs {
g2.Add(attr)
}
if !g1.Equal(g2) {
t.Errorf("Group.Add test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
g1, g2,
)
}
}
// TestGroupCopy tests Group.Clone and Group.DeepCopy
func TestGroupCopy(t *testing.T) {
type testData struct {
g Group
}
attrs := Attributes{
MakeAttr("attr1", TagInteger, Integer(1)),
MakeAttr("attr2", TagInteger, Integer(2)),
MakeAttr("attr3", TagInteger, Integer(3)),
}
tests := []testData{
{Group{TagJobGroup, nil}},
{Group{TagJobGroup, Attributes{}}},
{Group{TagJobGroup, attrs}},
}
for _, test := range tests {
clone := test.g.Clone()
if !test.g.Equal(clone) {
t.Errorf("testing Group.Clone\n"+
"expected: %#v\n"+
"present: %#v\n",
test.g,
clone,
)
}
copy := test.g.DeepCopy()
if !test.g.Equal(copy) {
t.Errorf("testing Group.DeepCopy\n"+
"expected: %#v\n"+
"present: %#v\n",
test.g,
copy,
)
}
}
}
// TestGroupEqualSimilar tests Group.Equal and Group.Similar
func TestGroupsEqualSimilar(t *testing.T) {
type testData struct {
groups1, groups2 Groups // A pair of Attributes slice
equal bool // Expected g1.Equal(g2) output
similar bool // Expected g2.Similar(g2) output
}
g1 := Group{
TagJobGroup,
Attributes{MakeAttr("attr1", TagInteger, Integer(1))},
}
g2 := Group{
TagJobGroup,
Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
}
g3 := Group{
TagPrinterGroup,
Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
}
tests := []testData{
{
// nil equal and similar to nil
groups1: nil,
groups2: nil,
equal: true,
similar: true,
},
{
// Empty groups equal and similar to empty groups
groups1: Groups{},
groups2: Groups{},
equal: true,
similar: true,
},
{
// nil similar but not equal to empty groups
groups1: nil,
groups2: Groups{},
equal: false,
similar: true,
},
{
// groups of different size neither equal nor similar
groups1: Groups{g1, g2, g3},
groups2: Groups{g1, g2},
equal: false,
similar: false,
},
{
// Same list of groups: equal and similar
groups1: Groups{g1, g2, g3},
groups2: Groups{g1, g2, g3},
equal: true,
similar: true,
},
{
// Groups with different group tags reordered.
// Similar but not equal.
groups1: Groups{g1, g2, g3},
groups2: Groups{g3, g1, g2},
equal: false,
similar: true,
},
{
// Groups with the same group tags reordered.
// Neither equal nor similar
groups1: Groups{g1, g2, g3},
groups2: Groups{g2, g1, g3},
equal: false,
similar: false,
},
}
for _, test := range tests {
equal := test.groups1.Equal(test.groups2)
similar := test.groups1.Similar(test.groups2)
if equal != test.equal {
t.Errorf("testing Groups.Equal:\n"+
"attrs 1: %s\n"+
"attrs 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.groups1, test.groups2,
test.equal, equal,
)
}
if similar != test.similar {
t.Errorf("testing Groups.Similar:\n"+
"attrs 1: %s\n"+
"attrs 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.groups1, test.groups2,
test.similar, similar,
)
}
}
}
// TestGroupsAdd tests Groups.Add
func TestGroupsAdd(t *testing.T) {
g1 := Group{
TagJobGroup,
Attributes{MakeAttr("attr1", TagInteger, Integer(1))},
}
g2 := Group{
TagJobGroup,
Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
}
g3 := Group{
TagPrinterGroup,
Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
}
groups1 := Groups{g1, g2, g3}
groups2 := Groups{}
groups2.Add(g1)
groups2.Add(g2)
groups2.Add(g3)
if !groups1.Equal(groups2) {
t.Errorf("Groups.Add test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
groups1, groups2,
)
}
}
// TestGroupsCopy tests Groups.Clone and Groups.DeepCopy
func TestGroupsCopy(t *testing.T) {
g1 := Group{
TagJobGroup,
Attributes{MakeAttr("attr1", TagInteger, Integer(1))},
}
g2 := Group{
TagJobGroup,
Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
}
g3 := Group{
TagPrinterGroup,
Attributes{MakeAttr("attr2", TagInteger, Integer(2))},
}
type testData struct {
groups Groups
}
tests := []testData{
{nil},
{Groups{}},
{Groups{g1, g2, g3}},
}
for _, test := range tests {
clone := test.groups.Clone()
if !test.groups.Equal(clone) {
t.Errorf("testing Groups.Clone\n"+
"expected: %#v\n"+
"present: %#v\n",
test.groups,
clone,
)
}
copy := test.groups.DeepCopy()
if !test.groups.Equal(copy) {
t.Errorf("testing Groups.DeepCopy\n"+
"expected: %#v\n"+
"present: %#v\n",
test.groups,
copy,
)
}
}
}
07070100000011000081A400000000000000000000000167F56AFB00000352000000000000000000000000000000000000001500000000goipp-1.2.0/index.md# goipp
[](http://godoc.org/github.com/OpenPrinting/goipp)

The goipp library is fairly complete implementation of IPP core protocol in
pure Go. Essentially, it is IPP messages parser/composer. Transport is
not implemented here, because Go standard library has an excellent built-in
HTTP client, and it doesn't make a lot of sense to wrap it here.
High-level requests, like "print a file" are also not implemented, only the
low-level stuff.
All documentation is on godoc.org -- follow the link above. Pull requests
are welcomed, assuming they don't break existing API.
For more information and software downloads, please visit the
[Project's page at GitHub](https://github.com/OpenPrinting/sane-airscan)
07070100000012000081A400000000000000000000000167F56AFB00002209000000000000000000000000000000000000001700000000goipp-1.2.0/message.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP protocol messages
*/
package goipp
import (
"bytes"
"fmt"
"io"
)
// Code represents Op(operation) or Status codes
type Code uint16
// Version represents a protocol version. It consist
// of Major and Minor version codes, packed into a single
// 16-bit word
type Version uint16
// DefaultVersion is the default IPP version (2.0 for now)
const DefaultVersion Version = 0x0200
// MakeVersion makes version from major and minor parts
func MakeVersion(major, minor uint8) Version {
return Version(major)<<8 | Version(minor)
}
// Major returns a major part of version
func (v Version) Major() uint8 {
return uint8(v >> 8)
}
// Minor returns a minor part of version
func (v Version) Minor() uint8 {
return uint8(v)
}
// String() converts version to string (i.e., "2.0")
func (v Version) String() string {
return fmt.Sprintf("%d.%d", v.Major(), v.Minor())
}
// Message represents a single IPP message, which may be either
// client request or server response
type Message struct {
// Common header
Version Version // Protocol version
Code Code // Operation for request, status for response
RequestID uint32 // Set in request, returned in response
// Groups of Attributes
//
// This field allows to represent messages with repeated
// groups of attributes with the same group tag. The most
// noticeable use case is the Get-Jobs response which uses
// multiple Job groups, one per returned job. See RFC 8011,
// 4.2.6.2. for more details
//
// See also the following discussions which explain the demand
// to implement this interface:
// https://github.com/OpenPrinting/goipp/issues/2
// https://github.com/OpenPrinting/goipp/pull/3
//
// With respect to backward compatibility, the following
// behavior is implemented here:
// 1. (*Message).Decode() fills both Groups and named per-group
// fields (i.e., Operation, Job etc)
// 2. (*Message).Encode() and (*Message) Print, if Groups != nil,
// uses Groups and ignores named per-group fields. Otherwise,
// named fields are used as in 1.0.0
// 3. (*Message) Equal(), for each message uses Groups if
// it is not nil or named per-group fields otherwise.
// In another words, Equal() compares messages as if
// they were encoded
//
// Since 1.1.0
Groups Groups
// Attributes, by group
Operation Attributes // Operation attributes
Job Attributes // Job attributes
Printer Attributes // Printer attributes
Unsupported Attributes // Unsupported attributes
Subscription Attributes // Subscription attributes
EventNotification Attributes // Event Notification attributes
Resource Attributes // Resource attributes
Document Attributes // Document attributes
System Attributes // System attributes
Future11 Attributes // \
Future12 Attributes // \
Future13 Attributes // | Reserved for future extensions
Future14 Attributes // /
Future15 Attributes // /
}
// NewRequest creates a new request message
//
// Use DefaultVersion as a first argument, if you don't
// have any specific needs
func NewRequest(v Version, op Op, id uint32) *Message {
return &Message{
Version: v,
Code: Code(op),
RequestID: id,
}
}
// NewResponse creates a new response message
//
// Use DefaultVersion as a first argument, if you don't
func NewResponse(v Version, status Status, id uint32) *Message {
return &Message{
Version: v,
Code: Code(status),
RequestID: id,
}
}
// NewMessageWithGroups creates a new message with Groups of
// attributes.
//
// Fields like m.Operation, m.Job. m.Printer... and so on will
// be properly filled automatically.
func NewMessageWithGroups(v Version, code Code,
id uint32, groups Groups) *Message {
m := &Message{
Version: v,
Code: code,
RequestID: id,
Groups: groups,
}
for _, grp := range m.Groups {
switch grp.Tag {
case TagOperationGroup:
m.Operation = append(m.Operation, grp.Attrs...)
case TagJobGroup:
m.Job = append(m.Job, grp.Attrs...)
case TagPrinterGroup:
m.Printer = append(m.Printer, grp.Attrs...)
case TagUnsupportedGroup:
m.Unsupported = append(m.Unsupported, grp.Attrs...)
case TagSubscriptionGroup:
m.Subscription = append(m.Subscription, grp.Attrs...)
case TagEventNotificationGroup:
m.EventNotification = append(m.EventNotification,
grp.Attrs...)
case TagResourceGroup:
m.Resource = append(m.Resource, grp.Attrs...)
case TagDocumentGroup:
m.Document = append(m.Document, grp.Attrs...)
case TagSystemGroup:
m.System = append(m.System, grp.Attrs...)
case TagFuture11Group:
m.Future11 = append(m.Future11, grp.Attrs...)
case TagFuture12Group:
m.Future12 = append(m.Future12, grp.Attrs...)
case TagFuture13Group:
m.Future13 = append(m.Future13, grp.Attrs...)
case TagFuture14Group:
m.Future14 = append(m.Future14, grp.Attrs...)
case TagFuture15Group:
m.Future15 = append(m.Future15, grp.Attrs...)
}
}
return m
}
// Equal checks that two messages are equal
func (m Message) Equal(m2 Message) bool {
if m.Version != m2.Version ||
m.Code != m2.Code ||
m.RequestID != m2.RequestID {
return false
}
groups := m.AttrGroups()
groups2 := m2.AttrGroups()
return groups.Equal(groups2)
}
// Similar checks that two messages are **logically** equal,
// which means the following:
// - Version, Code and RequestID are equal
// - Groups of attributes are Similar
func (m Message) Similar(m2 Message) bool {
if m.Version != m2.Version ||
m.Code != m2.Code ||
m.RequestID != m2.RequestID {
return false
}
groups := m.AttrGroups()
groups2 := m2.AttrGroups()
return groups.Similar(groups2)
}
// Reset the message into initial state
func (m *Message) Reset() {
*m = Message{}
}
// Encode message
func (m *Message) Encode(out io.Writer) error {
me := messageEncoder{
out: out,
}
return me.encode(m)
}
// EncodeBytes encodes message to byte slice
func (m *Message) EncodeBytes() ([]byte, error) {
var buf bytes.Buffer
err := m.Encode(&buf)
return buf.Bytes(), err
}
// Decode reads message from io.Reader
func (m *Message) Decode(in io.Reader) error {
return m.DecodeEx(in, DecoderOptions{})
}
// DecodeEx reads message from io.Reader
//
// It is extended version of the Decode method, with additional
// DecoderOptions parameter
func (m *Message) DecodeEx(in io.Reader, opt DecoderOptions) error {
md := messageDecoder{
in: in,
opt: opt,
}
m.Reset()
return md.decode(m)
}
// DecodeBytes decodes message from byte slice
func (m *Message) DecodeBytes(data []byte) error {
return m.Decode(bytes.NewBuffer(data))
}
// DecodeBytesEx decodes message from byte slice
//
// It is extended version of the DecodeBytes method, with additional
// DecoderOptions parameter
func (m *Message) DecodeBytesEx(data []byte, opt DecoderOptions) error {
return m.DecodeEx(bytes.NewBuffer(data), opt)
}
// Print pretty-prints the message. The 'request' parameter affects
// interpretation of Message.Code: it is interpreted either
// as [Op] or as [Status].
//
// Deprecated. Use [Formatter] instead.
func (m *Message) Print(out io.Writer, request bool) {
f := Formatter{}
if request {
f.FmtRequest(m)
} else {
f.FmtResponse(m)
}
f.WriteTo(out)
}
// AttrGroups returns [Message] attributes as a sequence of
// attribute groups.
//
// If [Message.Groups] is set, it will be returned.
//
// Otherwise, [Groups] will be reconstructed from [Message.Operation],
// [Message.Job], [Message.Printer] and so on.
//
// Groups with nil [Group.Attrs] will be skipped, but groups with non-nil
// will be not, even if len(Attrs) == 0
func (m *Message) AttrGroups() Groups {
// If m.Groups is set, use it
if m.Groups != nil {
return m.Groups
}
// Initialize slice of groups
groups := Groups{
{TagOperationGroup, m.Operation},
{TagJobGroup, m.Job},
{TagPrinterGroup, m.Printer},
{TagUnsupportedGroup, m.Unsupported},
{TagSubscriptionGroup, m.Subscription},
{TagEventNotificationGroup, m.EventNotification},
{TagResourceGroup, m.Resource},
{TagDocumentGroup, m.Document},
{TagSystemGroup, m.System},
{TagFuture11Group, m.Future11},
{TagFuture12Group, m.Future12},
{TagFuture13Group, m.Future13},
{TagFuture14Group, m.Future14},
{TagFuture15Group, m.Future15},
}
// Skip all empty groups
out := 0
for in := 0; in < len(groups); in++ {
if groups[in].Attrs != nil {
groups[out] = groups[in]
out++
}
}
return groups[:out]
}
07070100000013000081A400000000000000000000000167F56AFB000031D3000000000000000000000000000000000000001C00000000goipp-1.2.0/message_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Message tests
*/
package goipp
import (
"bytes"
"reflect"
"strings"
"testing"
)
// TestVersion tests Version functions
func TestVersion(t *testing.T) {
type testData struct {
major, minor uint8 // Major/minor parts
ver Version // Resulting version
str string // Its string representation
}
tests := []testData{
{
major: 2,
minor: 0,
ver: 0x0200,
str: "2.0",
},
{
major: 2,
minor: 1,
ver: 0x0201,
str: "2.1",
},
{
major: 1,
minor: 1,
ver: 0x0101,
str: "1.1",
},
}
for _, test := range tests {
ver := MakeVersion(test.major, test.minor)
if ver != test.ver {
t.Errorf("MakeVersion test failed:\n"+
"version expected: 0x%4.4x\n"+
"version present: 0x%4.4x\n",
uint(test.ver), uint(ver),
)
continue
}
str := ver.String()
if str != test.str {
t.Errorf("Version.String test failed:\n"+
"expected: %s\n"+
"present: %s\n",
test.str, str,
)
continue
}
major := ver.Major()
if major != test.major {
t.Errorf("Version.Major test failed:\n"+
"expected: %d\n"+
"present: %d\n",
test.major, major,
)
continue
}
minor := ver.Minor()
if minor != test.minor {
t.Errorf("Version.Minor test failed:\n"+
"expected: %d\n"+
"present: %d\n",
test.minor, minor,
)
continue
}
}
}
// TestNewRequestResponse tests NewRequest and NewResponse functions
func TestNewRequestResponse(t *testing.T) {
msg := &Message{
Version: MakeVersion(2, 0),
Code: 1,
RequestID: 0x12345,
}
rq := NewRequest(msg.Version, Op(msg.Code), msg.RequestID)
if !reflect.DeepEqual(msg, rq) {
t.Errorf("NewRequest test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
msg, rq,
)
}
rsp := NewResponse(msg.Version, Status(msg.Code), msg.RequestID)
if !reflect.DeepEqual(msg, rsp) {
t.Errorf("NewRequest test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
msg, rsp,
)
}
}
// TestNewMessageWithGroups tests the NewMessageWithGroups function.
func TestNewMessageWithGroups(t *testing.T) {
// Populate groups
ops := Group{
TagOperationGroup,
Attributes{
MakeAttr("ops", TagInteger, Integer(1)),
},
}
prn1 := Group{
TagPrinterGroup,
Attributes{
MakeAttr("prn1", TagInteger, Integer(2)),
},
}
prn2 := Group{
TagPrinterGroup,
Attributes{
MakeAttr("prn2", TagInteger, Integer(3)),
},
}
prn3 := Group{
TagPrinterGroup,
Attributes{
MakeAttr("prn3", TagInteger, Integer(4)),
},
}
job := Group{
TagJobGroup,
Attributes{
MakeAttr("job", TagInteger, Integer(5)),
},
}
unsupp := Group{
TagUnsupportedGroup,
Attributes{
MakeAttr("unsupp", TagInteger, Integer(6)),
},
}
sub := Group{
TagSubscriptionGroup,
Attributes{
MakeAttr("sub", TagInteger, Integer(7)),
},
}
evnt := Group{
TagEventNotificationGroup,
Attributes{
MakeAttr("evnt", TagInteger, Integer(8)),
},
}
res := Group{
TagResourceGroup,
Attributes{
MakeAttr("res", TagInteger, Integer(9)),
},
}
doc := Group{
TagDocumentGroup,
Attributes{
MakeAttr("doc", TagInteger, Integer(10)),
},
}
sys := Group{
TagSystemGroup,
Attributes{
MakeAttr("sys", TagInteger, Integer(11)),
},
}
future11 := Group{
TagFuture11Group,
Attributes{
MakeAttr("future11", TagInteger, Integer(12)),
},
}
future12 := Group{
TagFuture12Group,
Attributes{
MakeAttr("future12", TagInteger, Integer(13)),
},
}
future13 := Group{
TagFuture13Group,
Attributes{
MakeAttr("future13", TagInteger, Integer(14)),
},
}
future14 := Group{
TagFuture14Group,
Attributes{
MakeAttr("future14", TagInteger, Integer(15)),
},
}
future15 := Group{
TagFuture15Group,
Attributes{
MakeAttr("future15", TagInteger, Integer(16)),
},
}
groups := Groups{
ops,
prn1, prn2, prn3,
job,
unsupp,
sub,
evnt,
res,
doc,
sys,
future11,
future12,
future13,
future14,
future15,
}
msg := NewMessageWithGroups(DefaultVersion, 1, 123, groups)
expected := &Message{
Version: DefaultVersion,
Code: 1,
RequestID: 123,
Groups: groups,
Operation: ops.Attrs,
Job: job.Attrs,
Unsupported: unsupp.Attrs,
Subscription: sub.Attrs,
EventNotification: evnt.Attrs,
Resource: res.Attrs,
Document: doc.Attrs,
System: sys.Attrs,
Future11: future11.Attrs,
Future12: future12.Attrs,
Future13: future13.Attrs,
Future14: future14.Attrs,
Future15: future15.Attrs,
}
expected.Printer = prn1.Attrs
expected.Printer = append(expected.Printer, prn2.Attrs...)
expected.Printer = append(expected.Printer, prn3.Attrs...)
if !reflect.DeepEqual(msg, expected) {
t.Errorf("NewMessageWithGroups test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
expected,
msg,
)
}
}
// TestNewMessageWithGroups tests the Message.AttrGroups function.
func TestMessageAttrGroups(t *testing.T) {
// Create a message for testing
uri := "ipp://192/168.0.1/ipp/print"
m := NewRequest(DefaultVersion, OpCreateJob, 1)
m.Operation.Add(MakeAttr("attributes-charset",
TagCharset, String("utf-8")))
m.Operation.Add(MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")))
m.Operation.Add(MakeAttr("printer-uri",
TagURI, String(uri)))
m.Job.Add(MakeAttr("copies", TagInteger, Integer(1)))
// Compare m.AttrGroups() with expectations
groups := m.AttrGroups()
expected := Groups{
Group{
Tag: TagOperationGroup,
Attrs: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
MakeAttr("printer-uri",
TagURI, String(uri)),
},
},
Group{
Tag: TagJobGroup,
Attrs: Attributes{
MakeAttr("copies", TagInteger, Integer(1)),
},
},
}
if !reflect.DeepEqual(groups, expected) {
t.Errorf("Message.AttrGroups test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
expected, groups,
)
}
// Set m.Groups. Check that it takes precedence.
expected = Groups{
Group{
Tag: TagOperationGroup,
Attrs: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
},
},
}
m.Groups = expected
groups = m.AttrGroups()
if !reflect.DeepEqual(groups, expected) {
t.Errorf("Message.AttrGroups test failed:\n"+
"expected: %#v\n"+
"present: %#v\n",
expected, groups,
)
}
}
// TestMessageEqualSimilar tests Message.Equal and Message.Similar functions.
func TestMessageEqualSimilar(t *testing.T) {
type testData struct {
m1, m2 Message // Input messages
equal bool // Expected Message.Equal output
similar bool // Expected Message.Similar output
}
uri := "ipp://192/168.0.1/ipp/print"
tests := []testData{
// Empty messages are equal and similar
{
m1: Message{},
m2: Message{},
equal: true,
similar: true,
},
// Messages with different Version/Code/RequestID are
// neither equal or similar
{
m1: Message{},
m2: Message{Version: 1},
equal: false,
similar: false,
},
{
m1: Message{},
m2: Message{Code: 1},
equal: false,
similar: false,
},
{
m1: Message{},
m2: Message{RequestID: 1},
equal: false,
similar: false,
},
// If the same attributes represented as Message.Groups in one
// message and via Message.Operation/Job/Printer etc in the
// another message, these messages are equal and similar
{
m1: Message{
Groups: Groups{
Group{
Tag: TagOperationGroup,
Attrs: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
MakeAttr("printer-uri",
TagURI, String(uri)),
},
},
Group{
Tag: TagJobGroup,
Attrs: Attributes{
MakeAttr("copies", TagInteger, Integer(1)),
},
},
},
},
m2: Message{
Operation: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
MakeAttr("printer-uri",
TagURI, String(uri)),
},
Job: Attributes{
MakeAttr("copies", TagInteger, Integer(1)),
},
},
equal: true,
similar: true,
},
// Messages with the different order of the same set of attributes
// are similar but not equal.
{
m1: Message{
Operation: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
MakeAttr("printer-uri",
TagURI, String(uri)),
},
},
m2: Message{
Operation: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("printer-uri",
TagURI, String(uri)),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
},
},
equal: false,
similar: true,
},
}
for _, test := range tests {
equal := test.m1.Equal(test.m2)
if equal != test.equal {
var buf1, buf2 bytes.Buffer
test.m1.Print(&buf1, true)
test.m2.Print(&buf2, true)
t.Errorf("testing Message.Equal:\n"+
"message 1: %s\n"+
"message 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
&buf1, &buf2,
test.equal, equal,
)
}
similar := test.m1.Similar(test.m2)
if similar != test.similar {
var buf1, buf2 bytes.Buffer
test.m1.Print(&buf1, true)
test.m2.Print(&buf2, true)
t.Errorf("testing Message.Similar:\n"+
"message 1: %s\n"+
"message 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
&buf1, &buf2,
test.similar, similar,
)
}
}
}
// TestMessageReset tests Message.Reset function
func TestMessageReset(t *testing.T) {
uri := "ipp://192/168.0.1/ipp/print"
m := Message{
Groups: Groups{
Group{
Tag: TagOperationGroup,
Attrs: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
MakeAttr("printer-uri",
TagURI, String(uri)),
},
},
Group{
Tag: TagJobGroup,
Attrs: Attributes{
MakeAttr("copies", TagInteger, Integer(1)),
},
},
},
}
m.Reset()
if !reflect.ValueOf(m).IsZero() {
t.Errorf("Message.Reset test failed")
}
}
// TestMessagePrint tests Message.Print function
func TestMessagePrint(t *testing.T) {
uri := "ipp://192/168.0.1/ipp/print"
m := Message{
Code: 2,
Version: MakeVersion(2, 0),
RequestID: 1,
Groups: Groups{
Group{
Tag: TagOperationGroup,
Attrs: Attributes{
MakeAttr("attributes-charset",
TagCharset, String("utf-8")),
MakeAttr("attributes-natural-language",
TagLanguage, String("en-US")),
MakeAttr("printer-uri",
TagURI, String(uri)),
},
},
Group{
Tag: TagJobGroup,
Attrs: Attributes{
MakeAttr("copies", TagInteger, Integer(1)),
},
},
},
}
// Check request formatting
reqExpected := []string{
`{`,
` REQUEST-ID 1`,
` VERSION 2.0`,
` OPERATION Print-Job`,
``,
` GROUP operation-attributes-tag`,
` ATTR "attributes-charset" charset: utf-8`,
` ATTR "attributes-natural-language" naturalLanguage: en-US`,
` ATTR "printer-uri" uri: ipp://192/168.0.1/ipp/print`,
``,
` GROUP job-attributes-tag`,
` ATTR "copies" integer: 1`,
`}`,
}
var buf bytes.Buffer
m.Print(&buf, true)
exp := strings.Join(reqExpected, "\n") + "\n"
if buf.String() != exp {
t.Errorf("Message.Print test failed for request:\n"+
"expected: %s\n"+
"present: %s\n",
exp, &buf,
)
}
// Check response formatting
rspExpected := []string{
`{`,
` REQUEST-ID 1`,
` VERSION 2.0`,
` STATUS successful-ok`,
``,
` GROUP operation-attributes-tag`,
` ATTR "attributes-charset" charset: utf-8`,
` ATTR "attributes-natural-language" naturalLanguage: en-US`,
` ATTR "printer-uri" uri: ipp://192/168.0.1/ipp/print`,
``,
` GROUP job-attributes-tag`,
` ATTR "copies" integer: 1`,
`}`,
}
buf.Reset()
m.Code = 0
m.Print(&buf, false)
exp = strings.Join(rspExpected, "\n") + "\n"
if buf.String() != exp {
t.Errorf("Message.Print test failed for response:\n"+
"expected: %s\n"+
"present: %s\n",
exp, &buf,
)
}
}
07070100000014000081A400000000000000000000000167F56AFB00004461000000000000000000000000000000000000001200000000goipp-1.2.0/op.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Operation Codes
*/
package goipp
import (
"fmt"
)
// Op represents an IPP Operation Code
type Op Code
// Op codes
const (
OpPrintJob Op = 0x0002 // Print-Job: Print a single file
OpPrintURI Op = 0x0003 // Print-URI: Print a single URL
OpValidateJob Op = 0x0004 // Validate-Job: Validate job values prior to submission
OpCreateJob Op = 0x0005 // Create-Job: Create an empty print job
OpSendDocument Op = 0x0006 // Send-Document: Add a file to a job
OpSendURI Op = 0x0007 // Send-URI: Add a URL to a job
OpCancelJob Op = 0x0008 // Cancel-Job: Cancel a job
OpGetJobAttributes Op = 0x0009 // Get-Job-Attribute: Get information about a job
OpGetJobs Op = 0x000a // Get-Jobs: Get a list of jobs
OpGetPrinterAttributes Op = 0x000b // Get-Printer-Attributes: Get information about a printer
OpHoldJob Op = 0x000c // Hold-Job: Hold a job for printing
OpReleaseJob Op = 0x000d // Release-Job: Release a job for printing
OpRestartJob Op = 0x000e // Restart-Job: Reprint a job
OpPausePrinter Op = 0x0010 // Pause-Printer: Stop a printer
OpResumePrinter Op = 0x0011 // Resume-Printer: Start a printer
OpPurgeJobs Op = 0x0012 // Purge-Jobs: Delete all jobs
OpSetPrinterAttributes Op = 0x0013 // Set-Printer-Attributes: Set printer values
OpSetJobAttributes Op = 0x0014 // Set-Job-Attributes: Set job values
OpGetPrinterSupportedValues Op = 0x0015 // Get-Printer-Supported-Values: Get supported values
OpCreatePrinterSubscriptions Op = 0x0016 // Create-Printer-Subscriptions: Create one or more printer subscriptions
OpCreateJobSubscriptions Op = 0x0017 // Create-Job-Subscriptions: Create one of more job subscriptions
OpGetSubscriptionAttributes Op = 0x0018 // Get-Subscription-Attributes: Get subscription information
OpGetSubscriptions Op = 0x0019 // Get-Subscriptions: Get list of subscriptions
OpRenewSubscription Op = 0x001a // Renew-Subscription: Renew a printer subscription
OpCancelSubscription Op = 0x001b // Cancel-Subscription: Cancel a subscription
OpGetNotifications Op = 0x001c // Get-Notifications: Get notification events
OpSendNotifications Op = 0x001d // Send-Notifications: Send notification events
OpGetResourceAttributes Op = 0x001e // Get-Resource-Attributes: Get resource information
OpGetResourceData Op = 0x001f // Get-Resource-Data: Get resource data
OpGetResources Op = 0x0020 // Get-Resources: Get list of resources
OpGetPrintSupportFiles Op = 0x0021 // Get-Printer-Support-Files: Get printer support files
OpEnablePrinter Op = 0x0022 // Enable-Printer: Accept new jobs for a printer
OpDisablePrinter Op = 0x0023 // Disable-Printer: Reject new jobs for a printer
OpPausePrinterAfterCurrentJob Op = 0x0024 // Pause-Printer-After-Current-Job: Stop printer after the current job
OpHoldNewJobs Op = 0x0025 // Hold-New-Jobs: Hold new jobs
OpReleaseHeldNewJobs Op = 0x0026 // Release-Held-New-Jobs: Release new jobs that were previously held
OpDeactivatePrinter Op = 0x0027 // Deactivate-Printer: Stop a printer and do not accept jobs
OpActivatePrinter Op = 0x0028 // Activate-Printer: Start a printer and accept jobs
OpRestartPrinter Op = 0x0029 // Restart-Printer: Restart a printer
OpShutdownPrinter Op = 0x002a // Shutdown-Printer: Turn a printer off
OpStartupPrinter Op = 0x002b // Startup-Printer: Turn a printer on
OpReprocessJob Op = 0x002c // Reprocess-Job: Reprint a job
OpCancelCurrentJob Op = 0x002d // Cancel-Current-Job: Cancel the current job
OpSuspendCurrentJob Op = 0x002e // Suspend-Current-Job: Suspend the current job
OpResumeJob Op = 0x002f // Resume-Job: Resume the current job
OpPromoteJob Op = 0x0030 // Promote-Job: Promote a job to print sooner
OpScheduleJobAfter Op = 0x0031 // Schedule-Job-After: Schedule a job to print after another
OpCancelDocument Op = 0x0033 // Cancel-Document: Cancel a document
OpGetDocumentAttributes Op = 0x0034 // Get-Document-Attributes: Get document information
OpGetDocuments Op = 0x0035 // Get-Documents: Get a list of documents in a job
OpDeleteDocument Op = 0x0036 // Delete-Document: Delete a document
OpSetDocumentAttributes Op = 0x0037 // Set-Document-Attributes: Set document values
OpCancelJobs Op = 0x0038 // Cancel-Jobs: Cancel all jobs (administrative)
OpCancelMyJobs Op = 0x0039 // Cancel-My-Jobs: Cancel a user's jobs
OpResubmitJob Op = 0x003a // Resubmit-Job: Copy and reprint a job
OpCloseJob Op = 0x003b // Close-Job: Close a job and start printing
OpIdentifyPrinter Op = 0x003c // Identify-Printer: Make the printer beep, flash, or display a message for identification
OpValidateDocument Op = 0x003d // Validate-Document: Validate document values prior to submission
OpAddDocumentImages Op = 0x003e // Add-Document-Images: Add image(s) from the specified scanner source
OpAcknowledgeDocument Op = 0x003f // Acknowledge-Document: Acknowledge processing of a document
OpAcknowledgeIdentifyPrinter Op = 0x0040 // Acknowledge-Identify-Printer: Acknowledge action on an Identify-Printer request
OpAcknowledgeJob Op = 0x0041 // Acknowledge-Job: Acknowledge processing of a job
OpFetchDocument Op = 0x0042 // Fetch-Document: Fetch a document for processing
OpFetchJob Op = 0x0043 // Fetch-Job: Fetch a job for processing
OpGetOutputDeviceAttributes Op = 0x0044 // Get-Output-Device-Attributes: Get printer information for a specific output device
OpUpdateActiveJobs Op = 0x0045 // Update-Active-Jobs: Update the list of active jobs that a proxy has processed
OpDeregisterOutputDevice Op = 0x0046 // Deregister-Output-Device: Remove an output device
OpUpdateDocumentStatus Op = 0x0047 // Update-Document-Status: Update document values
OpUpdateJobStatus Op = 0x0048 // Update-Job-Status: Update job values
OpupdateOutputDeviceAttributes Op = 0x0049 // Update-Output-Device-Attributes: Update output device values
OpGetNextDocumentData Op = 0x004a // Get-Next-Document-Data: Scan more document data
OpAllocatePrinterResources Op = 0x004b // Allocate-Printer-Resources: Use resources for a printer
OpCreatePrinter Op = 0x004c // Create-Printer: Create a new service
OpDeallocatePrinterResources Op = 0x004d // Deallocate-Printer-Resources: Stop using resources for a printer
OpDeletePrinter Op = 0x004e // Delete-Printer: Delete an existing service
OpGetPrinters Op = 0x004f // Get-Printers: Get a list of services
OpShutdownOnePrinter Op = 0x0050 // Shutdown-One-Printer: Shutdown a service
OpStartupOnePrinter Op = 0x0051 // Startup-One-Printer: Start a service
OpCancelResource Op = 0x0052 // Cancel-Resource: Uninstall a resource
OpCreateResource Op = 0x0053 // Create-Resource: Create a new (empty) resource
OpInstallResource Op = 0x0054 // Install-Resource: Install a resource
OpSendResourceData Op = 0x0055 // Send-Resource-Data: Upload the data for a resource
OpSetResourceAttributes Op = 0x0056 // Set-Resource-Attributes: Set resource object attributes
OpCreateResourceSubscriptions Op = 0x0057 // Create-Resource-Subscriptions: Create event subscriptions for a resource
OpCreateSystemSubscriptions Op = 0x0058 // Create-System-Subscriptions: Create event subscriptions for a system
OpDisableAllPrinters Op = 0x0059 // Disable-All-Printers: Stop accepting new jobs on all services
OpEnableAllPrinters Op = 0x005a // Enable-All-Printers: Start accepting new jobs on all services
OpGetSystemAttributes Op = 0x005b // Get-System-Attributes: Get system object attributes
OpGetSystemSupportedValues Op = 0x005c // Get-System-Supported-Values: Get supported values for system object attributes
OpPauseAllPrinters Op = 0x005d // Pause-All-Printers: Stop all services immediately
OpPauseAllPrintersAfterCurrentJob Op = 0x005e // Pause-All-Printers-After-Current-Job: Stop all services after processing the current jobs
OpRegisterOutputDevice Op = 0x005f // Register-Output-Device: Register a remote service
OpRestartSystem Op = 0x0060 // Restart-System: Restart all services
OpResumeAllPrinters Op = 0x0061 // Resume-All-Printers: Start job processing on all services
OpSetSystemAttributes Op = 0x0062 // Set-System-Attributes: Set system object attributes
OpShutdownAllPrinters Op = 0x0063 // Shutdown-All-Printers: Shutdown all services
OpStartupAllPrinters Op = 0x0064 // Startup-All-Printers: Startup all services
OpCupsGetDefault Op = 0x4001 // CUPS-Get-Default: Get the default printer
OpCupsGetPrinters Op = 0x4002 // CUPS-Get-Printers: Get a list of printers and/or classes
OpCupsAddModifyPrinter Op = 0x4003 // CUPS-Add-Modify-Printer: Add or modify a printer
OpCupsDeletePrinter Op = 0x4004 // CUPS-Delete-Printer: Delete a printer
OpCupsGetClasses Op = 0x4005 // CUPS-Get-Classes: Get a list of classes
OpCupsAddModifyClass Op = 0x4006 // CUPS-Add-Modify-Class: Add or modify a class
OpCupsDeleteClass Op = 0x4007 // CUPS-Delete-Class: Delete a class
OpCupsAcceptJobs Op = 0x4008 // CUPS-Accept-Jobs: Accept new jobs on a printer
OpCupsRejectJobs Op = 0x4009 // CUPS-Reject-Jobs: Reject new jobs on a printer
OpCupsSetDefault Op = 0x400a // CUPS-Set-Default: Set the default printer
OpCupsGetDevices Op = 0x400b // CUPS-Get-Devices: Get a list of supported devices
OpCupsGetPpds Op = 0x400c // CUPS-Get-PPDs: Get a list of supported drivers
OpCupsMoveJob Op = 0x400d // CUPS-Move-Job: Move a job to a different printer
OpCupsAuthenticateJob Op = 0x400e // CUPS-Authenticate-Job: Authenticate a job
OpCupsGetPpd Op = 0x400f // CUPS-Get-PPD: Get a PPD file
OpCupsGetDocument Op = 0x4027 // CUPS-Get-Document: Get a document file
OpCupsCreateLocalPrinter Op = 0x4028 // CUPS-Create-Local-Printer: Create a local (temporary) printer
)
// String() returns a Status name, as defined by RFC 8010
func (op Op) String() string {
if s := opNames[op]; s != "" {
return s
}
return fmt.Sprintf("0x%4.4x", int(op))
}
var opNames = map[Op]string{
OpPrintJob: "Print-Job",
OpPrintURI: "Print-URI",
OpValidateJob: "Validate-Job",
OpCreateJob: "Create-Job",
OpSendDocument: "Send-Document",
OpSendURI: "Send-URI",
OpCancelJob: "Cancel-Job",
OpGetJobAttributes: "Get-Job-Attribute",
OpGetJobs: "Get-Jobs",
OpGetPrinterAttributes: "Get-Printer-Attributes",
OpHoldJob: "Hold-Job",
OpReleaseJob: "Release-Job",
OpRestartJob: "Restart-Job",
OpPausePrinter: "Pause-Printer",
OpResumePrinter: "Resume-Printer",
OpPurgeJobs: "Purge-Jobs",
OpSetPrinterAttributes: "Set-Printer-Attributes",
OpSetJobAttributes: "Set-Job-Attributes",
OpGetPrinterSupportedValues: "Get-Printer-Supported-Values",
OpCreatePrinterSubscriptions: "Create-Printer-Subscriptions",
OpCreateJobSubscriptions: "Create-Job-Subscriptions",
OpGetSubscriptionAttributes: "Get-Subscription-Attributes",
OpGetSubscriptions: "Get-Subscriptions",
OpRenewSubscription: "Renew-Subscription",
OpCancelSubscription: "Cancel-Subscription",
OpGetNotifications: "Get-Notifications",
OpSendNotifications: "Send-Notifications",
OpGetResourceAttributes: "Get-Resource-Attributes",
OpGetResourceData: "Get-Resource-Data",
OpGetResources: "Get-Resources",
OpGetPrintSupportFiles: "Get-Printer-Support-Files",
OpEnablePrinter: "Enable-Printer",
OpDisablePrinter: "Disable-Printer",
OpPausePrinterAfterCurrentJob: "Pause-Printer-After-Current-Job",
OpHoldNewJobs: "Hold-New-Jobs",
OpReleaseHeldNewJobs: "Release-Held-New-Jobs",
OpDeactivatePrinter: "Deactivate-Printer",
OpActivatePrinter: "Activate-Printer",
OpRestartPrinter: "Restart-Printer",
OpShutdownPrinter: "Shutdown-Printer",
OpStartupPrinter: "Startup-Printer",
OpReprocessJob: "Reprocess-Job",
OpCancelCurrentJob: "Cancel-Current-Job",
OpSuspendCurrentJob: "Suspend-Current-Job",
OpResumeJob: "Resume-Job",
OpPromoteJob: "Promote-Job",
OpScheduleJobAfter: "Schedule-Job-After",
OpCancelDocument: "Cancel-Document",
OpGetDocumentAttributes: "Get-Document-Attributes",
OpGetDocuments: "Get-Documents",
OpDeleteDocument: "Delete-Document",
OpSetDocumentAttributes: "Set-Document-Attributes",
OpCancelJobs: "Cancel-Jobs",
OpCancelMyJobs: "Cancel-My-Jobs",
OpResubmitJob: "Resubmit-Job",
OpCloseJob: "Close-Job",
OpIdentifyPrinter: "Identify-Printer",
OpValidateDocument: "Validate-Document",
OpAddDocumentImages: "Add-Document-Images",
OpAcknowledgeDocument: "Acknowledge-Document",
OpAcknowledgeIdentifyPrinter: "Acknowledge-Identify-Printer",
OpAcknowledgeJob: "Acknowledge-Job",
OpFetchDocument: "Fetch-Document",
OpFetchJob: "Fetch-Job",
OpGetOutputDeviceAttributes: "Get-Output-Device-Attributes",
OpUpdateActiveJobs: "Update-Active-Jobs",
OpDeregisterOutputDevice: "Deregister-Output-Device",
OpUpdateDocumentStatus: "Update-Document-Status",
OpUpdateJobStatus: "Update-Job-Status",
OpupdateOutputDeviceAttributes: "Update-Output-Device-Attributes",
OpGetNextDocumentData: "Get-Next-Document-Data",
OpAllocatePrinterResources: "Allocate-Printer-Resources",
OpCreatePrinter: "Create-Printer",
OpDeallocatePrinterResources: "Deallocate-Printer-Resources",
OpDeletePrinter: "Delete-Printer",
OpGetPrinters: "Get-Printers",
OpShutdownOnePrinter: "Shutdown-One-Printer",
OpStartupOnePrinter: "Startup-One-Printer",
OpCancelResource: "Cancel-Resource",
OpCreateResource: "Create-Resource",
OpInstallResource: "Install-Resource",
OpSendResourceData: "Send-Resource-Data",
OpSetResourceAttributes: "Set-Resource-Attributes",
OpCreateResourceSubscriptions: "Create-Resource-Subscriptions",
OpCreateSystemSubscriptions: "Create-System-Subscriptions",
OpDisableAllPrinters: "Disable-All-Printers",
OpEnableAllPrinters: "Enable-All-Printers",
OpGetSystemAttributes: "Get-System-Attributes",
OpGetSystemSupportedValues: "Get-System-Supported-Values",
OpPauseAllPrinters: "Pause-All-Printers",
OpPauseAllPrintersAfterCurrentJob: "Pause-All-Printers-After-Current-Job",
OpRegisterOutputDevice: "Register-Output-Device",
OpRestartSystem: "Restart-System",
OpResumeAllPrinters: "Resume-All-Printers",
OpSetSystemAttributes: "Set-System-Attributes",
OpShutdownAllPrinters: "Shutdown-All-Printers",
OpStartupAllPrinters: "Startup-All-Printers",
OpCupsGetDefault: "CUPS-Get-Default",
OpCupsGetPrinters: "CUPS-Get-Printers",
OpCupsAddModifyPrinter: "CUPS-Add-Modify-Printer",
OpCupsDeletePrinter: "CUPS-Delete-Printer",
OpCupsGetClasses: "CUPS-Get-Classes",
OpCupsAddModifyClass: "CUPS-Add-Modify-Class",
OpCupsDeleteClass: "CUPS-Delete-Class",
OpCupsAcceptJobs: "CUPS-Accept-Jobs",
OpCupsRejectJobs: "CUPS-Reject-Jobs",
OpCupsSetDefault: "CUPS-Set-Default",
OpCupsGetDevices: "CUPS-Get-Devices",
OpCupsGetPpds: "CUPS-Get-PPDs",
OpCupsMoveJob: "CUPS-Move-Job",
OpCupsAuthenticateJob: "CUPS-Authenticate-Job",
OpCupsGetPpd: "CUPS-Get-PPD",
OpCupsGetDocument: "CUPS-Get-Document",
OpCupsCreateLocalPrinter: "CUPS-Create-Local-Printer",
}
07070100000015000081A400000000000000000000000167F56AFB000003BA000000000000000000000000000000000000001700000000goipp-1.2.0/op_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Operation Codes tests
*/
package goipp
import "testing"
// TestOpString tests Op.String method
func TestOpString(t *testing.T) {
type testData struct {
op Op // Input Op code
s string // Expected output string
}
tests := []testData{
{OpPrintJob, "Print-Job"},
{OpPrintURI, "Print-URI"},
{OpPausePrinter, "Pause-Printer"},
{OpRestartSystem, "Restart-System"},
{OpCupsGetDefault, "CUPS-Get-Default"},
{OpCupsGetPpd, "CUPS-Get-PPD"},
{OpCupsCreateLocalPrinter, "CUPS-Create-Local-Printer"},
{0xabcd, "0xabcd"},
}
for _, test := range tests {
s := test.op.String()
if s != test.s {
t.Errorf("testing Op.String:\n"+
"input: 0x%4.4x\n"+
"expected: %s\n"+
"present: %s\n",
int(test.op), test.s, s,
)
}
}
}
07070100000016000081A400000000000000000000000167F56AFB000026A1000000000000000000000000000000000000001600000000goipp-1.2.0/status.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Status Codes
*/
package goipp
import (
"fmt"
)
// Status represents an IPP Status Code
type Status Code
// Status codes
const (
StatusOk Status = 0x0000 // successful-ok
StatusOkIgnoredOrSubstituted Status = 0x0001 // successful-ok-ignored-or-substituted-attributes
StatusOkConflicting Status = 0x0002 // successful-ok-conflicting-attributes
StatusOkIgnoredSubscriptions Status = 0x0003 // successful-ok-ignored-subscriptions
StatusOkIgnoredNotifications Status = 0x0004 // successful-ok-ignored-notifications
StatusOkTooManyEvents Status = 0x0005 // successful-ok-too-many-events
StatusOkButCancelSubscription Status = 0x0006 // successful-ok-but-cancel-subscription
StatusOkEventsComplete Status = 0x0007 // successful-ok-events-complete
StatusRedirectionOtherSite Status = 0x0200 // redirection-other-site
StatusCupsSeeOther Status = 0x0280 // cups-see-other
StatusErrorBadRequest Status = 0x0400 // client-error-bad-request
StatusErrorForbidden Status = 0x0401 // client-error-forbidden
StatusErrorNotAuthenticated Status = 0x0402 // client-error-not-authenticated
StatusErrorNotAuthorized Status = 0x0403 // client-error-not-authorized
StatusErrorNotPossible Status = 0x0404 // client-error-not-possible
StatusErrorTimeout Status = 0x0405 // client-error-timeout
StatusErrorNotFound Status = 0x0406 // client-error-not-found
StatusErrorGone Status = 0x0407 // client-error-gone
StatusErrorRequestEntity Status = 0x0408 // client-error-request-entity-too-large
StatusErrorRequestValue Status = 0x0409 // client-error-request-value-too-long
StatusErrorDocumentFormatNotSupported Status = 0x040a // client-error-document-format-not-supported
StatusErrorAttributesOrValues Status = 0x040b // client-error-attributes-or-values-not-supported
StatusErrorURIScheme Status = 0x040c // client-error-uri-scheme-not-supported
StatusErrorCharset Status = 0x040d // client-error-charset-not-supported
StatusErrorConflicting Status = 0x040e // client-error-conflicting-attributes
StatusErrorCompressionNotSupported Status = 0x040f // client-error-compression-not-supported
StatusErrorCompressionError Status = 0x0410 // client-error-compression-error
StatusErrorDocumentFormatError Status = 0x0411 // client-error-document-format-error
StatusErrorDocumentAccess Status = 0x0412 // client-error-document-access-error
StatusErrorAttributesNotSettable Status = 0x0413 // client-error-attributes-not-settable
StatusErrorIgnoredAllSubscriptions Status = 0x0414 // client-error-ignored-all-subscriptions
StatusErrorTooManySubscriptions Status = 0x0415 // client-error-too-many-subscriptions
StatusErrorIgnoredAllNotifications Status = 0x0416 // client-error-ignored-all-notifications
StatusErrorPrintSupportFileNotFound Status = 0x0417 // client-error-print-support-file-not-found
StatusErrorDocumentPassword Status = 0x0418 // client-error-document-password-error
StatusErrorDocumentPermission Status = 0x0419 // client-error-document-permission-error
StatusErrorDocumentSecurity Status = 0x041a // client-error-document-security-error
StatusErrorDocumentUnprintable Status = 0x041b // client-error-document-unprintable-error
StatusErrorAccountInfoNeeded Status = 0x041c // client-error-account-info-needed
StatusErrorAccountClosed Status = 0x041d // client-error-account-closed
StatusErrorAccountLimitReached Status = 0x041e // client-error-account-limit-reached
StatusErrorAccountAuthorizationFailed Status = 0x041f // client-error-account-authorization-failed
StatusErrorNotFetchable Status = 0x0420 // client-error-not-fetchable
StatusErrorInternal Status = 0x0500 // server-error-internal-error
StatusErrorOperationNotSupported Status = 0x0501 // server-error-operation-not-supported
StatusErrorServiceUnavailable Status = 0x0502 // server-error-service-unavailable
StatusErrorVersionNotSupported Status = 0x0503 // server-error-version-not-supported
StatusErrorDevice Status = 0x0504 // server-error-device-error
StatusErrorTemporary Status = 0x0505 // server-error-temporary-error
StatusErrorNotAcceptingJobs Status = 0x0506 // server-error-not-accepting-jobs
StatusErrorBusy Status = 0x0507 // server-error-busy
StatusErrorJobCanceled Status = 0x0508 // server-error-job-canceled
StatusErrorMultipleJobsNotSupported Status = 0x0509 // server-error-multiple-document-jobs-not-supported
StatusErrorPrinterIsDeactivated Status = 0x050a // server-error-printer-is-deactivated
StatusErrorTooManyJobs Status = 0x050b // server-error-too-many-jobs
StatusErrorTooManyDocuments Status = 0x050c // server-error-too-many-documents
)
// String() returns a Status name, as defined by RFC 8010
func (status Status) String() string {
if s := statusNames[status]; s != "" {
return s
}
return fmt.Sprintf("0x%4.4x", int(status))
}
var statusNames = map[Status]string{
StatusOk: "successful-ok",
StatusOkIgnoredOrSubstituted: "successful-ok-ignored-or-substituted-attributes",
StatusOkConflicting: "successful-ok-conflicting-attributes",
StatusOkIgnoredSubscriptions: "successful-ok-ignored-subscriptions",
StatusOkIgnoredNotifications: "successful-ok-ignored-notifications",
StatusOkTooManyEvents: "successful-ok-too-many-events",
StatusOkButCancelSubscription: "successful-ok-but-cancel-subscription",
StatusOkEventsComplete: "successful-ok-events-complete",
StatusRedirectionOtherSite: "redirection-other-site",
StatusCupsSeeOther: "cups-see-other",
StatusErrorBadRequest: "client-error-bad-request",
StatusErrorForbidden: "client-error-forbidden",
StatusErrorNotAuthenticated: "client-error-not-authenticated",
StatusErrorNotAuthorized: "client-error-not-authorized",
StatusErrorNotPossible: "client-error-not-possible",
StatusErrorTimeout: "client-error-timeout",
StatusErrorNotFound: "client-error-not-found",
StatusErrorGone: "client-error-gone",
StatusErrorRequestEntity: "client-error-request-entity-too-large",
StatusErrorRequestValue: "client-error-request-value-too-long",
StatusErrorDocumentFormatNotSupported: "client-error-document-format-not-supported",
StatusErrorAttributesOrValues: "client-error-attributes-or-values-not-supported",
StatusErrorURIScheme: "client-error-uri-scheme-not-supported",
StatusErrorCharset: "client-error-charset-not-supported",
StatusErrorConflicting: "client-error-conflicting-attributes",
StatusErrorCompressionNotSupported: "client-error-compression-not-supported",
StatusErrorCompressionError: "client-error-compression-error",
StatusErrorDocumentFormatError: "client-error-document-format-error",
StatusErrorDocumentAccess: "client-error-document-access-error",
StatusErrorAttributesNotSettable: "client-error-attributes-not-settable",
StatusErrorIgnoredAllSubscriptions: "client-error-ignored-all-subscriptions",
StatusErrorTooManySubscriptions: "client-error-too-many-subscriptions",
StatusErrorIgnoredAllNotifications: "client-error-ignored-all-notifications",
StatusErrorPrintSupportFileNotFound: "client-error-print-support-file-not-found",
StatusErrorDocumentPassword: "client-error-document-password-error",
StatusErrorDocumentPermission: "client-error-document-permission-error",
StatusErrorDocumentSecurity: "client-error-document-security-error",
StatusErrorDocumentUnprintable: "client-error-document-unprintable-error",
StatusErrorAccountInfoNeeded: "client-error-account-info-needed",
StatusErrorAccountClosed: "client-error-account-closed",
StatusErrorAccountLimitReached: "client-error-account-limit-reached",
StatusErrorAccountAuthorizationFailed: "client-error-account-authorization-failed",
StatusErrorNotFetchable: "client-error-not-fetchable",
StatusErrorInternal: "server-error-internal-error",
StatusErrorOperationNotSupported: "server-error-operation-not-supported",
StatusErrorServiceUnavailable: "server-error-service-unavailable",
StatusErrorVersionNotSupported: "server-error-version-not-supported",
StatusErrorDevice: "server-error-device-error",
StatusErrorTemporary: "server-error-temporary-error",
StatusErrorNotAcceptingJobs: "server-error-not-accepting-jobs",
StatusErrorBusy: "server-error-busy",
StatusErrorJobCanceled: "server-error-job-canceled",
StatusErrorMultipleJobsNotSupported: "server-error-multiple-document-jobs-not-supported",
StatusErrorPrinterIsDeactivated: "server-error-printer-is-deactivated",
StatusErrorTooManyJobs: "server-error-too-many-jobs",
StatusErrorTooManyDocuments: "server-error-too-many-documents",
}
07070100000017000081A400000000000000000000000167F56AFB000004C3000000000000000000000000000000000000001B00000000goipp-1.2.0/status_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Status Codes tests
*/
package goipp
import "testing"
// TestStatusString tests Status.String method
func TestStatusString(t *testing.T) {
type testData struct {
status Status // Input Op code
s string // Expected output string
}
tests := []testData{
{StatusOk, "successful-ok"},
{StatusOkConflicting, "successful-ok-conflicting-attributes"},
{StatusOkEventsComplete, "successful-ok-events-complete"},
{StatusRedirectionOtherSite, "redirection-other-site"},
{StatusErrorBadRequest, "client-error-bad-request"},
{StatusErrorForbidden, "client-error-forbidden"},
{StatusErrorNotFetchable, "client-error-not-fetchable"},
{StatusErrorInternal, "server-error-internal-error"},
{StatusErrorTooManyDocuments, "server-error-too-many-documents"},
{0xabcd, "0xabcd"},
}
for _, test := range tests {
s := test.status.String()
if s != test.s {
t.Errorf("testing Status.String:\n"+
"input: 0x%4.4x\n"+
"expected: %s\n"+
"present: %s\n",
int(test.status), test.s, s,
)
}
}
}
07070100000018000081A400000000000000000000000167F56AFB00001CA1000000000000000000000000000000000000001300000000goipp-1.2.0/tag.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Tags
*/
package goipp
import (
"fmt"
)
// Tag represents a tag used in the binary representation of an IPP message.
//
// Normally, a Tag is a single-byte value ranging from 0x00 to 0xff.
//
// However, IPP also provides an extension tag mechanism that supports
// 32-bit tags. When using this mechanism, the tag is set to [TagExtension],
// and the actual 32-bit tag value is encoded as a big-endian integer
// in the attribute value.
//
// This mechanism is described in RFC 8010, Section 3.5.2.
//
// To send an [Attribute] with an extended tag value:
//
// - Set the value tag to TagExtension
// - Use [Binary] to represent the Attribute value
// - Encode the extended tag value as a big-endian integer in the first
// 4 bytes of the attribute value
// - Note that the extended tag value must be within the allowed range
// (0x00000000 to 0x7fffffff, inclusive)
//
// The goipp library enforces these rules during message encoding and decoding.
//
// Note: This API has changed since version 1.1.0 of this library.
// Previously, the library automatically converted tags with values exceeding
// single-byte range into extended tag representation. However, this caused
// issues during reception because automatically converted tags with extended
// representation format but smaller values became indistinguishable from
// normal tags with the same value, despite requiring different handling.
type Tag int
// Tag values
const (
// Delimiter tags
TagZero Tag = 0x00 // Zero tag - used for separators
TagOperationGroup Tag = 0x01 // Operation group
TagJobGroup Tag = 0x02 // Job group
TagEnd Tag = 0x03 // End-of-attributes
TagPrinterGroup Tag = 0x04 // Printer group
TagUnsupportedGroup Tag = 0x05 // Unsupported attributes group
TagSubscriptionGroup Tag = 0x06 // Subscription group
TagEventNotificationGroup Tag = 0x07 // Event group
TagResourceGroup Tag = 0x08 // Resource group
TagDocumentGroup Tag = 0x09 // Document group
TagSystemGroup Tag = 0x0a // System group
TagFuture11Group Tag = 0x0b // Future group 11
TagFuture12Group Tag = 0x0c // Future group 12
TagFuture13Group Tag = 0x0d // Future group 13
TagFuture14Group Tag = 0x0e // Future group 14
TagFuture15Group Tag = 0x0f // Future group 15
// Value tags
TagUnsupportedValue Tag = 0x10 // Unsupported value
TagDefault Tag = 0x11 // Default value
TagUnknown Tag = 0x12 // Unknown value
TagNoValue Tag = 0x13 // No-value value
TagNotSettable Tag = 0x15 // Not-settable value
TagDeleteAttr Tag = 0x16 // Delete-attribute value
TagAdminDefine Tag = 0x17 // Admin-defined value
TagInteger Tag = 0x21 // Integer value
TagBoolean Tag = 0x22 // Boolean value
TagEnum Tag = 0x23 // Enumeration value
TagString Tag = 0x30 // Octet string value
TagDateTime Tag = 0x31 // Date/time value
TagResolution Tag = 0x32 // Resolution value
TagRange Tag = 0x33 // Range value
TagBeginCollection Tag = 0x34 // Beginning of collection value
TagTextLang Tag = 0x35 // Text-with-language value
TagNameLang Tag = 0x36 // Name-with-language value
TagEndCollection Tag = 0x37 // End of collection value
TagText Tag = 0x41 // Text value
TagName Tag = 0x42 // Name value
TagReservedString Tag = 0x43 // Reserved for future string value
TagKeyword Tag = 0x44 // Keyword value
TagURI Tag = 0x45 // URI value
TagURIScheme Tag = 0x46 // URI scheme value
TagCharset Tag = 0x47 // Character set value
TagLanguage Tag = 0x48 // Language value
TagMimeType Tag = 0x49 // MIME media type value
TagMemberName Tag = 0x4a // Collection member name value
TagExtension Tag = 0x7f // Extension point for 32-bit tags
)
// IsDelimiter returns true for delimiter tags
func (tag Tag) IsDelimiter() bool {
return uint(tag) < 0x10
}
// IsGroup returns true for group tags
func (tag Tag) IsGroup() bool {
return tag.IsDelimiter() && tag != TagZero && tag != TagEnd
}
// Type returns Type of Value that corresponds to the tag
func (tag Tag) Type() Type {
if tag.IsDelimiter() {
return TypeInvalid
}
switch tag {
case TagInteger, TagEnum:
return TypeInteger
case TagBoolean:
return TypeBoolean
case TagUnsupportedValue, TagDefault, TagUnknown, TagNotSettable,
TagNoValue, TagDeleteAttr, TagAdminDefine:
// These tags not expected to have value
return TypeVoid
case TagText, TagName, TagReservedString, TagKeyword, TagURI, TagURIScheme,
TagCharset, TagLanguage, TagMimeType, TagMemberName:
return TypeString
case TagDateTime:
return TypeDateTime
case TagResolution:
return TypeResolution
case TagRange:
return TypeRange
case TagTextLang, TagNameLang:
return TypeTextWithLang
case TagBeginCollection:
return TypeCollection
case TagEndCollection:
return TypeVoid
case TagExtension:
return TypeBinary
default:
return TypeBinary
}
}
// String() returns a tag name, as defined by RFC 8010
func (tag Tag) String() string {
if 0 <= tag && int(tag) < len(tagNames) {
if s := tagNames[tag]; s != "" {
return s
}
}
if tag < 0x100 {
return fmt.Sprintf("0x%2.2x", uint32(tag))
}
return fmt.Sprintf("0x%8.8x", uint(tag))
}
var tagNames = [...]string{
// Delimiter tags
TagZero: "zero",
TagOperationGroup: "operation-attributes-tag",
TagJobGroup: "job-attributes-tag",
TagEnd: "end-of-attributes-tag",
TagPrinterGroup: "printer-attributes-tag",
TagUnsupportedGroup: "unsupported-attributes-tag",
TagSubscriptionGroup: "subscription-attributes-tag",
TagEventNotificationGroup: "event-notification-attributes-tag",
TagResourceGroup: "resource-attributes-tag",
TagDocumentGroup: "document-attributes-tag",
TagSystemGroup: "system-attributes-tag",
// Value tags
TagUnsupportedValue: "unsupported",
TagDefault: "default",
TagUnknown: "unknown",
TagNoValue: "no-value",
TagNotSettable: "not-settable",
TagDeleteAttr: "delete-attribute",
TagAdminDefine: "admin-define",
TagInteger: "integer",
TagBoolean: "boolean",
TagEnum: "enum",
TagString: "octetString",
TagDateTime: "dateTime",
TagResolution: "resolution",
TagRange: "rangeOfInteger",
TagBeginCollection: "collection",
TagTextLang: "textWithLanguage",
TagNameLang: "nameWithLanguage",
TagEndCollection: "endCollection",
TagText: "textWithoutLanguage",
TagName: "nameWithoutLanguage",
TagKeyword: "keyword",
TagURI: "uri",
TagURIScheme: "uriScheme",
TagCharset: "charset",
TagLanguage: "naturalLanguage",
TagMimeType: "mimeMediaType",
TagMemberName: "memberAttrName",
TagExtension: "extension",
}
07070100000019000081A400000000000000000000000167F56AFB00000E72000000000000000000000000000000000000001800000000goipp-1.2.0/tag_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* IPP Tags tests
*/
package goipp
import "testing"
// TestTagIsDelimiter tests Tag.IsDelimiter function
func TestTagIsDelimiter(t *testing.T) {
type testData struct {
t Tag
answer bool
}
tests := []testData{
{TagZero, true},
{TagOperationGroup, true},
{TagJobGroup, true},
{TagEnd, true},
{TagFuture15Group, true},
{TagUnsupportedValue, false},
{TagUnknown, false},
{TagInteger, false},
{TagBeginCollection, false},
{TagEndCollection, false},
{TagExtension, false},
}
for _, test := range tests {
answer := test.t.IsDelimiter()
if answer != test.answer {
t.Errorf("testing Tag.IsDelimiter:\n"+
"tag: %s (0x%.2x)\n"+
"expected: %v\n"+
"present: %v\n",
test.t, uint32(test.t), test.answer, answer,
)
}
}
}
// TestTagIsGroup tests Tag.IsGroup function
func TestTagIsGroup(t *testing.T) {
type testData struct {
t Tag
answer bool
}
tests := []testData{
{TagZero, false},
{TagOperationGroup, true},
{TagJobGroup, true},
{TagEnd, false},
{TagPrinterGroup, true},
{TagUnsupportedGroup, true},
{TagSubscriptionGroup, true},
{TagEventNotificationGroup, true},
{TagResourceGroup, true},
{TagDocumentGroup, true},
{TagSystemGroup, true},
{TagFuture11Group, true},
{TagFuture12Group, true},
{TagFuture13Group, true},
{TagFuture14Group, true},
{TagFuture15Group, true},
{TagInteger, false},
}
for _, test := range tests {
answer := test.t.IsGroup()
if answer != test.answer {
t.Errorf("testing Tag.IsGroup:\n"+
"tag: %s (0x%.2x)\n"+
"expected: %v\n"+
"present: %v\n",
test.t, uint32(test.t), test.answer, answer,
)
}
}
}
// TestTagType tests Tag.Type function
func TestTagType(t *testing.T) {
type testData struct {
t Tag
answer Type
}
tests := []testData{
{TagZero, TypeInvalid},
{TagInteger, TypeInteger},
{TagEnum, TypeInteger},
{TagBoolean, TypeBoolean},
{TagUnsupportedValue, TypeVoid},
{TagDefault, TypeVoid},
{TagUnknown, TypeVoid},
{TagNotSettable, TypeVoid},
{TagNoValue, TypeVoid},
{TagDeleteAttr, TypeVoid},
{TagAdminDefine, TypeVoid},
{TagText, TypeString},
{TagName, TypeString},
{TagReservedString, TypeString},
{TagKeyword, TypeString},
{TagURI, TypeString},
{TagURIScheme, TypeString},
{TagCharset, TypeString},
{TagLanguage, TypeString},
{TagMimeType, TypeString},
{TagMemberName, TypeString},
{TagDateTime, TypeDateTime},
{TagResolution, TypeResolution},
{TagRange, TypeRange},
{TagTextLang, TypeTextWithLang},
{TagNameLang, TypeTextWithLang},
{TagBeginCollection, TypeCollection},
{TagEndCollection, TypeVoid},
{TagExtension, TypeBinary},
{0x1234, TypeBinary},
}
for _, test := range tests {
answer := test.t.Type()
if answer != test.answer {
t.Errorf("testing Tag.Type:\n"+
"tag: %s (0x%.2x)\n"+
"expected: %v\n"+
"present: %v\n",
test.t, uint32(test.t), test.answer, answer,
)
}
}
}
// TestTagString tests Tag.String function
func TestTagString(t *testing.T) {
type testData struct {
t Tag
answer string
}
tests := []testData{
{TagZero, "zero"},
{TagUnsupportedValue, "unsupported"},
{-1, "0xffffffff"},
{0xff, "0xff"},
{0x1234, "0x00001234"},
}
for _, test := range tests {
answer := test.t.String()
if answer != test.answer {
t.Errorf("testing Tag.IsGroup:\n"+
"tag: %s (0x%.2x)\n"+
"expected: %v\n"+
"present: %v\n",
test.t, uint32(test.t), test.answer, answer,
)
}
}
}
0707010000001A000081A400000000000000000000000167F56AFB000005CE000000000000000000000000000000000000001400000000goipp-1.2.0/type.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Enumeration of value types
*/
package goipp
import (
"fmt"
)
// Type enumerates all possible value types
type Type int
// Type values
const (
TypeInvalid Type = -1 // Invalid Value type
TypeVoid Type = iota // Value is Void
TypeInteger // Value is Integer
TypeBoolean // Value is Boolean
TypeString // Value is String
TypeDateTime // Value is Time
TypeResolution // Value is Resolution
TypeRange // Value is Range
TypeTextWithLang // Value is TextWithLang
TypeBinary // Value is Binary
TypeCollection // Value is Collection
)
// String converts Type to string, for debugging
func (t Type) String() string {
if t == TypeInvalid {
return "Invalid"
}
if 0 <= t && int(t) < len(typeNames) {
if s := typeNames[t]; s != "" {
return s
}
}
return fmt.Sprintf("0x%4.4x", uint(t))
}
var typeNames = [...]string{
TypeVoid: "Void",
TypeInteger: "Integer",
TypeBoolean: "Boolean",
TypeString: "String",
TypeDateTime: "DateTime",
TypeResolution: "Resolution",
TypeRange: "Range",
TypeTextWithLang: "TextWithLang",
TypeBinary: "Binary",
TypeCollection: "Collection",
}
0707010000001B000081A400000000000000000000000167F56AFB000003DC000000000000000000000000000000000000001900000000goipp-1.2.0/type_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Tests for enumeration of value types
*/
package goipp
import "testing"
// TestTypeString tests Type.String function
func TestTypeString(t *testing.T) {
type testData struct {
ty Type // Input Type
s string // Expected output string
}
tests := []testData{
{TypeInvalid, "Invalid"},
{TypeVoid, "Void"},
{TypeBoolean, "Boolean"},
{TypeString, "String"},
{TypeDateTime, "DateTime"},
{TypeResolution, "Resolution"},
{TypeRange, "Range"},
{TypeTextWithLang, "TextWithLang"},
{TypeBinary, "Binary"},
{TypeCollection, "Collection"},
{0x1234, "0x1234"},
}
for _, test := range tests {
s := test.ty.String()
if s != test.s {
t.Errorf("testing Type.String:\n"+
"input: %d\n"+
"expected: %s\n"+
"present: %s\n",
int(test.ty), test.s, s,
)
}
}
}
0707010000001C000081A400000000000000000000000167F56AFB0000474A000000000000000000000000000000000000001500000000goipp-1.2.0/value.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Values for message attributes
*/
package goipp
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math"
"time"
)
// Values represents a sequence of values with tags.
// Usually Values used as a "payload" of Attribute
type Values []struct {
T Tag // The tag
V Value // The value
}
// Add Value to Values
func (values *Values) Add(t Tag, v Value) {
*values = append(*values, struct {
T Tag
V Value
}{t, v})
}
// String converts Values to string
func (values Values) String() string {
if len(values) == 1 {
return values[0].V.String()
}
var buf bytes.Buffer
buf.Write([]byte("["))
for i, v := range values {
if i != 0 {
buf.Write([]byte(","))
}
buf.Write([]byte(v.V.String()))
}
buf.Write([]byte("]"))
return buf.String()
}
// Clone creates a shallow copy of Values.
// For nil input it returns nil output.
func (values Values) Clone() Values {
var values2 Values
if values != nil {
values2 = make(Values, len(values))
copy(values2, values)
}
return values2
}
// DeepCopy creates a deep copy of Values
// For nil input it returns nil output.
func (values Values) DeepCopy() Values {
var values2 Values
if values != nil {
values2 = make(Values, len(values))
for i := range values {
values2[i].T = values[i].T
values2[i].V = values[i].V.DeepCopy()
}
}
return values2
}
// Equal performs deep check of equality of two Values.
//
// Note, Values(nil) and Values{} are not Equal but Similar.
func (values Values) Equal(values2 Values) bool {
if len(values) != len(values2) {
return false
}
if (values == nil) != (values2 == nil) {
return false
}
for i, v := range values {
v2 := values2[i]
if v.T != v2.T || !ValueEqual(v.V, v2.V) {
return false
}
}
return true
}
// Similar performs deep check of **logical** equality of two Values
//
// Note, Values(nil) and Values{} are not Equal but Similar.
func (values Values) Similar(values2 Values) bool {
if len(values) != len(values2) {
return false
}
for i, v := range values {
v2 := values2[i]
if v.T != v2.T || !ValueSimilar(v.V, v2.V) {
return false
}
}
return true
}
// Value represents an attribute value
//
// IPP uses typed values, and type of each value is unambiguously
// defined by the attribute tag
type Value interface {
String() string
Type() Type
DeepCopy() Value
encode() ([]byte, error)
decode([]byte) (Value, error)
}
var (
_ = Value(Binary(nil))
_ = Value(Boolean(false))
_ = Value(Collection(nil))
_ = Value(Integer(0))
_ = Value(Range{})
_ = Value(Resolution{})
_ = Value(String(""))
_ = Value(TextWithLang{})
_ = Value(Time{time.Time{}})
_ = Value(Void{})
)
// IntegerOrRange is a Value of type Integer or Range
type IntegerOrRange interface {
Value
// Within checks that x fits within the range:
//
// for Integer: x == Integer's value
// for Range: Lower <= x && x <= Upper
Within(x int) bool
}
var (
_ = IntegerOrRange(Integer(0))
_ = IntegerOrRange(Range{})
)
// ValueEqual checks if two values are equal
//
// Equality means that types and values are equal. For structured
// values, like Collection, deep comparison is performed
func ValueEqual(v1, v2 Value) bool {
if v1.Type() != v2.Type() {
return false
}
switch v1.Type() {
case TypeDateTime:
return v1.(Time).Equal(v2.(Time).Time)
case TypeBinary:
return bytes.Equal(v1.(Binary), v2.(Binary))
case TypeCollection:
c1 := Attributes(v1.(Collection))
c2 := Attributes(v2.(Collection))
return c1.Equal(c2)
}
return v1 == v2
}
// ValueSimilar checks if two values are **logically** equal,
// which means the following:
// - If values are equal (i.e., ValueEqual() returns true),
// they are similar.
// - Binary and String values are similar, if they represent
// the same sequence of bytes.
// - Two collections are similar, if they contain the same
// set of attributes (but may be differently ordered) and
// values of these attributes are similar.
func ValueSimilar(v1, v2 Value) bool {
if ValueEqual(v1, v2) {
return true
}
t1 := v1.Type()
t2 := v2.Type()
switch {
case t1 == TypeBinary && t2 == TypeString:
return bytes.Equal(v1.(Binary), []byte(v2.(String)))
case t1 == TypeString && t2 == TypeBinary:
return bytes.Equal([]byte(v1.(String)), v2.(Binary))
case t1 == TypeCollection && t2 == TypeCollection:
return Attributes(v1.(Collection)).Similar(
Attributes(v2.(Collection)))
}
return false
}
// Void is the Value that represents "no value"
//
// Use with: TagUnsupportedValue, TagDefault, TagUnknown,
// TagNotSettable, TagDeleteAttr, TagAdminDefine
type Void struct{}
// String converts Void Value to string
func (Void) String() string { return "" }
// Type returns type of Value (TypeVoid for Void)
func (Void) Type() Type { return TypeVoid }
// DeepCopy returns a deep copy of the Void Value
func (v Void) DeepCopy() Value {
return v
}
// Encode Void Value into wire format
func (v Void) encode() ([]byte, error) {
return []byte{}, nil
}
// Decode Void Value from wire format
func (Void) decode([]byte) (Value, error) {
return Void{}, nil
}
// Integer is the Value that represents 32-bit signed int
//
// Use with: TagInteger, TagEnum
type Integer int32
// String converts Integer value to string
func (v Integer) String() string { return fmt.Sprintf("%d", int32(v)) }
// Type returns type of Value (TypeInteger for Integer)
func (Integer) Type() Type { return TypeInteger }
// DeepCopy returns a deep copy of the Integer Value
func (v Integer) DeepCopy() Value {
return v
}
// Within checks that x fits within the range
//
// It implements IntegerOrRange interface
func (v Integer) Within(x int) bool {
return x == int(v)
}
// Encode Integer Value into wire format
func (v Integer) encode() ([]byte, error) {
return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil
}
// Decode Integer Value from wire format
func (Integer) decode(data []byte) (Value, error) {
if len(data) != 4 {
return nil, errors.New("value must be 4 bytes")
}
return Integer(int32(binary.BigEndian.Uint32(data))), nil
}
// Boolean is the Value that contains true of false
//
// Use with: TagBoolean
type Boolean bool
// String converts Boolean value to string
func (v Boolean) String() string { return fmt.Sprintf("%t", bool(v)) }
// Type returns type of Value (TypeBoolean for Boolean)
func (Boolean) Type() Type { return TypeBoolean }
// DeepCopy returns a deep copy of the Boolean Value
func (v Boolean) DeepCopy() Value {
return v
}
// Encode Boolean Value into wire format
func (v Boolean) encode() ([]byte, error) {
if v {
return []byte{1}, nil
}
return []byte{0}, nil
}
// Decode Boolean Value from wire format
func (Boolean) decode(data []byte) (Value, error) {
if len(data) != 1 {
return nil, errors.New("value must be 1 byte")
}
return Boolean(data[0] != 0), nil
}
// String is the Value that represents string of text
//
// Use with: TagText, TagName, TagReservedString, TagKeyword, TagURI,
// TagURIScheme, TagCharset, TagLanguage, TagMimeType, TagMemberName
type String string
// String converts String value to string
func (v String) String() string { return string(v) }
// Type returns type of Value (TypeString for String)
func (String) Type() Type { return TypeString }
// DeepCopy returns a deep copy of the String Value
func (v String) DeepCopy() Value {
return v
}
// Encode String Value into wire format
func (v String) encode() ([]byte, error) {
return []byte(v), nil
}
// Decode String Value from wire format
func (String) decode(data []byte) (Value, error) {
return String(data), nil
}
// Time is the Value that represents DataTime
//
// Use with: TagTime
type Time struct{ time.Time }
// String converts Time value to string
func (v Time) String() string { return v.Time.Format(time.RFC3339) }
// Type returns type of Value (TypeDateTime for Time)
func (Time) Type() Type { return TypeDateTime }
// DeepCopy returns a deep copy of the Time Value
func (v Time) DeepCopy() Value {
return v
}
// Encode Time Value into wire format
func (v Time) encode() ([]byte, error) {
// From RFC2579:
//
// field octets contents range
// ----- ------ -------- -----
// 1 1-2 year* 0..65536
// 2 3 month 1..12
// 3 4 day 1..31
// 4 5 hour 0..23
// 5 6 minutes 0..59
// 6 7 seconds 0..60
// (use 60 for leap-second)
// 7 8 deci-seconds 0..9
// 8 9 direction from UTC '+' / '-'
// 9 10 hours from UTC* 0..13
// 10 11 minutes from UTC 0..59
//
// * Notes:
// - the value of year is in network-byte order
// - daylight saving time in New Zealand is +13
year := v.Year()
_, zone := v.Zone()
dir := byte('+')
if zone < 0 {
zone = -zone
dir = '-'
}
return []byte{
byte(year >> 8), byte(year),
byte(v.Month()),
byte(v.Day()),
byte(v.Hour()),
byte(v.Minute()),
byte(v.Second()),
byte(v.Nanosecond() / 100000000),
dir,
byte(zone / 3600),
byte((zone / 60) % 60),
}, nil
}
// Decode Time Value from wire format
func (Time) decode(data []byte) (Value, error) {
// Check size
if len(data) != 11 {
return nil, errors.New("value must be 11 bytes")
}
// Validate ranges
var err error
switch {
case data[2] < 1 || data[2] > 12:
err = fmt.Errorf("bad month %d", data[2])
case data[3] < 1 || data[3] > 31:
err = fmt.Errorf("bad day %d", data[3])
case data[4] > 23:
err = fmt.Errorf("bad hours %d", data[4])
case data[5] > 59:
err = fmt.Errorf("bad minutes %d", data[5])
case data[6] > 60:
err = fmt.Errorf("bad seconds %d", data[6])
case data[7] > 9:
err = fmt.Errorf("bad deciseconds %d", data[7])
case data[8] != '+' && data[8] != '-':
return nil, errors.New("bad UTC sign")
case data[9] > 11:
err = fmt.Errorf("bad UTC hours %d", data[9])
case data[10] > 59:
err = fmt.Errorf("bad UTC minutes %d", data[10])
}
if err != nil {
return Time{}, err
}
// Decode time zone
tzName := fmt.Sprintf("UTC%c%d", data[8], data[9])
if data[10] != 0 {
tzName += fmt.Sprintf(":%d", data[10])
}
tzOff := 3600*int(data[9]) + 60*int(data[10])
if data[8] == '-' {
tzOff = -tzOff
}
tz := time.FixedZone(tzName, tzOff)
// Decode time
t := time.Date(
int(binary.BigEndian.Uint16(data[0:2])), // year
time.Month(data[2]), // month
int(data[3]), // day
int(data[4]), // hour
int(data[5]), // min
int(data[6]), // sec
int(data[7])*100000000, // nsec
tz, // time zone
)
return Time{t}, nil
}
// Resolution is the Value that represents image resolution.
//
// Use with: TagResolution
type Resolution struct {
Xres, Yres int // X/Y resolutions
Units Units // Resolution units
}
// String converts Resolution value to string
func (v Resolution) String() string {
return fmt.Sprintf("%dx%d%s", v.Xres, v.Yres, v.Units)
}
// Type returns type of Value (TypeResolution for Resolution)
func (Resolution) Type() Type { return TypeResolution }
// DeepCopy returns a deep copy of the Resolution Value
func (v Resolution) DeepCopy() Value {
return v
}
// Encode Resolution Value into wire format
func (v Resolution) encode() ([]byte, error) {
// Wire format
// 4 bytes: Xres
// 4 bytes: Yres
// 1 byte: Units
x, y := v.Xres, v.Yres
return []byte{
byte(x >> 24), byte(x >> 16), byte(x >> 8), byte(x),
byte(y >> 24), byte(y >> 16), byte(y >> 8), byte(y),
byte(v.Units),
}, nil
}
// Decode Resolution Value from wire format
func (Resolution) decode(data []byte) (Value, error) {
if len(data) != 9 {
return nil, errors.New("value must be 9 bytes")
}
return Resolution{
Xres: int(int32(binary.BigEndian.Uint32(data[0:4]))),
Yres: int(int32(binary.BigEndian.Uint32(data[4:8]))),
Units: Units(data[8]),
}, nil
}
// Units represents resolution units
type Units uint8
// Resolution units codes
const (
UnitsDpi Units = 3 // Dots per inch
UnitsDpcm Units = 4 // Dots per cm
)
// String converts Units to string
func (u Units) String() string {
switch u {
case UnitsDpi:
return "dpi"
case UnitsDpcm:
return "dpcm"
default:
return fmt.Sprintf("unknown(0x%2.2x)", uint8(u))
}
}
// Range is the Value that represents a range of 32-bit signed integers
//
// Use with: TagRange
type Range struct {
Lower, Upper int // Lower/upper bounds
}
// String converts Range value to string
func (v Range) String() string {
return fmt.Sprintf("%d-%d", v.Lower, v.Upper)
}
// Type returns type of Value (TypeRange for Range)
func (Range) Type() Type { return TypeRange }
// DeepCopy returns a deep copy of the Range Value
func (v Range) DeepCopy() Value {
return v
}
// Encode Range Value into wire format
func (v Range) encode() ([]byte, error) {
// Wire format
// 4 bytes: Lower
// 4 bytes: Upper
l, u := v.Lower, v.Upper
return []byte{
byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l),
byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u),
}, nil
}
// Within checks that x fits within the range
//
// It implements IntegerOrRange interface
func (v Range) Within(x int) bool {
return v.Lower <= x && x <= v.Upper
}
// Decode Range Value from wire format
func (Range) decode(data []byte) (Value, error) {
if len(data) != 8 {
return nil, errors.New("value must be 8 bytes")
}
return Range{
Lower: int(int32(binary.BigEndian.Uint32(data[0:4]))),
Upper: int(int32(binary.BigEndian.Uint32(data[4:8]))),
}, nil
}
// TextWithLang is the Value that represents a combination
// of two strings:
// - text on some natural language (i.e., "hello")
// - name of that language (i.e., "en")
//
// Use with: TagTextLang, TagNameLang
type TextWithLang struct {
Lang, Text string // Language and text
}
// String converts TextWithLang value to string
func (v TextWithLang) String() string { return v.Text + " [" + v.Lang + "]" }
// Type returns type of Value (TypeTextWithLang for TextWithLang)
func (TextWithLang) Type() Type { return TypeTextWithLang }
// DeepCopy returns a deep copy of the TextWithLang Value
func (v TextWithLang) DeepCopy() Value {
return v
}
// Encode TextWithLang Value into wire format
func (v TextWithLang) encode() ([]byte, error) {
// Wire format
// 2 bytes: len(Lang)
// variable: Lang
// 2 bytes: len(Text)
// variable: Text
lang := []byte(v.Lang)
text := []byte(v.Text)
if len(lang) > math.MaxUint16 {
return nil, fmt.Errorf("Lang exceeds %d bytes", math.MaxUint16)
}
if len(text) > math.MaxUint16 {
return nil, fmt.Errorf("Text exceeds %d bytes", math.MaxUint16)
}
data := make([]byte, 2+2+len(lang)+len(text))
binary.BigEndian.PutUint16(data, uint16(len(lang)))
copy(data[2:], []byte(lang))
data2 := data[2+len(lang):]
binary.BigEndian.PutUint16(data2, uint16(len(text)))
copy(data2[2:], []byte(text))
return data, nil
}
// Decode TextWithLang Value from wire format
func (TextWithLang) decode(data []byte) (Value, error) {
var langLen, textLen int
var lang, text string
// Unpack language length
if len(data) < 2 {
return nil, errors.New("truncated language length")
}
langLen = int(binary.BigEndian.Uint16(data[0:2]))
data = data[2:]
// Unpack language value
if len(data) < langLen {
return nil, errors.New("truncated language name")
}
lang = string(data[:langLen])
data = data[langLen:]
// Unpack text length
if len(data) < 2 {
return nil, errors.New("truncated text length")
}
textLen = int(binary.BigEndian.Uint16(data[0:2]))
data = data[2:]
// Unpack text value
if len(data) < textLen {
return nil, errors.New("truncated text string")
}
text = string(data[:textLen])
data = data[textLen:]
// We must have consumed all bytes at this point
if len(data) != 0 {
return nil, fmt.Errorf("extra %d bytes at the end of value",
len(data))
}
// Return a value
return TextWithLang{Lang: lang, Text: text}, nil
}
// Binary is the Value that represents a raw binary data
type Binary []byte
// String converts Binary value to string
func (v Binary) String() string {
return fmt.Sprintf("%x", []byte(v))
}
// Type returns type of Value (TypeBinary for Binary)
func (Binary) Type() Type { return TypeBinary }
// DeepCopy returns a deep copy of the Binary Value
func (v Binary) DeepCopy() Value {
v2 := make(Binary, len(v))
copy(v2, v)
return v2
}
// Encode TextWithLang Value into wire format
func (v Binary) encode() ([]byte, error) {
return []byte(v), nil
}
// Decode Binary Value from wire format
func (Binary) decode(data []byte) (Value, error) {
return Binary(data), nil
}
// Collection is the Value that represents collection of attributes
//
// Use with: TagBeginCollection
type Collection Attributes
// Add Attribute to Attributes
func (v *Collection) Add(attr Attribute) {
*v = append(*v, attr)
}
// String converts Collection to string
func (v Collection) String() string {
var buf bytes.Buffer
buf.Write([]byte("{"))
for i, attr := range v {
if i > 0 {
buf.Write([]byte(" "))
}
fmt.Fprintf(&buf, "%s=%s", attr.Name, attr.Values)
}
buf.Write([]byte("}"))
return buf.String()
}
// Type returns type of Value (TypeCollection for Collection)
func (Collection) Type() Type { return TypeCollection }
// DeepCopy returns a deep copy of the Collection Value
func (v Collection) DeepCopy() Value {
return Collection(Attributes(v).DeepCopy())
}
// Encode Collection Value into wire format
func (Collection) encode() ([]byte, error) {
// Note, TagBeginCollection attribute contains
// no data, collection itself handled the different way
return []byte{}, nil
}
// Decode Collection Value from wire format
func (Collection) decode(data []byte) (Value, error) {
panic("internal error")
}
0707010000001D000081A400000000000000000000000167F56AFB000058F8000000000000000000000000000000000000001A00000000goipp-1.2.0/value_test.go/* Go IPP - IPP core protocol implementation in pure Go
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* Values test
*/
package goipp
import (
"bytes"
"errors"
"reflect"
"strings"
"testing"
"time"
)
// TestValueEncode tests Value.encode for all value types
func TestValueEncode(t *testing.T) {
noError := errors.New("")
longstr := strings.Repeat("x", 65536)
loc1 := time.FixedZone("UTC+3:30", 3*3600+1800)
tm1, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53+03:30", loc1)
loc2 := time.FixedZone("UTC-3", -3*3600)
tm2, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53-03:00", loc2)
type testData struct {
v Value // Input value
data []byte // Expected output data
err string // Expected error string ("" if no error)
}
tests := []testData{
// Simple values
{Binary{}, []byte{}, ""},
{Binary{1, 2, 3}, []byte{1, 2, 3}, ""},
{Boolean(false), []byte{0}, ""},
{Boolean(true), []byte{1}, ""},
{Integer(0), []byte{0, 0, 0, 0}, ""},
{Integer(0x01020304), []byte{1, 2, 3, 4}, ""},
{String(""), []byte{}, ""},
{String("Hello"), []byte("Hello"), ""},
{Void{}, []byte{}, ""},
// Range
{
v: Range{0x01020304, 0x05060708},
data: []byte{1, 2, 3, 4, 5, 6, 7, 8},
},
{
v: Range{-100, 100},
data: []byte{
0xff, 0xff, 0xff, 0x9c, 0x00, 0x00, 0x00, 0x64,
},
},
// Resolution
{
v: Resolution{0x01020304, 0x05060708, 0x09},
data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
},
{
v: Resolution{150, 300, UnitsDpi},
data: []byte{
0x00, 0x00, 0x00, 0x96, // 150
0x00, 0x00, 0x01, 0x2c, // 300
0x03,
},
},
// TextWithLang
{
v: TextWithLang{"en-US", "Hello!"},
data: []byte{
0x00, 0x05,
'e', 'n', '-', 'U', 'S',
0x00, 0x06,
'H', 'e', 'l', 'l', 'o', '!',
},
},
{
v: TextWithLang{"ru-RU", "Привет!"},
data: []byte{
0x00, 0x05,
'r', 'u', '-', 'R', 'U',
0x00, 0x0d,
0xd0, 0x9f, 0xd1, 0x80, 0xd0, 0xb8, 0xd0, 0xb2,
0xd0, 0xb5, 0xd1, 0x82, 0x21,
},
},
{
v: TextWithLang{"en-US", longstr},
err: "Text exceeds 65535 bytes",
},
{
v: TextWithLang{longstr, "hello"},
err: "Lang exceeds 65535 bytes",
},
// Time
{
v: Time{tm1},
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x1e, // Minutes from UTC
},
},
{
v: Time{tm2},
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'-', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
},
// Collection
//
// Note, Collection.encode is the stub and encodes
// as Void. Actual collection encoding handled the
// different way.
{
v: Collection{
MakeAttribute("test", TagString, String("")),
},
data: []byte{},
},
}
for _, test := range tests {
data, err := test.v.encode()
if err == nil {
err = noError
}
vstr := test.v.String()
if len(vstr) > 40 {
vstr = vstr[:40] + "..."
}
if err.Error() != test.err {
t.Errorf("testing %s.encode:\n"+
"value: %s\n"+
"error expected: %q\n"+
"error present: %q\n",
reflect.TypeOf(test.v).String(),
vstr,
test.err,
err,
)
continue
}
if test.err == "" && !bytes.Equal(data, test.data) {
t.Errorf("testing %s.encode:\n"+
"value: %s\n"+
"data expected: %x\n"+
"data present: %x\n",
reflect.TypeOf(test.v).String(),
vstr,
test.data,
data,
)
}
}
}
// TestValueEncode tests Value.decode for all value types
func TestValueDecode(t *testing.T) {
noError := errors.New("")
loc1 := time.FixedZone("UTC+3:30", 3*3600+1800)
tm1, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53+03:30", loc1)
loc2 := time.FixedZone("UTC-3", -3*3600)
tm2, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53-03:00", loc2)
type testData struct {
data []byte // Input data
v Value // Expected output value
err string // Expected error string ("" if no error)
}
tests := []testData{
// Simple types
{[]byte{}, Binary{}, ""},
{[]byte{1, 2, 3, 4}, Binary{1, 2, 3, 4}, ""},
{[]byte{0}, Boolean(false), ""},
{[]byte{1}, Boolean(true), ""},
{[]byte{0, 1}, Boolean(false), "value must be 1 byte"},
{[]byte{1, 2, 3, 4}, Integer(0x01020304), ""},
{[]byte{0xff, 0xff, 0xff, 0xff}, Integer(-1), ""},
{[]byte{}, Integer(0), "value must be 4 bytes"},
{[]byte{1, 2, 3, 4, 5}, Integer(0), "value must be 4 bytes"},
{[]byte{}, Void{}, ""},
{[]byte("hello"), String("hello"), ""},
{[]byte{1, 2, 3, 4, 5}, Void{}, ""},
// Range
{
data: []byte{1, 2, 3, 4, 5, 6, 7, 8},
v: Range{0x01020304, 0x05060708},
},
{
data: []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
v: Range{-1, -1},
},
{
data: []byte{1, 2, 3, 4, 5, 6, 7},
v: Range{},
err: "value must be 8 bytes",
},
{
data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
v: Range{},
err: "value must be 8 bytes",
},
// Resolution
{
data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
v: Resolution{0x01020304, 0x05060708, 0x09},
},
{
data: []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 9,
},
v: Resolution{-1, -1, 0x09},
},
{
data: []byte{1, 2, 3, 4, 5, 6, 7, 8},
v: Resolution{},
err: "value must be 9 bytes",
},
{
data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
v: Resolution{},
err: "value must be 9 bytes",
},
// Time
{
// Good time, UTC+...
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x1e, // Minutes from UTC
},
v: Time{tm1},
},
{
// Good time, UTC-...
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'-', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{tm2},
},
{
// Truncated data
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
},
v: Time{},
err: "value must be 11 bytes",
},
{
// Extra data
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
0,
},
v: Time{},
err: "value must be 11 bytes",
},
{
// Bad month
data: []byte{
0x07, 0xe9,
0, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad month 0",
},
{
// Bad day
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
32, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad day 32",
},
{
// Bad hours
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
99, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad hours 99",
},
{
// Bad minutes
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
88, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad minutes 88",
},
{
// Bad seconds
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
77, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad seconds 77",
},
{
// Bad deciseconds
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
100, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad deciseconds 100",
},
{
// Bad UTC sign
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'?', // Direction from UTC, +/-
0x03, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad UTC sign",
},
{
// Bad UTC hours
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
66, // Hours from UTC
0x00, // Minutes from UTC
},
v: Time{},
err: "bad UTC hours 66",
},
{
// Bad UTC minutes
data: []byte{
0x07, 0xe9,
0x03, // Month, 1...12
0x1d, // Day, 1...31
0x10, // Hour, 0...23
0x30, // Minutes, 0...59
0x35, // Seconds, 0...59
0x00, // Deci-seconds, 0...9
'+', // Direction from UTC, +/-
0x03, // Hours from UTC
166, // Minutes from UTC
},
v: Time{},
err: "bad UTC minutes 166",
},
// TextWithLang
{
data: []byte{
0x00, 0x05,
'e', 'n', '-', 'U', 'S',
0x00, 0x06,
'H', 'e', 'l', 'l', 'o', '!',
},
v: TextWithLang{"en-US", "Hello!"},
},
{
data: []byte{
0x00, 0x05,
'r', 'u', '-', 'R', 'U',
0x00, 0x0d,
0xd0, 0x9f, 0xd1, 0x80, 0xd0, 0xb8, 0xd0, 0xb2,
0xd0, 0xb5, 0xd1, 0x82, 0x21,
},
v: TextWithLang{"ru-RU", "Привет!"},
},
{
// truncated language length
data: []byte{},
v: TextWithLang{},
err: "truncated language length",
},
{
// truncated language name
data: []byte{
0x00, 0x05,
'e',
},
v: TextWithLang{},
err: "truncated language name",
},
{
// truncated text length
data: []byte{
0x00, 0x05,
'e', 'n', '-', 'U', 'S',
0x00,
},
v: TextWithLang{},
err: "truncated text length",
},
{
// truncated text string
data: []byte{
0x00, 0x05,
'e', 'n', '-', 'U', 'S',
0x00, 0x06,
'H', 'e',
},
v: TextWithLang{},
err: "truncated text string",
},
{
// extra data bytes
data: []byte{
0x00, 0x05,
'e', 'n', '-', 'U', 'S',
0x00, 0x06,
'H', 'e', 'l', 'l', 'o', '!',
0, 2, 3,
},
v: TextWithLang{},
err: "extra 3 bytes at the end of value",
},
}
for _, test := range tests {
v, err := test.v.decode(test.data)
if err == nil {
err = noError
}
if err.Error() != test.err {
t.Errorf("testing %s.decode:\n"+
"value: %s\n"+
"error expected: %q\n"+
"error present: %q\n",
reflect.TypeOf(test.v).String(),
v,
test.err,
err,
)
continue
}
if test.err == "" && !reflect.DeepEqual(v, test.v) {
t.Errorf("testing %s.decode:\n"+
"data: %x\n"+
"value expected: %#v\n"+
"value present: %#v\n",
reflect.TypeOf(test.v).String(),
test.data,
test.v,
v,
)
}
}
}
// TestValueCollectionDecode tests Collection.decode for all value types
func TestValueCollectionDecode(t *testing.T) {
// Collection.decode is a stub and must panic
defer func() {
recover()
}()
v := Collection{}
v.decode([]byte{})
t.Errorf("Collection.decode() method is a stub and must panic")
}
// TestValueString rests Value.String method for various
// kinds of the Value
func TestValueString(t *testing.T) {
loc1 := time.FixedZone("UTC+3:30", 3*3600+1800)
tm1, _ := time.ParseInLocation(time.RFC3339, "2025-03-29T16:48:53+03:30", loc1)
type testData struct {
v Value // Input value
s string // Expected output string
}
tests := []testData{
// Simple types
{Binary{}, ""},
{Binary{1, 2, 3}, "010203"},
{Integer(123), "123"},
{Integer(-321), "-321"},
{Range{-100, 200}, "-100-200"},
{Range{-100, -50}, "-100--50"},
{Resolution{150, 300, UnitsDpi}, "150x300dpi"},
{Resolution{100, 200, UnitsDpcm}, "100x200dpcm"},
{Resolution{75, 150, 10}, "75x150unknown(0x0a)"},
{String("hello"), "hello"},
{TextWithLang{"en-US", "hello"}, "hello [en-US]"},
{Time{tm1}, "2025-03-29T16:48:53+03:30"},
{Void{}, ""},
// Collections
{Collection{}, "{}"},
{
v: Collection{
MakeAttr("attr1", TagInteger, Integer(1)),
MakeAttr("attr2", TagString, String("hello")),
},
s: "{attr1=1 attr2=hello}",
},
}
for _, test := range tests {
s := test.v.String()
if s != test.s {
t.Errorf("testing %s.String:\n"+
"value: %#v\n"+
"expected: %q\n"+
"present: %q\n",
reflect.TypeOf(test.v).String(),
test.v, test.s, s,
)
}
}
}
// TestValueType rests Value.Type method for various
// kinds of the Value
func TestValueType(t *testing.T) {
type testData struct {
v Value // Input value
tp Type // Expected output type
}
tests := []testData{
{Binary(nil), TypeBinary},
{Boolean(false), TypeBoolean},
{Collection(nil), TypeCollection},
{Integer(0), TypeInteger},
{Range{}, TypeRange},
{Resolution{}, TypeResolution},
{String(""), TypeString},
{TextWithLang{}, TypeTextWithLang},
{Time{time.Time{}}, TypeDateTime},
{Void{}, TypeVoid},
}
for _, test := range tests {
tp := test.v.Type()
if tp != test.tp {
t.Errorf("testing %s.Type:\n"+
"expected: %q\n"+
"present: %q\n",
reflect.TypeOf(test.v).String(),
test.tp, tp,
)
}
}
}
// TestValueEqualSimilar tests ValueEqual and ValueSimilar
func TestValueEqualSimilar(t *testing.T) {
tm1 := time.Now()
tm2 := tm1.Add(time.Hour)
type testData struct {
v1, v2 Value // A pair of values
equal bool // Expected ValueEqual(v1,v2) output
similar bool // Expected ValueSimilar(v1,v2) output
}
tests := []testData{
// Simple types
{Integer(0), Integer(0), true, true},
{Integer(0), Integer(1), false, false},
{Integer(0), String("hello"), false, false},
{Time{tm1}, Time{tm1}, true, true},
{Time{tm1}, Time{tm2}, false, false},
{Binary{}, Binary{}, true, true},
{Binary{}, Binary{1, 2, 3}, false, false},
{Binary{1, 2, 3}, Binary{4, 5, 6}, false, false},
{Binary("hello"), Binary("hello"), true, true},
{String("hello"), String("hello"), true, true},
{Binary("hello"), String("hello"), false, true},
{String("hello"), Binary("hello"), false, true},
// Collections
//
// Note, ValueEqual for Collection values is a thin wrapper
// around Attributes.Equal. So the serious testing will be
// performed there. Here we only test a couple of simple
// cases.
//
// The same is true for ValueSimilar.
{Collection{}, Collection{}, true, true},
{
v1: Collection{
MakeAttr("attr1", TagInteger, Integer(1)),
MakeAttr("attr2", TagString, String("hello")),
},
v2: Collection{
MakeAttr("attr2", TagString, String("hello")),
MakeAttr("attr1", TagInteger, Integer(1)),
},
equal: false,
similar: true,
},
}
for _, test := range tests {
equal := ValueEqual(test.v1, test.v2)
similar := ValueSimilar(test.v1, test.v2)
if equal != test.equal {
t.Errorf("testing ValueEqual:\n"+
"value 1: %s\n"+
"value 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.v1, test.v2,
test.equal, equal,
)
}
if similar != test.similar {
t.Errorf("testing ValueSimilar:\n"+
"value 1: %s\n"+
"value 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.v1, test.v2,
test.similar, similar,
)
}
}
}
// TestValuesString tests Values.String function
func TestValuesString(t *testing.T) {
type testData struct {
v Values // Input Values
s string // Expected output
}
tests := []testData{
{
v: nil,
s: "[]",
},
{
v: Values{},
s: "[]",
},
{
v: Values{{TagInteger, Integer(5)}},
s: "5",
},
{
v: Values{{TagInteger, Integer(5)}, {TagEnum, Integer(6)}},
s: "[5,6]",
},
}
for _, test := range tests {
s := test.v.String()
if s != test.s {
t.Errorf("testing Values.String:\n"+
"value: %#v\n"+
"expected: %q\n"+
"present: %q\n",
test.v, test.s, s,
)
}
}
}
// TestValuesEqualSimilar tests Values.Equal and Values.Similar
func TestValuesEqualSimilar(t *testing.T) {
type testData struct {
v1, v2 Values // A pair of values
equal bool // Expected v1.Equal(v2) output
similar bool // Expected v2.Similar(v2) output
}
tests := []testData{
{
v1: nil,
v2: nil,
equal: true,
similar: true,
},
{
v1: Values{},
v2: Values{},
equal: true,
similar: true,
},
{
v1: Values{},
v2: nil,
equal: false,
similar: true,
},
{
v1: Values{},
v2: Values{{TagInteger, Integer(5)}},
equal: false,
similar: false,
},
{
v1: Values{
{TagInteger, Integer(5)},
{TagEnum, Integer(6)},
},
v2: Values{
{TagInteger, Integer(5)},
{TagEnum, Integer(6)},
},
equal: true,
similar: true,
},
{
v1: Values{
{TagInteger, Integer(5)},
{TagEnum, Integer(6)}},
v2: Values{
{TagInteger, Integer(5)},
{TagInteger, Integer(6)}},
equal: false,
similar: false,
},
{
v1: Values{{TagInteger, Integer(6)}, {TagEnum, Integer(5)}},
v2: Values{{TagInteger, Integer(5)}, {TagEnum, Integer(6)}},
equal: false,
similar: false,
},
{
v1: Values{
{TagString, String("hello")},
{TagString, Binary("world")},
},
v2: Values{
{TagString, String("hello")},
{TagString, Binary("world")},
},
equal: true,
similar: true,
},
{
v1: Values{
{TagString, Binary("hello")},
{TagString, String("world")},
},
v2: Values{
{TagString, String("hello")},
{TagString, Binary("world")},
},
equal: false,
similar: true,
},
}
for _, test := range tests {
equal := test.v1.Equal(test.v2)
similar := test.v1.Similar(test.v2)
if equal != test.equal {
t.Errorf("testing Values.Equal:\n"+
"values 1: %s\n"+
"values 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.v1, test.v2,
test.equal, equal,
)
}
if similar != test.similar {
t.Errorf("testing Values.Similar:\n"+
"values 1: %s\n"+
"values 2: %s\n"+
"expected: %v\n"+
"present: %v\n",
test.v1, test.v2,
test.similar, similar,
)
}
}
}
// TestValuesCopy tests Values.Clone and Values.DeepCopy methods
func TestValuesCopy(t *testing.T) {
values := Values{}
values.Add(TagBoolean, Boolean(true))
values.Add(TagExtension, Binary{})
values.Add(TagString, Binary{1, 2, 3})
values.Add(TagInteger, Integer(123))
values.Add(TagEnum, Integer(-321))
values.Add(TagRange, Range{-100, 200})
values.Add(TagRange, Range{-100, -50})
values.Add(TagResolution, Resolution{150, 300, UnitsDpi})
values.Add(TagResolution, Resolution{100, 200, UnitsDpcm})
values.Add(TagResolution, Resolution{75, 150, 10})
values.Add(TagString, String("hello"))
values.Add(TagTextLang, TextWithLang{"en-US", "hello"})
values.Add(TagDateTime, Time{time.Now()})
values.Add(TagNoValue, Void{})
values.Add(TagBeginCollection,
Collection{MakeAttribute("test", TagString, String(""))})
type testData struct {
values Values
}
tests := []testData{
{nil},
{Values{}},
{values},
}
for _, test := range tests {
clone := test.values.Clone()
if !test.values.Equal(clone) {
t.Errorf("testing Attributes.Clone\n"+
"expected: %#v\n"+
"present: %#v\n",
test.values,
clone,
)
}
copy := test.values.DeepCopy()
if !test.values.Equal(copy) {
t.Errorf("testing Attributes.DeepCopy\n"+
"expected: %#v\n"+
"present: %#v\n",
test.values,
copy,
)
}
}
}
// TestIntegerOrRange tests IntegerOrRange interface implementation
// by Integer and Range types
func TestIntegerOrRange(t *testing.T) {
type testData struct {
v IntegerOrRange // Value being tested
x int // IntegerOrRange.Within input
within bool // IntegerOrRange.Within expected output
}
tests := []testData{
{
v: Integer(5),
x: 5,
within: true,
},
{
v: Integer(5),
x: 6,
within: false,
},
{
v: Range{-5, 5},
x: 0,
within: true,
},
{
v: Range{-5, 5},
x: -5,
within: true,
},
{
v: Range{-5, 5},
x: 5,
within: true,
},
{
v: Range{-5, 5},
x: -6,
within: false,
},
{
v: Range{-5, 5},
x: 6,
within: false,
},
}
for _, test := range tests {
within := test.v.Within(test.x)
if within != test.within {
t.Errorf("testing %s.Within:\n"+
"value: %#v\n"+
"param: %v\n"+
"expected: %v\n"+
"present: %v\n",
reflect.TypeOf(test.v).String(),
test.v, test.x,
test.within, within,
)
}
}
}
// TestCollection tests Collection.Add method
func TestCollectionAdd(t *testing.T) {
col1 := Collection{
MakeAttr("attr1", TagInteger, Integer(1)),
MakeAttr("attr2", TagString, String("hello")),
}
col2 := Collection{}
col2.Add(MakeAttr("attr1", TagInteger, Integer(1)))
col2.Add(MakeAttr("attr2", TagString, String("hello")))
if !ValueEqual(col1, col2) {
t.Errorf("Collection.Add test failed")
}
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!588 blocks