diff --git a/go.mod b/go.mod index 8defdbd..08d5bc7 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,9 @@ go 1.14 require ( github.com/godbus/dbus/v5 v5.0.3 github.com/holoplot/go-avahi v0.0.0-20200423113835-c8b94bb23ec8 - github.com/kr/pretty v0.1.0 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/sirupsen/logrus v1.7.0 - github.com/stretchr/testify v1.4.0 // indirect - golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.4 // indirect + github.com/stretchr/testify v1.6.1 + golang.org/x/sys v0.0.0-20201118182958-a01c418693c7 // indirect + gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect ) diff --git a/go.sum b/go.sum index 837ed59..6ce8a4b 100644 --- a/go.sum +++ b/go.sum @@ -6,26 +6,28 @@ github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/holoplot/go-avahi v0.0.0-20200423113835-c8b94bb23ec8 h1:f2Do820Rplz3WUKjQvYg8PRcHs/7nHhCoKWdt6+GFZY= github.com/holoplot/go-avahi v0.0.0-20200423113835-c8b94bb23ec8/go.mod h1:UVLO1hQkLAXpZDWt2qnPuHxGRZf8Yrh2fd2J44b+dxM= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7 h1:Z991aAXPjz0tLnj74pVXW3eWJ5lHMIBvbRfMq4M2jHA= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 73586b4..eadf1cc 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,8 @@ package main import ( "context" - "encoding/binary" + "fmt" + "ippserver/packages/ipp" "ippserver/packages/mdnsserver" "net/http" @@ -10,9 +11,13 @@ import ( ) func main() { + customFormatter := new(log.TextFormatter) + customFormatter.TimestampFormat = "2006-01-02 15:04:05" + log.SetFormatter(customFormatter) + customFormatter.FullTimestamp = true ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // cancel when we are finished consuming integers + defer cancel() go mdnsserver.Run(ctx) http.HandleFunc("/ipp/print", handle) @@ -30,70 +35,18 @@ func handle(w http.ResponseWriter, r *http.Request) { http.Error(w, "Unsupported method", http.StatusMethodNotAllowed) } - var request ippRequest - request.operationAttributes = make(map[string]string) - request.requestedAttributes = make([]string, 0) - log.Info(r.Header) - log.Info(r.ContentLength) - //body := make([]byte, r.ContentLength) - //r.Body.Read(body) - binary.Read(r.Body, binary.BigEndian, &request.header.versionNumber) - binary.Read(r.Body, binary.BigEndian, &request.header.operationId) - binary.Read(r.Body, binary.BigEndian, &request.header.requestId) - var tag uint8 - binary.Read(r.Body, binary.BigEndian, &tag) - if tag == operationAttributesTag { - var length uint16 - binary.Read(r.Body, binary.BigEndian, &tag) - for tag != endOfAttributesTag { - log.Infof("Value tag %x", tag) - switch tag { - case charsetValueTag, uriValueTag, naturalLanguagageValueTag: - binary.Read(r.Body, binary.BigEndian, &length) - attributeName := make([]byte, length) - binary.Read(r.Body, binary.BigEndian, attributeName) - binary.Read(r.Body, binary.BigEndian, &length) - attributeValue := make([]byte, length) - binary.Read(r.Body, binary.BigEndian, attributeValue) - request.operationAttributes[string(attributeName)] = string(attributeValue) - log.Infof("%v %v", string(attributeName), string(attributeValue)) - binary.Read(r.Body, binary.BigEndian, &tag) - case keywordValueTag: - binary.Read(r.Body, binary.BigEndian, &length) - attributeName := make([]byte, length) - set := make([]string, 0) - binary.Read(r.Body, binary.BigEndian, &attributeName) - binary.Read(r.Body, binary.BigEndian, &length) - value := make([]byte, length) - binary.Read(r.Body, binary.BigEndian, &value) - set = append(set, string(value)) - binary.Read(r.Body, binary.BigEndian, &tag) - for tag == keywordValueTag { - var additionalValue uint16 - binary.Read(r.Body, binary.BigEndian, &additionalValue) - binary.Read(r.Body, binary.BigEndian, &length) - value = make([]byte, length) - binary.Read(r.Body, binary.BigEndian, &value) - set = append(set, string(value)) - binary.Read(r.Body, binary.BigEndian, &tag) - } - if string(attributeName) == "requested-attributes" { - request.requestedAttributes = set - } - log.Infof("%v %v", string(attributeName), set) - default: - log.Error("Unsupported tag") + // body := make([]byte, r.ContentLength) + // io.ReadFull(r.Body, body) + // log.Infof("Body %x", body) - } - } - log.Infof("Value tag %x", tag) - } else { - log.Error("unexpected tag") - // TODO Return something sensible here - - } - - log.Infof("request %+v", request) - //log.Infof("Body %v", string(body)) + request := ipp.NewRequest() + request.UnMarshal(r.Body) + fmt.Printf("%v", request) + response := ipp.NewResponse(ipp.SuccessfulOk, request.RequestId()) + a := ipp.NewCharSetValue("attributes-charset", "utf-8") + response.AddOperatonAttribute(a) + data := response.Marshal() + log.Infof("% x", data) + w.Write(data) } diff --git a/messages.go b/messages.go deleted file mode 100644 index 130dd4a..0000000 --- a/messages.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -// References -// https://tools.ietf.org/html/rfc8010 -// https://tools.ietf.org/html/rfc8011 - -// Defined atribute group tags -const ( - beginAttributeGroupTag = 0x00 - operationAttributesTag = 0x01 - jobAttributesTag = 0x02 - endOfAttributesTag = 0x03 - printerAttributesTag = 0x04 - unsupportedAttributesTag = 0x05 -) - -// Defined value tags -// from rfc8010 -const ( - // Out of band - unsupportedValueTag = 0x10 - unknownValueTag = 0x12 - noValueValueTag = 0x13 - // Integer values - integerValueTag = 0x21 - booleanIntegerTag = 0x22 - enumValueTag = 0x23 - // Character string values - textWithoutLanguagageValueTag = 0x41 - nameWithoutLanguagageValueTag = 0x42 - keywordValueTag = 0x44 - uriValueTag = 0x45 - uriSchemeValueTag = 0x46 - charsetValueTag = 0x47 - naturalLanguagageValueTag = 0x48 - mimeMediaTypeValueTag = 0x49 - memberAttrNameValueTag = 0x4a -) - -// Operation-id, defined in rfc8011 -const ( - PrintJob = 0x0002 - PrintURI = 0x0003 - ValidateJob = 0x0004 - CreateJob = 0x0005 - SendDocument = 0x0006 - SendURI = 0x0007 - CancelJob = 0x0008 - GetJobAttributes = 0x0009 - GetJobs = 0x000a - GetPrinterAttributes = 0x000b - HoldJob = 0x000c - ReleaseJob = 0x000d - RestartJob = 0x000e - PausePrinter = 0x0010 - ResumePrinter = 0x0011 - PurgeJobs = 0x0012 -) - -type ippRequestHeader struct { - versionNumber uint16 - operationId uint16 - requestId uint32 -} - -type ippRequest struct { - header ippRequestHeader - operationAttributes map[string]string - requestedAttributes []string -} diff --git a/packages/ipp/charsetAttribute.go b/packages/ipp/charsetAttribute.go new file mode 100644 index 0000000..7b0402a --- /dev/null +++ b/packages/ipp/charsetAttribute.go @@ -0,0 +1,5 @@ +package ipp + +type charsetAttribute struct { +} + diff --git a/packages/ipp/charsetvalue.go b/packages/ipp/charsetvalue.go new file mode 100644 index 0000000..2cac464 --- /dev/null +++ b/packages/ipp/charsetvalue.go @@ -0,0 +1,47 @@ +package ipp + +import ( + "io" +) + +type charSetValue struct { + name string + value string +} + +func NewCharSetValue(name string, value string) *charSetValue { + c := new(charSetValue) + c.name = name + c.value = value + return c +} + +func (c charSetValue) String() string { + return c.name + ":" + c.value +} +func (c *charSetValue) valueTag() tag { + return charsetValueTag +} + +func (c *charSetValue) unmarshal(byteStream io.Reader) { + c.name, c.value = unmarshalSingleValue(byteStream) +} + +func (c *charSetValue) marshal() []byte { + l := 5 + len(c.name) + len(c.value) + b := make([]byte, l, l) + b[0] = byte(charsetValueTag) + marshalNameValue(c.name, c.value, b[1:]) + return b +} + +func (c *charSetValue) marshalInto([]byte) int { + return 0 +} + +func (c *charSetValue) size() int { + l := 1 + 4 // The attribute tag + 2 lengths + l += len(c.name) + l += len(c.value) + return l +} diff --git a/packages/ipp/keyword.go b/packages/ipp/keyword.go new file mode 100644 index 0000000..655479a --- /dev/null +++ b/packages/ipp/keyword.go @@ -0,0 +1,45 @@ +package ipp + +import "io" + +type keyWord struct { + name string + values []string +} + +func newKeyWord() *keyWord { + k := new(keyWord) + return k +} + +func (k *keyWord) string() string { + return "a uriValue" +} +func (k *keyWord) valueTag() tag { + return keyWordValueTag +} + +func (k *keyWord) unmarshal(byteStream io.Reader) { + +} + +func (k *keyWord) marshal() []byte { + return []byte{} +} + +func (k *keyWord) addValue(v string) { + k.values = append(k.values, v) +} + +func (k *keyWord) size() int { + l := 1 + 2 // The value tag (0x44) + name-length field (2 bytes) + l += len(k.name) + l += 2 // value-length field (2 bytes) + l += len(k.values[0]) + // Add all additional values + for _, v := range k.values[1:] { + l += 1 + 4 // The value tag (0x44) + 2 length fields (2 bytes) + l += len(v) + } + return l +} diff --git a/packages/ipp/messages.go b/packages/ipp/messages.go new file mode 100644 index 0000000..da6c279 --- /dev/null +++ b/packages/ipp/messages.go @@ -0,0 +1,252 @@ +package ipp + +import ( + "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 + textWithoutLanguagageValueTag tag = 0x41 + nameWithoutLanguagageValueTag tag = 0x42 + keyWordValueTag tag = 0x44 + uriValueTag tag = 0x45 + uriSchemeValueTag tag = 0x46 + charsetValueTag tag = 0x47 + naturalLanguagageValueTag tag = 0x48 + mimeMediaTypeValueTag tag = 0x49 + memberAttrNameValueTag tag = 0x4a +) + +// Operation-id, 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 statusCode uint16 + +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) +} + +type ippRequestHeader struct { + versionNumber versionNumber + operationId operationId + requestId uint32 +} + +func (h *ippRequestHeader) marshal(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) 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 + unmarshal(io.Reader) + marshal() []byte + size() int +} + +type Request struct { + header ippRequestHeader + operationAttributes map[string]Attribute + jobAttributes map[string]Attribute + printerAttributes map[string]Attribute +} + +func NewRequest() *Request { + r := new(Request) + r.operationAttributes = make(map[string]Attribute) + r.jobAttributes = make(map[string]Attribute) + r.printerAttributes = make(map[string]Attribute) + + 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()) + } + return s +} + +// log.Info(r.Header) +// log.Info(r.ContentLength) + +//body := make([]byte, r.ContentLength) +//body.Read(body) + +func (r *Request) UnMarshal(body io.Reader) { + r.header.marshal(body) + log.Infof("Header %v", r.header) + var tag tag + err := binary.Read(body, binary.BigEndian, &tag) + if err != nil { + log.Error(err.Error()) + } + log.Infof("got tag - %v", tag) + + if tag == operationAttributes { + nextoperationattr: + for tag != endOfAttributes { + var lastKeyword *keyWord + err = binary.Read(body, binary.BigEndian, &tag) + if err != nil { + log.Error(err.Error()) + } + log.Infof("Value tag - %v", tag) + switch tag { + case endOfAttributes: + err = binary.Read(body, binary.BigEndian, &tag) + if err == io.EOF { + // No more data + return + } + if err != nil { + log.Error(err.Error()) + } + log.Infof("got tag - %v", tag) + err = binary.Read(body, binary.BigEndian, &tag) + if err != nil { + log.Error(err.Error()) + } + log.Infof("got tag - %v", tag) + + break nextoperationattr + case charsetValueTag: + c := NewCharSetValue("", "") + c.unmarshal(body) + r.operationAttributes[c.name] = c + log.Infof("%v %v", c.name, c.value) + case uriValueTag: + u := newUriValue("", "") + u.unmarshal(body) + r.operationAttributes[u.name] = u + log.Infof("%v %v", u.name, u.value) + case naturalLanguagageValueTag: + n := newNaturalLanguagage("", "") + n.unmarshal(body) + r.operationAttributes[n.name] = n + log.Infof("%v %v", n.name, n.value) + case keyWordValueTag: + k := newKeyWord() + k.unmarshal(body) + r.operationAttributes[k.name] = k + if k.name == "" { + lastKeyword.addValue(k.values[0]) + } else { + lastKeyword = k + } + default: + log.Errorf("Unsupported tag %v", tag) + + } + } + log.Infof("Value tag %v", tag) + } else { + log.Error("unexpected tag") + // TODO Return something sensible here + + } +} + +func (r *Request) RequestId() uint32 { + return r.header.requestId +} + +func unmarshalSingleValue(byteStream io.Reader) (string, string) { + var length uint16 + binary.Read(byteStream, binary.BigEndian, &length) + //log.Infof("Length %v", length) + attributeName := make([]byte, length) + if length > 0 { + binary.Read(byteStream, binary.BigEndian, attributeName) + //log.Infof("Valuename %v", string(attributeName)) + } + binary.Read(byteStream, binary.BigEndian, &length) + //log.Infof("Length %v", length) + attributeValue := make([]byte, length) + binary.Read(byteStream, binary.BigEndian, attributeValue) + //log.Infof("Value %v", string(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)) +} diff --git a/packages/ipp/naturallanguagage.go b/packages/ipp/naturallanguagage.go new file mode 100644 index 0000000..1f27a55 --- /dev/null +++ b/packages/ipp/naturallanguagage.go @@ -0,0 +1,43 @@ +package ipp + +import ( + "io" +) + +type naturalLanguagage struct { + name string + value string +} + +func newNaturalLanguagage(name, value string) *naturalLanguagage { + c := new(naturalLanguagage) + c.name = name + c.value = value + return c +} + +func (c naturalLanguagage) String() string { + return c.name + ":" + c.value +} +func (c *naturalLanguagage) valueTag() tag { + return naturalLanguagageValueTag +} + +func (c *naturalLanguagage) unmarshal(byteStream io.Reader) { + c.name, c.value = unmarshalSingleValue(byteStream) +} + +func (c *naturalLanguagage) marshal() []byte { + l := 5 + len(c.name) + len(c.value) + b := make([]byte, l, l) + b[0] = byte(naturalLanguagageValueTag) + marshalNameValue(c.name, c.value, b[1:]) + return b +} + +func (c *naturalLanguagage) size() int { + l := 1 + 4 // The attribute tag + 2 lengths + l += len(c.name) + l += len(c.value) + return l +} diff --git a/packages/ipp/operationid_string.go b/packages/ipp/operationid_string.go new file mode 100644 index 0000000..efb1b35 --- /dev/null +++ b/packages/ipp/operationid_string.go @@ -0,0 +1,50 @@ +// Code generated by "stringer -type operationId"; DO NOT EDIT. + +package ipp + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[PrintJob-2] + _ = x[PrintURI-3] + _ = x[ValidateJob-4] + _ = x[CreateJob-5] + _ = x[SendDocument-6] + _ = x[SendURI-7] + _ = x[CancelJob-8] + _ = x[GetJobAttributes-9] + _ = x[GetJobs-10] + _ = x[GetPrinterAttributes-11] + _ = x[HoldJob-12] + _ = x[ReleaseJob-13] + _ = x[RestartJob-14] + _ = x[PausePrinter-16] + _ = x[ResumePrinter-17] + _ = x[PurgeJobs-18] +} + +const ( + _operationId_name_0 = "PrintJobPrintURIValidateJobCreateJobSendDocumentSendURICancelJobGetJobAttributesGetJobsGetPrinterAttributesHoldJobReleaseJobRestartJob" + _operationId_name_1 = "PausePrinterResumePrinterPurgeJobs" +) + +var ( + _operationId_index_0 = [...]uint8{0, 8, 16, 27, 36, 48, 55, 64, 80, 87, 107, 114, 124, 134} + _operationId_index_1 = [...]uint8{0, 12, 25, 34} +) + +func (i operationId) String() string { + switch { + case 2 <= i && i <= 14: + i -= 2 + return _operationId_name_0[_operationId_index_0[i]:_operationId_index_0[i+1]] + case 16 <= i && i <= 18: + i -= 16 + return _operationId_name_1[_operationId_index_1[i]:_operationId_index_1[i+1]] + default: + return "operationId(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/packages/ipp/response.go b/packages/ipp/response.go new file mode 100644 index 0000000..e1f36e0 --- /dev/null +++ b/packages/ipp/response.go @@ -0,0 +1,91 @@ +package ipp + +import "encoding/binary" + +type ippResponseHeader struct { + versionNumber versionNumber + statusCode statusCode + requestId uint32 +} + +func (h *ippResponseHeader) marshal() []byte { + a := make([]byte, 8, 8) + binary.BigEndian.PutUint16(a[0:2], uint16(h.versionNumber)) + binary.BigEndian.PutUint16(a[2:4], uint16(h.statusCode)) + binary.BigEndian.PutUint32(a[4:8], h.requestId) + + return a +} + +type Response struct { + header ippResponseHeader + operationAttributes []Attribute + jobAttributes []Attribute + printerAttributes []Attribute +} + +func NewResponse(code statusCode, requestId uint32) *Response { + r := new(Response) + 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) Marshal() []byte { + a := make([]byte, 0, 20) + a = append(a, r.header.marshal()...) + if len(r.operationAttributes) > 0 { + a = append(a, byte(operationAttributes)) + for _, e := range r.operationAttributes { + a = append(a, e.marshal()...) + } + } + if len(r.jobAttributes) > 0 { + a = append(a, byte(jobAttributes)) + for _, e := range r.jobAttributes { + a = append(a, e.marshal()...) + } + } + if len(r.printerAttributes) > 0 { + a = append(a, byte(printerAttributes)) + for _, e := range r.printerAttributes { + a = append(a, e.marshal()...) + } + + } + a = append(a, byte(endOfAttributes)) + return a +} + +func (r *Response) AddPrinterAttribute(a Attribute) { + r.printerAttributes = append(r.printerAttributes, a) +} + +func (r *Response) AddOperatonAttribute(a Attribute) { + r.operationAttributes = append(r.operationAttributes, a) +} + +func (r *Response) AddJobAttribute(a Attribute) { + r.jobAttributes = append(r.jobAttributes, a) +} diff --git a/packages/ipp/response_test.go b/packages/ipp/response_test.go new file mode 100644 index 0000000..45b7922 --- /dev/null +++ b/packages/ipp/response_test.go @@ -0,0 +1,29 @@ +package ipp + +import ( + "fmt" + "testing" +) + +func TestMarshalResponseHeader(T *testing.T) { + var h ippResponseHeader + + h.versionNumber = 0x0101 + h.statusCode = SuccessfulOk + h.requestId = 0xdeadbeef + + b := h.marshal() + fmt.Printf("% x\n", b) + +} + +func TestMarshalCompleteResponse(T *testing.T) { + r := NewResponse(SuccessfulOk, 0xdeadbeef) + + a := NewCharSetValue("attributes-charset", "UTF-8") + r.AddPrinterAttribute(a) + + b := r.Marshal() + fmt.Printf("% x\n", b) + +} diff --git a/packages/ipp/tag_string.go b/packages/ipp/tag_string.go new file mode 100644 index 0000000..8101cc3 --- /dev/null +++ b/packages/ipp/tag_string.go @@ -0,0 +1,85 @@ +// Code generated by "stringer -type tag"; DO NOT EDIT. + +package ipp + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[beginAttribute-0] + _ = x[operationAttributes-1] + _ = x[jobAttributes-2] + _ = x[endOfAttributes-3] + _ = x[printerAttributes-4] + _ = x[unsupportedAttributes-5] + _ = x[unsupportedValueTag-16] + _ = x[unknownValueTag-18] + _ = x[noValueValueTag-19] + _ = x[integerValueTag-33] + _ = x[booleanValueTag-34] + _ = x[enumValueTag-35] + _ = x[octetStringValueTag-48] + _ = x[dateTimeValueTag-49] + _ = x[resolutionValueTag-50] + _ = x[rangeOfIntegerValueTag-51] + _ = x[begCollectionValueTag-52] + _ = x[textWithLanguageValueTag-53] + _ = x[nameWithLanguageValueTag-54] + _ = x[endCollectionValueTag-55] + _ = x[textWithoutLanguagageValueTag-65] + _ = x[nameWithoutLanguagageValueTag-66] + _ = x[keyWordValueTag-68] + _ = x[uriValueTag-69] + _ = x[uriSchemeValueTag-70] + _ = x[charsetValueTag-71] + _ = x[naturalLanguagageValueTag-72] + _ = x[mimeMediaTypeValueTag-73] + _ = x[memberAttrNameValueTag-74] +} + +const ( + _tag_name_0 = "beginAttributeoperationAttributesjobAttributesendOfAttributesprinterAttributesunsupportedAttributes" + _tag_name_1 = "unsupportedValueTag" + _tag_name_2 = "unknownValueTagnoValueValueTag" + _tag_name_3 = "integerValueTagbooleanValueTagenumValueTag" + _tag_name_4 = "octetStringValueTagdateTimeValueTagresolutionValueTagrangeOfIntegerValueTagbegCollectionValueTagtextWithLanguageValueTagnameWithLanguageValueTagendCollectionValueTag" + _tag_name_5 = "textWithoutLanguagageValueTagnameWithoutLanguagageValueTag" + _tag_name_6 = "keywordValueTaguriValueTaguriSchemeValueTagcharsetValueTagnaturalLanguagageValueTagmimeMediaTypeValueTagmemberAttrNameValueTag" +) + +var ( + _tag_index_0 = [...]uint8{0, 14, 33, 46, 61, 78, 99} + _tag_index_2 = [...]uint8{0, 15, 30} + _tag_index_3 = [...]uint8{0, 15, 30, 42} + _tag_index_4 = [...]uint8{0, 19, 35, 53, 75, 96, 120, 144, 165} + _tag_index_5 = [...]uint8{0, 29, 58} + _tag_index_6 = [...]uint8{0, 15, 26, 43, 58, 83, 104, 126} +) + +func (i tag) String() string { + switch { + case i <= 5: + return _tag_name_0[_tag_index_0[i]:_tag_index_0[i+1]] + case i == 16: + return _tag_name_1 + case 18 <= i && i <= 19: + i -= 18 + return _tag_name_2[_tag_index_2[i]:_tag_index_2[i+1]] + case 33 <= i && i <= 35: + i -= 33 + return _tag_name_3[_tag_index_3[i]:_tag_index_3[i+1]] + case 48 <= i && i <= 55: + i -= 48 + return _tag_name_4[_tag_index_4[i]:_tag_index_4[i+1]] + case 65 <= i && i <= 66: + i -= 65 + return _tag_name_5[_tag_index_5[i]:_tag_index_5[i+1]] + case 68 <= i && i <= 74: + i -= 68 + return _tag_name_6[_tag_index_6[i]:_tag_index_6[i+1]] + default: + return "tag(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/packages/ipp/urivalue.go b/packages/ipp/urivalue.go new file mode 100644 index 0000000..b570c56 --- /dev/null +++ b/packages/ipp/urivalue.go @@ -0,0 +1,42 @@ +package ipp + +import ( + "io" +) + +type uriValue struct { + name string + value string +} + +func newUriValue(name, value string) *uriValue { + u := new(uriValue) + u.name = name + u.value = value + return u +} + +func (u uriValue) String() string { + return u.name + ":" + u.value +} + +func (u *uriValue) valueTag() tag { + return uriValueTag +} + +func (u *uriValue) unmarshal(byteStream io.Reader) { + u.name, u.value = unmarshalSingleValue(byteStream) +} + +func (u *uriValue) marshal() []byte { + res := make([]byte, u.size()) + res[0] = byte(uriValueTag) + marshalNameValue(u.name, u.value, res[1:]) + return res +} +func (u *uriValue) size() int { + l := 1 + 4 // The attribute tag + 2 lengths + l += len(u.name) + l += len(u.value) + return l +} diff --git a/packages/ipp/urivalue_test.go b/packages/ipp/urivalue_test.go new file mode 100644 index 0000000..c7f9ad3 --- /dev/null +++ b/packages/ipp/urivalue_test.go @@ -0,0 +1,17 @@ +package ipp + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMarshalUriValue(T *testing.T) { + var u uriValue + u.name = "foo" + u.value = "bar" + b := u.marshal() + assert.Len(T, b, 11) + fmt.Printf("% x\n", b) +} diff --git a/tools/README.txt b/tools/README.txt index 52fa0b5..f93b466 100644 --- a/tools/README.txt +++ b/tools/README.txt @@ -1,5 +1,7 @@ ../work/ippsample/tools/ipptool -v -f 1-untitled.ras ipp://brn30055cb5e3ae:631/ipp/print printjob.ipp +ipptool -t -v ipp://brn30055cb5e3ae:631/ipp/print get-printer-attributes.test + ippserver -2 -M FUBAR -d . -vv -k -f image/pwg-raster testprinter henrik@drpork:~/ipp$ cat printjob.ipp