3 Commits

38 changed files with 156 additions and 41 deletions

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
@@ -100,7 +102,7 @@ func printFile(fname string) {
fmt.Print(rb)
}
/*
requesting-user-name:chronos (nameWithoutLanguagageValueTag)
job-name:2 - Untitled (nameWithoutLanguagageValueTag)

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
type charsetAttribute struct {

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
type KeyWord struct {

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,6 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
//Package ipp provides functonality to handle ipp messages
//go:generate stringer -type jobState -type printerState
package ipp
@@ -115,6 +118,7 @@ const (
SuccessfulOkIgnoredOrSubstitutedAttributes statusCode = 0x0001
SuccessfulOkConflictingAttributes statusCode = 0x0002
ClientErrorBadRequest statusCode = 0x0400
ServerErrorServiceUnavailable statusCode = 0x0502
)
type versionNumber uint16

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
type MimeMediaType struct {

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import "io"

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (
@@ -65,6 +67,7 @@ func (r *Request) RequestID() uint32 {
return r.header.requestID
}
// Operation returns the operation is of the request.
func (r *Request) Operation() OperationID {
return r.header.operationID
}

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (
@@ -52,6 +54,10 @@ func (r Response) String() string {
return r.header.String() + "\n" + r.a.String()
}
func (r Response) Header() ippResponseHeader {
return r.header
}
// Marshal converts the response object to a wire formatted byte stream.
func (r *Response) Marshal() []byte {
a := make([]byte, 0, 20)

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import "encoding/binary"

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import "io"

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package ipp
import (

View File

@@ -1,7 +1,10 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package mdnsserver
import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
@@ -9,40 +12,40 @@ import (
"github.com/holoplot/go-avahi"
)
func Run(ctx context.Context) {
func Run(ctx context.Context, location string, port uint16, name string) {
conn, err := dbus.SystemBus()
if err != nil {
return
}
a, err := avahi.ServerNew(conn)
if err != nil {
log.Fatalf("Avahi new failed: %v", err)
log.Fatalf("Failed to connect to avahi: %v", err)
}
eg, err := a.EntryGroupNew()
if err != nil {
log.Fatalf("EntryGroupNew() failed: %v", err)
log.Fatalf("Failed to create entry group: %v", err)
}
fqdn, err := a.GetHostNameFqdn()
if err != nil {
log.Fatalf("GetHostNameFqdn() failed: %v", err)
log.Fatalf("failed to get hostname: %v", err)
}
var txt [][]byte
txt = append(txt, []byte("note=burken"))
notestring := fmt.Sprintf("note=%v", location)
txt = append(txt, []byte(notestring))
txt = append(txt, []byte("product=ChroBroPrint V1"))
txt = append(txt, []byte("Color=T"))
txt = append(txt, []byte("rp=ipp/print"))
txt = append(txt, []byte("ty=ChroBroPrint"))
err = eg.AddService(avahi.InterfaceUnspec, avahi.ProtoUnspec, 0, "ChroBroPrint", "_ipp._tcp", "local", fqdn, 1234, txt)
err = eg.AddService(avahi.InterfaceUnspec, avahi.ProtoUnspec, 0, name, "_ipp._tcp", "local", fqdn, port, txt)
if err != nil {
log.Fatalf("AddService() failed: %v", err)
log.Fatalf("Failed to add service to avahi: %v", err)
}
err = eg.Commit()
if err != nil {
log.Fatalf("Commit() failed: %v", err)
log.Fatalf("Failed to commit avahi changes: %v", err)
}
<-ctx.Done()

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import "ippserver/packages/ipp"

View File

@@ -1,20 +1,21 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"bytes"
"fmt"
"ippserver/packages/ipp"
"net/http"
log "github.com/sirupsen/logrus"
)
func handleGetJobAttributes(r *ipp.Request, requestID uint32) *ipp.Response {
func handleGetJobAttributes(r *ipp.Request, requestID uint32) (*ipp.Response, error) {
request := ipp.NewRequest(ipp.GetJobAttributes, requestID)
request.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
request.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", "ipp://"+printerURI))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", ippurl.String()))
request.AddOperatonAttribute(r.GetAttribute("job-id"))
request.AddOperatonAttribute(r.GetAttribute("requesting-user-name"))
request.AddOperatonAttribute(r.GetAttribute("requested-attributes"))
@@ -24,9 +25,9 @@ func handleGetJobAttributes(r *ipp.Request, requestID uint32) *ipp.Response {
downStreamRequest := request.Marshal()
b := bytes.NewBuffer(downStreamRequest)
downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", b)
downStreamResponse, err := http.Post(httpurl.String(), "application/ipp", b)
if err != nil {
fmt.Print(err)
return nil, err
}
rb := ipp.NewResponse(0, 0)
@@ -42,5 +43,5 @@ func handleGetJobAttributes(r *ipp.Request, requestID uint32) *ipp.Response {
response.AddJobAttribute(rb.GetAttribute("job-state"))
response.AddJobAttribute(rb.GetAttribute("job-state-reasons"))
return response
return response, nil
}

View File

@@ -1,20 +1,21 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"bytes"
"fmt"
"ippserver/packages/ipp"
"net/http"
log "github.com/sirupsen/logrus"
)
func handleGetJobs(r *ipp.Request, requestID uint32) *ipp.Response {
func handleGetJobs(r *ipp.Request, requestID uint32) (*ipp.Response, error) {
request := ipp.NewRequest(ipp.GetJobs, requestID)
request.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
request.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", "ipp://"+printerURI))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", ippurl.String()))
request.AddOperatonAttribute(r.GetAttribute("requesting-user-name"))
request.AddOperatonAttribute(r.GetAttribute("requested-attributes"))
@@ -23,9 +24,9 @@ func handleGetJobs(r *ipp.Request, requestID uint32) *ipp.Response {
downStreamRequest := request.Marshal()
b := bytes.NewBuffer(downStreamRequest)
downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", b)
downStreamResponse, err := http.Post(httpurl.String(), "application/ipp", b)
if err != nil {
fmt.Print(err)
return nil, err
}
//log.Printf("response HTTP status: %v %v", downStreamResponse.StatusCode, downStreamResponse.Status)
@@ -43,5 +44,5 @@ func handleGetJobs(r *ipp.Request, requestID uint32) *ipp.Response {
response.AddJobAttribute(rb.GetAttribute("job-state"))
response.AddJobAttribute(rb.GetAttribute("job-state-reasons"))
return response
return response, nil
}

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import "ippserver/packages/ipp"

View File

@@ -1,8 +1,9 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"bytes"
"fmt"
"io"
"ippserver/packages/ipp"
"net/http"
@@ -12,15 +13,15 @@ import (
log "github.com/sirupsen/logrus"
)
const printerURI = "brn30055cb5e3ae.local:631/ipp/print"
func handlePrintJob(r *ipp.Request, byteStream io.Reader, requestID uint32) *ipp.Response {
//const printerURI = "brn30055cb5e3ae.local:631/ipp/print"
func handlePrintJob(r *ipp.Request, byteStream io.Reader, requestID uint32) (*ipp.Response, error) {
// This request is what will be sent to the real printer
request := ipp.NewRequest(ipp.PrintJob, requestID)
request.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
request.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", "ipp://"+printerURI))
//request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", "ipp://"+printerURI))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", ippurl.String()))
request.AddOperatonAttribute(r.GetAttribute("requesting-user-name"))
request.AddOperatonAttribute(r.GetAttribute("job-name"))
request.AddOperatonAttribute(ipp.NewMimeMediaType("document-format", "image/pwg-raster"))
@@ -36,9 +37,10 @@ func handlePrintJob(r *ipp.Request, byteStream io.Reader, requestID uint32) *ipp
b := bytes.NewBuffer(downStreamRequest)
mr := io.MultiReader(b, byteStream)
downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", mr)
//downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", mr)
downStreamResponse, err := http.Post(httpurl.String(), "application/ipp", mr)
if err != nil {
fmt.Print(err)
return nil, err
}
rb := ipp.NewResponse(0, 0)
@@ -64,5 +66,5 @@ func handlePrintJob(r *ipp.Request, byteStream io.Reader, requestID uint32) *ipp
response.AddJobAttribute(rb.GetAttribute("job-state"))
response.AddJobAttribute(rb.GetAttribute("job-state-reasons"))
return response
return response, nil
}

View File

@@ -1,30 +1,62 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
"context"
"flag"
"fmt"
"ippserver/packages/ipp"
"ippserver/packages/mdnsserver"
"net/http"
"net/url"
"sync"
log "github.com/sirupsen/logrus"
)
var (
loglevel string
location string
port uint
printerURI string
printerName string
ippurl, httpurl *url.URL
)
func init() {
flag.StringVar(&loglevel, "loglevel", "info", "The wanted loglevel error/info/debug")
flag.StringVar(&location, "location", "somewhere", "locaton of the printer as shown in mDNS")
flag.UintVar(&port, "port", 1234, "tcp port")
flag.StringVar(&printerURI, "printer", "", "URL to the real printer, typical ipp://printername.local:631/ipp/print")
flag.StringVar(&printerName, "name", "ChroBroPrint", "Name of the printer advertised with mDNS")
}
func main() {
var err error
flag.Parse()
customFormatter := new(log.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
log.SetFormatter(customFormatter)
customFormatter.FullTimestamp = true
log.SetLevel(log.InfoLevel)
ippurl, err = url.Parse(printerURI)
if err != nil {
fmt.Printf("Failed to parse printer URL %v", err.Error())
return
}
httpurl, _ = url.Parse(printerURI)
httpurl.Scheme = "http"
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go mdnsserver.Run(ctx)
go mdnsserver.Run(ctx, location, uint16(port), printerName)
http.HandleFunc("/ipp/print", handle)
log.Info("http server started on :1234")
err := http.ListenAndServe(":1234", nil)
log.Infof("http server starting on :%v", port)
err = http.ListenAndServe(fmt.Sprintf(":%v", port), nil)
if err != nil {
log.Fatal("ListenAndServe: " + err.Error())
}
@@ -39,29 +71,32 @@ func handle(w http.ResponseWriter, r *http.Request) {
mut.Unlock()
if r.Method != http.MethodPost {
http.Error(w, "Unsupported method", http.StatusMethodNotAllowed)
}
request := ipp.NewRequest(0, 0)
request.UnMarshal(r.Body)
log.Infof("Upstream Request: \n%v\n", request)
log.Infof("Upstream Request: id: %v op: %v", request.RequestID(), request.Operation())
var response *ipp.Response
var err error
switch request.Operation() {
case ipp.GetPrinterAttributes:
response = handleGetPrinterAttributes(request)
case ipp.PrintJob:
response = handlePrintJob(request, r.Body, requestID)
response, err = handlePrintJob(request, r.Body, requestID)
case ipp.GetJobs:
response = handleGetJobs(request, requestID)
response, err = handleGetJobs(request, requestID)
case ipp.ValidateJob:
response = handleValidateJob(request)
case ipp.GetJobAttributes:
response = handleGetJobAttributes(request, requestID)
response, err = handleGetJobAttributes(request, requestID)
default:
response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestID())
}
log.Infof("Upstream Response:\n%v\n", response)
if err != nil {
log.Errorf("Failed to handle request: %v", err.Error())
response = ipp.NewResponse(ipp.ServerErrorServiceUnavailable, request.RequestID())
}
log.Infof("Upstream Response: %v", response.Header())
data := response.Marshal()
w.Write(data)
}

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import "ippserver/packages/ipp"

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import "ippserver/packages/ipp"

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import "ippserver/packages/ipp"

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (

View File

@@ -1,3 +1,5 @@
// Copyright 2021, Henrik Sölver henrik.solver@gmail.com
// SPDX-License-Identifier: BSD-3-Clause
package main
import (
@@ -18,7 +20,7 @@ func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go mdnsserver.Run(ctx)
go mdnsserver.Run(ctx, "some location", 1234, "ChroBroPrint")
http.HandleFunc("/ipp/print", handle)