diff --git a/packages/ipp/boolean.go b/packages/ipp/boolean.go new file mode 100644 index 0000000..c38be27 --- /dev/null +++ b/packages/ipp/boolean.go @@ -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 +} diff --git a/packages/ipp/charsetvalue.go b/packages/ipp/charsetvalue.go index 2cac464..d6c3b6a 100644 --- a/packages/ipp/charsetvalue.go +++ b/packages/ipp/charsetvalue.go @@ -16,9 +16,14 @@ func NewCharSetValue(name string, value string) *charSetValue { return c } +func (c charSetValue) Name() string { + return c.name +} + func (c charSetValue) String() string { return c.name + ":" + c.value } + func (c *charSetValue) valueTag() tag { return charsetValueTag } diff --git a/packages/ipp/enum.go b/packages/ipp/enum.go index 4123774..334d95c 100644 --- a/packages/ipp/enum.go +++ b/packages/ipp/enum.go @@ -21,9 +21,14 @@ func NewEnum(name string, value int32) *enum { return e } +func (e enum) Name() string { + return e.name +} + func (e enum) String() string { return e.name + ":" + fmt.Sprint(e.value) } + func (e *enum) valueTag() tag { return enumValueTag } diff --git a/packages/ipp/keyword.go b/packages/ipp/keyword.go index f702cdc..0de6b25 100644 --- a/packages/ipp/keyword.go +++ b/packages/ipp/keyword.go @@ -11,6 +11,10 @@ func NewKeyWord(name string, values ...string) *keyWord { return k } +func (k keyWord) Name() string { + return k.sos.name +} + func (k keyWord) String() string { return k.sos.String() } diff --git a/packages/ipp/messages.go b/packages/ipp/messages.go index 39985f6..8d4b929 100644 --- a/packages/ipp/messages.go +++ b/packages/ipp/messages.go @@ -4,6 +4,8 @@ import ( "encoding/binary" "fmt" "io" + + log "github.com/sirupsen/logrus" ) // References @@ -121,3 +123,130 @@ func marshalNameValue(name, value string, b []byte) { p += 2 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) + } + } +} diff --git a/packages/ipp/mimemediatype.go b/packages/ipp/mimemediatype.go index 1e5c4b6..7043224 100644 --- a/packages/ipp/mimemediatype.go +++ b/packages/ipp/mimemediatype.go @@ -11,6 +11,10 @@ func NewMimeMediaType(name string, values ...string) *mimeMediaType { return m } +func (m mimeMediaType) Name() string { + return m.sos.name +} + func (m mimeMediaType) String() string { return m.sos.String() } diff --git a/packages/ipp/namewithoutlanguage.go b/packages/ipp/namewithoutlanguage.go index fdaac16..9fce224 100644 --- a/packages/ipp/namewithoutlanguage.go +++ b/packages/ipp/namewithoutlanguage.go @@ -14,6 +14,10 @@ func NewNameWithoutLanguage(name, value string) *NameWithoutLanguage { return c } +func (c NameWithoutLanguage) Name() string { + return c.name +} + func (c NameWithoutLanguage) String() string { return c.name + ":" + c.value } diff --git a/packages/ipp/naturallanguage.go b/packages/ipp/naturallanguage.go index 43da042..310dcdd 100644 --- a/packages/ipp/naturallanguage.go +++ b/packages/ipp/naturallanguage.go @@ -16,9 +16,14 @@ func NewNaturalLanguage(name, value string) *naturalLanguage { return c } +func (c naturalLanguage) Name() string { + return c.name +} + func (c naturalLanguage) String() string { return c.name + ":" + c.value } + func (c *naturalLanguage) valueTag() tag { return naturalLanguageValueTag } diff --git a/packages/ipp/request.go b/packages/ipp/request.go index c9ad66a..3363bbc 100644 --- a/packages/ipp/request.go +++ b/packages/ipp/request.go @@ -5,23 +5,21 @@ import ( "encoding/binary" "fmt" "io" - - log "github.com/sirupsen/logrus" ) -type ippRequestHeader struct { +type ippMessageHeader struct { versionNumber versionNumber operationId OperationId 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.operationId) binary.Read(byteStream, binary.BigEndian, &h.requestId) } -func (h *ippRequestHeader) marshal() []byte { +func (h *ippMessageHeader) marshal() []byte { b := make([]byte, 0, 8) buf := bytes.NewBuffer(b) binary.Write(buf, binary.BigEndian, h.versionNumber) @@ -30,25 +28,17 @@ func (h *ippRequestHeader) marshal() []byte { 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) } -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 + a *attributes + header ippMessageHeader } func NewRequest(op OperationId, requestId uint32) *Request { @@ -56,112 +46,18 @@ func NewRequest(op OperationId, requestId uint32) *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) - + r.a = new(attributes) 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 + return r.header.String() + "\n" + r.a.String() } 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) - } - } + //log.Printf("Header %v", r.header) + r.a = UnMarshalAttributues(body) } func (r *Request) RequestId() uint32 { @@ -173,31 +69,48 @@ func (r *Request) Operation() OperationId { } 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 { - // //s = r.size + var buf bytes.Buffer buf.Write(r.header.marshal()) - if len(r.operationAttributes) > 0 { + if len(r.a.operation) > 0 { buf.WriteByte(byte(operationAttributes)) - for _, e := range r.operationAttributes { + for _, e := range r.a.operation { buf.Write(e.marshal()) } } - if len(r.jobAttributes) > 0 { + if len(r.a.job) > 0 { buf.WriteByte(byte(jobAttributes)) - for _, e := range r.jobAttributes { + for _, e := range r.a.job { buf.Write(e.marshal()) } } - if len(r.printerAttributes) > 0 { + if len(r.a.printer) > 0 { buf.WriteByte(byte(printerAttributes)) - for _, e := range r.printerAttributes { + for _, e := range r.a.printer { buf.Write(e.marshal()) } } buf.WriteByte(byte(endOfAttributes)) 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) +} diff --git a/packages/ipp/request_test.go b/packages/ipp/request_test.go index 296f7af..9b38577 100644 --- a/packages/ipp/request_test.go +++ b/packages/ipp/request_test.go @@ -66,8 +66,8 @@ func TestUnmarshalRequestPrinterAttributes(T *testing.T) { 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, req.a.operation, 4) + v := req.GetAttribute("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") diff --git a/packages/ipp/resolution.go b/packages/ipp/resolution.go index 9ba8fe9..c78111f 100644 --- a/packages/ipp/resolution.go +++ b/packages/ipp/resolution.go @@ -40,6 +40,10 @@ func (r *resolution) unmarshal(byteStream io.Reader) { binary.Read(byteStream, binary.BigEndian, &r.units) } +func (r resolution) Name() string { + return r.name +} + func (r resolution) String() string { return fmt.Sprintf("%v:%v,%v,%v", r.name, r.crossFeedResolution, r.feedResolution, r.units) } diff --git a/packages/ipp/response.go b/packages/ipp/response.go index fca8e1c..f07c7c7 100644 --- a/packages/ipp/response.go +++ b/packages/ipp/response.go @@ -3,6 +3,7 @@ package ipp import ( "encoding/binary" "fmt" + "io" ) type ippResponseHeader struct { @@ -24,92 +25,69 @@ func (h *ippResponseHeader) marshal() []byte { 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 { - operationAttributes []Attribute - jobAttributes []Attribute - printerAttributes []Attribute - header ippResponseHeader + a *attributes + header ippResponseHeader } func NewResponse(code statusCode, requestId uint32) *Response { r := new(Response) + r.a = new(attributes) r.header.versionNumber = 0x0101 r.header.requestId = requestId r.header.statusCode = code - r.operationAttributes = make([]Attribute, 0) - r.printerAttributes = make([]Attribute, 0) - r.jobAttributes = make([]Attribute, 0) 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 { - - 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 + return r.header.String() + "\n" + r.a.String() } func (r *Response) Marshal() []byte { a := make([]byte, 0, 20) a = append(a, r.header.marshal()...) - if len(r.operationAttributes) > 0 { + if len(r.a.operation) > 0 { a = append(a, byte(operationAttributes)) - for _, e := range r.operationAttributes { + for _, e := range r.a.operation { a = append(a, e.marshal()...) } } - if len(r.jobAttributes) > 0 { + if len(r.a.job) > 0 { a = append(a, byte(jobAttributes)) - for _, e := range r.jobAttributes { + for _, e := range r.a.job { a = append(a, e.marshal()...) } } - if len(r.printerAttributes) > 0 { + if len(r.a.printer) > 0 { a = append(a, byte(printerAttributes)) - for _, e := range r.printerAttributes { + for _, e := range r.a.printer { a = append(a, e.marshal()...) } - } a = append(a, byte(endOfAttributes)) 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) { - r.printerAttributes = append(r.printerAttributes, a) + r.a.addAttribute(printerAttributes, a) } func (r *Response) AddOperatonAttribute(a Attribute) { - r.operationAttributes = append(r.operationAttributes, a) + r.a.addAttribute(operationAttributes, a) } func (r *Response) AddJobAttribute(a Attribute) { - r.jobAttributes = append(r.jobAttributes, a) + r.a.addAttribute(jobAttributes, a) } diff --git a/packages/ipp/textWithoutLanguage.go b/packages/ipp/textWithoutLanguage.go index 1c53a13..0b7668b 100644 --- a/packages/ipp/textWithoutLanguage.go +++ b/packages/ipp/textWithoutLanguage.go @@ -14,9 +14,14 @@ func NewtextWithoutLanguage(name, value string) *textWithoutLanguage { return c } +func (c textWithoutLanguage) Name() string { + return c.name +} + func (c textWithoutLanguage) String() string { return c.name + ":" + c.value } + func (c *textWithoutLanguage) valueTag() tag { return textWithoutLanguageValueTag } diff --git a/packages/ipp/urivalue.go b/packages/ipp/urivalue.go index 79c2b63..e24f1a3 100644 --- a/packages/ipp/urivalue.go +++ b/packages/ipp/urivalue.go @@ -16,6 +16,10 @@ func NewUriValue(name, value string) *uriValue { return u } +func (u uriValue) Name() string { + return u.name +} + func (u uriValue) String() string { return u.name + ":" + u.value }