added more code comments for docs
This commit is contained in:
parent
a88a2fa1e4
commit
edb3dbf35d
32
attribute.go
32
attribute.go
|
@ -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 {
|
||||
|
|
20
constants.go
20
constants.go
|
@ -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,
|
||||
|
|
6
error.go
6
error.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
12
response.go
12
response.go
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue