go-ipp/request.go

250 lines
5.9 KiB
Go
Raw Normal View History

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
}
if err := enc.Encode(AttributeCharset, Charset); err != nil {
2019-03-10 17:43:47 +00:00
return nil, err
}
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 {
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
}
}