2022-11-17 11:02:26 +00:00
|
|
|
/*
|
|
|
|
Copyright 2022 Dolysis Consulting Limited
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
|
|
|
|
For changes see the git log
|
|
|
|
*/
|
2019-03-10 17:43:47 +00:00
|
|
|
package ipp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
2019-03-13 21:54:08 +00:00
|
|
|
"io"
|
2019-03-10 17:43:47 +00:00
|
|
|
)
|
|
|
|
|
2020-03-27 23:27:56 +00:00
|
|
|
// Request defines a ipp request
|
2019-03-10 17:43:47 +00:00
|
|
|
type Request struct {
|
2020-03-09 11:58:54 +00:00
|
|
|
ProtocolVersionMajor int8
|
|
|
|
ProtocolVersionMinor int8
|
2020-03-05 15:29:09 +00:00
|
|
|
|
2020-03-09 11:21:57 +00:00
|
|
|
Operation int16
|
2020-03-05 15:29:09 +00:00
|
|
|
RequestId int32
|
2019-03-10 17:43:47 +00:00
|
|
|
|
|
|
|
OperationAttributes map[string]interface{}
|
|
|
|
JobAttributes map[string]interface{}
|
|
|
|
PrinterAttributes map[string]interface{}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
File io.Reader
|
|
|
|
FileSize int
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
|
2020-03-27 23:27:56 +00:00
|
|
|
// NewRequest creates a new ipp request
|
2020-03-09 11:21:57 +00:00
|
|
|
func NewRequest(op int16, reqID int32) *Request {
|
2019-03-10 17:43:47 +00:00
|
|
|
return &Request{
|
2020-03-05 15:29:09 +00:00
|
|
|
ProtocolVersionMajor: ProtocolVersionMajor,
|
|
|
|
ProtocolVersionMinor: ProtocolVersionMinor,
|
|
|
|
Operation: op,
|
|
|
|
RequestId: reqID,
|
|
|
|
OperationAttributes: make(map[string]interface{}),
|
|
|
|
JobAttributes: make(map[string]interface{}),
|
|
|
|
PrinterAttributes: make(map[string]interface{}),
|
|
|
|
File: nil,
|
|
|
|
FileSize: -1,
|
2019-03-10 17:43:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 23:27:56 +00:00
|
|
|
// Encode encodes the request to a byte slice
|
2019-03-10 17:43:47 +00:00
|
|
|
func (r *Request) Encode() ([]byte, error) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
enc := NewAttributeEncoder(buf)
|
|
|
|
|
2020-03-05 15:29:09 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMajor); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:29:09 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, r.ProtocolVersionMinor); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-09 11:21:57 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, r.Operation); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:29:09 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, r.RequestId); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, int8(TagOperation)); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-09 09:36:29 +00:00
|
|
|
if err := enc.Encode(AttributeCharset, Charset); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-09 09:36:29 +00:00
|
|
|
if err := enc.Encode(AttributeNaturalLanguage, CharsetLanguage); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.OperationAttributes) > 0 {
|
|
|
|
for attr, value := range r.OperationAttributes {
|
|
|
|
if err := enc.Encode(attr, value); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.JobAttributes) > 0 {
|
2019-03-13 21:54:08 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, int8(TagJob)); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for attr, value := range r.JobAttributes {
|
|
|
|
if err := enc.Encode(attr, value); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.PrinterAttributes) > 0 {
|
2019-03-13 21:54:08 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, int8(TagPrinter)); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for attr, value := range r.PrinterAttributes {
|
|
|
|
if err := enc.Encode(attr, value); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-13 21:54:08 +00:00
|
|
|
if err := binary.Write(buf, binary.BigEndian, int8(TagEnd)); err != nil {
|
2019-03-10 17:43:47 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
2020-03-05 15:29:09 +00:00
|
|
|
|
2020-03-27 23:27:56 +00:00
|
|
|
// RequestDecoder reads and decodes a request from a stream
|
2020-03-05 15:29:09 +00:00
|
|
|
type RequestDecoder struct {
|
|
|
|
reader io.Reader
|
|
|
|
}
|
|
|
|
|
2020-03-27 23:27:56 +00:00
|
|
|
// NewRequestDecoder returns a new decoder that reads from r
|
2020-03-05 15:29:09 +00:00
|
|
|
func NewRequestDecoder(r io.Reader) *RequestDecoder {
|
|
|
|
return &RequestDecoder{
|
|
|
|
reader: r,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 23:27:56 +00:00
|
|
|
// Decode decodes a ipp request into a request struct. additional data will be written to an io.Writer if data is not nil
|
2020-03-05 15:29:09 +00:00
|
|
|
func (d *RequestDecoder) Decode(data io.Writer) (*Request, error) {
|
|
|
|
req := new(Request)
|
|
|
|
|
2020-03-06 11:22:56 +00:00
|
|
|
if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMajor); err != nil {
|
2020-03-05 15:29:09 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-06 11:22:56 +00:00
|
|
|
if err := binary.Read(d.reader, binary.BigEndian, &req.ProtocolVersionMinor); err != nil {
|
2020-03-05 15:29:09 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-06 11:22:56 +00:00
|
|
|
if err := binary.Read(d.reader, binary.BigEndian, &req.Operation); err != nil {
|
2020-03-05 15:29:09 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-06 11:22:56 +00:00
|
|
|
if err := binary.Read(d.reader, binary.BigEndian, &req.RequestId); err != nil {
|
2020-03-05 15:29:09 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
startByteSlice := make([]byte, 1)
|
|
|
|
|
|
|
|
tag := TagCupsInvalid
|
|
|
|
previousAttributeName := ""
|
|
|
|
tagSet := false
|
|
|
|
|
|
|
|
attribDecoder := NewAttributeDecoder(d.reader)
|
|
|
|
|
|
|
|
// decode attribute buffer
|
|
|
|
for {
|
|
|
|
if _, err := d.reader.Read(startByteSlice); err != nil {
|
2020-03-06 13:47:37 +00:00
|
|
|
// when we read from a stream, we may get an EOF if we want to read the end tag
|
|
|
|
// all data should be read and we can ignore the error
|
|
|
|
if err == io.EOF {
|
2020-03-27 14:59:54 +00:00
|
|
|
break
|
2020-03-06 13:47:37 +00:00
|
|
|
}
|
2020-03-05 15:29:09 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-09 11:47:23 +00:00
|
|
|
startByte := int8(startByteSlice[0])
|
2020-03-05 15:29:09 +00:00
|
|
|
|
|
|
|
// check if attributes are completed
|
|
|
|
if startByte == TagEnd {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if startByte == TagOperation {
|
2020-03-06 11:22:56 +00:00
|
|
|
if req.OperationAttributes == nil {
|
|
|
|
req.OperationAttributes = make(map[string]interface{})
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:29:09 +00:00
|
|
|
tag = TagOperation
|
|
|
|
tagSet = true
|
2020-03-06 11:22:56 +00:00
|
|
|
|
2020-03-05 15:29:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if startByte == TagJob {
|
2020-03-06 11:22:56 +00:00
|
|
|
if req.JobAttributes == nil {
|
|
|
|
req.JobAttributes = make(map[string]interface{})
|
|
|
|
}
|
2020-03-05 15:29:09 +00:00
|
|
|
tag = TagJob
|
|
|
|
tagSet = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if startByte == TagPrinter {
|
2020-03-06 11:22:56 +00:00
|
|
|
if req.PrinterAttributes == nil {
|
|
|
|
req.PrinterAttributes = make(map[string]interface{})
|
|
|
|
}
|
2020-03-05 15:29:09 +00:00
|
|
|
tag = TagPrinter
|
|
|
|
tagSet = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if tagSet {
|
|
|
|
if _, err := d.reader.Read(startByteSlice); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-09 11:47:23 +00:00
|
|
|
startByte = int8(startByteSlice[0])
|
2020-03-05 15:29:09 +00:00
|
|
|
}
|
|
|
|
|
2020-03-09 11:21:57 +00:00
|
|
|
attrib, err := attribDecoder.Decode(startByte)
|
2020-03-05 15:29:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if attrib.Name != "" {
|
|
|
|
appendAttributeToRequest(req, tag, attrib.Name, attrib.Value)
|
|
|
|
previousAttributeName = attrib.Name
|
|
|
|
} else {
|
|
|
|
appendAttributeToRequest(req, tag, previousAttributeName, attrib.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
tagSet = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if data != nil {
|
|
|
|
if _, err := io.Copy(data, d.reader); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2020-03-09 11:47:23 +00:00
|
|
|
func appendAttributeToRequest(req *Request, tag int8, name string, value interface{}) {
|
2020-03-05 15:29:09 +00:00
|
|
|
switch tag {
|
|
|
|
case TagOperation:
|
|
|
|
req.OperationAttributes[name] = value
|
|
|
|
case TagPrinter:
|
|
|
|
req.PrinterAttributes[name] = value
|
|
|
|
case TagJob:
|
|
|
|
req.JobAttributes[name] = value
|
|
|
|
}
|
|
|
|
}
|