10 Commits

Author SHA1 Message Date
ce94bf0d89 Merge pull request 'Add unmarshalling to the boolean attribute.' (#13) from boolean-unmarshal into master
Reviewed-on: #13
2021-02-09 21:38:11 +01:00
b1fafb43b2 Add unmarshalling to the boolean attribute. 2021-02-09 21:34:58 +01:00
5c11761c48 Merge pull request 'Add collection type, use buffered bytestream.' (#12) from add-collection-type into master
Reviewed-on: #12
2021-02-09 21:34:12 +01:00
48aa81238d Add collection type, use buffered bytestream.
Fixed some more attribute types.
2021-02-09 21:31:27 +01:00
0805cec129 Fix lint warnings. 2021-01-09 12:10:43 +01:00
f449b535db Add rangeOfInteger attribute. 2020-12-29 21:45:00 +01:00
1eff8711d3 Add integer attribute type.
Merges some common code between integer and enum.
Enum can now bw a set.
Add valuer must take a interface{} in addValue since it can be different types.
2020-12-29 20:53:01 +01:00
e0a0c88d95 Start client implementation. 2020-12-28 20:16:59 +01:00
53aaaf5521 Merge attribute handling from request and response to commen code. 2020-12-28 20:05:01 +01:00
04a4b4157f 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>
2020-12-27 09:16:32 +01:00
27 changed files with 742 additions and 272 deletions

39
client/main.go Normal file
View File

@@ -0,0 +1,39 @@
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)
}

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

@@ -0,0 +1,66 @@
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,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
} }
@@ -29,7 +34,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, l) b := make([]byte, 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

@@ -0,0 +1,74 @@
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,52 +1,41 @@
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
value int32 values []int32
} }
func NewEnum(name string, value int32) *enum { func NewEnum(name string, values ...int32) *enum {
e := new(enum) e := new(enum)
e.name = name e.name = name
e.value = value e.values = values
return e return e
} }
func (e enum) String() string { func (e enum) Name() string {
return e.name + ":" + fmt.Sprint(e.value) return e.name
} }
func (e enum) String() string {
return e.name + ":" + fmt.Sprint(e.values)
}
func (e *enum) valueTag() tag { func (e *enum) valueTag() tag {
return enumValueTag return enumValueTag
} }
func (e *enum) unmarshal(byteStream io.Reader) { func (e *enum) size() int {
log.Warn("Unmarshal of enum is not implemented yet") return 9 + len(e.name)
}
func (e *enum) addValue(v interface{}) {
e.values = append(e.values, v.(int32))
} }
func (e *enum) marshal() []byte { func (e *enum) marshal() []byte {
l := 3 + len(e.name) + 6 return marshalInteger(enumValueTag, e.name, e.values)
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
} }

67
packages/ipp/integer.go Normal file
View File

@@ -0,0 +1,67 @@
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

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

View File

@@ -1,9 +1,13 @@
//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
@@ -54,30 +58,31 @@ const (
memberAttrNameValueTag tag = 0x4a memberAttrNameValueTag tag = 0x4a
) )
// Operation-id, defined in rfc8011 // OperationID is 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
@@ -86,6 +91,7 @@ const (
type statusCode uint16 type statusCode uint16
// status code defenitions
const ( const (
SuccessfulOk statusCode = 0x0000 SuccessfulOk statusCode = 0x0000
ClientErrorBadRequest statusCode = 0x0400 ClientErrorBadRequest statusCode = 0x0400
@@ -98,6 +104,19 @@ 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)
@@ -121,3 +140,172 @@ 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,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()
} }
@@ -27,6 +31,6 @@ func (m *mimeMediaType) marshal() []byte {
return m.sos.marshal() return m.sos.marshal()
} }
func (m *mimeMediaType) addValue(v string) { func (m *mimeMediaType) addValue(v interface{}) {
m.sos.AddValue(v) m.sos.AddValue(v.(string))
} }

View File

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

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
} }
@@ -29,7 +34,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, l) b := make([]byte, 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

@@ -0,0 +1,55 @@
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

@@ -0,0 +1,24 @@
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,203 +1,118 @@
package ipp package ipp
import ( import (
"bufio"
"bytes" "bytes"
"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)
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 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(interface{})
} }
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 {
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.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) buffbody := bufio.NewReader(body)
log.Debugf("Header %v", r.header) r.header.unmarshal(buffbody)
var tag tag //log.Printf("Header %v", r.header)
err := binary.Read(body, binary.BigEndian, &tag) r.a = UnMarshalAttributes(buffbody)
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 {
return r.header.requestId return r.header.requestID
} }
func (r *Request) Operation() OperationId { func (r *Request) Operation() OperationID {
return r.header.operationId return r.header.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

@@ -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.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

@@ -1,115 +1,95 @@
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, 8) a := make([]byte, 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
} }
type Response struct { func (h *ippResponseHeader) unmarshal(byteStream io.Reader) {
operationAttributes []Attribute binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
jobAttributes []Attribute binary.Read(byteStream, binary.BigEndian, &h.statusCode)
printerAttributes []Attribute binary.Read(byteStream, binary.BigEndian, &h.requestID)
header ippResponseHeader
} }
func NewResponse(code statusCode, requestId uint32) *Response { type Response struct {
a *attributes
header ippResponseHeader
}
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) {
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.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

@@ -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, l) res := make([]byte, l)
p := 0 p := 0
res[p] = byte(s.vTag) res[p] = byte(s.vTag)
p += 1 p += 1

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
} }
@@ -27,7 +32,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, l) b := make([]byte, 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,13 +9,17 @@ 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)