345 lines
11 KiB
Go
345 lines
11 KiB
Go
/*
|
|
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
|
|
*/
|
|
package ipp
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
)
|
|
|
|
// Document wraps an io.Reader with more information, needed for encoding
|
|
type Document struct {
|
|
Document io.Reader
|
|
Size int
|
|
Name string
|
|
MimeType string
|
|
}
|
|
|
|
// IPPClient implements a generic ipp client
|
|
type IPPClient struct {
|
|
username string
|
|
adapter Adapter
|
|
}
|
|
|
|
// NewIPPClient creates a new generic ipp client (used HttpAdapter internally)
|
|
func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient {
|
|
adapter := NewHttpAdapter(host, port, username, password, useTLS)
|
|
|
|
return &IPPClient{
|
|
username: username,
|
|
adapter: adapter,
|
|
}
|
|
}
|
|
|
|
// NewIPPClientWithAdapter creates a new generic ipp client with given Adapter
|
|
func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient {
|
|
return &IPPClient{
|
|
username: username,
|
|
adapter: adapter,
|
|
}
|
|
}
|
|
|
|
func (c *IPPClient) getPrinterUri(printer string) string {
|
|
return fmt.Sprintf("ipp://localhost/printers/%s", printer)
|
|
}
|
|
|
|
func (c *IPPClient) getJobUri(jobID int) string {
|
|
return fmt.Sprintf("ipp://localhost/jobs/%d", jobID)
|
|
}
|
|
|
|
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) {
|
|
if _, ok := req.OperationAttributes[AttributeRequestingUserName]; ok == false {
|
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
|
}
|
|
|
|
return c.adapter.SendRequest(url, req, additionalResponseData)
|
|
}
|
|
|
|
// 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)
|
|
|
|
req := NewRequest(OperationCreateJob, 1)
|
|
req.OperationAttributes[AttributePrinterURI] = printerURI
|
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
|
|
|
// set defaults for some attributes, may get overwritten
|
|
req.OperationAttributes[AttributeJobName] = docs[0].Name
|
|
req.OperationAttributes[AttributeCopies] = 1
|
|
req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority
|
|
|
|
for key, value := range jobAttributes {
|
|
req.JobAttributes[key] = value
|
|
}
|
|
|
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
if len(resp.JobAttributes) == 0 {
|
|
return 0, errors.New("server doesn't returned a job id")
|
|
}
|
|
|
|
jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int)
|
|
|
|
documentCount := len(docs) - 1
|
|
|
|
for docID, doc := range docs {
|
|
req = NewRequest(OperationSendDocument, 2)
|
|
req.OperationAttributes[AttributePrinterURI] = printerURI
|
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
|
req.OperationAttributes[AttributeJobID] = jobID
|
|
req.OperationAttributes[AttributeDocumentName] = doc.Name
|
|
req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType
|
|
req.OperationAttributes[AttributeLastDocument] = docID == documentCount
|
|
req.File = doc.Document
|
|
req.FileSize = doc.Size
|
|
|
|
_, err = c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
}
|
|
|
|
return jobID, nil
|
|
}
|
|
|
|
// 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)
|
|
|
|
req := NewRequest(OperationPrintJob, 1)
|
|
req.OperationAttributes[AttributePrinterURI] = printerURI
|
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
|
req.OperationAttributes[AttributeJobName] = doc.Name
|
|
req.OperationAttributes[AttributeDocumentFormat] = doc.MimeType
|
|
|
|
// set defaults for some attributes, may get overwritten
|
|
req.OperationAttributes[AttributeCopies] = 1
|
|
req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority
|
|
|
|
for key, value := range jobAttributes {
|
|
req.JobAttributes[key] = value
|
|
}
|
|
|
|
req.File = doc.Document
|
|
req.FileSize = doc.Size
|
|
|
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
if len(resp.JobAttributes) == 0 {
|
|
return 0, errors.New("server doesn't returned a job id")
|
|
}
|
|
|
|
jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int)
|
|
|
|
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) {
|
|
return -1, err
|
|
}
|
|
|
|
fileName := path.Base(filePath)
|
|
|
|
document, err := os.Open(filePath)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer document.Close()
|
|
|
|
jobAttributes[AttributeJobName] = fileName
|
|
|
|
return c.PrintDocuments([]Document{
|
|
{
|
|
Document: document,
|
|
Name: fileName,
|
|
Size: int(fileStats.Size()),
|
|
MimeType: MimeTypeOctetStream,
|
|
},
|
|
}, 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)
|
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
|
|
|
if attributes == nil {
|
|
req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes
|
|
} else {
|
|
req.OperationAttributes[AttributeRequestedAttributes] = attributes
|
|
}
|
|
|
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(resp.PrinterAttributes) == 0 {
|
|
return nil, errors.New("server doesn't return any printer attributes")
|
|
}
|
|
|
|
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)
|
|
|
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
|
return err
|
|
}
|
|
|
|
// PausePrinter pauses a printer
|
|
func (c *IPPClient) PausePrinter(printer string) error {
|
|
req := NewRequest(OperationPausePrinter, 1)
|
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
|
|
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
|
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)
|
|
|
|
if attributes == nil {
|
|
req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes
|
|
} else {
|
|
req.OperationAttributes[AttributeRequestedAttributes] = attributes
|
|
}
|
|
|
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("jobs", jobID), req, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(resp.JobAttributes) == 0 {
|
|
return nil, errors.New("server doesn't return any job attributes")
|
|
}
|
|
|
|
return resp.JobAttributes[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)
|
|
req.OperationAttributes[AttributeMyJobs] = myJobs
|
|
|
|
if printer != "" {
|
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
|
} else if class != "" {
|
|
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(printer)
|
|
} else {
|
|
req.OperationAttributes[AttributePrinterURI] = "ipp://localhost/"
|
|
}
|
|
|
|
if firstJobId > 0 {
|
|
req.OperationAttributes[AttributeFirstJobID] = firstJobId
|
|
}
|
|
|
|
if limit > 0 {
|
|
req.OperationAttributes[AttributeLimit] = limit
|
|
}
|
|
|
|
if myJobs {
|
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
|
}
|
|
|
|
if attributes == nil {
|
|
req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes
|
|
} else {
|
|
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID)
|
|
}
|
|
|
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
jobIDMap := make(map[int]Attributes)
|
|
|
|
for _, jobAttributes := range resp.JobAttributes {
|
|
jobIDMap[jobAttributes[AttributeJobID][0].Value.(int)] = jobAttributes
|
|
}
|
|
|
|
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)
|
|
req.OperationAttributes[AttributePurgeJobs] = purge
|
|
|
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
|
return err
|
|
}
|
|
|
|
// CancelAllJob 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)
|
|
req.OperationAttributes[AttributePurgeJobs] = purge
|
|
|
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
|
return err
|
|
}
|
|
|
|
// RestartJob restarts a job
|
|
func (c *IPPClient) RestartJob(jobID int) error {
|
|
req := NewRequest(OperationRestartJob, 1)
|
|
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
|
|
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
|
return err
|
|
}
|
|
|
|
// HoldJobUntil holds a job
|
|
func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
|
|
req := NewRequest(OperationRestartJob, 1)
|
|
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
|
req.JobAttributes[AttributeHoldJobUntil] = holdUntil
|
|
|
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
|
return err
|
|
}
|
|
|
|
// TestConnection tests if a tcp connection to the remote server is possible
|
|
func (c *IPPClient) TestConnection() error {
|
|
return c.adapter.TestConnection()
|
|
}
|