first beta release

This commit is contained in:
Fabian Weber 2019-03-15 16:20:02 +01:00
parent 83b3e83978
commit 7606089b66
8 changed files with 537 additions and 51 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
idea
cmd

View file

@ -361,8 +361,6 @@ func (d *AttributeDecoder) Decode(tag Tag) (*Attribute, error) {
}
attr.Name = name
fmt.Printf("attr name: %v\n", name)
switch attr.Tag {
case TagEnum, TagInteger:
val, err := d.decodeInteger()
@ -436,8 +434,6 @@ func (d *AttributeDecoder) decodeString() (string, error) {
return "", err
}
fmt.Printf("string length: %v\n", length)
if length == 0 {
return "", nil
}

View file

@ -1,15 +0,0 @@
package main
import (
"bytes"
"fmt"
"github.com/phin1x/go-ipp"
)
func main() {
doc := new(bytes.Buffer)
doc.WriteString("asdfasdf")
ippclt := ipp.NewIPPClient("", 0, "", "", false)
fmt.Println(ippclt.Print(doc, doc.Len(), "blub", "testprint", 1, 50))
}

View file

@ -268,11 +268,23 @@ const (
PrinterStateStopped = 0x0005
)
type JobStateFilter string
const (
JobStateFilterNotCompleted = "not-completed"
JobStateFilterCompleted = "completed"
)
const (
CharsetLanguage = "en-US"
Charset = "utf-8"
ProtocolVersionMajor = uint8(2)
ProtocolVersionMinor = uint8(0)
DefaultJobPriority = 50
MimeTypePostscript = "application/postscript"
MimeTypeOctetStream = "application/octet-stream"
)
var (

View file

@ -1 +1,269 @@
package ipp
import "strings"
type CUPSClient struct {
*IPPClient
}
func NewCUPSClient(host string, port int, username, password string, useTLS bool) *CUPSClient {
ippClient := NewIPPClient(host, port, username, password, useTLS)
return &CUPSClient{ippClient}
}
func (c *CUPSClient) GetDevices() (map[string]Attributes, error) {
req := NewRequest(OperationCupsGetDevices, 1)
resp, err := c.SendRequest(c.getHttpUri("", nil), req)
if err != nil {
return nil, err
}
printerNameMap := make(map[string]Attributes)
for _, printerAttributes := range resp.Printers {
printerNameMap[printerAttributes["device-uri"][0].Value.(string)] = printerAttributes
}
return printerNameMap, nil
}
func (c *CUPSClient) MoveJob(jobID int, destPrinter string) error {
req := NewRequest(OperationCupsMoveJob, 1)
req.OperationAttributes["job-uri"] = c.getJobUri(jobID)
req.PrinterAttributes["job-printer-uri"] = c.getPrinterUri(destPrinter)
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req)
return err
}
func (c *CUPSClient) MoveAllJob(srcPrinter, destPrinter string) error {
req := NewRequest(OperationCupsMoveJob, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(srcPrinter)
req.PrinterAttributes["job-printer-uri"] = c.getPrinterUri(destPrinter)
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req)
return err
}
func (c *CUPSClient) GetPPDs() (map[string]Attributes, error) {
req := NewRequest(OperationCupsGetPpds, 1)
resp, err := c.SendRequest(c.getHttpUri("", nil), req)
if err != nil {
return nil, err
}
ppdNameMap := make(map[string]Attributes)
for _, printerAttributes := range resp.Printers {
ppdNameMap[printerAttributes["ppd-name"][0].Value.(string)] = printerAttributes
}
return ppdNameMap, nil
}
func (c *CUPSClient) AcceptJobs(printer, destPrinter string) error {
req := NewRequest(OperationCupsAcceptJobs, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) RejectJobs(printer, destPrinter string) error {
req := NewRequest(OperationCupsRejectJobs, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) AddPrinterToClass(class, printer string) error {
attributes, err := c.GetPrinterAttributes(class, []string{"member-uris"})
if err != nil {
return err
}
memberURIList := make([]string, 0)
for _, member := range attributes["member-uris"] {
memberString := strings.Split(member.Value.(string), "/")
printerName := memberString[len(memberString)-1]
if printerName == printer {
return nil
}
memberURIList = append(memberURIList, member.Value.(string))
}
memberURIList = append(memberURIList, c.getPrinterUri(printer))
req := NewRequest(OperationCupsAddModifyClass, 1)
req.OperationAttributes["printer-uri"] = c.getClassUri(class)
req.PrinterAttributes["member-uris"] = memberURIList
_, err = c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) DeletePrinterFromClass(class, printer string) error {
attributes, err := c.GetPrinterAttributes(class, []string{"member-uris"})
if err != nil {
return err
}
memberURIList := make([]string, 0)
for _, member := range attributes["member-uris"] {
memberString := strings.Split(member.Value.(string), "/")
printerName := memberString[len(memberString)-1]
if printerName != printer {
memberURIList = append(memberURIList, member.Value.(string))
}
}
if len(memberURIList) == 0 {
return c.DeleteClass(class)
}
req := NewRequest(OperationCupsAddModifyClass, 1)
req.OperationAttributes["printer-uri"] = c.getClassUri(class)
req.PrinterAttributes["member-uris"] = memberURIList
_, err = c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) DeleteClass(class string) error {
req := NewRequest(OperationCupsDeleteClass, 1)
req.OperationAttributes["printer-uri"] = c.getClassUri(class)
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) CreatePrinter(name, deviceURI, ppd string, shared bool, errorPolicy string, information, location string) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(name)
req.OperationAttributes["ppd-name"] = ppd
req.OperationAttributes["printer-is-shared"] = shared
req.PrinterAttributes["printer-state-reason"] = "none"
req.PrinterAttributes["device-uri"] = deviceURI
req.PrinterAttributes["printer-info"] = information
req.PrinterAttributes["printer-location"] = location
req.PrinterAttributes["printer-error-policy"] = errorPolicy
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) SetPrinterPPD(printer, ppd string) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.OperationAttributes["ppd-name"] = ppd
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) SetPrinterDeviceURI(printer, deviceURI string) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.PrinterAttributes["device-uri"] = deviceURI
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) SetPrinterIsShared(printer string, shared bool) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.OperationAttributes["printer-is-shared"] = shared
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) SetPrinterErrorPolicy(printer string, errorPolicy string) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.PrinterAttributes["printer-error-policy"] = errorPolicy
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) SetPrinterInformation(printer, information string) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.PrinterAttributes["printer-info"] = information
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) SetPrinterLocation(printer, location string) error {
req := NewRequest(OperationCupsAddModifyPrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.PrinterAttributes["printer-location"] = location
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) DeletePrinter(printer string) error {
req := NewRequest(OperationCupsDeletePrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *CUPSClient) GetPrinters(attributes []string) (map[string]Attributes, error) {
req := NewRequest(OperationCupsGetPrinters, 1)
if attributes == nil {
req.OperationAttributes["requested-attributes"] = DefaultPrinterAttributes
} else {
req.OperationAttributes["requested-attributes"] = append(attributes, "printer-name")
}
resp, err := c.SendRequest(c.getHttpUri("", nil), req)
if err != nil {
return nil, err
}
printerNameMap := make(map[string]Attributes)
for _, printerAttributes := range resp.Printers {
printerNameMap[printerAttributes["printer-name"][0].Value.(string)] = printerAttributes
}
return printerNameMap, nil
}
func (c *CUPSClient) GetClasses(attributes []string) (map[string]Attributes, error) {
req := NewRequest(OperationCupsGetClasses, 1)
if attributes == nil {
req.OperationAttributes["requested-attributes"] = DefaultClassAttributes
} else {
req.OperationAttributes["requested-attributes"] = append(attributes, "printer-name")
}
resp, err := c.SendRequest(c.getHttpUri("", nil), req)
if err != nil {
return nil, err
}
printerNameMap := make(map[string]Attributes)
for _, printerAttributes := range resp.Printers {
printerNameMap[printerAttributes["printer-name"][0].Value.(string)] = printerAttributes
}
return printerNameMap, nil
}

View file

@ -3,8 +3,11 @@ package ipp
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"path"
@ -15,6 +18,13 @@ const (
ippContentType = "application/ipp"
)
type Document struct {
Document io.Reader
Size int
Name string
MimeType string
}
type IPPClient struct {
host string
port int
@ -37,15 +47,38 @@ func NewIPPClient(host string, port int, username, password string, useTLS bool)
return &IPPClient{host, port, username, password, useTLS, &httpClient}
}
func (c *IPPClient) getHttpUri(namespace, object string) string {
func (c *IPPClient) getHttpUri(namespace string, object interface{}) string {
proto := "http"
if c.useTLS {
proto = "https"
}
return fmt.Sprintf("%s://%s:%d/%s/%s", proto, c.host, c.port, namespace, object)
uri := fmt.Sprintf("%s://%s:%d", proto, c.host, c.port)
if namespace != "" {
uri = fmt.Sprintf("%s/%s", uri, namespace)
}
func (c *IPPClient) call(url string, req *Request) (*Response, error) {
if object != nil {
uri = fmt.Sprintf("%s/%v", uri, object)
}
return uri
}
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)
}
func (c *IPPClient) SendRequest(url string, req *Request) (*Response, error) {
payload, err := req.Encode()
if err != nil {
return nil, err
@ -84,14 +117,16 @@ func (c *IPPClient) call(url string, req *Request) (*Response, error) {
return nil, fmt.Errorf("ipp server returned with http status code %d", httpResp.StatusCode)
}
//httpyBody, _ := ioutil.ReadAll(httpResp.Body)
//fmt.Println(httpyBody)
//read the response into a temp buffer due to some wired EOF errors
httpBody, _ := ioutil.ReadAll(httpResp.Body)
//fmt.Println(httpBody)
return NewResponseDecoder(bytes.NewBuffer(httpBody)).Decode()
return NewResponseDecoder(httpResp.Body).Decode()
//return NewResponseDecoder(httpResp.Body).Decode()
}
func (c *IPPClient) Print(document io.Reader, size int, printer, jobName string, copies, priority int) (int, error) {
printerURI := fmt.Sprintf("ipp://localhost/printers/%s", printer)
func (c *IPPClient) Print(docs []Document, printer, jobName string, copies, priority int) (int, error) {
printerURI := c.getPrinterUri(printer)
req := NewRequest(OperationCreateJob, 1)
req.OperationAttributes["printer-uri"] = printerURI
@ -100,41 +135,46 @@ func (c *IPPClient) Print(document io.Reader, size int, printer, jobName string,
req.JobAttributes["copies"] = copies
req.JobAttributes["job-priority"] = priority
resp, err := c.call(c.getHttpUri("printers", printerURI), req)
resp, err := c.SendRequest(c.getHttpUri("printers", printer), req)
if err != nil {
return -1, err
}
jobID := resp.Jobs[0]["job-id"][0].Value.(int)
fmt.Println(jobID)
if len(resp.Jobs) == 0 {
return 0, errors.New("server doesn't returned a job id")
}
jobID := resp.Jobs[0]["job-id"][0].Value.(int)
documentCount := len(docs) - 1
for docID, doc := range docs {
req = NewRequest(OperationSendDocument, 2)
req.OperationAttributes["printer-uri"] = printerURI
req.OperationAttributes["requesting-user-name"] = c.username
req.OperationAttributes["job-id"] = jobID
req.OperationAttributes["document-name"] = jobName
req.OperationAttributes["document-format"] = "application/octet-stream"
req.OperationAttributes["last-document"] = true
req.File = document
req.FileSize = size
req.OperationAttributes["document-name"] = doc.Name
req.OperationAttributes["document-format"] = doc.MimeType
req.OperationAttributes["last-document"] = docID == documentCount
req.File = doc.Document
req.FileSize = doc.Size
resp, err = c.call(c.getHttpUri("printers", printerURI), req)
resp, err = c.SendRequest(c.getHttpUri("printers", printer), req)
if err != nil {
return -1, err
}
}
return jobID, nil
}
func (c *IPPClient) PrintFile(filePath, printer, jobName string, copies, priority int) (int, error) {
func (c *IPPClient) PrintFile(filePath, printer string, copies, priority int) (int, error) {
fileStats, err := os.Stat(filePath)
if os.IsNotExist(err) {
return -1, err
}
if jobName == "" {
jobName = path.Base(filePath)
}
fileName := path.Base(filePath)
document, err := os.Open(filePath)
if err != nil {
@ -142,5 +182,162 @@ func (c *IPPClient) PrintFile(filePath, printer, jobName string, copies, priorit
}
defer document.Close()
return c.Print(document, int(fileStats.Size()), printer, jobName, copies, priority)
return c.Print([]Document{
{
Document: document,
Name: fileName,
Size: int(fileStats.Size()),
MimeType: MimeTypeOctetStream,
},
}, printer, fileName, copies, priority)
}
func (c *IPPClient) GetPrinterAttributes(printer string, attributes []string) (Attributes, error) {
req := NewRequest(OperationGetPrinterAttributes, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.OperationAttributes["requesting-user-name"] = c.username
if attributes == nil {
req.OperationAttributes["requested-attributes"] = DefaultPrinterAttributes
} else {
req.OperationAttributes["requested-attributes"] = attributes
}
resp, err := c.SendRequest(c.getHttpUri("printers", printer), req)
if err != nil {
return nil, err
}
if len(resp.Printers) == 0 {
return nil, errors.New("server doesn't return any printer attributes")
}
return resp.Printers[0], nil
}
func (c *IPPClient) ResumePrinter(printer string) error {
req := NewRequest(OperationResumePrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *IPPClient) PausePrinter(printer string) error {
req := NewRequest(OperationPausePrinter, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *IPPClient) GetJobAttributes(jobID int, attributes []string) (Attributes, error) {
req := NewRequest(OperationGetJobAttributes, 1)
req.OperationAttributes["job-uri"] = c.getJobUri(jobID)
if attributes == nil {
req.OperationAttributes["requested-attributes"] = DefaultJobAttributes
} else {
req.OperationAttributes["requested-attributes"] = attributes
}
resp, err := c.SendRequest(c.getHttpUri("jobs", jobID), req)
if err != nil {
return nil, err
}
if len(resp.Printers) == 0 {
return nil, errors.New("server doesn't return any job attributes")
}
return resp.Printers[0], nil
}
func (c *IPPClient) GetJobs(printer string, whichJobs JobStateFilter, myJobs bool, attributes []string) (map[int]Attributes, error) {
req := NewRequest(OperationGetJobs, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.OperationAttributes["which-jobs"] = string(whichJobs)
req.OperationAttributes["my-jobs"] = myJobs
if attributes == nil {
req.OperationAttributes["requested-attributes"] = DefaultJobAttributes
} else {
req.OperationAttributes["requested-attributes"] = append(attributes, "job-id")
}
resp, err := c.SendRequest(c.getHttpUri("", nil), req)
if err != nil {
return nil, err
}
jobIDMap := make(map[int]Attributes)
for _, jobAttributes := range resp.Jobs {
jobIDMap[jobAttributes["job-id"][0].Value.(int)] = jobAttributes
}
return jobIDMap, nil
}
func (c *IPPClient) CancelJob(jobID int, purge bool) error {
req := NewRequest(OperationCancelJob, 1)
req.OperationAttributes["job-uri"] = c.getJobUri(jobID)
req.OperationAttributes["purge-jobs"] = purge
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req)
return err
}
func (c *IPPClient) CancelAllJob(printer string, purge bool) error {
req := NewRequest(OperationCancelJob, 1)
req.OperationAttributes["printer-uri"] = c.getPrinterUri(printer)
req.OperationAttributes["purge-jobs"] = purge
_, err := c.SendRequest(c.getHttpUri("admin", ""), req)
return err
}
func (c *IPPClient) RestartJob(jobID int) error {
req := NewRequest(OperationRestartJob, 1)
req.OperationAttributes["job-uri"] = c.getJobUri(jobID)
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req)
return err
}
func (c *IPPClient) HoldJobUntil(jobID int, holdUntil string) error {
req := NewRequest(OperationRestartJob, 1)
req.OperationAttributes["job-uri"] = c.getJobUri(jobID)
req.JobAttributes["job-hold-until"] = holdUntil
_, err := c.SendRequest(c.getHttpUri("jobs", ""), req)
return err
}
func (c *IPPClient) PrintTestPage(printer string) (int, error) {
testPage := new(bytes.Buffer)
testPage.WriteString("#PDF-BANNER\n")
testPage.WriteString("Template default-testpage.pdf\n")
testPage.WriteString("Show printer-name printer-info printer-location printer-make-and-model printer-driver-name")
testPage.WriteString("printer-driver-version paper-size imageable-area job-id options time-at-creation")
testPage.WriteString("time-at-processing\n\n")
return c.Print([]Document{
{
Document: testPage,
Name: "Test Page",
Size: testPage.Len(),
MimeType: MimeTypePostscript,
},
}, printer, "Test Page", 1, DefaultJobPriority)
}
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
}

View file

@ -93,7 +93,6 @@ func (d *ResponseDecoder) Decode() (*Response, error) {
}
startByte := startByteSlice[0]
fmt.Printf("start byte: %v\n", startByte)
// check if attributes are completed
if uint8(startByte) == TagEnd {
@ -137,7 +136,6 @@ func (d *ResponseDecoder) Decode() (*Response, error) {
startByte = startByteSlice[0]
}
fmt.Printf("tag byte: %v\n", startByte)
attrib, err := attribDecoder.Decode(Tag(startByte))
if err != nil {
return nil, err
@ -153,8 +151,6 @@ func (d *ResponseDecoder) Decode() (*Response, error) {
tagSet = false
}
fmt.Println("asdf")
if len(tempAttributes) > 0 && tag != TagCupsInvalid {
appendAttribute(resp, tag, tempAttributes)
}
@ -163,6 +159,10 @@ func (d *ResponseDecoder) Decode() (*Response, error) {
return nil, err
}
if resp.StatusCode != 0 {
return resp, errors.New(resp.OperationAttributes["status-message"][0].Value.(string))
}
return resp, nil
}

27
utils.go Normal file
View file

@ -0,0 +1,27 @@
package ipp
import (
"fmt"
"os"
"path"
)
func ParseControlFile(jobID int, spoolDirectory string) (*Response, error) {
if spoolDirectory == "" {
spoolDirectory = "/var/spool/cups"
}
controlFilePath := path.Join(spoolDirectory, fmt.Sprintf("c%d", jobID))
if _, err := os.Stat(controlFilePath); err != nil {
return nil, err
}
controlFile, err := os.Open(controlFilePath)
if err != nil {
return nil, err
}
defer controlFile.Close()
return NewResponseDecoder(controlFile).Decode()
}