312 lines
8.4 KiB
Go
312 lines
8.4 KiB
Go
//Package ipp provides functonality to handle ipp messages
|
|
package ipp
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// References
|
|
// https://tools.ietf.org/html/rfc8010
|
|
// https://tools.ietf.org/html/rfc8011
|
|
|
|
// Defined value tags
|
|
// from rfc8010
|
|
type tag uint8
|
|
|
|
const (
|
|
// attribute group tags
|
|
beginAttribute tag = 0x00
|
|
operationAttributes tag = 0x01
|
|
jobAttributes tag = 0x02
|
|
endOfAttributes tag = 0x03
|
|
printerAttributes tag = 0x04
|
|
unsupportedAttributes tag = 0x05
|
|
// Out of band
|
|
unsupportedValueTag tag = 0x10
|
|
unknownValueTag tag = 0x12
|
|
noValueValueTag tag = 0x13
|
|
|
|
// Integer values
|
|
integerValueTag tag = 0x21
|
|
booleanValueTag tag = 0x22
|
|
enumValueTag tag = 0x23
|
|
|
|
// octetString Tags
|
|
octetStringValueTag tag = 0x30
|
|
dateTimeValueTag tag = 0x31
|
|
resolutionValueTag tag = 0x32
|
|
rangeOfIntegerValueTag tag = 0x33
|
|
begCollectionValueTag tag = 0x34
|
|
textWithLanguageValueTag tag = 0x35
|
|
nameWithLanguageValueTag tag = 0x36
|
|
endCollectionValueTag tag = 0x37
|
|
|
|
// Character string values
|
|
textWithoutLanguageValueTag tag = 0x41
|
|
nameWithoutLanguageValueTag tag = 0x42
|
|
keyWordValueTag tag = 0x44
|
|
uriValueTag tag = 0x45
|
|
uriSchemeValueTag tag = 0x46
|
|
charsetValueTag tag = 0x47
|
|
naturalLanguageValueTag tag = 0x48
|
|
mimeMediaTypeValueTag tag = 0x49
|
|
memberAttrNameValueTag tag = 0x4a
|
|
)
|
|
|
|
// OperationID is defined in rfc8011
|
|
type OperationID uint16
|
|
|
|
const (
|
|
PrintJob OperationID = 0x0002
|
|
PrintURI OperationID = 0x0003
|
|
ValidateJob OperationID = 0x0004
|
|
CreateJob OperationID = 0x0005
|
|
SendDocument OperationID = 0x0006
|
|
SendURI OperationID = 0x0007
|
|
CancelJob OperationID = 0x0008
|
|
GetJobAttributes OperationID = 0x0009
|
|
GetJobs OperationID = 0x000a
|
|
GetPrinterAttributes OperationID = 0x000b
|
|
HoldJob OperationID = 0x000c
|
|
ReleaseJob OperationID = 0x000d
|
|
RestartJob OperationID = 0x000e
|
|
PausePrinter OperationID = 0x0010
|
|
ResumePrinter OperationID = 0x0011
|
|
PurgeJobs OperationID = 0x0012
|
|
)
|
|
|
|
type printerState int32
|
|
|
|
// printerstate defenitions
|
|
const (
|
|
Idle printerState = 3
|
|
Processing printerState = 4
|
|
Stopped printerState = 5
|
|
)
|
|
|
|
type statusCode uint16
|
|
|
|
// status code defenitions
|
|
const (
|
|
SuccessfulOk statusCode = 0x0000
|
|
ClientErrorBadRequest statusCode = 0x0400
|
|
)
|
|
|
|
type versionNumber uint16
|
|
|
|
func (v versionNumber) String() string {
|
|
vn := uint16(v)
|
|
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) {
|
|
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), string(attributeValue)
|
|
}
|
|
|
|
func marshalNameValue(name, value string, b []byte) {
|
|
p := 0
|
|
binary.BigEndian.PutUint16(b[p:p+2], uint16(len(name)))
|
|
p += 2
|
|
copy(b[p:], []byte(name))
|
|
p += len(name)
|
|
binary.BigEndian.PutUint16(b[p:p+2], uint16(len(value)))
|
|
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 (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))
|
|
}
|
|
}
|
|
}
|