go-ipp/ipp-client.go

345 lines
11 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 (
2019-03-15 15:20:02 +00:00
"errors"
2019-03-10 17:43:47 +00:00
"fmt"
"io"
"os"
"path"
)
2020-03-27 23:27:56 +00:00
// Document wraps an io.Reader with more information, needed for encoding
2019-03-15 15:20:02 +00:00
type Document struct {
Document io.Reader
Size int
Name string
MimeType string
}
2020-03-27 23:27:56 +00:00
// IPPClient implements a generic ipp client
2019-03-10 17:43:47 +00:00
type IPPClient struct {
username string
adapter Adapter
2019-03-10 17:43:47 +00:00
}
// NewIPPClient creates a new generic ipp client (used HttpAdapter internally)
2019-03-10 17:43:47 +00:00
func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient {
adapter := NewHttpAdapter(host, port, username, password, useTLS)
2019-03-10 17:43:47 +00:00
return &IPPClient{
username: username,
adapter: adapter,
2019-03-15 15:20:02 +00:00
}
}
2019-03-15 15:20:02 +00:00
// NewIPPClientWithAdapter creates a new generic ipp client with given Adapter
func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient {
return &IPPClient{
username: username,
adapter: adapter,
2019-03-15 15:20:02 +00:00
}
}
2019-03-15 15:20:02 +00:00
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)
2019-03-10 17:43:47 +00:00
}
2019-03-15 15:20:02 +00:00
func (c *IPPClient) getClassUri(printer string) string {
return fmt.Sprintf("ipp://localhost/classes/%s", printer)
}
2020-03-27 23:27:56 +00:00
// 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) {
2021-02-26 10:24:22 +00:00
if _, ok := req.OperationAttributes[AttributeRequestingUserName]; ok == false {
req.OperationAttributes[AttributeRequestingUserName] = c.username
}
return c.adapter.SendRequest(url, req, additionalResponseData)
2019-03-10 17:43:47 +00:00
}
2020-03-27 23:27:56 +00:00
// 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
2019-06-08 22:03:50 +00:00
func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttributes map[string]interface{}) (int, error) {
2019-03-15 15:20:02 +00:00
printerURI := c.getPrinterUri(printer)
2019-03-10 17:43:47 +00:00
req := NewRequest(OperationCreateJob, 1)
req.OperationAttributes[AttributePrinterURI] = printerURI
req.OperationAttributes[AttributeRequestingUserName] = c.username
2019-06-08 22:03:50 +00:00
// set defaults for some attributes, may get overwritten
req.OperationAttributes[AttributeJobName] = docs[0].Name
req.OperationAttributes[AttributeCopies] = 1
req.OperationAttributes[AttributeJobPriority] = DefaultJobPriority
2019-06-08 22:03:50 +00:00
for key, value := range jobAttributes {
req.JobAttributes[key] = value
}
2019-03-10 17:43:47 +00:00
2021-02-26 10:24:22 +00:00
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
2019-03-10 17:43:47 +00:00
if err != nil {
return -1, err
}
2020-03-05 15:31:05 +00:00
if len(resp.JobAttributes) == 0 {
2019-03-15 15:20:02 +00:00
return 0, errors.New("server doesn't returned a job id")
}
jobID := resp.JobAttributes[0][AttributeJobID][0].Value.(int)
2019-03-10 17:43:47 +00:00
2019-03-15 15:20:02 +00:00
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
2019-03-15 15:20:02 +00:00
req.File = doc.Document
req.FileSize = doc.Size
2021-02-26 10:24:22 +00:00
_, err = c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
2019-03-15 15:20:02 +00:00
if err != nil {
return -1, err
}
2019-03-10 17:43:47 +00:00
}
return jobID, nil
2019-03-13 21:54:08 +00:00
}
2020-03-27 23:27:56 +00:00
// 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
2019-06-08 22:03:50 +00:00
// 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
2021-02-26 10:24:22 +00:00
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
if err != nil {
return -1, err
}
2020-03-05 15:31:05 +00:00
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
}
2020-03-27 23:27:56 +00:00
// PrintFile prints a local file on the file system. custom job settings can be specified via the jobAttributes parameter
2019-06-08 22:03:50 +00:00
func (c *IPPClient) PrintFile(filePath, printer string, jobAttributes map[string]interface{}) (int, error) {
2019-03-13 21:54:08 +00:00
fileStats, err := os.Stat(filePath)
if os.IsNotExist(err) {
return -1, err
}
2019-03-15 15:20:02 +00:00
fileName := path.Base(filePath)
2019-03-13 21:54:08 +00:00
document, err := os.Open(filePath)
if err != nil {
return 0, err
}
defer document.Close()
jobAttributes[AttributeJobName] = fileName
2019-06-08 22:03:50 +00:00
return c.PrintDocuments([]Document{
2019-03-15 15:20:02 +00:00
{
Document: document,
Name: fileName,
Size: int(fileStats.Size()),
MimeType: MimeTypeOctetStream,
},
2019-06-08 22:03:50 +00:00
}, printer, jobAttributes)
2019-03-15 15:20:02 +00:00
}
2020-03-27 23:27:56 +00:00
// GetPrinterAttributes returns the requested attributes for the specified printer, if attributes is nil the default attributes will be used
2019-03-15 15:20:02 +00:00
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
2019-03-15 15:20:02 +00:00
if attributes == nil {
req.OperationAttributes[AttributeRequestedAttributes] = DefaultPrinterAttributes
2019-03-15 15:20:02 +00:00
} else {
req.OperationAttributes[AttributeRequestedAttributes] = attributes
2019-03-15 15:20:02 +00:00
}
2021-02-26 10:24:22 +00:00
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
2019-03-15 15:20:02 +00:00
if err != nil {
return nil, err
}
2020-03-05 15:31:05 +00:00
if len(resp.PrinterAttributes) == 0 {
2019-03-15 15:20:02 +00:00
return nil, errors.New("server doesn't return any printer attributes")
}
2020-03-05 15:31:05 +00:00
return resp.PrinterAttributes[0], nil
2019-03-15 15:20:02 +00:00
}
2020-03-27 23:27:56 +00:00
// ResumePrinter resumes a printer
2019-03-15 15:20:02 +00:00
func (c *IPPClient) ResumePrinter(printer string) error {
req := NewRequest(OperationResumePrinter, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
2019-03-15 15:20:02 +00:00
2021-02-26 10:24:22 +00:00
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
2019-03-15 15:20:02 +00:00
return err
}
2020-03-27 23:27:56 +00:00
// PausePrinter pauses a printer
2019-03-15 15:20:02 +00:00
func (c *IPPClient) PausePrinter(printer string) error {
req := NewRequest(OperationPausePrinter, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
2019-03-15 15:20:02 +00:00
2021-02-26 10:24:22 +00:00
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
2019-03-15 15:20:02 +00:00
return err
}
2020-03-27 23:27:56 +00:00
// GetJobAttributes returns the requested attributes for the specified job, if attributes is nil the default job will be used
2019-03-15 15:20:02 +00:00
func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes, error) {
req := NewRequest(OperationGetJobAttributes, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
2019-03-15 15:20:02 +00:00
if attributes == nil {
req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes
2019-03-15 15:20:02 +00:00
} else {
req.OperationAttributes[AttributeRequestedAttributes] = attributes
2019-03-15 15:20:02 +00:00
}
2021-02-26 10:24:22 +00:00
resp, err := c.SendRequest(c.adapter.GetHttpUri("jobs", jobID), req, nil)
2019-03-15 15:20:02 +00:00
if err != nil {
return nil, err
}
if len(resp.JobAttributes) == 0 {
2019-03-15 15:20:02 +00:00
return nil, errors.New("server doesn't return any job attributes")
}
return resp.JobAttributes[0], nil
2019-03-15 15:20:02 +00:00
}
2020-03-27 23:27:56 +00:00
// GetJobs returns jobs from a printer or class
2020-03-09 11:47:23 +00:00
func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool, firstJobId, limit int, attributes []string) (map[int]Attributes, error) {
2019-03-15 15:20:02 +00:00
req := NewRequest(OperationGetJobs, 1)
req.OperationAttributes[AttributeWhichJobs] = string(whichJobs)
req.OperationAttributes[AttributeMyJobs] = myJobs
2019-03-15 15:20:02 +00:00
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
}
2019-03-15 15:20:02 +00:00
if attributes == nil {
req.OperationAttributes[AttributeRequestedAttributes] = DefaultJobAttributes
2019-03-15 15:20:02 +00:00
} else {
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID)
2019-03-15 15:20:02 +00:00
}
2021-02-26 10:24:22 +00:00
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
2019-03-15 15:20:02 +00:00
if err != nil {
return nil, err
}
jobIDMap := make(map[int]Attributes)
2020-03-05 15:31:05 +00:00
for _, jobAttributes := range resp.JobAttributes {
jobIDMap[jobAttributes[AttributeJobID][0].Value.(int)] = jobAttributes
2019-03-15 15:20:02 +00:00
}
return jobIDMap, nil
}
2020-03-27 23:27:56 +00:00
// CancelJob cancels a job. if purge is true, the job will also be removed
2019-03-15 15:20:02 +00:00
func (c *IPPClient) CancelJob(jobID int, purge bool) error {
req := NewRequest(OperationCancelJob, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
req.OperationAttributes[AttributePurgeJobs] = purge
2019-03-15 15:20:02 +00:00
2021-02-26 10:24:22 +00:00
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
2019-03-15 15:20:02 +00:00
return err
}
2020-03-27 23:30:48 +00:00
// CancelAllJob cancels all jobs for a specified printer. if purge is true, the jobs will also be removed
2019-03-15 15:20:02 +00:00
func (c *IPPClient) CancelAllJob(printer string, purge bool) error {
req := NewRequest(OperationCancelJobs, 1)
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
req.OperationAttributes[AttributePurgeJobs] = purge
2019-03-15 15:20:02 +00:00
2021-02-26 10:24:22 +00:00
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
2019-03-15 15:20:02 +00:00
return err
}
2020-03-27 23:27:56 +00:00
// RestartJob restarts a job
2019-03-15 15:20:02 +00:00
func (c *IPPClient) RestartJob(jobID int) error {
req := NewRequest(OperationRestartJob, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
2019-03-15 15:20:02 +00:00
2021-02-26 10:24:22 +00:00
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
2019-03-15 15:20:02 +00:00
return err
}
2020-03-27 23:30:48 +00:00
// HoldJobUntil holds a job
2019-03-15 15:20:02 +00:00
func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
req := NewRequest(OperationRestartJob, 1)
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
req.JobAttributes[AttributeHoldJobUntil] = holdUntil
2019-03-15 15:20:02 +00:00
2021-02-26 10:24:22 +00:00
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
2019-03-15 15:20:02 +00:00
return err
}
2020-03-27 23:27:56 +00:00
// TestConnection tests if a tcp connection to the remote server is possible
2019-03-15 15:20:02 +00:00
func (c *IPPClient) TestConnection() error {
return c.adapter.TestConnection()
2019-03-13 21:54:08 +00:00
}