6 Commits

Author SHA1 Message Date
77ef627fe2 More development.
More types.
Fixed attribute groups in requests.
Started on client.
Saving data to file.
2020-12-26 20:58:38 +01:00
8b5f69cb52 More types. Printing from chromeos works a little bit. 2020-12-25 23:25:39 +01:00
7ecbae27c2 More types. 2020-12-23 23:21:47 +01:00
a1261c1b8f Spelling corrections. 2020-12-23 21:51:30 +01:00
c3c608b28a WIP: Fix keyword handling 2020-12-05 21:29:57 +01:00
74c1ce257a Move request to a separate file and add test. 2020-12-05 21:26:10 +01:00
27 changed files with 270 additions and 740 deletions

View File

@@ -1,39 +0,0 @@
package main
import (
"bytes"
"fmt"
"ippserver/packages/ipp"
"net/http"
log "github.com/sirupsen/logrus"
)
//application/ipp
// brn30055cb5e3ae.local:631/ipp/print
func main() {
customFormatter := new(log.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
log.SetFormatter(customFormatter)
customFormatter.FullTimestamp = true
log.SetLevel(log.DebugLevel)
const printerUri = "brn30055cb5e3ae.local:631/ipp/print"
request := ipp.NewRequest(ipp.GetPrinterAttributes, 10)
request.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
request.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", "ipp://"+printerUri))
r := request.Marshal()
b := bytes.NewBuffer(r)
httpResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", b)
if err != nil {
fmt.Print(err)
return
}
rb := ipp.NewResponse(0, 0)
rb.UnMarshal(httpResponse.Body)
fmt.Print(r)
}

View File

@@ -1,66 +0,0 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
// Boolean is the ipp attribute boolean
type Boolean struct {
name string
value bool
}
// NewBoolean creates a nre boolean attribute
func NewBoolean(name string, value bool) *Boolean {
b := new(Boolean)
b.name = name
b.value = value
return b
}
// Name gets tha name of the boolean attribute
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) {
name, data := unmarshalSingleAttribute(byteStream)
b.name = name
if data[0] == 0 {
b.value = false
return
}
b.value = true
}
func (b *Boolean) marshal() []byte {
l := 5 + len(b.name)
ba := make([]byte, 0, l)
buf := bytes.NewBuffer(ba)
buf.WriteByte(byte(booleanValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(b.name)))
buf.WriteString(b.name)
binary.Write(buf, binary.BigEndian, uint16(1))
if b.value {
buf.WriteByte(byte(1))
} else {
buf.WriteByte(byte(0))
}
return buf.Bytes()
}
func (b *Boolean) size() int {
l := 5 + len(b.name)
return l
}

View File

@@ -16,14 +16,9 @@ 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
} }
@@ -34,7 +29,7 @@ func (c *charSetValue) unmarshal(byteStream io.Reader) {
func (c *charSetValue) marshal() []byte { func (c *charSetValue) marshal() []byte {
l := 5 + len(c.name) + len(c.value) l := 5 + len(c.name) + len(c.value)
b := make([]byte, l) b := make([]byte, l, l)
b[0] = byte(charsetValueTag) b[0] = byte(charsetValueTag)
marshalNameValue(c.name, c.value, b[1:]) marshalNameValue(c.name, c.value, b[1:])
return b return b

View File

@@ -1,74 +0,0 @@
package ipp
import (
"bufio"
"encoding/binary"
log "github.com/sirupsen/logrus"
)
// Currently the collection data is just consumed and dropped from the bytestream
func consumeCollection(byteStream *bufio.Reader) {
// RFC8010 Section 3.1.6
var length uint16
binary.Read(byteStream, binary.BigEndian, &length)
collectionName := make([]byte, length)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, collectionName)
}
log.Info("collection name " + string(collectionName))
err := binary.Read(byteStream, binary.BigEndian, &length) //Always zero ??
if err != nil {
log.Fatal("error ", err.Error())
}
if length != 0 {
log.Fatal("Should be zero")
}
// Member attributes
done:
for {
var t tag
binary.Read(byteStream, binary.BigEndian, &t)
log.Debug("Collection tag ", t)
switch t {
case endCollectionValueTag:
binary.Read(byteStream, binary.BigEndian, &length)
if length != 0 {
log.Fatal("Should be zero")
}
binary.Read(byteStream, binary.BigEndian, &length)
if length != 0 {
log.Fatal("Should be zero")
}
case memberAttrNameValueTag:
// RFC8010 Section 3.7.1
binary.Read(byteStream, binary.BigEndian, &length) //Always zero ??
if length != 0 {
log.Fatal("Should be zero")
}
binary.Read(byteStream, binary.BigEndian, &length) // Value length
memberName := make([]byte, length)
binary.Read(byteStream, binary.BigEndian, memberName)
log.Debugf("Member name: %v", string(memberName))
var memberValueTag tag
binary.Read(byteStream, binary.BigEndian, &memberValueTag)
log.Debug("Member value tag: ", memberValueTag)
binary.Read(byteStream, binary.BigEndian, &length) //Always zero ??
if length != 0 {
log.Fatal("Should be zero")
}
var memberValueLength uint16
binary.Read(byteStream, binary.BigEndian, &memberValueLength)
memberValue := make([]byte, memberValueLength)
binary.Read(byteStream, binary.BigEndian, memberValue)
log.Debugf("Member Value: % x", memberValue)
default:
// Next tag is one that can not be handled in the collection
// Put it back in the byte stream and return to main loop
byteStream.UnreadByte()
break done
}
}
log.Debug("Collection done")
}

View File

@@ -1,41 +1,52 @@
package ipp package ipp
import ( import (
"bytes"
"encoding/binary"
"fmt" "fmt"
"io"
log "github.com/sirupsen/logrus"
) )
type enum struct { type enum struct {
name string name string
values []int32 value int32
} }
func NewEnum(name string, values ...int32) *enum { func NewEnum(name string, value int32) *enum {
e := new(enum) e := new(enum)
e.name = name e.name = name
e.values = values e.value = value
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.values) return e.name + ":" + fmt.Sprint(e.value)
} }
func (e *enum) valueTag() tag { func (e *enum) valueTag() tag {
return enumValueTag return enumValueTag
} }
func (e *enum) size() int { func (e *enum) unmarshal(byteStream io.Reader) {
return 9 + len(e.name) log.Warn("Unmarshal of enum is not implemented yet")
}
func (e *enum) addValue(v interface{}) {
e.values = append(e.values, v.(int32))
} }
func (e *enum) marshal() []byte { func (e *enum) marshal() []byte {
return marshalInteger(enumValueTag, e.name, e.values) 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,67 +0,0 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
type integer struct {
name string
values []int32
}
func NewInteger(name string, values ...int32) *integer {
e := new(integer)
e.name = name
e.values = values
return e
}
func (i integer) Name() string {
return i.name
}
func (i integer) String() string {
return i.name + ":" + fmt.Sprint(i.values)
}
func (i *integer) valueTag() tag {
return integerValueTag
}
func (i *integer) size() int {
return 9 + len(i.name) // The attribute tag + 2 lengths
}
func (i *integer) addValue(v interface{}) {
i.values = append(i.values, v.(int32))
}
func (i *integer) marshal() []byte {
return marshalInteger(integerValueTag, i.name, i.values)
}
func marshalInteger(t tag, name string, values []int32) []byte {
l := 9 + len(name)
b := make([]byte, 0, l)
buf := bytes.NewBuffer(b)
buf.WriteByte(byte(integerValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(name)))
buf.WriteString(name)
binary.Write(buf, binary.BigEndian, uint16(4))
binary.Write(buf, binary.BigEndian, values[0])
for _, v := range values[1:] {
buf.WriteByte(byte(integerValueTag))
binary.Write(buf, binary.BigEndian, uint16(0))
binary.Write(buf, binary.BigEndian, uint16(4))
binary.Write(buf, binary.BigEndian, v)
}
return buf.Bytes()
}
func unmarshalSingleInteger(byteStream io.Reader) (string, int32) {
name, data := unmarshalSingleAttribute(byteStream)
return name, int32(binary.BigEndian.Uint32(data))
}

View File

@@ -1,38 +0,0 @@
package ipp
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnMarshalSinglePositiveInteger(T *testing.T) {
testdata := []byte{
0x00, 0x04,
0x66, 0x6c, 0x6f, 0x70, //flop
0x00, 0x04,
0x00, 0x0, 0x0, 0x4,
}
buf := bytes.NewBuffer(testdata)
n, v := unmarshalSingleInteger(buf)
assert.Equal(T, "flop", n, "Should be equal")
assert.Equal(T, int32(4), v, "Should be equal")
}
func TestUnMarshalSingleNegativeInteger(T *testing.T) {
testdata := []byte{
0x00, 0x04,
0x66, 0x6c, 0x6f, 0x70, //flop
0x00, 0x04,
0xff, 0xff, 0xff, 0xfc,
}
buf := bytes.NewBuffer(testdata)
n, v := unmarshalSingleInteger(buf)
assert.Equal(T, "flop", n, "Should be equal")
assert.Equal(T, int32(-4), v, "Should be equal")
}

View File

@@ -11,10 +11,6 @@ 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()
} }
@@ -31,7 +27,7 @@ func (k *keyWord) marshal() []byte {
return k.sos.marshal() return k.sos.marshal()
} }
func (k *keyWord) addValue(v interface{}) { func (k *keyWord) addValue(v string) {
k.sos.AddValue(v.(string)) k.sos.AddValue(v)
} }

View File

@@ -1,13 +1,9 @@
//Package ipp provides functonality to handle ipp messages
package ipp package ipp
import ( import (
"bufio"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
log "github.com/sirupsen/logrus"
) )
// References // References
@@ -58,31 +54,30 @@ const (
memberAttrNameValueTag tag = 0x4a memberAttrNameValueTag tag = 0x4a
) )
// OperationID is defined in rfc8011 // Operation-id, defined in rfc8011
type OperationID uint16 type OperationId uint16
const ( const (
PrintJob OperationID = 0x0002 PrintJob OperationId = 0x0002
PrintURI OperationID = 0x0003 PrintURI OperationId = 0x0003
ValidateJob OperationID = 0x0004 ValidateJob OperationId = 0x0004
CreateJob OperationID = 0x0005 CreateJob OperationId = 0x0005
SendDocument OperationID = 0x0006 SendDocument OperationId = 0x0006
SendURI OperationID = 0x0007 SendURI OperationId = 0x0007
CancelJob OperationID = 0x0008 CancelJob OperationId = 0x0008
GetJobAttributes OperationID = 0x0009 GetJobAttributes OperationId = 0x0009
GetJobs OperationID = 0x000a GetJobs OperationId = 0x000a
GetPrinterAttributes OperationID = 0x000b GetPrinterAttributes OperationId = 0x000b
HoldJob OperationID = 0x000c HoldJob OperationId = 0x000c
ReleaseJob OperationID = 0x000d ReleaseJob OperationId = 0x000d
RestartJob OperationID = 0x000e RestartJob OperationId = 0x000e
PausePrinter OperationID = 0x0010 PausePrinter OperationId = 0x0010
ResumePrinter OperationID = 0x0011 ResumePrinter OperationId = 0x0011
PurgeJobs OperationID = 0x0012 PurgeJobs OperationId = 0x0012
) )
type printerState int32 type printerState int32
// printerstate defenitions
const ( const (
Idle printerState = 3 Idle printerState = 3
Processing printerState = 4 Processing printerState = 4
@@ -91,7 +86,6 @@ const (
type statusCode uint16 type statusCode uint16
// status code defenitions
const ( const (
SuccessfulOk statusCode = 0x0000 SuccessfulOk statusCode = 0x0000
ClientErrorBadRequest statusCode = 0x0400 ClientErrorBadRequest statusCode = 0x0400
@@ -104,19 +98,6 @@ func (v versionNumber) String() string {
return fmt.Sprintf("%x.%x", vn&0xff00>>8, vn&0x00ff) return fmt.Sprintf("%x.%x", vn&0xff00>>8, vn&0x00ff)
} }
func unmarshalSingleAttribute(byteStream io.Reader) (string, []byte) {
var length uint16
binary.Read(byteStream, binary.BigEndian, &length)
attributeName := make([]byte, length)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, attributeName)
}
binary.Read(byteStream, binary.BigEndian, &length)
attributeValue := make([]byte, length)
binary.Read(byteStream, binary.BigEndian, attributeValue)
return string(attributeName), attributeValue
}
func unmarshalSingleValue(byteStream io.Reader) (string, string) { func unmarshalSingleValue(byteStream io.Reader) (string, string) {
var length uint16 var length uint16
binary.Read(byteStream, binary.BigEndian, &length) binary.Read(byteStream, binary.BigEndian, &length)
@@ -140,172 +121,3 @@ 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 (a *attributes) addAttribute(group tag, attr Attribute) {
switch group {
case operationAttributes:
a.operation = append(a.operation, attr)
case jobAttributes:
a.job = append(a.job, attr)
case printerAttributes:
a.printer = append(a.printer, attr)
default:
log.Error("Unknown attribute group")
}
}
func UnMarshalAttributes(bytestream *bufio.Reader) *attributes {
a := new(attributes)
var t tag
err := binary.Read(bytestream, 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(bytestream, binary.BigEndian, &t)
if err != nil {
log.Fatal("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(bytestream)
a.addAttribute(currentAttributeGroup, c)
log.Debugf("%v %v", c.name, c.value)
case booleanValueTag:
na := NewBoolean("", false)
na.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, na)
case uriValueTag:
u := NewURIValue("", "")
u.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, u)
log.Debugf("%v %v", u.name, u.value)
case naturalLanguageValueTag:
n := NewNaturalLanguage("", "")
n.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, n)
log.Debugf("%v %v", n.name, n.value)
case keyWordValueTag:
name, value := unmarshalSingleValue(bytestream)
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(bytestream)
a.addAttribute(currentAttributeGroup, n)
log.Debugf("%v %v", n.name, n.value)
case textWithoutLanguageValueTag:
attr := NewtextWithoutLanguage("", "")
attr.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, attr)
log.Debugf("%v %v", attr.name, attr.value)
case mimeMediaTypeValueTag:
name, value := unmarshalSingleValue(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
m := NewMimeMediaType(name, value)
a.addAttribute(currentAttributeGroup, m)
lastAddValuer = m
}
log.Debugf("%v : %v", name, value)
case integerValueTag:
name, value := unmarshalSingleInteger(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
i := NewInteger(name, value)
a.addAttribute(currentAttributeGroup, i)
lastAddValuer = i
}
log.Debugf("%v : %v", name, value)
case rangeOfIntegerValueTag:
name, value := unmarshalSingleRangeOfInteger(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
r := NewRangeOfInteger(name, value)
a.addAttribute(currentAttributeGroup, r)
lastAddValuer = r
}
log.Debugf("%v : %v", name, value)
case enumValueTag:
name, value := unmarshalSingleInteger(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
e := NewEnum(name, value)
a.addAttribute(currentAttributeGroup, e)
lastAddValuer = e
}
log.Debugf("%v : %v", name, value)
case begCollectionValueTag:
// For now just consume the collection
consumeCollection(bytestream)
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(bytestream)
a.addAttribute(currentAttributeGroup, res)
log.Debugf("Resolution %v", res)
default:
log.Errorf("Unsupported tag %v (%x)", t, uint8(t))
}
}
}

View File

@@ -11,10 +11,6 @@ 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()
} }
@@ -31,6 +27,6 @@ func (m *mimeMediaType) marshal() []byte {
return m.sos.marshal() return m.sos.marshal()
} }
func (m *mimeMediaType) addValue(v interface{}) { func (m *mimeMediaType) addValue(v string) {
m.sos.AddValue(v.(string)) m.sos.AddValue(v)
} }

View File

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

View File

@@ -16,14 +16,9 @@ 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
} }
@@ -34,7 +29,7 @@ func (c *naturalLanguage) unmarshal(byteStream io.Reader) {
func (c *naturalLanguage) marshal() []byte { func (c *naturalLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value) l := 5 + len(c.name) + len(c.value)
b := make([]byte, l) b := make([]byte, l, l)
b[0] = byte(naturalLanguageValueTag) b[0] = byte(naturalLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:]) marshalNameValue(c.name, c.value, b[1:])
return b return b

View File

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

View File

@@ -1,55 +0,0 @@
package ipp
import (
"encoding/binary"
"fmt"
"io"
log "github.com/sirupsen/logrus"
)
type IRange struct {
lower int32
upper int32
}
type rangeOfInteger struct {
name string
values []IRange
}
func NewRangeOfInteger(name string, values ...IRange) *rangeOfInteger {
r := new(rangeOfInteger)
r.name = name
r.values = values
return r
}
func (r *rangeOfInteger) Name() string {
return r.name
}
func (r rangeOfInteger) String() string {
return r.name + ":" + fmt.Sprint(r.values)
}
func (r *rangeOfInteger) valueTag() tag {
return rangeOfIntegerValueTag
}
func (r *rangeOfInteger) marshal() []byte {
log.Error("marshal rangeOfInteger is not implemented yet")
return []byte{}
}
func (r *rangeOfInteger) addValue(v interface{}) {
r.values = append(r.values, v.(IRange))
}
func unmarshalSingleRangeOfInteger(byteStream io.Reader) (string, IRange) {
name, data := unmarshalSingleAttribute(byteStream)
var r IRange
r.lower = int32(binary.BigEndian.Uint32(data[0:4]))
r.upper = int32(binary.BigEndian.Uint32(data[4:8]))
return name, r
}

View File

@@ -1,24 +0,0 @@
package ipp
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnMarshalSingleRange(T *testing.T) {
testdata := []byte{
0x00, 0x04,
0x66, 0x6c, 0x6f, 0x70, //flop
0x00, 0x08,
0x00, 0x0, 0x0, 0x4, 0x00, 0x0, 0x0, 0x5,
}
buf := bytes.NewBuffer(testdata)
n, v := unmarshalSingleRangeOfInteger(buf)
assert.Equal(T, "flop", n, "Should be equal")
assert.Equal(T, int32(4), v.lower, "Should be equal")
assert.Equal(T, int32(5), v.upper, "Should be equal")
}

View File

@@ -1,118 +1,203 @@
package ipp package ipp
import ( import (
"bufio"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
log "github.com/sirupsen/logrus"
) )
type ippMessageHeader struct { type ippRequestHeader struct {
versionNumber versionNumber versionNumber versionNumber
operationID OperationID operationId OperationId
requestID uint32 requestId uint32
} }
func (h *ippMessageHeader) unmarshal(byteStream io.Reader) { func (h *ippRequestHeader) 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 *ippMessageHeader) marshal() []byte { func (h *ippRequestHeader) 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)
binary.Write(buf, binary.BigEndian, h.operationID) binary.Write(buf, binary.BigEndian, h.operationId)
binary.Write(buf, binary.BigEndian, h.requestID) binary.Write(buf, binary.BigEndian, h.requestId)
return buf.Bytes() return buf.Bytes()
} }
func (h ippMessageHeader) String() string { func (h ippRequestHeader) 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(interface{}) addValue(string)
} }
type Request struct { type Request struct {
a *attributes operationAttributes map[string]Attribute
header ippMessageHeader jobAttributes map[string]Attribute
printerAttributes map[string]Attribute
header ippRequestHeader
} }
func NewRequest(op OperationID, requestID uint32) *Request { func NewRequest(op OperationId, requestId uint32) *Request {
r := new(Request) r := new(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.a = new(attributes) r.operationAttributes = make(map[string]Attribute)
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 {
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 *Request) UnMarshal(body io.Reader) { func (r *Request) UnMarshal(body io.Reader) {
buffbody := bufio.NewReader(body) r.header.unmarshal(body)
r.header.unmarshal(buffbody) log.Debugf("Header %v", r.header)
//log.Printf("Header %v", r.header) var tag tag
r.a = UnMarshalAttributes(buffbody) 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)
} }
func (r *Request) RequestID() uint32 { var lastAddValuer AddValuer
return r.header.requestID 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) Operation() OperationID { func (r *Request) RequestId() uint32 {
return r.header.operationID return r.header.requestId
}
func (r *Request) Operation() OperationId {
return r.header.operationId
} }
func (r *Request) GetAttribute(name string) Attribute { func (r *Request) GetAttribute(name string) Attribute {
for _, a := range r.a.operation { return r.operationAttributes[name]
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.a.operation) > 0 { if len(r.operationAttributes) > 0 {
buf.WriteByte(byte(operationAttributes)) buf.WriteByte(byte(operationAttributes))
for _, e := range r.a.operation { for _, e := range r.operationAttributes {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
if len(r.a.job) > 0 { if len(r.jobAttributes) > 0 {
buf.WriteByte(byte(jobAttributes)) buf.WriteByte(byte(jobAttributes))
for _, e := range r.a.job { for _, e := range r.jobAttributes {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
if len(r.a.printer) > 0 { if len(r.printerAttributes) > 0 {
buf.WriteByte(byte(printerAttributes)) buf.WriteByte(byte(printerAttributes))
for _, e := range r.a.printer { for _, e := range r.printerAttributes {
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

@@ -64,10 +64,10 @@ func TestUnmarshalRequestPrinterAttributes(T *testing.T) {
req.UnMarshal(buf) req.UnMarshal(buf)
fmt.Print(req) fmt.Print(req)
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.a.operation, 4) assert.Len(T, req.operationAttributes, 4)
v := req.GetAttribute("requested-attributes").(*keyWord).sos.values v := req.operationAttributes["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,10 +40,6 @@ 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

@@ -1,95 +1,115 @@
package ipp package ipp
import ( import (
"bufio"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io"
) )
type ippResponseHeader struct { type ippResponseHeader struct {
versionNumber versionNumber versionNumber versionNumber
statusCode statusCode statusCode statusCode
requestID uint32 requestId uint32
} }
func (h ippResponseHeader) String() string { func (h ippResponseHeader) String() string {
return fmt.Sprintf("Version number: %v Status code: %v Request Id: %v", h.versionNumber, h.statusCode, h.requestID) return fmt.Sprintf("Version number: %v Status code: %v Request Id: %v", h.versionNumber, h.statusCode, h.requestId)
} }
func (h *ippResponseHeader) marshal() []byte { func (h *ippResponseHeader) marshal() []byte {
a := make([]byte, 8) a := make([]byte, 8, 8)
binary.BigEndian.PutUint16(a[0:2], uint16(h.versionNumber)) binary.BigEndian.PutUint16(a[0:2], uint16(h.versionNumber))
binary.BigEndian.PutUint16(a[2:4], uint16(h.statusCode)) binary.BigEndian.PutUint16(a[2:4], uint16(h.statusCode))
binary.BigEndian.PutUint32(a[4:8], h.requestID) binary.BigEndian.PutUint32(a[4:8], h.requestId)
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 {
a *attributes operationAttributes []Attribute
jobAttributes []Attribute
printerAttributes []Attribute
header ippResponseHeader 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.a.operation) > 0 { if len(r.operationAttributes) > 0 {
a = append(a, byte(operationAttributes)) a = append(a, byte(operationAttributes))
for _, e := range r.a.operation { for _, e := range r.operationAttributes {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
if len(r.a.job) > 0 { if len(r.jobAttributes) > 0 {
a = append(a, byte(jobAttributes)) a = append(a, byte(jobAttributes))
for _, e := range r.a.job { for _, e := range r.jobAttributes {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
if len(r.a.printer) > 0 { if len(r.printerAttributes) > 0 {
a = append(a, byte(printerAttributes)) a = append(a, byte(printerAttributes))
for _, e := range r.a.printer { for _, e := range r.printerAttributes {
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) {
buffbody := bufio.NewReader(body)
r.header.unmarshal(buffbody)
//log.Printf("Header %v", r.header)
r.a = UnMarshalAttributes(buffbody)
}
func (r *Response) AddPrinterAttribute(a Attribute) { func (r *Response) AddPrinterAttribute(a Attribute) {
r.a.addAttribute(printerAttributes, a) r.printerAttributes = append(r.printerAttributes, a)
} }
func (r *Response) AddOperatonAttribute(a Attribute) { func (r *Response) AddOperatonAttribute(a Attribute) {
r.a.addAttribute(operationAttributes, a) r.operationAttributes = append(r.operationAttributes, a)
} }
func (r *Response) AddJobAttribute(a Attribute) { func (r *Response) AddJobAttribute(a Attribute) {
r.a.addAttribute(jobAttributes, a) r.jobAttributes = append(r.jobAttributes, a)
} }

View File

@@ -10,7 +10,7 @@ func TestMarshalResponseHeader(T *testing.T) {
h.versionNumber = 0x0101 h.versionNumber = 0x0101
h.statusCode = SuccessfulOk h.statusCode = SuccessfulOk
h.requestID = 0xdeadbeef h.requestId = 0xdeadbeef
b := h.marshal() b := h.marshal()
fmt.Printf("% x\n", b) fmt.Printf("% x\n", b)

View File

@@ -44,7 +44,7 @@ func (s *setOfStrings) marshal() []byte {
for i := range s.values[1:] { for i := range s.values[1:] {
l += 5 + len(s.values[i+1]) l += 5 + len(s.values[i+1])
} }
res := make([]byte, l) res := make([]byte, l, l)
p := 0 p := 0
res[p] = byte(s.vTag) res[p] = byte(s.vTag)
p += 1 p += 1

View File

@@ -14,14 +14,9 @@ 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
} }
@@ -32,7 +27,7 @@ func (c *textWithoutLanguage) unmarshal(byteStream io.Reader) {
func (c *textWithoutLanguage) marshal() []byte { func (c *textWithoutLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value) l := 5 + len(c.name) + len(c.value)
b := make([]byte, l) b := make([]byte, l, l)
b[0] = byte(textWithoutLanguageValueTag) b[0] = byte(textWithoutLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:]) marshalNameValue(c.name, c.value, b[1:])
return b return b

View File

@@ -9,17 +9,13 @@ type uriValue struct {
value string value string
} }
func NewURIValue(name, value string) *uriValue { func NewUriValue(name, value string) *uriValue {
u := new(uriValue) u := new(uriValue)
u.name = name u.name = name
u.value = value u.value = value
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
} }

View File

@@ -3,6 +3,6 @@ package main
import "ippserver/packages/ipp" import "ippserver/packages/ipp"
func handleGetJobs(r *ipp.Request) *ipp.Response { func handleGetJobs(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID()) response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId())
return response return response
} }

View File

@@ -3,13 +3,13 @@ package main
import "ippserver/packages/ipp" import "ippserver/packages/ipp"
func handleGetPrinterAttributes(r *ipp.Request) *ipp.Response { func handleGetPrinterAttributes(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID()) response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId())
var a ipp.Attribute var a ipp.Attribute
a = ipp.NewCharSetValue("attributes-charset", "utf-8") a = ipp.NewCharSetValue("attributes-charset", "utf-8")
response.AddOperatonAttribute(a) response.AddOperatonAttribute(a)
a = ipp.NewNaturalLanguage("attributes-natural-language", "en") a = ipp.NewNaturalLanguage("attributes-natural-language", "en")
response.AddOperatonAttribute(a) response.AddOperatonAttribute(a)
a = ipp.NewURIValue("printer-uri", "ipp://drpork:1234/ipp/print") a = ipp.NewUriValue("printer-uri", "ipp://drpork:1234/ipp/print")
response.AddOperatonAttribute(a) response.AddOperatonAttribute(a)
a = ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBro 001") a = ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBro 001")
response.AddOperatonAttribute(a) response.AddOperatonAttribute(a)

View File

@@ -16,7 +16,7 @@ func handlePrintJob(r *ipp.Request, byteStream io.Reader) *ipp.Response {
} }
defer f.Close() defer f.Close()
io.Copy(f, byteStream) io.Copy(f, byteStream)
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID()) response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId())
return response return response
} }

View File

@@ -57,7 +57,7 @@ func handle(w http.ResponseWriter, r *http.Request) {
case ipp.GetJobs: case ipp.GetJobs:
response = handleGetJobs(request) response = handleGetJobs(request)
default: default:
response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestID()) response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestId())
} }
log.Infof("Response:\n%v\n", response) log.Infof("Response:\n%v\n", response)