Merge pull request #18 from gardenofconcepts/feature/socket-integration
Added adapter implementation including socket adapter
This commit is contained in:
commit
6a590a71e5
|
@ -0,0 +1,5 @@
|
||||||
|
# Credits
|
||||||
|
|
||||||
|
* Original socket adapter code is mostly taken from [korylprince/printer-manager-cups](https://github.com/korylprince/printer-manager-cups)
|
||||||
|
([MIT](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/LICENSE) licensed):
|
||||||
|
[conn.go](https://github.com/korylprince/printer-manager-cups/blob/v1.0.9/cups/conn.go)
|
|
@ -0,0 +1,118 @@
|
||||||
|
package ipp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpAdapter struct {
|
||||||
|
host string
|
||||||
|
port int
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
useTLS bool
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHttpAdapter(host string, port int, username, password string, useTLS bool) *HttpAdapter {
|
||||||
|
httpClient := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HttpAdapter{
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
useTLS: useTLS,
|
||||||
|
client: &httpClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HttpAdapter) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) {
|
||||||
|
payload, err := req.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var body io.Reader
|
||||||
|
size := len(payload)
|
||||||
|
|
||||||
|
if req.File != nil && req.FileSize != -1 {
|
||||||
|
size += req.FileSize
|
||||||
|
|
||||||
|
body = io.MultiReader(bytes.NewBuffer(payload), req.File)
|
||||||
|
} else {
|
||||||
|
body = bytes.NewBuffer(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq, err := http.NewRequest("POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq.Header.Set("Content-Length", strconv.Itoa(size))
|
||||||
|
httpReq.Header.Set("Content-Type", ContentTypeIPP)
|
||||||
|
|
||||||
|
if h.username != "" && h.password != "" {
|
||||||
|
httpReq.SetBasicAuth(h.username, h.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpResp, err := h.client.Do(httpReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer httpResp.Body.Close()
|
||||||
|
|
||||||
|
if httpResp.StatusCode != 200 {
|
||||||
|
return nil, HTTPError{
|
||||||
|
Code: httpResp.StatusCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := NewResponseDecoder(httpResp.Body).Decode(additionalResponseData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = resp.CheckForErrors()
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HttpAdapter) GetHttpUri(namespace string, object interface{}) string {
|
||||||
|
proto := "http"
|
||||||
|
if h.useTLS {
|
||||||
|
proto = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := fmt.Sprintf("%s://%s:%d", proto, h.host, h.port)
|
||||||
|
|
||||||
|
if namespace != "" {
|
||||||
|
uri = fmt.Sprintf("%s/%s", uri, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
if object != nil {
|
||||||
|
uri = fmt.Sprintf("%s/%v", uri, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HttpAdapter) TestConnection() error {
|
||||||
|
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", h.host, h.port))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
package ipp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var socketNotFoundError = errors.New("unable to locate CUPS socket")
|
||||||
|
var certNotFoundError = errors.New("unable to locate CUPS certificate")
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultSocketSearchPaths = []string{"/var/run/cupsd", "/var/run/cups/cups.sock", "/run/cups/cups.sock"}
|
||||||
|
DefaultCertSearchPaths = []string{"/etc/cups/certs/0", "/run/cups/certs/0"}
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultRequestRetryLimit = 3
|
||||||
|
|
||||||
|
type SocketAdapter struct {
|
||||||
|
host string
|
||||||
|
useTLS bool
|
||||||
|
SocketSearchPaths []string
|
||||||
|
CertSearchPaths []string
|
||||||
|
requestRetryLimit int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSocketAdapter(host string, useTLS bool) *SocketAdapter {
|
||||||
|
return &SocketAdapter{
|
||||||
|
host: host,
|
||||||
|
useTLS: useTLS,
|
||||||
|
SocketSearchPaths: DefaultSocketSearchPaths,
|
||||||
|
CertSearchPaths: DefaultCertSearchPaths,
|
||||||
|
requestRetryLimit: defaultRequestRetryLimit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//DoRequest performs the given IPP request to the given URL, returning the IPP response or an error if one occurred
|
||||||
|
func (h *SocketAdapter) SendRequest(url string, r *Request, _ io.Writer) (*Response, error) {
|
||||||
|
for i := 0; i < h.requestRetryLimit; i++ {
|
||||||
|
// encode request
|
||||||
|
payload, err := r.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to encode IPP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sock, err := h.GetSocket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if cert isn't found, do a request to generate it
|
||||||
|
cert, err := h.GetCert()
|
||||||
|
if err != nil && err != certNotFoundError {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Length", strconv.Itoa(len(payload)))
|
||||||
|
req.Header.Set("Content-Type", ContentTypeIPP)
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Local %s", cert))
|
||||||
|
|
||||||
|
unixClient := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", sock)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// send request
|
||||||
|
resp, err := unixClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to perform HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusUnauthorized {
|
||||||
|
// retry with newly generated cert
|
||||||
|
resp.Body.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil, fmt.Errorf("server did not return Status OK: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer response to avoid read issues
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if _, err := io.Copy(buf, resp.Body); err != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil, fmt.Errorf("unable to buffer response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
// decode reply
|
||||||
|
ippResp, err := NewResponseDecoder(bytes.NewReader(buf.Bytes())).Decode(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode IPP response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ippResp.CheckForErrors(); err != nil {
|
||||||
|
return nil, fmt.Errorf("received error IPP response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ippResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("request retry limit exceeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetSocket returns the path to the cupsd socket by searching SocketSearchPaths
|
||||||
|
func (h *SocketAdapter) GetSocket() (string, error) {
|
||||||
|
for _, path := range h.SocketSearchPaths {
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
} else if os.IsPermission(err) {
|
||||||
|
return "", errors.New("unable to access socket: Access denied")
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("unable to access socket: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Mode()&os.ModeSocket != 0 {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", socketNotFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetCert returns the current CUPs authentication certificate by searching CertSearchPaths
|
||||||
|
func (h *SocketAdapter) GetCert() (string, error) {
|
||||||
|
for _, path := range h.CertSearchPaths {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
} else if os.IsPermission(err) {
|
||||||
|
return "", errors.New("unable to access certificate: Access denied")
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("unable to access certificate: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if _, err := io.Copy(buf, f); err != nil {
|
||||||
|
return "", fmt.Errorf("unable to access certificate: %v", err)
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", certNotFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SocketAdapter) GetHttpUri(namespace string, object interface{}) string {
|
||||||
|
proto := "http"
|
||||||
|
if h.useTLS {
|
||||||
|
proto = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := fmt.Sprintf("%s://%s", proto, h.host)
|
||||||
|
|
||||||
|
if namespace != "" {
|
||||||
|
uri = fmt.Sprintf("%s/%s", uri, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
if object != nil {
|
||||||
|
uri = fmt.Sprintf("%s/%v", uri, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SocketAdapter) TestConnection() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ipp
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type Adapter interface {
|
||||||
|
SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error)
|
||||||
|
GetHttpUri(namespace string, object interface{}) string
|
||||||
|
TestConnection() error
|
||||||
|
}
|
|
@ -10,17 +10,23 @@ type CUPSClient struct {
|
||||||
*IPPClient
|
*IPPClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCUPSClient creates a new cups ipp client
|
// NewCUPSClient creates a new cups ipp client (used HttpAdapter internally)
|
||||||
func NewCUPSClient(host string, port int, username, password string, useTLS bool) *CUPSClient {
|
func NewCUPSClient(host string, port int, username, password string, useTLS bool) *CUPSClient {
|
||||||
ippClient := NewIPPClient(host, port, username, password, useTLS)
|
ippClient := NewIPPClient(host, port, username, password, useTLS)
|
||||||
return &CUPSClient{ippClient}
|
return &CUPSClient{ippClient}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewCUPSClient creates a new cups ipp client with given Adapter
|
||||||
|
func NewCUPSClientWithAdapter(username string, adapter Adapter) *CUPSClient {
|
||||||
|
ippClient := NewIPPClientWithAdapter(username, adapter)
|
||||||
|
return &CUPSClient{ippClient}
|
||||||
|
}
|
||||||
|
|
||||||
// GetDevices returns a map of device uris and printer attributes
|
// GetDevices returns a map of device uris and printer attributes
|
||||||
func (c *CUPSClient) GetDevices() (map[string]Attributes, error) {
|
func (c *CUPSClient) GetDevices() (map[string]Attributes, error) {
|
||||||
req := NewRequest(OperationCupsGetDevices, 1)
|
req := NewRequest(OperationCupsGetDevices, 1)
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("", nil), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -40,7 +46,7 @@ func (c *CUPSClient) MoveJob(jobID int, destPrinter string) error {
|
||||||
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
||||||
req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter)
|
req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +56,7 @@ func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(srcPrinter)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(srcPrinter)
|
||||||
req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter)
|
req.PrinterAttributes[AttributeJobPrinterURI] = c.getPrinterUri(destPrinter)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +64,7 @@ func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error {
|
||||||
func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) {
|
func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) {
|
||||||
req := NewRequest(OperationCupsGetPPDs, 1)
|
req := NewRequest(OperationCupsGetPPDs, 1)
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("", nil), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -77,7 +83,7 @@ func (c *CUPSClient) AcceptJobs(printer string) error {
|
||||||
req := NewRequest(OperationCupsAcceptJobs, 1)
|
req := NewRequest(OperationCupsAcceptJobs, 1)
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +92,7 @@ func (c *CUPSClient) RejectJobs(printer string) error {
|
||||||
req := NewRequest(OperationCupsRejectJobs, 1)
|
req := NewRequest(OperationCupsRejectJobs, 1)
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +124,7 @@ func (c *CUPSClient) AddPrinterToClass(class, printer string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
|
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
|
||||||
req.PrinterAttributes[AttributeMemberURIs] = memberURIList
|
req.PrinterAttributes[AttributeMemberURIs] = memberURIList
|
||||||
|
|
||||||
_, err = c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +154,7 @@ func (c *CUPSClient) DeletePrinterFromClass(class, printer string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
|
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
|
||||||
req.PrinterAttributes[AttributeMemberURIs] = memberURIList
|
req.PrinterAttributes[AttributeMemberURIs] = memberURIList
|
||||||
|
|
||||||
_, err = c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +163,7 @@ func (c *CUPSClient) DeleteClass(class string) error {
|
||||||
req := NewRequest(OperationCupsDeleteClass, 1)
|
req := NewRequest(OperationCupsDeleteClass, 1)
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
|
req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +179,7 @@ func (c *CUPSClient) CreatePrinter(name, deviceURI, ppd string, shared bool, err
|
||||||
req.PrinterAttributes[AttributePrinterLocation] = location
|
req.PrinterAttributes[AttributePrinterLocation] = location
|
||||||
req.PrinterAttributes[AttributePrinterErrorPolicy] = string(errorPolicy)
|
req.PrinterAttributes[AttributePrinterErrorPolicy] = string(errorPolicy)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +189,7 @@ func (c *CUPSClient) SetPrinterPPD(printer, ppd string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.OperationAttributes[AttributePPDName] = ppd
|
req.OperationAttributes[AttributePPDName] = ppd
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +199,7 @@ func (c *CUPSClient) SetPrinterDeviceURI(printer, deviceURI string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.PrinterAttributes[AttributeDeviceURI] = deviceURI
|
req.PrinterAttributes[AttributeDeviceURI] = deviceURI
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +209,7 @@ func (c *CUPSClient) SetPrinterIsShared(printer string, shared bool) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.OperationAttributes[AttributePrinterIsShared] = shared
|
req.OperationAttributes[AttributePrinterIsShared] = shared
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +219,7 @@ func (c *CUPSClient) SetPrinterErrorPolicy(printer string, errorPolicy string) e
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.PrinterAttributes[AttributePrinterErrorPolicy] = string(errorPolicy)
|
req.PrinterAttributes[AttributePrinterErrorPolicy] = string(errorPolicy)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +229,7 @@ func (c *CUPSClient) SetPrinterInformation(printer, information string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.PrinterAttributes[AttributePrinterInfo] = information
|
req.PrinterAttributes[AttributePrinterInfo] = information
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +239,7 @@ func (c *CUPSClient) SetPrinterLocation(printer, location string) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.PrinterAttributes[AttributePrinterLocation] = location
|
req.PrinterAttributes[AttributePrinterLocation] = location
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +248,7 @@ func (c *CUPSClient) DeletePrinter(printer string) error {
|
||||||
req := NewRequest(OperationCupsDeletePrinter, 1)
|
req := NewRequest(OperationCupsDeletePrinter, 1)
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +262,7 @@ func (c *CUPSClient) GetPrinters(attributes []string) (map[string]Attributes, er
|
||||||
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName)
|
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("", nil), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -280,7 +286,7 @@ func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, err
|
||||||
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName)
|
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributePrinterName)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("", nil), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
128
ipp-client.go
128
ipp-client.go
|
@ -1,16 +1,11 @@
|
||||||
package ipp
|
package ipp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Document wraps an io.Reader with more information, needed for encoding
|
// Document wraps an io.Reader with more information, needed for encoding
|
||||||
|
@ -23,45 +18,26 @@ type Document struct {
|
||||||
|
|
||||||
// IPPClient implements a generic ipp client
|
// IPPClient implements a generic ipp client
|
||||||
type IPPClient struct {
|
type IPPClient struct {
|
||||||
host string
|
|
||||||
port int
|
|
||||||
username string
|
username string
|
||||||
password string
|
adapter Adapter
|
||||||
useTLS bool
|
|
||||||
|
|
||||||
client *http.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIPPClient creates a new generic ipp client
|
// NewIPPClient creates a new generic ipp client (used HttpAdapter internally)
|
||||||
func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient {
|
func NewIPPClient(host string, port int, username, password string, useTLS bool) *IPPClient {
|
||||||
httpClient := http.Client{
|
adapter := NewHttpAdapter(host, port, username, password, useTLS)
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &IPPClient{host, port, username, password, useTLS, &httpClient}
|
return &IPPClient{
|
||||||
|
username: username,
|
||||||
|
adapter: adapter,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *IPPClient) getHttpUri(namespace string, object interface{}) string {
|
// NewIPPClientWithAdapter creates a new generic ipp client with given Adapter
|
||||||
proto := "http"
|
func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient {
|
||||||
if c.useTLS {
|
return &IPPClient{
|
||||||
proto = "https"
|
username: username,
|
||||||
|
adapter: adapter,
|
||||||
}
|
}
|
||||||
|
|
||||||
uri := fmt.Sprintf("%s://%s:%d", proto, c.host, c.port)
|
|
||||||
|
|
||||||
if namespace != "" {
|
|
||||||
uri = fmt.Sprintf("%s/%s", uri, namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
if object != nil {
|
|
||||||
uri = fmt.Sprintf("%s/%v", uri, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
return uri
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *IPPClient) getPrinterUri(printer string) string {
|
func (c *IPPClient) getPrinterUri(printer string) string {
|
||||||
|
@ -78,53 +54,11 @@ func (c *IPPClient) getClassUri(printer string) string {
|
||||||
|
|
||||||
// SendRequest sends a request to a remote uri end returns the response
|
// 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) {
|
func (c *IPPClient) SendRequest(url string, req *Request, additionalResponseData io.Writer) (*Response, error) {
|
||||||
payload, err := req.Encode()
|
if _, ok := req.OperationAttributes[AttributeRequestingUserName]; ok == false {
|
||||||
if err != nil {
|
req.OperationAttributes[AttributeRequestingUserName] = c.username
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body io.Reader
|
return c.adapter.SendRequest(url, req, additionalResponseData)
|
||||||
size := len(payload)
|
|
||||||
|
|
||||||
if req.File != nil && req.FileSize != -1 {
|
|
||||||
size += req.FileSize
|
|
||||||
|
|
||||||
body = io.MultiReader(bytes.NewBuffer(payload), req.File)
|
|
||||||
} else {
|
|
||||||
body = bytes.NewBuffer(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", url, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
httpReq.Header.Set("Content-Length", strconv.Itoa(size))
|
|
||||||
httpReq.Header.Set("Content-Type", ContentTypeIPP)
|
|
||||||
|
|
||||||
if c.username != "" && c.password != "" {
|
|
||||||
httpReq.SetBasicAuth(c.username, c.password)
|
|
||||||
}
|
|
||||||
|
|
||||||
httpResp, err := c.client.Do(httpReq)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer httpResp.Body.Close()
|
|
||||||
|
|
||||||
if httpResp.StatusCode != 200 {
|
|
||||||
return nil, HTTPError{
|
|
||||||
Code: httpResp.StatusCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := NewResponseDecoder(httpResp.Body).Decode(additionalResponseData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = resp.CheckForErrors()
|
|
||||||
return resp, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
|
@ -144,7 +78,7 @@ func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttribute
|
||||||
req.JobAttributes[key] = value
|
req.JobAttributes[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("printers", printer), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
@ -168,7 +102,7 @@ func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttribute
|
||||||
req.File = doc.Document
|
req.File = doc.Document
|
||||||
req.FileSize = doc.Size
|
req.FileSize = doc.Size
|
||||||
|
|
||||||
_, err = c.SendRequest(c.getHttpUri("printers", printer), req, nil)
|
_, err = c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
@ -198,7 +132,7 @@ func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[str
|
||||||
req.File = doc.Document
|
req.File = doc.Document
|
||||||
req.FileSize = doc.Size
|
req.FileSize = doc.Size
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("printers", printer), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
@ -251,7 +185,7 @@ func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (A
|
||||||
req.OperationAttributes[AttributeRequestedAttributes] = attributes
|
req.OperationAttributes[AttributeRequestedAttributes] = attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("printers", printer), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("printers", printer), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -268,7 +202,7 @@ func (c *IPPClient) ResumePrinter(printer string) error {
|
||||||
req := NewRequest(OperationResumePrinter, 1)
|
req := NewRequest(OperationResumePrinter, 1)
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +211,7 @@ func (c *IPPClient) PausePrinter(printer string) error {
|
||||||
req := NewRequest(OperationPausePrinter, 1)
|
req := NewRequest(OperationPausePrinter, 1)
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +226,7 @@ func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes
|
||||||
req.OperationAttributes[AttributeRequestedAttributes] = attributes
|
req.OperationAttributes[AttributeRequestedAttributes] = attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("jobs", jobID), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("jobs", jobID), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -336,7 +270,7 @@ func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool
|
||||||
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID)
|
req.OperationAttributes[AttributeRequestedAttributes] = append(attributes, AttributeJobID)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.SendRequest(c.getHttpUri("", nil), req, nil)
|
resp, err := c.SendRequest(c.adapter.GetHttpUri("", nil), req, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -356,7 +290,7 @@ func (c *IPPClient) CancelJob(jobID int, purge bool) error {
|
||||||
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
||||||
req.OperationAttributes[AttributePurgeJobs] = purge
|
req.OperationAttributes[AttributePurgeJobs] = purge
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +300,7 @@ func (c *IPPClient) CancelAllJob(printer string, purge bool) error {
|
||||||
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer)
|
||||||
req.OperationAttributes[AttributePurgeJobs] = purge
|
req.OperationAttributes[AttributePurgeJobs] = purge
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +309,7 @@ func (c *IPPClient) RestartJob(jobID int) error {
|
||||||
req := NewRequest(OperationRestartJob, 1)
|
req := NewRequest(OperationRestartJob, 1)
|
||||||
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,17 +319,11 @@ func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
|
||||||
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID)
|
||||||
req.JobAttributes[AttributeHoldJobUntil] = holdUntil
|
req.JobAttributes[AttributeHoldJobUntil] = holdUntil
|
||||||
|
|
||||||
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil)
|
_, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestConnection tests if a tcp connection to the remote server is possible
|
// TestConnection tests if a tcp connection to the remote server is possible
|
||||||
func (c *IPPClient) TestConnection() error {
|
func (c *IPPClient) TestConnection() error {
|
||||||
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", c.host, c.port))
|
return c.adapter.TestConnection()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue