Merge attribute handling from request and response to commen code.

This commit was merged in pull request #7.
This commit is contained in:
2020-12-28 20:05:01 +01:00
parent 04a4b4157f
commit 53aaaf5521
14 changed files with 291 additions and 172 deletions

59
packages/ipp/boolean.go Normal file
View File

@@ -0,0 +1,59 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
log "github.com/sirupsen/logrus"
)
type boolean struct {
name string
value bool
}
func NewBoolean(name string, value bool) *boolean {
b := new(boolean)
b.name = name
b.value = value
return b
}
func (b boolean) Name() string {
return b.name
}
func (b boolean) String() string {
return b.name + ":" + fmt.Sprint(b.value)
}
func (b *boolean) valueTag() tag {
return booleanValueTag
}
func (b *boolean) unmarshal(byteStream io.Reader) {
log.Warn("Unmarshal of boolean is not implemented yet")
}
func (e *boolean) marshal() []byte {
l := 5 + len(e.name)
b := make([]byte, 0, l)
buf := bytes.NewBuffer(b)
buf.WriteByte(byte(booleanValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(e.name)))
buf.WriteString(e.name)
binary.Write(buf, binary.BigEndian, uint16(1))
if e.value {
buf.WriteByte(byte(1))
} else {
buf.WriteByte(byte(0))
}
return buf.Bytes()
}
func (e *boolean) size() int {
l := 5 + len(e.name)
return l
}

View File

@@ -16,9 +16,14 @@ func NewCharSetValue(name string, value string) *charSetValue {
return c return c
} }
func (c charSetValue) Name() string {
return c.name
}
func (c charSetValue) String() string { func (c charSetValue) String() string {
return c.name + ":" + c.value return c.name + ":" + c.value
} }
func (c *charSetValue) valueTag() tag { func (c *charSetValue) valueTag() tag {
return charsetValueTag return charsetValueTag
} }

View File

@@ -21,9 +21,14 @@ func NewEnum(name string, value int32) *enum {
return e return e
} }
func (e enum) Name() string {
return e.name
}
func (e enum) String() string { func (e enum) String() string {
return e.name + ":" + fmt.Sprint(e.value) return e.name + ":" + fmt.Sprint(e.value)
} }
func (e *enum) valueTag() tag { func (e *enum) valueTag() tag {
return enumValueTag return enumValueTag
} }

View File

@@ -11,6 +11,10 @@ func NewKeyWord(name string, values ...string) *keyWord {
return k return k
} }
func (k keyWord) Name() string {
return k.sos.name
}
func (k keyWord) String() string { func (k keyWord) String() string {
return k.sos.String() return k.sos.String()
} }

View File

@@ -4,6 +4,8 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
log "github.com/sirupsen/logrus"
) )
// References // References
@@ -121,3 +123,130 @@ func marshalNameValue(name, value string, b []byte) {
p += 2 p += 2
copy(b[p:], []byte(value)) copy(b[p:], []byte(value))
} }
type Attribute interface {
Name() string
valueTag() tag
marshal() []byte
//size() int
}
type attributes struct {
operation []Attribute
printer []Attribute
job []Attribute
}
func (a *attributes) String() string{
s := " OperationAttributes" + "\n"
for _, a := range a.operation {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " PrinterAttributes" + "\n"
for _, a := range a.printer {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " JobAttributes" + "\n"
for _, a := range a.job {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
}
func (as *attributes) addAttribute(group tag, a Attribute) {
switch group {
case operationAttributes:
as.operation = append(as.operation, a)
case jobAttributes:
as.job = append(as.job, a)
case printerAttributes:
as.printer = append(as.printer, a)
default:
log.Error("Unknown attribute group")
}
}
func UnMarshalAttributues(body io.Reader) *attributes {
a := new(attributes)
var t tag
err := binary.Read(body, binary.BigEndian, &t)
if err != nil {
log.Error(err.Error())
}
log.Debugf("got tag - %v", t)
if t != operationAttributes && t != jobAttributes && t != printerAttributes {
log.Errorf("Unknown attribute group tag %v", t)
return nil
}
currentAttributeGroup := t
var lastAddValuer AddValuer
for {
err = binary.Read(body, binary.BigEndian, &t)
if err != nil {
log.Errorf("End of input before end of attributes tag (%v)", err.Error())
}
log.Debugf("Value tag - %v", t)
switch t {
case endOfAttributes:
return a
case charsetValueTag:
c := NewCharSetValue("", "")
c.unmarshal(body)
a.addAttribute(currentAttributeGroup, c)
log.Debugf("%v %v", c.name, c.value)
case uriValueTag:
u := NewUriValue("", "")
u.unmarshal(body)
a.addAttribute(currentAttributeGroup, u)
log.Debugf("%v %v", u.name, u.value)
case naturalLanguageValueTag:
n := NewNaturalLanguage("", "")
n.unmarshal(body)
a.addAttribute(currentAttributeGroup, 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)
a.addAttribute(currentAttributeGroup, k)
lastAddValuer = k
}
log.Debugf("%v : %v", name, value)
case nameWithoutLanguageValueTag:
n := NewNameWithoutLanguage("", "")
n.unmarshal(body)
a.addAttribute(currentAttributeGroup, 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)
a.addAttribute(currentAttributeGroup, m)
lastAddValuer = m
}
log.Debugf("%v : %v", name, value)
case jobAttributes:
log.Debug("Start job attributes")
currentAttributeGroup = jobAttributes
case printerAttributes:
log.Debug("Start printer attributes")
currentAttributeGroup = printerAttributes
case operationAttributes:
log.Debug("Start operation attributes")
currentAttributeGroup = operationAttributes
case resolutionValueTag:
res := NewResolution("", 0, 0)
res.unmarshal(body)
a.addAttribute(currentAttributeGroup, res)
log.Debugf("Resolution %v", res)
default:
log.Errorf("Unsupported tag %v", t)
}
}
}

View File

@@ -11,6 +11,10 @@ func NewMimeMediaType(name string, values ...string) *mimeMediaType {
return m return m
} }
func (m mimeMediaType) Name() string {
return m.sos.name
}
func (m mimeMediaType) String() string { func (m mimeMediaType) String() string {
return m.sos.String() return m.sos.String()
} }

View File

@@ -14,6 +14,10 @@ func NewNameWithoutLanguage(name, value string) *NameWithoutLanguage {
return c return c
} }
func (c NameWithoutLanguage) Name() string {
return c.name
}
func (c NameWithoutLanguage) String() string { func (c NameWithoutLanguage) String() string {
return c.name + ":" + c.value return c.name + ":" + c.value
} }

View File

@@ -16,9 +16,14 @@ func NewNaturalLanguage(name, value string) *naturalLanguage {
return c return c
} }
func (c naturalLanguage) Name() string {
return c.name
}
func (c naturalLanguage) String() string { func (c naturalLanguage) String() string {
return c.name + ":" + c.value return c.name + ":" + c.value
} }
func (c *naturalLanguage) valueTag() tag { func (c *naturalLanguage) valueTag() tag {
return naturalLanguageValueTag return naturalLanguageValueTag
} }

View File

@@ -5,23 +5,21 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
log "github.com/sirupsen/logrus"
) )
type ippRequestHeader struct { type ippMessageHeader struct {
versionNumber versionNumber versionNumber versionNumber
operationId OperationId operationId OperationId
requestId uint32 requestId uint32
} }
func (h *ippRequestHeader) unmarshal(byteStream io.Reader) { func (h *ippMessageHeader) unmarshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &h.versionNumber) binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
binary.Read(byteStream, binary.BigEndian, &h.operationId) binary.Read(byteStream, binary.BigEndian, &h.operationId)
binary.Read(byteStream, binary.BigEndian, &h.requestId) binary.Read(byteStream, binary.BigEndian, &h.requestId)
} }
func (h *ippRequestHeader) marshal() []byte { func (h *ippMessageHeader) marshal() []byte {
b := make([]byte, 0, 8) b := make([]byte, 0, 8)
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
binary.Write(buf, binary.BigEndian, h.versionNumber) binary.Write(buf, binary.BigEndian, h.versionNumber)
@@ -30,25 +28,17 @@ func (h *ippRequestHeader) marshal() []byte {
return buf.Bytes() return buf.Bytes()
} }
func (h ippRequestHeader) String() string { func (h ippMessageHeader) String() string {
return fmt.Sprintf("Version number: %v Operation Id: %v Request Id: %v", h.versionNumber, h.operationId, h.requestId) 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 { type AddValuer interface {
addValue(string) addValue(string)
} }
type Request struct { type Request struct {
operationAttributes map[string]Attribute a *attributes
jobAttributes map[string]Attribute header ippMessageHeader
printerAttributes map[string]Attribute
header ippRequestHeader
} }
func NewRequest(op OperationId, requestId uint32) *Request { func NewRequest(op OperationId, requestId uint32) *Request {
@@ -56,112 +46,18 @@ func NewRequest(op OperationId, requestId uint32) *Request {
r.header.operationId = op r.header.operationId = op
r.header.requestId = requestId r.header.requestId = requestId
r.header.versionNumber = 0x0200 r.header.versionNumber = 0x0200
r.operationAttributes = make(map[string]Attribute) r.a = new(attributes)
r.jobAttributes = make(map[string]Attribute)
r.printerAttributes = make(map[string]Attribute)
return r return r
} }
func (r Request) String() string { func (r Request) String() string {
s := r.header.String() + "\n" + " OperationAttributes" + "\n" return r.header.String() + "\n" + r.a.String()
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) { func (r *Request) UnMarshal(body io.Reader) {
r.header.unmarshal(body) r.header.unmarshal(body)
log.Debugf("Header %v", r.header) //log.Printf("Header %v", r.header)
var tag tag r.a = UnMarshalAttributues(body)
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 { func (r *Request) RequestId() uint32 {
@@ -173,31 +69,48 @@ func (r *Request) Operation() OperationId {
} }
func (r *Request) GetAttribute(name string) Attribute { func (r *Request) GetAttribute(name string) Attribute {
return r.operationAttributes[name] for _, a := range r.a.operation {
if a.Name() == name {
return a
}
}
return nil
} }
func (r *Request) Marshal() []byte { func (r *Request) Marshal() []byte {
// //s = r.size
var buf bytes.Buffer var buf bytes.Buffer
buf.Write(r.header.marshal()) buf.Write(r.header.marshal())
if len(r.operationAttributes) > 0 { if len(r.a.operation) > 0 {
buf.WriteByte(byte(operationAttributes)) buf.WriteByte(byte(operationAttributes))
for _, e := range r.operationAttributes { for _, e := range r.a.operation {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
if len(r.jobAttributes) > 0 { if len(r.a.job) > 0 {
buf.WriteByte(byte(jobAttributes)) buf.WriteByte(byte(jobAttributes))
for _, e := range r.jobAttributes { for _, e := range r.a.job {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
if len(r.printerAttributes) > 0 { if len(r.a.printer) > 0 {
buf.WriteByte(byte(printerAttributes)) buf.WriteByte(byte(printerAttributes))
for _, e := range r.printerAttributes { for _, e := range r.a.printer {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
buf.WriteByte(byte(endOfAttributes)) buf.WriteByte(byte(endOfAttributes))
return buf.Bytes() return buf.Bytes()
} }
func (r *Request) AddPrinterAttribute(a Attribute) {
r.a.addAttribute(printerAttributes, a)
}
func (r *Request) AddOperatonAttribute(a Attribute) {
r.a.addAttribute(operationAttributes, a)
}
func (r *Request) AddJobAttribute(a Attribute) {
r.a.addAttribute(jobAttributes, a)
}

View File

@@ -66,8 +66,8 @@ func TestUnmarshalRequestPrinterAttributes(T *testing.T) {
assert.Equal(T, versionNumber(0x0101), req.header.versionNumber, "Wrong version number") assert.Equal(T, versionNumber(0x0101), req.header.versionNumber, "Wrong version number")
assert.Equal(T, GetPrinterAttributes, req.header.operationId, "Wrong Operation") assert.Equal(T, GetPrinterAttributes, req.header.operationId, "Wrong Operation")
assert.Equal(T, uint32(17), req.header.requestId, "Wrong request id") assert.Equal(T, uint32(17), req.header.requestId, "Wrong request id")
assert.Len(T, req.operationAttributes, 4) assert.Len(T, req.a.operation, 4)
v := req.operationAttributes["requested-attributes"].(*keyWord).sos.values v := req.GetAttribute("requested-attributes").(*keyWord).sos.values
assert.Len(T, v, 7) assert.Len(T, v, 7)
assert.Contains(T, v, "printer-make-and-model") assert.Contains(T, v, "printer-make-and-model")
assert.Contains(T, v, "ipp-versions-supported") assert.Contains(T, v, "ipp-versions-supported")

View File

@@ -40,6 +40,10 @@ func (r *resolution) unmarshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &r.units) binary.Read(byteStream, binary.BigEndian, &r.units)
} }
func (r resolution) Name() string {
return r.name
}
func (r resolution) String() string { func (r resolution) String() string {
return fmt.Sprintf("%v:%v,%v,%v", r.name, r.crossFeedResolution, r.feedResolution, r.units) return fmt.Sprintf("%v:%v,%v,%v", r.name, r.crossFeedResolution, r.feedResolution, r.units)
} }

View File

@@ -3,6 +3,7 @@ package ipp
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io"
) )
type ippResponseHeader struct { type ippResponseHeader struct {
@@ -24,92 +25,69 @@ func (h *ippResponseHeader) marshal() []byte {
return a return a
} }
func (h *ippResponseHeader) unmarshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
binary.Read(byteStream, binary.BigEndian, &h.statusCode)
binary.Read(byteStream, binary.BigEndian, &h.requestId)
}
type Response struct { type Response struct {
operationAttributes []Attribute a *attributes
jobAttributes []Attribute header ippResponseHeader
printerAttributes []Attribute
header ippResponseHeader
} }
func NewResponse(code statusCode, requestId uint32) *Response { func NewResponse(code statusCode, requestId uint32) *Response {
r := new(Response) r := new(Response)
r.a = new(attributes)
r.header.versionNumber = 0x0101 r.header.versionNumber = 0x0101
r.header.requestId = requestId r.header.requestId = requestId
r.header.statusCode = code r.header.statusCode = code
r.operationAttributes = make([]Attribute, 0)
r.printerAttributes = make([]Attribute, 0)
r.jobAttributes = make([]Attribute, 0)
return r return r
} }
// func (r *Response) length() int {
// l := 8 + 1
// if len(r.jobAttributes) > 0 {
// l += 1 //
// for _, e := range r.jobAttributes {
// l += e.length()
// }
// }
// for _, e := range r.operationAttributes {
// l += e.length()
// }
// for _, e := range r.printerAttributes {
// l += e.length()
// }
// }
func (r Response) String() string { func (r Response) String() string {
return r.header.String() + "\n" + r.a.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 { func (r *Response) Marshal() []byte {
a := make([]byte, 0, 20) a := make([]byte, 0, 20)
a = append(a, r.header.marshal()...) a = append(a, r.header.marshal()...)
if len(r.operationAttributes) > 0 { if len(r.a.operation) > 0 {
a = append(a, byte(operationAttributes)) a = append(a, byte(operationAttributes))
for _, e := range r.operationAttributes { for _, e := range r.a.operation {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
if len(r.jobAttributes) > 0 { if len(r.a.job) > 0 {
a = append(a, byte(jobAttributes)) a = append(a, byte(jobAttributes))
for _, e := range r.jobAttributes { for _, e := range r.a.job {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
if len(r.printerAttributes) > 0 { if len(r.a.printer) > 0 {
a = append(a, byte(printerAttributes)) a = append(a, byte(printerAttributes))
for _, e := range r.printerAttributes { for _, e := range r.a.printer {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
a = append(a, byte(endOfAttributes)) a = append(a, byte(endOfAttributes))
return a return a
} }
func (r *Response) UnMarshal(body io.Reader) {
r.header.unmarshal(body)
//log.Printf("Header %v", r.header)
r.a = UnMarshalAttributues(body)
}
func (r *Response) AddPrinterAttribute(a Attribute) { func (r *Response) AddPrinterAttribute(a Attribute) {
r.printerAttributes = append(r.printerAttributes, a) r.a.addAttribute(printerAttributes, a)
} }
func (r *Response) AddOperatonAttribute(a Attribute) { func (r *Response) AddOperatonAttribute(a Attribute) {
r.operationAttributes = append(r.operationAttributes, a) r.a.addAttribute(operationAttributes, a)
} }
func (r *Response) AddJobAttribute(a Attribute) { func (r *Response) AddJobAttribute(a Attribute) {
r.jobAttributes = append(r.jobAttributes, a) r.a.addAttribute(jobAttributes, a)
} }

View File

@@ -14,9 +14,14 @@ func NewtextWithoutLanguage(name, value string) *textWithoutLanguage {
return c return c
} }
func (c textWithoutLanguage) Name() string {
return c.name
}
func (c textWithoutLanguage) String() string { func (c textWithoutLanguage) String() string {
return c.name + ":" + c.value return c.name + ":" + c.value
} }
func (c *textWithoutLanguage) valueTag() tag { func (c *textWithoutLanguage) valueTag() tag {
return textWithoutLanguageValueTag return textWithoutLanguageValueTag
} }

View File

@@ -16,6 +16,10 @@ func NewUriValue(name, value string) *uriValue {
return u return u
} }
func (u uriValue) Name() string {
return u.name
}
func (u uriValue) String() string { func (u uriValue) String() string {
return u.name + ":" + u.value return u.name + ":" + u.value
} }