2-add-keyword-support (#6)

More development.

More types.
Fixed attribute groups in requests.
Started on client.
Saving data to file.

More types. Printing from chromeos works a little bit.

More types.

Spelling corrections.

WIP: Fix keyword handling

Move request to a separate file and add test.

Co-authored-by: Henrik Sölver <henrik.solver@gmail.com>
Reviewed-on: #6
Co-Authored-By: henrik <henrik.solver@gmail.com>
Co-Committed-By: henrik <henrik.solver@gmail.com>
This commit was merged in pull request #6.
This commit is contained in:
2020-12-27 09:16:32 +01:00
parent 34c4385491
commit 04a4b4157f
23 changed files with 881 additions and 257 deletions

52
packages/ipp/enum.go Normal file
View File

@@ -0,0 +1,52 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
log "github.com/sirupsen/logrus"
)
type enum struct {
name string
value int32
}
func NewEnum(name string, value int32) *enum {
e := new(enum)
e.name = name
e.value = value
return e
}
func (e enum) String() string {
return e.name + ":" + fmt.Sprint(e.value)
}
func (e *enum) valueTag() tag {
return enumValueTag
}
func (e *enum) unmarshal(byteStream io.Reader) {
log.Warn("Unmarshal of enum is not implemented yet")
}
func (e *enum) marshal() []byte {
l := 3 + len(e.name) + 6
b := make([]byte, 0, l)
buf := bytes.NewBuffer(b)
buf.WriteByte(byte(enumValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(e.name)))
buf.WriteString(e.name)
binary.Write(buf, binary.BigEndian, uint16(4))
binary.Write(buf, binary.BigEndian, e.value)
return buf.Bytes()
}
func (e *enum) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(e.name)
l += 4
return l
}

View File

@@ -1,45 +1,33 @@
package ipp
import "io"
type keyWord struct {
name string
values []string
sos *setOfStrings
}
func newKeyWord() *keyWord {
func NewKeyWord(name string, values ...string) *keyWord {
k := new(keyWord)
k.sos = NewSetOfStrings(name, keyWordValueTag, values)
return k
}
func (k *keyWord) string() string {
return "a uriValue"
}
func (k *keyWord) valueTag() tag {
return keyWordValueTag
}
func (k *keyWord) unmarshal(byteStream io.Reader) {
}
func (k *keyWord) marshal() []byte {
return []byte{}
}
func (k *keyWord) addValue(v string) {
k.values = append(k.values, v)
func (k keyWord) String() string {
return k.sos.String()
}
func (k *keyWord) size() int {
l := 1 + 2 // The value tag (0x44) + name-length field (2 bytes)
l += len(k.name)
l += 2 // value-length field (2 bytes)
l += len(k.values[0])
// Add all additional values
for _, v := range k.values[1:] {
l += 1 + 4 // The value tag (0x44) + 2 length fields (2 bytes)
l += len(v)
}
return l
return k.sos.size()
}
func (k *keyWord) valueTag() tag {
return k.sos.valueTag()
}
func (k *keyWord) marshal() []byte {
return k.sos.marshal()
}
func (k *keyWord) addValue(v string) {
k.sos.AddValue(v)
}

View File

@@ -0,0 +1,36 @@
package ipp
import (
"testing"
"github.com/stretchr/testify/assert"
)
// func TestUnmarshalSimpleKeyword(T *testing.T) {
// testdata := []byte{
// 0x00, 0x14,
// 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
// 0x00, 0x16,
// 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x6c,
// }
// buf := bytes.NewBuffer(testdata)
// var k keyWord
// k.unmarshal(buf)
// assert.Equal(T, k.name, "requested-attributes")
// assert.Equal(T, k.values[0], "printer-make-and-model")
// }
func TestMarshalSimpleKeyword(T *testing.T) {
testdata := []byte{
0x44, 0x00, 0x14,
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x00, 0x16,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x6c,
}
k := NewKeyWord("requested-attributes")
k.addValue("printer-make-and-model")
m := k.marshal()
assert.Equal(T, testdata, m, "Should be equal")
}

View File

@@ -4,8 +4,6 @@ import (
"encoding/binary"
"fmt"
"io"
log "github.com/sirupsen/logrus"
)
// References
@@ -45,37 +43,45 @@ const (
endCollectionValueTag tag = 0x37
// Character string values
textWithoutLanguagageValueTag tag = 0x41
nameWithoutLanguagageValueTag tag = 0x42
keyWordValueTag tag = 0x44
uriValueTag tag = 0x45
uriSchemeValueTag tag = 0x46
charsetValueTag tag = 0x47
naturalLanguagageValueTag tag = 0x48
mimeMediaTypeValueTag tag = 0x49
memberAttrNameValueTag tag = 0x4a
textWithoutLanguageValueTag tag = 0x41
nameWithoutLanguageValueTag tag = 0x42
keyWordValueTag tag = 0x44
uriValueTag tag = 0x45
uriSchemeValueTag tag = 0x46
charsetValueTag tag = 0x47
naturalLanguageValueTag tag = 0x48
mimeMediaTypeValueTag tag = 0x49
memberAttrNameValueTag tag = 0x4a
)
// Operation-id, defined in rfc8011
type operationId uint16
type OperationId uint16
const (
PrintJob operationId = 0x0002
PrintURI operationId = 0x0003
ValidateJob operationId = 0x0004
CreateJob operationId = 0x0005
SendDocument operationId = 0x0006
SendURI operationId = 0x0007
CancelJob operationId = 0x0008
GetJobAttributes operationId = 0x0009
GetJobs operationId = 0x000a
GetPrinterAttributes operationId = 0x000b
HoldJob operationId = 0x000c
ReleaseJob operationId = 0x000d
RestartJob operationId = 0x000e
PausePrinter operationId = 0x0010
ResumePrinter operationId = 0x0011
PurgeJobs operationId = 0x0012
PrintJob OperationId = 0x0002
PrintURI OperationId = 0x0003
ValidateJob OperationId = 0x0004
CreateJob OperationId = 0x0005
SendDocument OperationId = 0x0006
SendURI OperationId = 0x0007
CancelJob OperationId = 0x0008
GetJobAttributes OperationId = 0x0009
GetJobs OperationId = 0x000a
GetPrinterAttributes OperationId = 0x000b
HoldJob OperationId = 0x000c
ReleaseJob OperationId = 0x000d
RestartJob OperationId = 0x000e
PausePrinter OperationId = 0x0010
ResumePrinter OperationId = 0x0011
PurgeJobs OperationId = 0x0012
)
type printerState int32
const (
Idle printerState = 3
Processing printerState = 4
Stopped printerState = 5
)
type statusCode uint16
@@ -92,151 +98,16 @@ func (v versionNumber) String() string {
return fmt.Sprintf("%x.%x", vn&0xff00>>8, vn&0x00ff)
}
type ippRequestHeader struct {
versionNumber versionNumber
operationId operationId
requestId uint32
}
func (h *ippRequestHeader) marshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
binary.Read(byteStream, binary.BigEndian, &h.operationId)
binary.Read(byteStream, binary.BigEndian, &h.requestId)
}
func (h ippRequestHeader) String() string {
return fmt.Sprintf("Version number: %v Operation Id: %v Request Id: %v", h.versionNumber, h.operationId, h.requestId)
}
type Attribute interface {
valueTag() tag
unmarshal(io.Reader)
marshal() []byte
size() int
}
type Request struct {
header ippRequestHeader
operationAttributes map[string]Attribute
jobAttributes map[string]Attribute
printerAttributes map[string]Attribute
}
func NewRequest() *Request {
r := new(Request)
r.operationAttributes = make(map[string]Attribute)
r.jobAttributes = make(map[string]Attribute)
r.printerAttributes = make(map[string]Attribute)
return r
}
func (r Request) String() string {
s := r.header.String() + "\n" + " OperationAttributes" + "\n"
for _, a := range r.operationAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
}
// log.Info(r.Header)
// log.Info(r.ContentLength)
//body := make([]byte, r.ContentLength)
//body.Read(body)
func (r *Request) UnMarshal(body io.Reader) {
r.header.marshal(body)
log.Infof("Header %v", r.header)
var tag tag
err := binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Error(err.Error())
}
log.Infof("got tag - %v", tag)
if tag == operationAttributes {
nextoperationattr:
for tag != endOfAttributes {
var lastKeyword *keyWord
err = binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Error(err.Error())
}
log.Infof("Value tag - %v", tag)
switch tag {
case endOfAttributes:
err = binary.Read(body, binary.BigEndian, &tag)
if err == io.EOF {
// No more data
return
}
if err != nil {
log.Error(err.Error())
}
log.Infof("got tag - %v", tag)
err = binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Error(err.Error())
}
log.Infof("got tag - %v", tag)
break nextoperationattr
case charsetValueTag:
c := NewCharSetValue("", "")
c.unmarshal(body)
r.operationAttributes[c.name] = c
log.Infof("%v %v", c.name, c.value)
case uriValueTag:
u := newUriValue("", "")
u.unmarshal(body)
r.operationAttributes[u.name] = u
log.Infof("%v %v", u.name, u.value)
case naturalLanguagageValueTag:
n := newNaturalLanguagage("", "")
n.unmarshal(body)
r.operationAttributes[n.name] = n
log.Infof("%v %v", n.name, n.value)
case keyWordValueTag:
k := newKeyWord()
k.unmarshal(body)
r.operationAttributes[k.name] = k
if k.name == "" {
lastKeyword.addValue(k.values[0])
} else {
lastKeyword = k
}
default:
log.Errorf("Unsupported tag %v", tag)
}
}
log.Infof("Value tag %v", tag)
} else {
log.Error("unexpected tag")
// TODO Return something sensible here
}
}
func (r *Request) RequestId() uint32 {
return r.header.requestId
}
func unmarshalSingleValue(byteStream io.Reader) (string, string) {
var length uint16
binary.Read(byteStream, binary.BigEndian, &length)
//log.Infof("Length %v", length)
attributeName := make([]byte, length)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, attributeName)
//log.Infof("Valuename %v", string(attributeName))
}
binary.Read(byteStream, binary.BigEndian, &length)
//log.Infof("Length %v", length)
attributeValue := make([]byte, length)
binary.Read(byteStream, binary.BigEndian, attributeValue)
//log.Infof("Value %v", string(attributeValue))
return string(attributeName), string(attributeValue)
}

View File

@@ -0,0 +1,32 @@
package ipp
type mimeMediaType struct {
sos *setOfStrings
}
func NewMimeMediaType(name string, values ...string) *mimeMediaType {
m := new(mimeMediaType)
m.sos = NewSetOfStrings(name, mimeMediaTypeValueTag, values)
return m
}
func (m mimeMediaType) String() string {
return m.sos.String()
}
func (m *mimeMediaType) size() int {
return m.sos.size()
}
func (m *mimeMediaType) valueTag() tag {
return m.sos.valueTag()
}
func (m *mimeMediaType) marshal() []byte {
return m.sos.marshal()
}
func (m *mimeMediaType) addValue(v string) {
m.sos.AddValue(v)
}

View File

@@ -0,0 +1,45 @@
package ipp
import "io"
type NameWithoutLanguage struct {
name string
value string
}
func NewNameWithoutLanguage(name, value string) *NameWithoutLanguage {
c := new(NameWithoutLanguage)
c.name = name
c.value = value
return c
}
func (c NameWithoutLanguage) String() string {
return c.name + ":" + c.value
}
func (c *NameWithoutLanguage) valueTag() tag {
return nameWithoutLanguageValueTag
}
func (c *NameWithoutLanguage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream)
}
func (c *NameWithoutLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value)
b := make([]byte, l, l)
b[0] = byte(nameWithoutLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:])
return b
}
func (c *NameWithoutLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name)
l += len(c.value)
return l
}
func (n NameWithoutLanguage) Value() string {
return n.value
}

View File

@@ -1,43 +0,0 @@
package ipp
import (
"io"
)
type naturalLanguagage struct {
name string
value string
}
func newNaturalLanguagage(name, value string) *naturalLanguagage {
c := new(naturalLanguagage)
c.name = name
c.value = value
return c
}
func (c naturalLanguagage) String() string {
return c.name + ":" + c.value
}
func (c *naturalLanguagage) valueTag() tag {
return naturalLanguagageValueTag
}
func (c *naturalLanguagage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream)
}
func (c *naturalLanguagage) marshal() []byte {
l := 5 + len(c.name) + len(c.value)
b := make([]byte, l, l)
b[0] = byte(naturalLanguagageValueTag)
marshalNameValue(c.name, c.value, b[1:])
return b
}
func (c *naturalLanguagage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name)
l += len(c.value)
return l
}

View File

@@ -0,0 +1,43 @@
package ipp
import (
"io"
)
type naturalLanguage struct {
name string
value string
}
func NewNaturalLanguage(name, value string) *naturalLanguage {
c := new(naturalLanguage)
c.name = name
c.value = value
return c
}
func (c naturalLanguage) String() string {
return c.name + ":" + c.value
}
func (c *naturalLanguage) valueTag() tag {
return naturalLanguageValueTag
}
func (c *naturalLanguage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream)
}
func (c *naturalLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value)
b := make([]byte, l, l)
b[0] = byte(naturalLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:])
return b
}
func (c *naturalLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name)
l += len(c.value)
return l
}

View File

@@ -36,7 +36,7 @@ var (
_operationId_index_1 = [...]uint8{0, 12, 25, 34}
)
func (i operationId) String() string {
func (i OperationId) String() string {
switch {
case 2 <= i && i <= 14:
i -= 2

View File

@@ -0,0 +1,26 @@
// Code generated by "stringer -type printerState ."; DO NOT EDIT.
package ipp
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Idle-3]
_ = x[Processing-4]
_ = x[Stopped-5]
}
const _printerState_name = "IdlePreocessingStopped"
var _printerState_index = [...]uint8{0, 4, 15, 22}
func (i printerState) String() string {
i -= 3
if i < 0 || i >= printerState(len(_printerState_index)-1) {
return "printerState(" + strconv.FormatInt(int64(i+3), 10) + ")"
}
return _printerState_name[_printerState_index[i]:_printerState_index[i+1]]
}

203
packages/ipp/request.go Normal file
View File

@@ -0,0 +1,203 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
log "github.com/sirupsen/logrus"
)
type ippRequestHeader struct {
versionNumber versionNumber
operationId OperationId
requestId uint32
}
func (h *ippRequestHeader) unmarshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
binary.Read(byteStream, binary.BigEndian, &h.operationId)
binary.Read(byteStream, binary.BigEndian, &h.requestId)
}
func (h *ippRequestHeader) marshal() []byte {
b := make([]byte, 0, 8)
buf := bytes.NewBuffer(b)
binary.Write(buf, binary.BigEndian, h.versionNumber)
binary.Write(buf, binary.BigEndian, h.operationId)
binary.Write(buf, binary.BigEndian, h.requestId)
return buf.Bytes()
}
func (h ippRequestHeader) String() string {
return fmt.Sprintf("Version number: %v Operation Id: %v Request Id: %v", h.versionNumber, h.operationId, h.requestId)
}
type Attribute interface {
valueTag() tag
marshal() []byte
//size() int
}
type AddValuer interface {
addValue(string)
}
type Request struct {
operationAttributes map[string]Attribute
jobAttributes map[string]Attribute
printerAttributes map[string]Attribute
header ippRequestHeader
}
func NewRequest(op OperationId, requestId uint32) *Request {
r := new(Request)
r.header.operationId = op
r.header.requestId = requestId
r.header.versionNumber = 0x0200
r.operationAttributes = make(map[string]Attribute)
r.jobAttributes = make(map[string]Attribute)
r.printerAttributes = make(map[string]Attribute)
return r
}
func (r Request) String() string {
s := r.header.String() + "\n" + " OperationAttributes" + "\n"
for _, a := range r.operationAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " PrinterAttributes" + "\n"
for _, a := range r.printerAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " JobAttributes" + "\n"
for _, a := range r.jobAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
}
func (r *Request) UnMarshal(body io.Reader) {
r.header.unmarshal(body)
log.Debugf("Header %v", r.header)
var tag tag
err := binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Error(err.Error())
}
log.Debugf("got tag - %v", tag)
var currentAttributeGroup map[string]Attribute
switch tag {
case operationAttributes:
currentAttributeGroup = r.operationAttributes
case jobAttributes:
currentAttributeGroup = r.jobAttributes
case printerAttributes:
currentAttributeGroup = r.printerAttributes
default:
log.Errorf("Unknown tag %v", tag)
}
var lastAddValuer AddValuer
for {
err = binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Errorf("End of input before end of attributes tag (%v)", err.Error())
}
log.Debugf("Value tag - %v", tag)
switch tag {
case endOfAttributes:
return
case charsetValueTag:
c := NewCharSetValue("", "")
c.unmarshal(body)
currentAttributeGroup[c.name] = c
log.Debugf("%v %v", c.name, c.value)
case uriValueTag:
u := NewUriValue("", "")
u.unmarshal(body)
currentAttributeGroup[u.name] = u
log.Debugf("%v %v", u.name, u.value)
case naturalLanguageValueTag:
n := NewNaturalLanguage("", "")
n.unmarshal(body)
currentAttributeGroup[n.name] = n
log.Debugf("%v %v", n.name, n.value)
case keyWordValueTag:
name, value := unmarshalSingleValue(body)
if name == "" {
lastAddValuer.addValue(value)
} else {
k := NewKeyWord(name, value)
currentAttributeGroup[name] = k
lastAddValuer = k
}
log.Debugf("%v : %v", name, value)
case nameWithoutLanguageValueTag:
n := NewNameWithoutLanguage("", "")
n.unmarshal(body)
currentAttributeGroup[n.name] = n
log.Debugf("%v %v", n.name, n.value)
case mimeMediaTypeValueTag:
name, value := unmarshalSingleValue(body)
if name == "" {
lastAddValuer.addValue(value)
} else {
m := NewMimeMediaType(name, value)
currentAttributeGroup[name] = m
lastAddValuer = m
}
log.Debugf("%v : %v", name, value)
case jobAttributes:
log.Debug("Start job attributes")
currentAttributeGroup = r.jobAttributes
case resolutionValueTag:
res := NewResolution("", 0, 0)
res.unmarshal(body)
currentAttributeGroup[res.name] = res
log.Debugf("Resolution %v", res)
default:
log.Errorf("Unsupported tag %v", tag)
}
}
}
func (r *Request) RequestId() uint32 {
return r.header.requestId
}
func (r *Request) Operation() OperationId {
return r.header.operationId
}
func (r *Request) GetAttribute(name string) Attribute {
return r.operationAttributes[name]
}
func (r *Request) Marshal() []byte {
// //s = r.size
var buf bytes.Buffer
buf.Write(r.header.marshal())
if len(r.operationAttributes) > 0 {
buf.WriteByte(byte(operationAttributes))
for _, e := range r.operationAttributes {
buf.Write(e.marshal())
}
}
if len(r.jobAttributes) > 0 {
buf.WriteByte(byte(jobAttributes))
for _, e := range r.jobAttributes {
buf.Write(e.marshal())
}
}
if len(r.printerAttributes) > 0 {
buf.WriteByte(byte(printerAttributes))
for _, e := range r.printerAttributes {
buf.Write(e.marshal())
}
}
buf.WriteByte(byte(endOfAttributes))
return buf.Bytes()
}

View File

@@ -0,0 +1,79 @@
package ipp
import (
"bytes"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
var testRequest = []byte{0x01, 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x11,
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, 0x75, 0x73,
0x45, // uriValueTag
0x00, 0x0b,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x75, 0x72, 0x69,
0x00, 0x20,
0x69, 0x70, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e, 0x32, 0x3a, 0x31, 0x32, 0x33, 0x34, 0x2f, 0x69, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x44, // keywordValueTag
0x00, 0x14,
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x00, 0x16,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x6d, 0x61, 0x6b, 0x65, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x6c,
0x44, // keywordValueTag
0x00, 0x00,
0x00, 0x16,
0x69, 0x70, 0x70, 0x2d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x44, // keywordValueTag
0x00, 0x00,
0x00, 0x16,
0x69, 0x70, 0x70, 0x2d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x44, // keywordValueTag
0x00, 0x00,
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,
0x44,
0x00, 0x00,
0x00, 0x0d,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65,
0x44,
0x00, 0x00,
0x00, 0x15,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73,
0x44,
0x00, 0x00,
0x00, 0x15,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x2d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x03}
func TestUnmarshalRequestPrinterAttributes(T *testing.T) {
buf := bytes.NewBuffer(testRequest)
req := NewRequest(GetPrinterAttributes, 17)
req.UnMarshal(buf)
fmt.Print(req)
assert.Equal(T, versionNumber(0x0101), req.header.versionNumber, "Wrong version number")
assert.Equal(T, GetPrinterAttributes, req.header.operationId, "Wrong Operation")
assert.Equal(T, uint32(17), req.header.requestId, "Wrong request id")
assert.Len(T, req.operationAttributes, 4)
v := req.operationAttributes["requested-attributes"].(*keyWord).sos.values
assert.Len(T, v, 7)
assert.Contains(T, v, "printer-make-and-model")
assert.Contains(T, v, "ipp-versions-supported")
assert.Contains(T, v, "ipp-features-supported")
assert.Contains(T, v, "document-format-supported")
assert.Contains(T, v, "printer-state-reasons")
assert.Contains(T, v, "printer-state-message")
assert.Contains(T, v, "printer-state")
}

View File

@@ -0,0 +1,63 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
type resolution struct {
name string
crossFeedResolution int32
feedResolution int32
units int8
}
func NewResolution(name string, xfres int32, fres int32) *resolution {
r := new(resolution)
r.name = name
r.crossFeedResolution = xfres
r.feedResolution = fres
r.units = 3 // 3 seems to mean dpi (rfc3805)
return r
}
func (r *resolution) unmarshal(byteStream io.Reader) {
var length uint16
binary.Read(byteStream, binary.BigEndian, &length)
attributeName := make([]byte, length)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, attributeName)
}
r.name = string(attributeName)
binary.Read(byteStream, binary.BigEndian, &length)
if length != 9 {
panic("Wrong length in resolution")
}
binary.Read(byteStream, binary.BigEndian, &r.crossFeedResolution)
binary.Read(byteStream, binary.BigEndian, &r.feedResolution)
binary.Read(byteStream, binary.BigEndian, &r.units)
}
func (r resolution) String() string {
return fmt.Sprintf("%v:%v,%v,%v", r.name, r.crossFeedResolution, r.feedResolution, r.units)
}
func (r *resolution) marshal() []byte {
b := make([]byte, 0, 14+len(r.name))
buf := bytes.NewBuffer(b)
buf.WriteByte(byte(resolutionValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(r.name)))
buf.WriteString(r.name)
binary.Write(buf, binary.BigEndian, uint16(9))
binary.Write(buf, binary.BigEndian, int32(r.crossFeedResolution))
binary.Write(buf, binary.BigEndian, int32(r.feedResolution))
buf.WriteByte(byte(r.units))
return buf.Bytes()
}
func (r *resolution) valueTag() tag {
return resolutionValueTag
}

View File

@@ -1,6 +1,9 @@
package ipp
import "encoding/binary"
import (
"encoding/binary"
"fmt"
)
type ippResponseHeader struct {
versionNumber versionNumber
@@ -8,6 +11,10 @@ type ippResponseHeader struct {
requestId uint32
}
func (h ippResponseHeader) String() string {
return fmt.Sprintf("Version number: %v Status code: %v Request Id: %v", h.versionNumber, h.statusCode, h.requestId)
}
func (h *ippResponseHeader) marshal() []byte {
a := make([]byte, 8, 8)
binary.BigEndian.PutUint16(a[0:2], uint16(h.versionNumber))
@@ -18,10 +25,10 @@ func (h *ippResponseHeader) marshal() []byte {
}
type Response struct {
header ippResponseHeader
operationAttributes []Attribute
jobAttributes []Attribute
printerAttributes []Attribute
header ippResponseHeader
}
func NewResponse(code statusCode, requestId uint32) *Response {
@@ -52,6 +59,23 @@ func NewResponse(code statusCode, requestId uint32) *Response {
// }
func (r Response) String() string {
s := r.header.String() + "\n" + " OperationAttributes" + "\n"
for _, a := range r.operationAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " PrinterAttributes" + "\n"
for _, a := range r.printerAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " JobAttributes" + "\n"
for _, a := range r.jobAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
}
func (r *Response) Marshal() []byte {
a := make([]byte, 0, 20)
a = append(a, r.header.marshal()...)

View File

@@ -0,0 +1,88 @@
package ipp
import "encoding/binary"
type setOfStrings struct {
name string
values []string
vTag tag
}
func NewSetOfStrings(name string, t tag, values []string) *setOfStrings {
s := new(setOfStrings)
s.name = name
s.vTag = t
s.values = values //make([]string, 0)
return s
}
func (s setOfStrings) String() string {
r := s.name + " :"
for _, v := range s.values {
r = r + " " + v
}
return r
}
func (s *setOfStrings) valueTag() tag {
return s.vTag
}
// func (k *keyWord) unmarshal(byteStream io.Reader) {
// if len(k.values) == 0 {
// var v string
// k.name, v = unmarshalSingleValue(byteStream)
// k.values = append(k.values, v)
// } else {
// var v string
// _, v = unmarshalSingleValue(byteStream)
// k.values = append(k.values, v)
// }
// }
func (s *setOfStrings) marshal() []byte {
l := 5 + len(s.name) + len(s.values[0])
for i := range s.values[1:] {
l += 5 + len(s.values[i+1])
}
res := make([]byte, l, l)
p := 0
res[p] = byte(s.vTag)
p += 1
binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.name)))
p += 2
copy(res[p:], []byte(s.name))
p += len(s.name)
binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.values[0])))
p += 2
copy(res[p:], []byte(s.values[0]))
p += len(s.values[0])
for i := range s.values[1:] {
res[p] = byte(s.vTag)
p += 1
binary.BigEndian.PutUint16(res[p:p+2], uint16(0))
p = p + 2
binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.values[i+1])))
p = p + 2
copy(res[p:], []byte(s.values[i+1]))
p += len(s.values[i+1])
}
return res
}
func (s *setOfStrings) AddValue(v string) {
s.values = append(s.values, v)
}
func (s *setOfStrings) size() int {
l := 1 + 2 // The value tag (0x44) + name-length field (2 bytes)
l += len(s.name)
l += 2 // value-length field (2 bytes)
l += len(s.values[0])
// Add all additional values
for _, v := range s.values[1:] {
l += 1 + 4 // The value tag (0x44) + 2 length fields (2 bytes)
l += len(v)
}
return l
}

View File

@@ -28,13 +28,13 @@ func _() {
_ = x[textWithLanguageValueTag-53]
_ = x[nameWithLanguageValueTag-54]
_ = x[endCollectionValueTag-55]
_ = x[textWithoutLanguagageValueTag-65]
_ = x[nameWithoutLanguagageValueTag-66]
_ = x[textWithoutLanguageValueTag-65]
_ = x[nameWithoutLanguageValueTag-66]
_ = x[keyWordValueTag-68]
_ = x[uriValueTag-69]
_ = x[uriSchemeValueTag-70]
_ = x[charsetValueTag-71]
_ = x[naturalLanguagageValueTag-72]
_ = x[naturalLanguageValueTag-72]
_ = x[mimeMediaTypeValueTag-73]
_ = x[memberAttrNameValueTag-74]
}

View File

@@ -0,0 +1,41 @@
package ipp
import "io"
type textWithoutLanguage struct {
name string
value string
}
func NewtextWithoutLanguage(name, value string) *textWithoutLanguage {
c := new(textWithoutLanguage)
c.name = name
c.value = value
return c
}
func (c textWithoutLanguage) String() string {
return c.name + ":" + c.value
}
func (c *textWithoutLanguage) valueTag() tag {
return textWithoutLanguageValueTag
}
func (c *textWithoutLanguage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream)
}
func (c *textWithoutLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value)
b := make([]byte, l, l)
b[0] = byte(textWithoutLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:])
return b
}
func (c *textWithoutLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name)
l += len(c.value)
return l
}

View File

@@ -9,7 +9,7 @@ type uriValue struct {
value string
}
func newUriValue(name, value string) *uriValue {
func NewUriValue(name, value string) *uriValue {
u := new(uriValue)
u.name = name
u.value = value