From af51cfebfc69892bb9e7635d4091d5f3b79ed3b9 Mon Sep 17 00:00:00 2001 From: Dennis Oehme Date: Fri, 26 Feb 2021 09:40:29 +0100 Subject: [PATCH 1/2] Added adapter implementation including Socket Adapter --- CREDITS.MD | 5 ++ adapter-http.go | 118 ++++++++++++++++++++++++++++ adapter-socket.go | 196 ++++++++++++++++++++++++++++++++++++++++++++++ adapter.go | 9 +++ cups-client.go | 8 +- ipp-client.go | 108 +++++-------------------- 6 files changed, 353 insertions(+), 91 deletions(-) create mode 100644 CREDITS.MD create mode 100644 adapter-http.go create mode 100644 adapter-socket.go create mode 100644 adapter.go diff --git a/CREDITS.MD b/CREDITS.MD new file mode 100644 index 0000000..b7d28cc --- /dev/null +++ b/CREDITS.MD @@ -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) diff --git a/adapter-http.go b/adapter-http.go new file mode 100644 index 0000000..131c8d4 --- /dev/null +++ b/adapter-http.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 +} diff --git a/adapter-socket.go b/adapter-socket.go new file mode 100644 index 0000000..a5806a8 --- /dev/null +++ b/adapter-socket.go @@ -0,0 +1,196 @@ +package ipp + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net" + "net/http" + "os" + "os/user" + "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) { + // set user field + user, err := user.Current() + if err != nil { + return nil, fmt.Errorf("unable to lookup current user: %v", err) + } + r.OperationAttributes[AttributeRequestingUserName] = user.Username + + 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 +} diff --git a/adapter.go b/adapter.go new file mode 100644 index 0000000..15ad2d0 --- /dev/null +++ b/adapter.go @@ -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 +} diff --git a/cups-client.go b/cups-client.go index 3cd4007..322bd64 100644 --- a/cups-client.go +++ b/cups-client.go @@ -10,12 +10,18 @@ type CUPSClient struct { *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 { ippClient := NewIPPClient(host, port, username, password, useTLS) 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 func (c *CUPSClient) GetDevices() (map[string]Attributes, error) { req := NewRequest(OperationCupsGetDevices, 1) diff --git a/ipp-client.go b/ipp-client.go index 51885c3..76bb27a 100644 --- a/ipp-client.go +++ b/ipp-client.go @@ -1,16 +1,11 @@ package ipp import ( - "bytes" - "crypto/tls" "errors" "fmt" "io" - "net" - "net/http" "os" "path" - "strconv" ) // Document wraps an io.Reader with more information, needed for encoding @@ -23,45 +18,30 @@ type Document struct { // IPPClient implements a generic ipp client type IPPClient struct { - host string - port int username string - password string - useTLS bool - - client *http.Client + adapter Adapter } -// 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 { - httpClient := http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, - } + adapter := NewHttpAdapter(host, port, username, password, useTLS) - return &IPPClient{host, port, username, password, useTLS, &httpClient} + 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) getHttpUri(namespace string, object interface{}) string { - proto := "http" - if c.useTLS { - proto = "https" - } - - 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 + return c.adapter.GetHttpUri(namespace, object) } func (c *IPPClient) getPrinterUri(printer string) string { @@ -78,53 +58,7 @@ func (c *IPPClient) getClassUri(printer string) string { // 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 { - 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 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 + 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 @@ -391,11 +325,5 @@ func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) 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 { - return err - } - conn.Close() - - return nil + return c.adapter.TestConnection() } From 9029d1aa19b0d746c4dbdf9e41c4426829cb14a2 Mon Sep 17 00:00:00 2001 From: Dennis Oehme Date: Fri, 26 Feb 2021 11:24:22 +0100 Subject: [PATCH 2/2] Address comments from review --- adapter-socket.go | 8 -------- cups-client.go | 38 +++++++++++++++++++------------------- ipp-client.go | 32 ++++++++++++++++---------------- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/adapter-socket.go b/adapter-socket.go index a5806a8..a0faee7 100644 --- a/adapter-socket.go +++ b/adapter-socket.go @@ -9,7 +9,6 @@ import ( "net" "net/http" "os" - "os/user" "strconv" ) @@ -43,13 +42,6 @@ func NewSocketAdapter(host string, useTLS bool) *SocketAdapter { //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) { - // set user field - user, err := user.Current() - if err != nil { - return nil, fmt.Errorf("unable to lookup current user: %v", err) - } - r.OperationAttributes[AttributeRequestingUserName] = user.Username - for i := 0; i < h.requestRetryLimit; i++ { // encode request payload, err := r.Encode() diff --git a/cups-client.go b/cups-client.go index 322bd64..54c4589 100644 --- a/cups-client.go +++ b/cups-client.go @@ -26,7 +26,7 @@ func NewCUPSClientWithAdapter(username string, adapter Adapter) *CUPSClient { func (c *CUPSClient) GetDevices() (map[string]Attributes, error) { 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 { return nil, err } @@ -46,7 +46,7 @@ func (c *CUPSClient) MoveJob(jobID int, destPrinter string) error { req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) 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 } @@ -56,7 +56,7 @@ func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(srcPrinter) 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 } @@ -64,7 +64,7 @@ func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error { func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) { 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 { return nil, err } @@ -83,7 +83,7 @@ func (c *CUPSClient) AcceptJobs(printer string) error { req := NewRequest(OperationCupsAcceptJobs, 1) 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 } @@ -92,7 +92,7 @@ func (c *CUPSClient) RejectJobs(printer string) error { req := NewRequest(OperationCupsRejectJobs, 1) 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 } @@ -124,7 +124,7 @@ func (c *CUPSClient) AddPrinterToClass(class, printer string) error { req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) req.PrinterAttributes[AttributeMemberURIs] = memberURIList - _, err = c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -154,7 +154,7 @@ func (c *CUPSClient) DeletePrinterFromClass(class, printer string) error { req.OperationAttributes[AttributePrinterURI] = c.getClassUri(class) req.PrinterAttributes[AttributeMemberURIs] = memberURIList - _, err = c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err = c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -163,7 +163,7 @@ func (c *CUPSClient) DeleteClass(class string) error { req := NewRequest(OperationCupsDeleteClass, 1) 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 } @@ -179,7 +179,7 @@ func (c *CUPSClient) CreatePrinter(name, deviceURI, ppd string, shared bool, err req.PrinterAttributes[AttributePrinterLocation] = location req.PrinterAttributes[AttributePrinterErrorPolicy] = string(errorPolicy) - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -189,7 +189,7 @@ func (c *CUPSClient) SetPrinterPPD(printer, ppd string) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.OperationAttributes[AttributePPDName] = ppd - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -199,7 +199,7 @@ func (c *CUPSClient) SetPrinterDeviceURI(printer, deviceURI string) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.PrinterAttributes[AttributeDeviceURI] = deviceURI - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -209,7 +209,7 @@ func (c *CUPSClient) SetPrinterIsShared(printer string, shared bool) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.OperationAttributes[AttributePrinterIsShared] = shared - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -219,7 +219,7 @@ func (c *CUPSClient) SetPrinterErrorPolicy(printer string, errorPolicy string) e req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.PrinterAttributes[AttributePrinterErrorPolicy] = string(errorPolicy) - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -229,7 +229,7 @@ func (c *CUPSClient) SetPrinterInformation(printer, information string) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.PrinterAttributes[AttributePrinterInfo] = information - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -239,7 +239,7 @@ func (c *CUPSClient) SetPrinterLocation(printer, location string) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.PrinterAttributes[AttributePrinterLocation] = location - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -248,7 +248,7 @@ func (c *CUPSClient) DeletePrinter(printer string) error { req := NewRequest(OperationCupsDeletePrinter, 1) 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 } @@ -262,7 +262,7 @@ func (c *CUPSClient) GetPrinters(attributes []string) (map[string]Attributes, er 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 { return nil, err } @@ -286,7 +286,7 @@ func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, err 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 { return nil, err } diff --git a/ipp-client.go b/ipp-client.go index 76bb27a..0a992a8 100644 --- a/ipp-client.go +++ b/ipp-client.go @@ -40,10 +40,6 @@ func NewIPPClientWithAdapter(username string, adapter Adapter) *IPPClient { } } -func (c *IPPClient) getHttpUri(namespace string, object interface{}) string { - return c.adapter.GetHttpUri(namespace, object) -} - func (c *IPPClient) getPrinterUri(printer string) string { return fmt.Sprintf("ipp://localhost/printers/%s", printer) } @@ -58,6 +54,10 @@ func (c *IPPClient) getClassUri(printer string) string { // 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) } @@ -78,7 +78,7 @@ func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttribute 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 { return -1, err } @@ -102,7 +102,7 @@ func (c *IPPClient) PrintDocuments(docs []Document, printer string, jobAttribute req.File = doc.Document 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 { return -1, err } @@ -132,7 +132,7 @@ func (c *IPPClient) PrintJob(doc Document, printer string, jobAttributes map[str req.File = doc.Document 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 { return -1, err } @@ -185,7 +185,7 @@ func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (A 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 { return nil, err } @@ -202,7 +202,7 @@ func (c *IPPClient) ResumePrinter(printer string) error { req := NewRequest(OperationResumePrinter, 1) 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 } @@ -211,7 +211,7 @@ func (c *IPPClient) PausePrinter(printer string) error { req := NewRequest(OperationPausePrinter, 1) 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 } @@ -226,7 +226,7 @@ func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (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 { return nil, err } @@ -270,7 +270,7 @@ func (c *IPPClient) GetJobs(printer, class string, whichJobs string, myJobs bool 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 { return nil, err } @@ -290,7 +290,7 @@ func (c *IPPClient) CancelJob(jobID int, purge bool) error { req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) req.OperationAttributes[AttributePurgeJobs] = purge - _, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) return err } @@ -300,7 +300,7 @@ func (c *IPPClient) CancelAllJob(printer string, purge bool) error { req.OperationAttributes[AttributePrinterURI] = c.getPrinterUri(printer) req.OperationAttributes[AttributePurgeJobs] = purge - _, err := c.SendRequest(c.getHttpUri("admin", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("admin", ""), req, nil) return err } @@ -309,7 +309,7 @@ func (c *IPPClient) RestartJob(jobID int) error { req := NewRequest(OperationRestartJob, 1) 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 } @@ -319,7 +319,7 @@ func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error { req.OperationAttributes[AttributeJobURI] = c.getJobUri(jobID) req.JobAttributes[AttributeHoldJobUntil] = holdUntil - _, err := c.SendRequest(c.getHttpUri("jobs", ""), req, nil) + _, err := c.SendRequest(c.adapter.GetHttpUri("jobs", ""), req, nil) return err }