added more code comments for docs

This commit is contained in:
Fabian 2020-03-28 00:27:56 +01:00
parent a88a2fa1e4
commit edb3dbf35d
6 changed files with 71 additions and 41 deletions

View File

@ -11,18 +11,17 @@ const (
sizeBoolean = int16(1)
)
// ipp attribute encoder
// encodes attribute to a io.Writer
// AttributeEncoder encodes attribute to a io.Writer
type AttributeEncoder struct {
writer io.Writer
}
// create a new attribute encoder from a writer
// NewAttributeEncoder returns a new encoder that writes to w
func NewAttributeEncoder(w io.Writer) *AttributeEncoder {
return &AttributeEncoder{w}
}
// encodes a attribute and its value to a io.Writer
// Encode encodes a attribute and its value to a io.Writer
// the tag is determined by the AttributeTagMapping map
func (e *AttributeEncoder) Encode(attribute string, value interface{}) error {
tag, ok := AttributeTagMapping[attribute]
@ -343,27 +342,31 @@ func (e *AttributeEncoder) writeNullByte() error {
return binary.Write(e.writer, binary.BigEndian, int16(0))
}
// representation of a ipp attribute
// a attribute contains a tag, witch identifies the type, the name of the attribute a the value
// Attribute defines an ipp attribute
type Attribute struct {
Tag int8
Name string
Value interface{}
}
// ipp attribute decoder
// reads from a io.Reader an decode the data into an attribute struct
// Resolution defines the resolution attribute
type Resolution struct {
Height int
Width int
Depth int8
}
// AttributeDecoder reads and decodes ipp from an input stream
type AttributeDecoder struct {
reader io.Reader
}
// create a new attribute decoder from a reader
// NewAttributeDecoder returns a new decoder that reads from r
func NewAttributeDecoder(r io.Reader) *AttributeDecoder {
return &AttributeDecoder{r}
}
// reads from a io.Reader and decode the attribute
// the type is identified by a tag passed as an argument
// Decode reads the next ipp attribute into a attribute struct. the type is identified by a tag passed as an argument
func (d *AttributeDecoder) Decode(tag int8) (*Attribute, error) {
attr := Attribute{Tag: tag}
@ -498,13 +501,6 @@ func (d *AttributeDecoder) decodeRange() ([]int, error) {
return r, nil
}
// represents the data of the resolution attribute
type Resolution struct {
Height int
Width int
Depth int8
}
func (d *AttributeDecoder) decodeResolution() (res Resolution, err error) {
_, err = d.readValueLength()
if err != nil {

View File

@ -1,5 +1,6 @@
package ipp
// ipp status codes
const (
StatusCupsInvalid int16 = -1
StatusOk int16 = 0x0000
@ -66,6 +67,7 @@ const (
StatusErrorCupsUpgradeRequired int16 = 0x1002
)
// ipp operations
const (
OperationCupsInvalid int16 = -0x0001
OperationCupsNone int16 = 0x0000
@ -186,6 +188,7 @@ const (
OperationCupsCreateLocalPrinter int16 = 0x4028
)
// ipp tags
const (
TagCupsInvalid int8 = -1
TagZero int8 = 0x00
@ -230,6 +233,7 @@ const (
TagExtension int8 = 0x7f
)
// job states
const (
JobStatePending int8 = 0x03
JobStateHeld int8 = 0x04
@ -240,6 +244,7 @@ const (
JobStateCompleted int8 = 0x09
)
// document states
const (
DocumentStatePending int8 = 0x03
DocumentStateProcessing int8 = 0x05
@ -248,18 +253,21 @@ const (
DocumentStateCompleted int8 = 0x08
)
// printer states
const (
PrinterStateIdle int8 = 0x0003
PrinterStateProcessing int8 = 0x0004
PrinterStateStopped int8 = 0x0005
)
// job state filter
const (
JobStateFilterNotCompleted = "not-completed"
JobStateFilterCompleted = "completed"
JobStateFilterAll = "all"
)
// error policies
const (
ErrorPolicyRetryJob = "retry-job"
ErrorPolicyAbortJob = "abort-job"
@ -267,6 +275,7 @@ const (
ErrorPolicyStopPrinter = "stop-printer"
)
// ipp defaults
const (
CharsetLanguage = "en-US"
Charset = "utf-8"
@ -274,13 +283,20 @@ const (
ProtocolVersionMinor = int8(0)
DefaultJobPriority = 50
)
// useful mime types for ipp
const (
MimeTypePostscript = "application/postscript"
MimeTypeOctetStream = "application/octet-stream"
)
// ipp content types
const (
ContentTypeIPP = "application/ipp"
)
// known ipp attributes
const (
AttributeCopies = "copies"
AttributeDocumentFormat = "document-format"
@ -337,6 +353,7 @@ const (
AttributeJobOriginatingUserName = "job-originating-user-name"
)
// Default attributes
var (
DefaultClassAttributes = []string{AttributePrinterName, AttributeMemberNames}
DefaultPrinterAttributes = []string{AttributePrinterName, AttributePrinterType, AttributePrinterLocation, AttributePrinterInfo,
@ -345,7 +362,10 @@ var (
DefaultJobAttributes = []string{AttributeJobID, AttributeJobName, AttributePrinterURI, AttributeJobState, AttributeJobStateReason,
AttributeJobHoldUntil, AttributeJobMediaProgress, AttributeJobKilobyteOctets, AttributeNumberOfDocuments, AttributeCopies,
AttributeJobOriginatingUserName}
)
// Attribute to tag mapping
var (
AttributeTagMapping = map[string]int8{
AttributeCharset: TagCharset,
AttributeNaturalLanguage: TagLanguage,

View File

@ -2,7 +2,7 @@ package ipp
import "fmt"
// check a given error whether a printer or class does not exist
// IsNotExistsError checks a given error whether a printer or class does not exist
func IsNotExistsError(err error) bool {
if err == nil {
return false
@ -11,7 +11,7 @@ func IsNotExistsError(err error) bool {
return err.Error() == "The printer or class does not exist."
}
//non ok ipp status codes
// IPPError used for non ok ipp status codes
type IPPError struct {
Status int16
Message string
@ -21,7 +21,7 @@ func (e IPPError) Error() string {
return fmt.Sprintf("ipp status: %d, message: %s", e.Status, e.Message)
}
// non 200 http codes
// HTTPError used for non 200 http codes
type HTTPError struct {
Code int
}

View File

@ -13,6 +13,7 @@ import (
"strconv"
)
// Document wraps an io.Reader with more information, needed for encoding
type Document struct {
Document io.Reader
Size int
@ -20,6 +21,7 @@ type Document struct {
MimeType string
}
// IPPClient implements a generic ipp client
type IPPClient struct {
host string
port int
@ -30,6 +32,7 @@ type IPPClient struct {
client *http.Client
}
// NewIPPClient creates a new generic ipp client
func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient {
httpClient := http.Client{
Transport: &http.Transport{
@ -73,6 +76,7 @@ func (c *IPPClient) getClassUri(printer string) string {
return fmt.Sprintf("ipp://localhost/classes/%s", printer)
}
// SendRequest sends a request to a remote uri end returns the response
func (c *IPPClient) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) {
payload, err := req.Encode()
if err != nil {
@ -123,7 +127,7 @@ func (c *IPPClient) SendRequest(url string, req *Request, additionalResponseData
return resp, err
}
// Print one or more `Document`s using IPP `Create-Job` followed by `Send-Document` request(s).
// PrintDocuments prints one or more documents using a Create-Job operation followed by one or more Send-Document operation(s). custom job settings can be specified via the jobAttributes parameter
func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttributes map[string]interface{}) (int, error) {
printerURI := c.getPrinterUri(printer)
@ -173,23 +177,7 @@ func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttribute
return jobID, nil
}
// Print a `Document` using an IPP `Print-Job` request.
//
// `jobAttributes` can contain arbitrary key/value pairs to control the way in which the
// document is printed. [RFC 2911 § 4.2](https://tools.ietf.org/html/rfc2911#section-4.2)
// defines some useful attributes:
//
// * [`job-priority`](https://tools.ietf.org/html/rfc2911#section-4.2.1): an integer between 1-100
// * [`copies`](https://tools.ietf.org/html/rfc2911#section-4.2.5): a positive integer
// * [`finishings`](https://tools.ietf.org/html/rfc2911#section-4.2.6): an enumeration
// * [`number-up`](https://tools.ietf.org/html/rfc2911#section-4.2.9): a positive integer
// * [`orientation-requested`](https://tools.ietf.org/html/rfc2911#section-4.2.10): an enumeration
// * [`media`](https://tools.ietf.org/html/rfc2911#section-4.2.11): a string
// * [`printer-resolution`](https://tools.ietf.org/html/rfc2911#section-4.2.12): a `Resolution`
// * [`print-quality`](https://tools.ietf.org/html/rfc2911#section-4.2.13): an enumeration
//
// Your print system may provide other attributes. Define custom attributes as needed in
// `AttributeTagMapping` and provide values here.
// PrintJob prints a document using a Print-Job operation. custom job settings can be specified via the jobAttributes parameter
func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[string]interface{}) (int, error) {
printerURI := c.getPrinterUri(printer)
@ -224,6 +212,7 @@ func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[str
return jobID, nil
}
// PrintFile prints a local file on the file system. custom job settings can be specified via the jobAttributes parameter
func (c *IPPClient) PrintFile(filePath, printer string, jobAttributes map[string]interface{}) (int, error) {
fileStats, err := os.Stat(filePath)
if os.IsNotExist(err) {
@ -250,6 +239,7 @@ func (c *IPPClient) PrintFile(filePath, printer string, jobAttributes map[string
}, printer, jobAttributes)
}
// GetPrinterAttributes returns the requested attributes for the specified printer, if attributes is nil the default attributes will be used
func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (Attributes, error) {
req := NewRequest(OperationGetPrinterAttributes, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
@ -273,6 +263,7 @@ func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (A
return resp.PrinterAttributes[0], nil
}
// ResumePrinter resumes a printer
func (c *IPPClient) ResumePrinter(printer string) error {
req := NewRequest(OperationResumePrinter, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
@ -281,6 +272,7 @@ func (c *IPPClient) ResumePrinter(printer string) error {
return err
}
// PausePrinter pauses a printer
func (c *IPPClient) PausePrinter(printer string) error {
req := NewRequest(OperationPausePrinter, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
@ -289,6 +281,7 @@ func (c *IPPClient) PausePrinter(printer string) error {
return err
}
// GetJobAttributes returns the requested attributes for the specified job, if attributes is nil the default job will be used
func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes, error) {
req := NewRequest(OperationGetJobAttributes, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
@ -311,6 +304,7 @@ func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes
return resp.PrinterAttributes[0], nil
}
// GetJobs returns jobs from a printer or class
func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]Attributes, error) {
req := NewRequest(OperationGetJobs, 1)
req.OperationAttributes[AttributeWhichJobs] = string(whichJobs)
@ -356,6 +350,7 @@ func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool
return jobIDMap, nil
}
// CancelJob cancels a job. if purge is true, the job will also be removed
func (c *IPPClient) CancelJob(jobID int, purge bool) error {
req := NewRequest(OperationCancelJob, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
@ -365,6 +360,7 @@ func (c *IPPClient) CancelJob(jobID int, purge bool) error {
return err
}
// CancelJob cancels all jobs for a specified printer. if purge is true, the jobs will also be removed
func (c *IPPClient) CancelAllJob(printer string, purge bool) error {
req := NewRequest(OperationCancelJobs, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
@ -374,6 +370,7 @@ func (c *IPPClient) CancelAllJob(printer string, purge bool) error {
return err
}
// RestartJob restarts a job
func (c *IPPClient) RestartJob(jobID int) error {
req := NewRequest(OperationRestartJob, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
@ -382,6 +379,7 @@ func (c *IPPClient) RestartJob(jobID int) error {
return err
}
// RestartJob holds a job
func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
req := NewRequest(OperationRestartJob, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
@ -391,6 +389,7 @@ func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
return err
}
// PrintTestPage prints a test page of type application/vnd.cups-pdf-banner
func (c *IPPClient) PrintTestPage(printer string) (int, error) {
testPage := new(bytes.Buffer)
testPage.WriteString("#PDF-BANNER\n")
@ -411,6 +410,7 @@ func (c *IPPClient) PrintTestPage(printer string) (int, error) {
})
}
// TestConnection tests if a tcp connection to the remote server is possible
func (c *IPPClient) TestConnection() error {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", c.host, c.port))
if err != nil {

View File

@ -6,6 +6,7 @@ import (
"io"
)
// Request defines a ipp request
type Request struct {
ProtocolVersionMajor int8
ProtocolVersionMinor int8
@ -21,6 +22,7 @@ type Request struct {
FileSize int
}
// NewRequest creates a new ipp request
func NewRequest(op int16, reqID int32) *Request {
return &Request{
ProtocolVersionMajor: ProtocolVersionMajor,
@ -35,6 +37,7 @@ func NewRequest(op int16, reqID int32) *Request {
}
}
// Encode encodes the request to a byte slice
func (r *Request) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
enc := NewAttributeEncoder(buf)
@ -104,16 +107,19 @@ func (r *Request) Encode() ([]byte, error) {
return buf.Bytes(), nil
}
// RequestDecoder reads and decodes a request from a stream
type RequestDecoder struct {
reader io.Reader
}
// NewRequestDecoder returns a new decoder that reads from r
func NewRequestDecoder(r io.Reader) *RequestDecoder {
return &RequestDecoder{
reader: r,
}
}
// Decode decodes a ipp request into a request struct. additional data will be written to an io.Writer if data is not nil
func (d *RequestDecoder) Decode(data io.Writer) (*Request, error) {
req := new(Request)

View File

@ -7,8 +7,10 @@ import (
"io"
)
// Attributes is a wrapper for a set of attributes
type Attributes map[string][]Attribute
// Response defines a ipp response
type Response struct {
ProtocolVersionMajor int8
ProtocolVersionMinor int8
@ -21,6 +23,7 @@ type Response struct {
JobAttributes []Attributes
}
// CheckForErrors checks the status code and returns a error if it is not zero. it also returns the status message if provided by the server
func (r *Response) CheckForErrors() error {
if r.StatusCode != StatusOk {
err := IPPError{
@ -38,6 +41,7 @@ func (r *Response) CheckForErrors() error {
return nil
}
// NewRequest creates a new ipp response
func NewResponse(statusCode int16, reqID int32) *Response {
return &Response{
ProtocolVersionMajor: ProtocolVersionMajor,
@ -50,7 +54,8 @@ func NewResponse(statusCode int16, reqID int32) *Response {
}
}
func (r *Response) Encode(data io.Writer) ([]byte, error) {
// Encode encodes the response to a byte slice
func (r *Response) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
enc := NewAttributeEncoder(buf)
@ -163,23 +168,26 @@ func (r *Response) Encode(data io.Writer) ([]byte, error) {
}
}
if err := binary.Write(buf, binary.BigEndian, int8(TagEnd)); err != nil {
if err := binary.Write(buf, binary.BigEndian, TagEnd); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// ResponseDecoder reads and decodes a response from a stream
type ResponseDecoder struct {
reader io.Reader
}
// NewResponseDecoder returns a new decoder that reads from r
func NewResponseDecoder(r io.Reader) *ResponseDecoder {
return &ResponseDecoder{
reader: r,
}
}
// Decode decodes a ipp response into a response struct. additional data will be written to an io.Writer if data is not nil
func (d *ResponseDecoder) Decode(data io.Writer) (*Response, error) {
/*
1 byte: Protocol Major Version - b