6 Commits

Author SHA1 Message Date
77ef627fe2 More development.
More types.
Fixed attribute groups in requests.
Started on client.
Saving data to file.
2020-12-26 20:58:38 +01:00
8b5f69cb52 More types. Printing from chromeos works a little bit. 2020-12-25 23:25:39 +01:00
7ecbae27c2 More types. 2020-12-23 23:21:47 +01:00
a1261c1b8f Spelling corrections. 2020-12-23 21:51:30 +01:00
c3c608b28a WIP: Fix keyword handling 2020-12-05 21:29:57 +01:00
74c1ce257a Move request to a separate file and add test. 2020-12-05 21:26:10 +01:00
39 changed files with 436 additions and 1490 deletions

View File

@@ -1,123 +0,0 @@
package main
import (
"bytes"
"fmt"
"io"
"ippserver/packages/ipp"
"net/http"
"os"
log "github.com/sirupsen/logrus"
)
const printerURI = "brn30055cb5e3ae.local:631/ipp/print"
//application/ipp
// brn30055cb5e3ae.local:631/ipp/print
func main() {
customFormatter := new(log.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
log.SetFormatter(customFormatter)
customFormatter.FullTimestamp = true
log.SetLevel(log.InfoLevel)
request := ipp.NewRequest(ipp.GetPrinterAttributes, 10)
request.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
request.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
request.AddOperatonAttribute(ipp.NewURIValue("printer-uri", "ipp://"+printerURI))
r := request.Marshal()
b := bytes.NewBuffer(r)
httpResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", b)
if err != nil {
fmt.Print(err)
return
}
rb := ipp.NewResponse(0, 0)
rb.UnMarshal(httpResponse.Body)
fmt.Print(rb)
// fmt.Print("\n-------------\n")
// printFile("4-untitled")
}
func printFile(fname string) {
f, err := os.Open(fname)
if err != nil {
log.Errorf("Failed to open file %v", fname)
return
}
// fileContents, err := ioutil.ReadAll(f)
// if err != nil {
// log.Errorf("Failed to read file %v", fname)
// return
// }
request := ipp.NewRequest(ipp.PrintJob, 10)
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.NewNameWithoutLanguage("requesting-user-name", "chronos"))
request.AddOperatonAttribute(ipp.NewNameWithoutLanguage("job-name", "foobar"))
request.AddOperatonAttribute(ipp.NewMimeMediaType("document-format", "image/pwg-raster"))
//request.AddJobAttribute(ipp.NewNameWithoutLanguage("ColorModel", "Gray"))
//request.AddJobAttribute(ipp.NewNameWithoutLanguage("cupsPrintQuality", "Normal"))
//request.AddJobAttribute(ipp.NewNameWithoutLanguage("Duplex", "DuplexNoTumble"))
//request.AddJobAttribute(ipp.NewNameWithoutLanguage("job-originating-host-name", "localhost"))
//request.AddJobAttribute(ipp.NewURIValue("name string", value string)
request.AddJobAttribute(ipp.NewKeyWord("media", "iso_a4_210x297mm"))
//request.AddJobAttribute(ipp.NewKeyWord("multiple-document-handling", "separate-documents-collated-copies"))
request.AddJobAttribute(ipp.NewKeyWord("output-bin", "face-down"))
//request.AddJobAttribute(ipp.NewNameWithoutLanguage("PageSize", "A4"))
request.AddJobAttribute(ipp.NewKeyWord("print-color-mode", "monochrome"))
request.AddJobAttribute(ipp.NewSetOfResolution("printer-resolution", ipp.Resolution{CrossFeedResolution: 600, FeedResolution: 600, Unit: 3}))
request.AddJobAttribute(ipp.NewKeyWord("sides", "two-sided-long-edge"))
fmt.Print(request)
r := request.Marshal()
b := bytes.NewBuffer(r)
mr := io.MultiReader(b, f)
httpResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", mr)
if err != nil {
fmt.Print(err)
return
}
fmt.Print("\n---response------\n")
log.Printf("HTTP status: %v %v", httpResponse.StatusCode, httpResponse.Status)
// resp, err := io.ReadAll(httpResponse.Body)
// if err != nil {
// log.Error("could not read response")
// return
// }
// fmt.Printf("% x", resp)
rb := ipp.NewResponse(0, 0)
rb.UnMarshal(httpResponse.Body)
fmt.Print(rb)
}
/*
requesting-user-name:chronos (nameWithoutLanguagageValueTag)
job-name:2 - Untitled (nameWithoutLanguagageValueTag)
document-format : image/pwg-raster (mimeMediaTypeValueTag)
document-format : application/octet-stream (mimeMediaTypeValueTag)
PrinterAttributes
JobAttributes
ColorModel:Gray (nameWithoutLanguagageValueTag)
cupsPrintQuality:Normal (nameWithoutLanguagageValueTag)
Duplex:DuplexNoTumble (nameWithoutLanguagageValueTag)
job-originating-host-name:localhost (nameWithoutLanguagageValueTag)
job-uuid:urn:uuid:67a09d25-5df8-3026-6e9c-b55f7afee4ca (uriValueTag)
media : iso_a4_210x297mm (keywordValueTag)
multiple-document-handling : separate-documents-collated-copies (keywordValueTag)
output-bin : face-down (keywordValueTag)
PageSize:A4 (nameWithoutLanguagageValueTag)
print-color-mode : monochrome (keywordValueTag)
printer-resolution:[{600 600 3}] (resolutionValueTag)
sides : two-sided-long-edge (keywordValueTag)
*/

View File

@@ -1,66 +0,0 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
// Boolean is the ipp attribute boolean
type Boolean struct {
name string
value bool
}
// NewBoolean creates a nre boolean attribute
func NewBoolean(name string, value bool) *Boolean {
b := new(Boolean)
b.name = name
b.value = value
return b
}
// Name gets tha name of the boolean attribute
func (b Boolean) Name() string {
return b.name
}
func (b Boolean) String() string {
return b.name + ":" + fmt.Sprint(b.value)
}
func (b *Boolean) valueTag() tag {
return booleanValueTag
}
func (b *Boolean) unmarshal(byteStream io.Reader) {
name, data := unmarshalSingleAttribute(byteStream)
b.name = name
if data[0] == 0 {
b.value = false
return
}
b.value = true
}
func (b *Boolean) marshal() []byte {
l := 5 + len(b.name)
ba := make([]byte, 0, l)
buf := bytes.NewBuffer(ba)
buf.WriteByte(byte(booleanValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(b.name)))
buf.WriteString(b.name)
binary.Write(buf, binary.BigEndian, uint16(1))
if b.value {
buf.WriteByte(byte(1))
} else {
buf.WriteByte(byte(0))
}
return buf.Bytes()
}
func (b *Boolean) size() int {
l := 5 + len(b.name)
return l
}

View File

@@ -4,47 +4,42 @@ import (
"io" "io"
) )
type CharSetValue struct { type charSetValue struct {
name string name string
value string value string
} }
func NewCharSetValue(name string, value string) *CharSetValue { func NewCharSetValue(name string, value string) *charSetValue {
c := new(CharSetValue) c := new(charSetValue)
c.name = name c.name = name
c.value = value c.value = value
return c return c
} }
func (c CharSetValue) Name() string { func (c charSetValue) String() string {
return c.name
}
func (c CharSetValue) String() string {
return c.name + ":" + c.value return c.name + ":" + c.value
} }
func (c *charSetValue) valueTag() tag {
func (c *CharSetValue) valueTag() tag {
return charsetValueTag return charsetValueTag
} }
func (c *CharSetValue) unmarshal(byteStream io.Reader) { func (c *charSetValue) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream) c.name, c.value = unmarshalSingleValue(byteStream)
} }
func (c *CharSetValue) marshal() []byte { func (c *charSetValue) marshal() []byte {
l := 5 + len(c.name) + len(c.value) l := 5 + len(c.name) + len(c.value)
b := make([]byte, l) b := make([]byte, l, l)
b[0] = byte(charsetValueTag) b[0] = byte(charsetValueTag)
marshalNameValue(c.name, c.value, b[1:]) marshalNameValue(c.name, c.value, b[1:])
return b return b
} }
func (c *CharSetValue) marshalInto([]byte) int { func (c *charSetValue) marshalInto([]byte) int {
return 0 return 0
} }
func (c *CharSetValue) size() int { func (c *charSetValue) size() int {
l := 1 + 4 // The attribute tag + 2 lengths l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name) l += len(c.name)
l += len(c.value) l += len(c.value)

View File

@@ -1,74 +0,0 @@
package ipp
import (
"bufio"
"encoding/binary"
log "github.com/sirupsen/logrus"
)
// Currently the collection data is just consumed and dropped from the bytestream
func consumeCollection(byteStream *bufio.Reader) {
// RFC8010 Section 3.1.6
var length uint16
binary.Read(byteStream, binary.BigEndian, &length)
collectionName := make([]byte, length)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, collectionName)
}
log.Info("collection name " + string(collectionName))
err := binary.Read(byteStream, binary.BigEndian, &length) //Always zero ??
if err != nil {
log.Fatal("error ", err.Error())
}
if length != 0 {
log.Fatal("Should be zero")
}
// Member attributes
done:
for {
var t tag
binary.Read(byteStream, binary.BigEndian, &t)
log.Debug("Collection tag ", t)
switch t {
case endCollectionValueTag:
binary.Read(byteStream, binary.BigEndian, &length)
if length != 0 {
log.Fatal("Should be zero")
}
binary.Read(byteStream, binary.BigEndian, &length)
if length != 0 {
log.Fatal("Should be zero")
}
case memberAttrNameValueTag:
// RFC8010 Section 3.7.1
binary.Read(byteStream, binary.BigEndian, &length) //Always zero ??
if length != 0 {
log.Fatal("Should be zero")
}
binary.Read(byteStream, binary.BigEndian, &length) // Value length
memberName := make([]byte, length)
binary.Read(byteStream, binary.BigEndian, memberName)
log.Debugf("Member name: %v", string(memberName))
var memberValueTag tag
binary.Read(byteStream, binary.BigEndian, &memberValueTag)
log.Debug("Member value tag: ", memberValueTag)
binary.Read(byteStream, binary.BigEndian, &length) //Always zero ??
if length != 0 {
log.Fatal("Should be zero")
}
var memberValueLength uint16
binary.Read(byteStream, binary.BigEndian, &memberValueLength)
memberValue := make([]byte, memberValueLength)
binary.Read(byteStream, binary.BigEndian, memberValue)
log.Debugf("Member Value: % x", memberValue)
default:
// Next tag is one that can not be handled in the collection
// Put it back in the byte stream and return to main loop
byteStream.UnreadByte()
break done
}
}
log.Debug("Collection done")
}

View File

@@ -1,41 +1,52 @@
package ipp package ipp
import ( import (
"bytes"
"encoding/binary"
"fmt" "fmt"
"io"
log "github.com/sirupsen/logrus"
) )
type Enum struct { type enum struct {
name string name string
values []int32 value int32
} }
func NewEnum(name string, values ...int32) *Enum { func NewEnum(name string, value int32) *enum {
e := new(Enum) e := new(enum)
e.name = name e.name = name
e.values = values e.value = value
return e return e
} }
func (e Enum) Name() string { func (e enum) String() string {
return e.name return e.name + ":" + fmt.Sprint(e.value)
} }
func (e *enum) valueTag() tag {
func (e Enum) String() string {
return e.name + ":" + fmt.Sprint(e.values)
}
func (e *Enum) valueTag() tag {
return enumValueTag return enumValueTag
} }
func (e *Enum) size() int { func (e *enum) unmarshal(byteStream io.Reader) {
return 9 + len(e.name) log.Warn("Unmarshal of enum is not implemented yet")
} }
func (e *Enum) addValue(v interface{}) { func (e *enum) marshal() []byte {
e.values = append(e.values, v.(int32)) l := 3 + len(e.name) + 6
b := make([]byte, 0, l)
buf := bytes.NewBuffer(b)
buf.WriteByte(byte(enumValueTag))
binary.Write(buf, binary.BigEndian, uint16(len(e.name)))
buf.WriteString(e.name)
binary.Write(buf, binary.BigEndian, uint16(4))
binary.Write(buf, binary.BigEndian, e.value)
return buf.Bytes()
} }
func (e *Enum) marshal() []byte { func (e *enum) size() int {
return marshalInteger(enumValueTag, e.name, e.values) l := 1 + 4 // The attribute tag + 2 lengths
l += len(e.name)
l += 4
return l
} }

View File

@@ -1,67 +0,0 @@
package ipp
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
type Integer struct {
name string
values []int32
}
func NewInteger(name string, values ...int32) *Integer {
e := new(Integer)
e.name = name
e.values = values
return e
}
func (i Integer) Name() string {
return i.name
}
func (i Integer) String() string {
return i.name + ":" + fmt.Sprint(i.values)
}
func (i *Integer) valueTag() tag {
return integerValueTag
}
func (i *Integer) size() int {
return 9 + len(i.name) // The attribute tag + 2 lengths
}
func (i *Integer) addValue(v interface{}) {
i.values = append(i.values, v.(int32))
}
func (i *Integer) marshal() []byte {
return marshalInteger(integerValueTag, i.name, i.values)
}
func marshalInteger(t tag, name string, values []int32) []byte {
l := 9 + len(name) + 9*(len(values)-1)
b := make([]byte, 0, l)
buf := bytes.NewBuffer(b)
buf.WriteByte(byte(t))
binary.Write(buf, binary.BigEndian, uint16(len(name)))
buf.WriteString(name)
binary.Write(buf, binary.BigEndian, uint16(4))
binary.Write(buf, binary.BigEndian, values[0])
for _, v := range values[1:] {
buf.WriteByte(byte(t))
binary.Write(buf, binary.BigEndian, uint16(0))
binary.Write(buf, binary.BigEndian, uint16(4))
binary.Write(buf, binary.BigEndian, v)
}
return buf.Bytes()
}
func unmarshalSingleInteger(byteStream io.Reader) (string, int32) {
name, data := unmarshalSingleAttribute(byteStream)
return name, int32(binary.BigEndian.Uint32(data))
}

View File

@@ -1,38 +0,0 @@
package ipp
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnMarshalSinglePositiveInteger(T *testing.T) {
testdata := []byte{
0x00, 0x04,
0x66, 0x6c, 0x6f, 0x70, //flop
0x00, 0x04,
0x00, 0x0, 0x0, 0x4,
}
buf := bytes.NewBuffer(testdata)
n, v := unmarshalSingleInteger(buf)
assert.Equal(T, "flop", n, "Should be equal")
assert.Equal(T, int32(4), v, "Should be equal")
}
func TestUnMarshalSingleNegativeInteger(T *testing.T) {
testdata := []byte{
0x00, 0x04,
0x66, 0x6c, 0x6f, 0x70, //flop
0x00, 0x04,
0xff, 0xff, 0xff, 0xfc,
}
buf := bytes.NewBuffer(testdata)
n, v := unmarshalSingleInteger(buf)
assert.Equal(T, "flop", n, "Should be equal")
assert.Equal(T, int32(-4), v, "Should be equal")
}

View File

@@ -1,37 +1,33 @@
package ipp package ipp
type KeyWord struct { type keyWord struct {
sos *SetOfStrings sos *setOfStrings
} }
func NewKeyWord(name string, values ...string) *KeyWord { func NewKeyWord(name string, values ...string) *keyWord {
k := new(KeyWord) k := new(keyWord)
k.sos = NewSetOfStrings(name, keyWordValueTag, values) k.sos = NewSetOfStrings(name, keyWordValueTag, values)
return k return k
} }
func (k KeyWord) Name() string { func (k keyWord) String() string {
return k.sos.name
}
func (k KeyWord) String() string {
return k.sos.String() return k.sos.String()
} }
func (k *KeyWord) size() int { func (k *keyWord) size() int {
return k.sos.size() return k.sos.size()
} }
func (k *KeyWord) valueTag() tag { func (k *keyWord) valueTag() tag {
return k.sos.valueTag() return k.sos.valueTag()
} }
func (k *KeyWord) marshal() []byte { func (k *keyWord) marshal() []byte {
return k.sos.marshal() return k.sos.marshal()
} }
func (k *KeyWord) addValue(v interface{}) { func (k *keyWord) addValue(v string) {
k.sos.AddValue(v.(string)) k.sos.AddValue(v)
} }

View File

@@ -1,24 +1,15 @@
//Package ipp provides functonality to handle ipp messages
//go:generate stringer -type jobState -type printerState
package ipp package ipp
import ( import (
"bufio"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
log "github.com/sirupsen/logrus"
) )
// References // References
// https://tools.ietf.org/html/rfc8010 // https://tools.ietf.org/html/rfc8010
// https://tools.ietf.org/html/rfc8011 // https://tools.ietf.org/html/rfc8011
// ErrNilAttribute is returned when an attempt to use nil as a attribute
var ErrNilAttribute = errors.New("can not add use nil as attribute")
// Defined value tags // Defined value tags
// from rfc8010 // from rfc8010
type tag uint8 type tag uint8
@@ -63,59 +54,41 @@ const (
memberAttrNameValueTag tag = 0x4a memberAttrNameValueTag tag = 0x4a
) )
// OperationID is defined in rfc8011 // Operation-id, defined in rfc8011
type OperationID uint16 type OperationId uint16
const ( const (
PrintJob OperationID = 0x0002 PrintJob OperationId = 0x0002
PrintURI OperationID = 0x0003 PrintURI OperationId = 0x0003
ValidateJob OperationID = 0x0004 ValidateJob OperationId = 0x0004
CreateJob OperationID = 0x0005 CreateJob OperationId = 0x0005
SendDocument OperationID = 0x0006 SendDocument OperationId = 0x0006
SendURI OperationID = 0x0007 SendURI OperationId = 0x0007
CancelJob OperationID = 0x0008 CancelJob OperationId = 0x0008
GetJobAttributes OperationID = 0x0009 GetJobAttributes OperationId = 0x0009
GetJobs OperationID = 0x000a GetJobs OperationId = 0x000a
GetPrinterAttributes OperationID = 0x000b GetPrinterAttributes OperationId = 0x000b
HoldJob OperationID = 0x000c HoldJob OperationId = 0x000c
ReleaseJob OperationID = 0x000d ReleaseJob OperationId = 0x000d
RestartJob OperationID = 0x000e RestartJob OperationId = 0x000e
PausePrinter OperationID = 0x0010 PausePrinter OperationId = 0x0010
ResumePrinter OperationID = 0x0011 ResumePrinter OperationId = 0x0011
PurgeJobs OperationID = 0x0012 PurgeJobs OperationId = 0x0012
) )
type printerState int32 type printerState int32
// printerstate defenitions
const ( const (
Idle printerState = 3 Idle printerState = 3
Processing printerState = 4 Processing printerState = 4
Stopped printerState = 5 Stopped printerState = 5
) )
// jobstate defined in rfc8011 ch 5.3.7
type jobState uint16
const (
pending jobState = 3
pendingHeld jobState = 4
processing jobState = 5
processingStoppped jobState = 6
cancelled jobState = 7
aborted jobState = 8
completed jobState = 9
)
type statusCode uint16 type statusCode uint16
// status code defenitions
const ( const (
SuccessfulOk statusCode = 0x0000 SuccessfulOk statusCode = 0x0000
SuccessfulOkIgnoredOrSubstitutedAttributes statusCode = 0x0001 ClientErrorBadRequest statusCode = 0x0400
SuccessfulOkConflictingAttributes statusCode = 0x0002
ClientErrorBadRequest statusCode = 0x0400
ServerErrorServiceUnavailable statusCode = 0x0502
) )
type versionNumber uint16 type versionNumber uint16
@@ -125,19 +98,6 @@ func (v versionNumber) String() string {
return fmt.Sprintf("%x.%x", vn&0xff00>>8, vn&0x00ff) return fmt.Sprintf("%x.%x", vn&0xff00>>8, vn&0x00ff)
} }
func unmarshalSingleAttribute(byteStream io.Reader) (string, []byte) {
var length uint16
binary.Read(byteStream, binary.BigEndian, &length)
attributeName := make([]byte, length)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, attributeName)
}
binary.Read(byteStream, binary.BigEndian, &length)
attributeValue := make([]byte, length)
binary.Read(byteStream, binary.BigEndian, attributeValue)
return string(attributeName), attributeValue
}
func unmarshalSingleValue(byteStream io.Reader) (string, string) { func unmarshalSingleValue(byteStream io.Reader) (string, string) {
var length uint16 var length uint16
binary.Read(byteStream, binary.BigEndian, &length) binary.Read(byteStream, binary.BigEndian, &length)
@@ -161,188 +121,3 @@ func marshalNameValue(name, value string, b []byte) {
p += 2 p += 2
copy(b[p:], []byte(value)) copy(b[p:], []byte(value))
} }
type Attribute interface {
Name() string
valueTag() tag
marshal() []byte
//size() int
}
type Attributes struct {
operation []Attribute
printer []Attribute
job []Attribute
unsupported []Attribute
}
func (a *Attributes) String() string {
s := " OperationAttributes" + "\n"
for _, a := range a.operation {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " PrinterAttributes" + "\n"
for _, a := range a.printer {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " JobAttributes" + "\n"
for _, a := range a.job {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " Unsupported" + "\n"
for _, a := range a.unsupported {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
}
func (a *Attributes) addAttribute(group tag, attr Attribute) {
switch group {
case operationAttributes:
a.operation = append(a.operation, attr)
case jobAttributes:
a.job = append(a.job, attr)
case printerAttributes:
a.printer = append(a.printer, attr)
case unsupportedAttributes:
a.unsupported = append(a.unsupported, attr)
default:
log.Errorf("Unknown attribute group %v", group)
}
}
func UnMarshalAttributes(bytestream *bufio.Reader) *Attributes {
a := new(Attributes)
var t tag
err := binary.Read(bytestream, binary.BigEndian, &t)
if err != nil {
log.Error(err.Error())
}
log.Debugf("got tag - %v", t)
if t != operationAttributes && t != jobAttributes && t != printerAttributes {
log.Errorf("Unknown attribute group tag %v", t)
return nil
}
currentAttributeGroup := t
var lastAddValuer AddValuer
for {
err = binary.Read(bytestream, binary.BigEndian, &t)
if err != nil {
log.Fatal("End of input before end of attributes tag (%v)", err.Error())
}
log.Debugf("Value tag - %v", t)
switch t {
case endOfAttributes:
return a
case charsetValueTag:
c := NewCharSetValue("", "")
c.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, c)
log.Debugf("%v %v", c.name, c.value)
case booleanValueTag:
na := NewBoolean("", false)
na.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, na)
case uriValueTag:
u := NewURIValue("", "")
u.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, u)
log.Debugf("%v %v", u.name, u.value)
case naturalLanguageValueTag:
n := NewNaturalLanguage("", "")
n.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, n)
log.Debugf("%v %v", n.name, n.value)
case keyWordValueTag:
name, value := unmarshalSingleValue(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
k := NewKeyWord(name, value)
a.addAttribute(currentAttributeGroup, k)
lastAddValuer = k
}
log.Debugf("%v : %v", name, value)
case nameWithoutLanguageValueTag:
n := NewNameWithoutLanguage("", "")
n.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, n)
log.Debugf("%v %v", n.name, n.value)
case textWithoutLanguageValueTag:
attr := NewtextWithoutLanguage("", "")
attr.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, attr)
log.Debugf("%v %v", attr.name, attr.value)
case mimeMediaTypeValueTag:
name, value := unmarshalSingleValue(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
m := NewMimeMediaType(name, value)
a.addAttribute(currentAttributeGroup, m)
lastAddValuer = m
}
log.Debugf("%v : %v", name, value)
case integerValueTag:
name, value := unmarshalSingleInteger(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
i := NewInteger(name, value)
a.addAttribute(currentAttributeGroup, i)
lastAddValuer = i
}
log.Debugf("%v : %v", name, value)
case rangeOfIntegerValueTag:
name, value := unmarshalSingleRangeOfInteger(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
r := NewRangeOfInteger(name, value)
a.addAttribute(currentAttributeGroup, r)
lastAddValuer = r
}
log.Debugf("%v : %v", name, value)
case enumValueTag:
name, value := unmarshalSingleInteger(bytestream)
if name == "" {
lastAddValuer.addValue(value)
} else {
e := NewEnum(name, value)
a.addAttribute(currentAttributeGroup, e)
lastAddValuer = e
}
log.Debugf("%v : %v", name, value)
case begCollectionValueTag:
// For now just consume the collection
consumeCollection(bytestream)
case unsupportedValueTag:
attr := NewUnsupportedValue()
attr.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, attr)
case jobAttributes:
log.Debug("Start job attributes")
currentAttributeGroup = jobAttributes
case printerAttributes:
log.Debug("Start printer attributes")
currentAttributeGroup = printerAttributes
case operationAttributes:
log.Debug("Start operation attributes")
currentAttributeGroup = operationAttributes
case unsupportedAttributes:
log.Debug("Start unsupported attributes")
currentAttributeGroup = unsupportedAttributes
case resolutionValueTag:
res := NewSetOfResolution("")
res.unmarshal(bytestream)
a.addAttribute(currentAttributeGroup, res)
log.Debugf("Resolution %v", res)
default:
log.Errorf("Unsupported tag %v (%x)", t, uint8(t))
}
}
}

View File

@@ -1,36 +1,32 @@
package ipp package ipp
type MimeMediaType struct { type mimeMediaType struct {
sos *SetOfStrings sos *setOfStrings
} }
func NewMimeMediaType(name string, values ...string) *MimeMediaType { func NewMimeMediaType(name string, values ...string) *mimeMediaType {
m := new(MimeMediaType) m := new(mimeMediaType)
m.sos = NewSetOfStrings(name, mimeMediaTypeValueTag, values) m.sos = NewSetOfStrings(name, mimeMediaTypeValueTag, values)
return m return m
} }
func (m MimeMediaType) Name() string { func (m mimeMediaType) String() string {
return m.sos.name
}
func (m MimeMediaType) String() string {
return m.sos.String() return m.sos.String()
} }
func (m *MimeMediaType) size() int { func (m *mimeMediaType) size() int {
return m.sos.size() return m.sos.size()
} }
func (m *MimeMediaType) valueTag() tag { func (m *mimeMediaType) valueTag() tag {
return m.sos.valueTag() return m.sos.valueTag()
} }
func (m *MimeMediaType) marshal() []byte { func (m *mimeMediaType) marshal() []byte {
return m.sos.marshal() return m.sos.marshal()
} }
func (m *MimeMediaType) addValue(v interface{}) { func (m *mimeMediaType) addValue(v string) {
m.sos.AddValue(v.(string)) m.sos.AddValue(v)
} }

View File

@@ -14,33 +14,29 @@ func NewNameWithoutLanguage(name, value string) *NameWithoutLanguage {
return c return c
} }
func (n NameWithoutLanguage) Name() string { func (c NameWithoutLanguage) String() string {
return n.name return c.name + ":" + c.value
} }
func (c *NameWithoutLanguage) valueTag() tag {
func (n NameWithoutLanguage) String() string {
return n.name + ":" + n.value
}
func (n *NameWithoutLanguage) valueTag() tag {
return nameWithoutLanguageValueTag return nameWithoutLanguageValueTag
} }
func (n *NameWithoutLanguage) unmarshal(byteStream io.Reader) { func (c *NameWithoutLanguage) unmarshal(byteStream io.Reader) {
n.name, n.value = unmarshalSingleValue(byteStream) c.name, c.value = unmarshalSingleValue(byteStream)
} }
func (n *NameWithoutLanguage) marshal() []byte { func (c *NameWithoutLanguage) marshal() []byte {
l := 5 + len(n.name) + len(n.value) l := 5 + len(c.name) + len(c.value)
b := make([]byte, l) b := make([]byte, l, l)
b[0] = byte(nameWithoutLanguageValueTag) b[0] = byte(nameWithoutLanguageValueTag)
marshalNameValue(n.name, n.value, b[1:]) marshalNameValue(c.name, c.value, b[1:])
return b return b
} }
func (n *NameWithoutLanguage) size() int { func (c *NameWithoutLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths l := 1 + 4 // The attribute tag + 2 lengths
l += len(n.name) l += len(c.name)
l += len(n.value) l += len(c.value)
return l return l
} }

View File

@@ -4,43 +4,38 @@ import (
"io" "io"
) )
type NaturalLanguage struct { type naturalLanguage struct {
name string name string
value string value string
} }
func NewNaturalLanguage(name, value string) *NaturalLanguage { func NewNaturalLanguage(name, value string) *naturalLanguage {
c := new(NaturalLanguage) c := new(naturalLanguage)
c.name = name c.name = name
c.value = value c.value = value
return c return c
} }
func (c NaturalLanguage) Name() string { func (c naturalLanguage) String() string {
return c.name
}
func (c NaturalLanguage) String() string {
return c.name + ":" + c.value return c.name + ":" + c.value
} }
func (c *naturalLanguage) valueTag() tag {
func (c *NaturalLanguage) valueTag() tag {
return naturalLanguageValueTag return naturalLanguageValueTag
} }
func (c *NaturalLanguage) unmarshal(byteStream io.Reader) { func (c *naturalLanguage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream) c.name, c.value = unmarshalSingleValue(byteStream)
} }
func (c *NaturalLanguage) marshal() []byte { func (c *naturalLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value) l := 5 + len(c.name) + len(c.value)
b := make([]byte, l) b := make([]byte, l, l)
b[0] = byte(naturalLanguageValueTag) b[0] = byte(naturalLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:]) marshalNameValue(c.name, c.value, b[1:])
return b return b
} }
func (c *NaturalLanguage) size() int { func (c *naturalLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name) l += len(c.name)
l += len(c.value) l += len(c.value)

View File

@@ -36,7 +36,7 @@ var (
_operationId_index_1 = [...]uint8{0, 12, 25, 34} _operationId_index_1 = [...]uint8{0, 12, 25, 34}
) )
func (i OperationID) String() string { func (i OperationId) String() string {
switch { switch {
case 2 <= i && i <= 14: case 2 <= i && i <= 14:
i -= 2 i -= 2

View File

@@ -1,4 +1,4 @@
// Code generated by "stringer -type jobState -type printerState"; DO NOT EDIT. // Code generated by "stringer -type printerState ."; DO NOT EDIT.
package ipp package ipp
@@ -13,9 +13,9 @@ func _() {
_ = x[Stopped-5] _ = x[Stopped-5]
} }
const _printerState_name = "IdleProcessingStopped" const _printerState_name = "IdlePreocessingStopped"
var _printerState_index = [...]uint8{0, 4, 14, 21} var _printerState_index = [...]uint8{0, 4, 15, 22}
func (i printerState) String() string { func (i printerState) String() string {
i -= 3 i -= 3

View File

@@ -1,55 +0,0 @@
package ipp
import (
"encoding/binary"
"fmt"
"io"
log "github.com/sirupsen/logrus"
)
type IRange struct {
lower int32
upper int32
}
type RangeOfInteger struct {
name string
values []IRange
}
func NewRangeOfInteger(name string, values ...IRange) *RangeOfInteger {
r := new(RangeOfInteger)
r.name = name
r.values = values
return r
}
func (r *RangeOfInteger) Name() string {
return r.name
}
func (r RangeOfInteger) String() string {
return r.name + ":" + fmt.Sprint(r.values)
}
func (r *RangeOfInteger) valueTag() tag {
return rangeOfIntegerValueTag
}
func (r *RangeOfInteger) marshal() []byte {
log.Error("marshal rangeOfInteger is not implemented yet")
return []byte{}
}
func (r *RangeOfInteger) addValue(v interface{}) {
r.values = append(r.values, v.(IRange))
}
func unmarshalSingleRangeOfInteger(byteStream io.Reader) (string, IRange) {
name, data := unmarshalSingleAttribute(byteStream)
var r IRange
r.lower = int32(binary.BigEndian.Uint32(data[0:4]))
r.upper = int32(binary.BigEndian.Uint32(data[4:8]))
return name, r
}

View File

@@ -1,24 +0,0 @@
package ipp
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnMarshalSingleRange(T *testing.T) {
testdata := []byte{
0x00, 0x04,
0x66, 0x6c, 0x6f, 0x70, //flop
0x00, 0x08,
0x00, 0x0, 0x0, 0x4, 0x00, 0x0, 0x0, 0x5,
}
buf := bytes.NewBuffer(testdata)
n, v := unmarshalSingleRangeOfInteger(buf)
assert.Equal(T, "flop", n, "Should be equal")
assert.Equal(T, int32(4), v.lower, "Should be equal")
assert.Equal(T, int32(5), v.upper, "Should be equal")
}

View File

@@ -1,149 +1,203 @@
package ipp package ipp
import ( import (
"bufio"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
log "github.com/sirupsen/logrus"
) )
type ippMessageHeader struct { type ippRequestHeader struct {
versionNumber versionNumber versionNumber versionNumber
operationID OperationID operationId OperationId
requestID uint32 requestId uint32
} }
func (h *ippMessageHeader) unmarshal(byteStream io.Reader) { func (h *ippRequestHeader) unmarshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &h.versionNumber) binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
binary.Read(byteStream, binary.BigEndian, &h.operationID) binary.Read(byteStream, binary.BigEndian, &h.operationId)
binary.Read(byteStream, binary.BigEndian, &h.requestID) binary.Read(byteStream, binary.BigEndian, &h.requestId)
} }
func (h *ippMessageHeader) marshal() []byte { func (h *ippRequestHeader) marshal() []byte {
b := make([]byte, 0, 8) b := make([]byte, 0, 8)
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
binary.Write(buf, binary.BigEndian, h.versionNumber) binary.Write(buf, binary.BigEndian, h.versionNumber)
binary.Write(buf, binary.BigEndian, h.operationID) binary.Write(buf, binary.BigEndian, h.operationId)
binary.Write(buf, binary.BigEndian, h.requestID) binary.Write(buf, binary.BigEndian, h.requestId)
return buf.Bytes() return buf.Bytes()
} }
func (h ippMessageHeader) String() string { func (h ippRequestHeader) String() string {
return fmt.Sprintf("Version number: %v Operation Id: %v Request Id: %v", h.versionNumber, h.operationID, h.requestID) return fmt.Sprintf("Version number: %v Operation Id: %v Request Id: %v", h.versionNumber, h.operationId, h.requestId)
}
type Attribute interface {
valueTag() tag
marshal() []byte
//size() int
} }
type AddValuer interface { type AddValuer interface {
addValue(interface{}) addValue(string)
} }
type Request struct { type Request struct {
a *Attributes operationAttributes map[string]Attribute
header ippMessageHeader jobAttributes map[string]Attribute
printerAttributes map[string]Attribute
header ippRequestHeader
} }
func NewRequest(op OperationID, requestID uint32) *Request { func NewRequest(op OperationId, requestId uint32) *Request {
r := new(Request) r := new(Request)
r.header.operationID = op r.header.operationId = op
r.header.requestID = requestID r.header.requestId = requestId
r.header.versionNumber = 0x0200 r.header.versionNumber = 0x0200
r.a = new(Attributes) r.operationAttributes = make(map[string]Attribute)
r.jobAttributes = make(map[string]Attribute)
r.printerAttributes = make(map[string]Attribute)
return r return r
} }
func (r Request) String() string { func (r Request) String() string {
return r.header.String() + "\n" + r.a.String() s := r.header.String() + "\n" + " OperationAttributes" + "\n"
for _, a := range r.operationAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " PrinterAttributes" + "\n"
for _, a := range r.printerAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " JobAttributes" + "\n"
for _, a := range r.jobAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
} }
func (r *Request) UnMarshal(body io.Reader) { func (r *Request) UnMarshal(body io.Reader) {
buffbody := bufio.NewReader(body) r.header.unmarshal(body)
r.header.unmarshal(buffbody) log.Debugf("Header %v", r.header)
r.a = UnMarshalAttributes(buffbody) var tag tag
err := binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Error(err.Error())
}
log.Debugf("got tag - %v", tag)
var currentAttributeGroup map[string]Attribute
switch tag {
case operationAttributes:
currentAttributeGroup = r.operationAttributes
case jobAttributes:
currentAttributeGroup = r.jobAttributes
case printerAttributes:
currentAttributeGroup = r.printerAttributes
default:
log.Errorf("Unknown tag %v", tag)
}
var lastAddValuer AddValuer
for {
err = binary.Read(body, binary.BigEndian, &tag)
if err != nil {
log.Errorf("End of input before end of attributes tag (%v)", err.Error())
}
log.Debugf("Value tag - %v", tag)
switch tag {
case endOfAttributes:
return
case charsetValueTag:
c := NewCharSetValue("", "")
c.unmarshal(body)
currentAttributeGroup[c.name] = c
log.Debugf("%v %v", c.name, c.value)
case uriValueTag:
u := NewUriValue("", "")
u.unmarshal(body)
currentAttributeGroup[u.name] = u
log.Debugf("%v %v", u.name, u.value)
case naturalLanguageValueTag:
n := NewNaturalLanguage("", "")
n.unmarshal(body)
currentAttributeGroup[n.name] = n
log.Debugf("%v %v", n.name, n.value)
case keyWordValueTag:
name, value := unmarshalSingleValue(body)
if name == "" {
lastAddValuer.addValue(value)
} else {
k := NewKeyWord(name, value)
currentAttributeGroup[name] = k
lastAddValuer = k
}
log.Debugf("%v : %v", name, value)
case nameWithoutLanguageValueTag:
n := NewNameWithoutLanguage("", "")
n.unmarshal(body)
currentAttributeGroup[n.name] = n
log.Debugf("%v %v", n.name, n.value)
case mimeMediaTypeValueTag:
name, value := unmarshalSingleValue(body)
if name == "" {
lastAddValuer.addValue(value)
} else {
m := NewMimeMediaType(name, value)
currentAttributeGroup[name] = m
lastAddValuer = m
}
log.Debugf("%v : %v", name, value)
case jobAttributes:
log.Debug("Start job attributes")
currentAttributeGroup = r.jobAttributes
case resolutionValueTag:
res := NewResolution("", 0, 0)
res.unmarshal(body)
currentAttributeGroup[res.name] = res
log.Debugf("Resolution %v", res)
default:
log.Errorf("Unsupported tag %v", tag)
}
}
} }
func (r *Request) RequestID() uint32 { func (r *Request) RequestId() uint32 {
return r.header.requestID return r.header.requestId
} }
// Operation returns the operation is of the request. func (r *Request) Operation() OperationId {
func (r *Request) Operation() OperationID { return r.header.operationId
return r.header.operationID
} }
func (r *Request) GetAttribute(name string) Attribute { func (r *Request) GetAttribute(name string) Attribute {
for _, a := range r.a.operation { return r.operationAttributes[name]
if a.Name() == name {
return a
}
}
for _, a := range r.a.job {
if a.Name() == name {
return a
}
}
for _, a := range r.a.printer {
if a.Name() == name {
return a
}
}
for _, a := range r.a.unsupported {
if a.Name() == name {
return a
}
}
return nil
} }
// Marshal converts the request object to a ipp request
func (r *Request) Marshal() []byte { func (r *Request) Marshal() []byte {
// //s = r.size
var buf bytes.Buffer var buf bytes.Buffer
buf.Write(r.header.marshal()) buf.Write(r.header.marshal())
if len(r.a.operation) > 0 { if len(r.operationAttributes) > 0 {
buf.WriteByte(byte(operationAttributes)) buf.WriteByte(byte(operationAttributes))
for _, e := range r.a.operation { for _, e := range r.operationAttributes {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
if len(r.a.job) > 0 { if len(r.jobAttributes) > 0 {
buf.WriteByte(byte(jobAttributes)) buf.WriteByte(byte(jobAttributes))
for _, e := range r.a.job { for _, e := range r.jobAttributes {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
if len(r.a.printer) > 0 { if len(r.printerAttributes) > 0 {
buf.WriteByte(byte(printerAttributes)) buf.WriteByte(byte(printerAttributes))
for _, e := range r.a.printer { for _, e := range r.printerAttributes {
buf.Write(e.marshal()) buf.Write(e.marshal())
} }
} }
buf.WriteByte(byte(endOfAttributes)) buf.WriteByte(byte(endOfAttributes))
return buf.Bytes() return buf.Bytes()
} }
// AddPrinterAttribute adds a printer attribute to the request object
func (r *Request) AddPrinterAttribute(a Attribute) error {
if a != nil {
r.a.addAttribute(printerAttributes, a)
return nil
}
return ErrNilAttribute
}
// AddOperatonAttribute adds a operation attribute to the request object
func (r *Request) AddOperatonAttribute(a Attribute) error {
if a != nil {
r.a.addAttribute(operationAttributes, a)
return nil
}
return ErrNilAttribute
}
// AddJobAttribute adds a job attribute to the request object
func (r *Request) AddJobAttribute(a Attribute) error {
if a != nil {
r.a.addAttribute(jobAttributes, a)
return nil
}
return ErrNilAttribute
}

View File

@@ -64,10 +64,10 @@ func TestUnmarshalRequestPrinterAttributes(T *testing.T) {
req.UnMarshal(buf) req.UnMarshal(buf)
fmt.Print(req) fmt.Print(req)
assert.Equal(T, versionNumber(0x0101), req.header.versionNumber, "Wrong version number") assert.Equal(T, versionNumber(0x0101), req.header.versionNumber, "Wrong version number")
assert.Equal(T, GetPrinterAttributes, req.header.operationID, "Wrong Operation") assert.Equal(T, GetPrinterAttributes, req.header.operationId, "Wrong Operation")
assert.Equal(T, uint32(17), req.header.requestID, "Wrong request id") assert.Equal(T, uint32(17), req.header.requestId, "Wrong request id")
assert.Len(T, req.a.operation, 4) assert.Len(T, req.operationAttributes, 4)
v := req.GetAttribute("requested-attributes").(*KeyWord).sos.values v := req.operationAttributes["requested-attributes"].(*keyWord).sos.values
assert.Len(T, v, 7) assert.Len(T, v, 7)
assert.Contains(T, v, "printer-make-and-model") assert.Contains(T, v, "printer-make-and-model")
assert.Contains(T, v, "ipp-versions-supported") assert.Contains(T, v, "ipp-versions-supported")

View File

@@ -1,95 +1,63 @@
package ipp package ipp
import ( import (
"bufio"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io"
log "github.com/sirupsen/logrus"
) )
// Resolution represents a ipp resolution attribute type resolution struct {
type Resolution struct { name string
CrossFeedResolution int32 crossFeedResolution int32
FeedResolution int32 feedResolution int32
Unit int8 // 3 seems to mean dpi (rfc3805) units int8
} }
// SetOfResolutions represents a set ipp resolution attributes func NewResolution(name string, xfres int32, fres int32) *resolution {
type SetOfResolutions struct { r := new(resolution)
name string
sor []Resolution
}
// NewSetOfResolution creats a new set of ipp resolution attributes
func NewSetOfResolution(name string, resSet ...Resolution) *SetOfResolutions {
r := new(SetOfResolutions)
r.name = name r.name = name
r.sor = resSet r.crossFeedResolution = xfres
r.feedResolution = fres
r.units = 3 // 3 seems to mean dpi (rfc3805)
return r return r
} }
func (r *SetOfResolutions) unmarshal(byteStream *bufio.Reader) { func (r *resolution) unmarshal(byteStream io.Reader) {
var length uint16 var length uint16
var res Resolution binary.Read(byteStream, binary.BigEndian, &length)
for { attributeName := make([]byte, length)
binary.Read(byteStream, binary.BigEndian, &length) if length > 0 {
attributeName := make([]byte, length) binary.Read(byteStream, binary.BigEndian, attributeName)
if length > 0 {
binary.Read(byteStream, binary.BigEndian, attributeName)
r.name = string(attributeName)
}
binary.Read(byteStream, binary.BigEndian, &length)
if length != 9 {
panic("Wrong length in resolution")
}
binary.Read(byteStream, binary.BigEndian, &res.CrossFeedResolution)
binary.Read(byteStream, binary.BigEndian, &res.FeedResolution)
binary.Read(byteStream, binary.BigEndian, &res.Unit)
r.sor = append(r.sor, res)
var t tag
err := binary.Read(byteStream, binary.BigEndian, &t)
if err != nil {
log.Error(err.Error())
}
if t != resolutionValueTag {
byteStream.UnreadByte()
return
}
} }
r.name = string(attributeName)
binary.Read(byteStream, binary.BigEndian, &length)
if length != 9 {
panic("Wrong length in resolution")
}
binary.Read(byteStream, binary.BigEndian, &r.crossFeedResolution)
binary.Read(byteStream, binary.BigEndian, &r.feedResolution)
binary.Read(byteStream, binary.BigEndian, &r.units)
} }
// Name returns the name of the attribute func (r resolution) String() string {
func (r SetOfResolutions) Name() string { return fmt.Sprintf("%v:%v,%v,%v", r.name, r.crossFeedResolution, r.feedResolution, r.units)
return r.name
} }
func (r SetOfResolutions) String() string { func (r *resolution) marshal() []byte {
return fmt.Sprintf("%v:%v", r.name, r.sor)
}
func (r *SetOfResolutions) marshal() []byte { b := make([]byte, 0, 14+len(r.name))
b := make([]byte, 0, 14+len(r.name)+14*(len(r.sor)-1))
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
buf.WriteByte(byte(resolutionValueTag))
for i, res := range r.sor { binary.Write(buf, binary.BigEndian, uint16(len(r.name)))
buf.WriteByte(byte(resolutionValueTag)) buf.WriteString(r.name)
if i == 0 { binary.Write(buf, binary.BigEndian, uint16(9))
binary.Write(buf, binary.BigEndian, uint16(len(r.name))) binary.Write(buf, binary.BigEndian, int32(r.crossFeedResolution))
buf.WriteString(r.name) binary.Write(buf, binary.BigEndian, int32(r.feedResolution))
} else { buf.WriteByte(byte(r.units))
binary.Write(buf, binary.BigEndian, uint16(0))
}
binary.Write(buf, binary.BigEndian, uint16(9))
binary.Write(buf, binary.BigEndian, int32(res.CrossFeedResolution))
binary.Write(buf, binary.BigEndian, int32(res.FeedResolution))
buf.WriteByte(byte(res.Unit))
}
return buf.Bytes() return buf.Bytes()
} }
func (r *SetOfResolutions) valueTag() tag { func (r *resolution) valueTag() tag {
return resolutionValueTag return resolutionValueTag
} }

View File

@@ -1,144 +1,115 @@
package ipp package ipp
import ( import (
"bufio"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io"
) )
type ippResponseHeader struct { type ippResponseHeader struct {
versionNumber versionNumber versionNumber versionNumber
statusCode statusCode statusCode statusCode
requestID uint32 requestId uint32
} }
func (h ippResponseHeader) String() string { func (h ippResponseHeader) String() string {
return fmt.Sprintf("Version number: %v Status code: %v Request Id: %v", h.versionNumber, h.statusCode, h.requestID) return fmt.Sprintf("Version number: %v Status code: %v Request Id: %v", h.versionNumber, h.statusCode, h.requestId)
} }
func (h *ippResponseHeader) marshal() []byte { func (h *ippResponseHeader) marshal() []byte {
a := make([]byte, 8) a := make([]byte, 8, 8)
binary.BigEndian.PutUint16(a[0:2], uint16(h.versionNumber)) binary.BigEndian.PutUint16(a[0:2], uint16(h.versionNumber))
binary.BigEndian.PutUint16(a[2:4], uint16(h.statusCode)) binary.BigEndian.PutUint16(a[2:4], uint16(h.statusCode))
binary.BigEndian.PutUint32(a[4:8], h.requestID) binary.BigEndian.PutUint32(a[4:8], h.requestId)
return a return a
} }
func (h *ippResponseHeader) unmarshal(byteStream io.Reader) {
binary.Read(byteStream, binary.BigEndian, &h.versionNumber)
binary.Read(byteStream, binary.BigEndian, &h.statusCode)
binary.Read(byteStream, binary.BigEndian, &h.requestID)
}
// Response represens a ipp response object
type Response struct { type Response struct {
a *Attributes operationAttributes []Attribute
header ippResponseHeader jobAttributes []Attribute
printerAttributes []Attribute
header ippResponseHeader
} }
// NewResponse creates a new ipp response object func NewResponse(code statusCode, requestId uint32) *Response {
func NewResponse(code statusCode, requestID uint32) *Response {
r := new(Response) r := new(Response)
r.a = new(Attributes)
r.header.versionNumber = 0x0101 r.header.versionNumber = 0x0101
r.header.requestID = requestID r.header.requestId = requestId
r.header.statusCode = code r.header.statusCode = code
r.operationAttributes = make([]Attribute, 0)
r.printerAttributes = make([]Attribute, 0)
r.jobAttributes = make([]Attribute, 0)
return r return r
} }
// func (r *Response) length() int {
// l := 8 + 1
// if len(r.jobAttributes) > 0 {
// l += 1 //
// for _, e := range r.jobAttributes {
// l += e.length()
// }
// }
// for _, e := range r.operationAttributes {
// l += e.length()
// }
// for _, e := range r.printerAttributes {
// l += e.length()
// }
// }
func (r Response) String() string { func (r Response) String() string {
return r.header.String() + "\n" + r.a.String()
s := r.header.String() + "\n" + " OperationAttributes" + "\n"
for _, a := range r.operationAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " PrinterAttributes" + "\n"
for _, a := range r.printerAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
s = s + " JobAttributes" + "\n"
for _, a := range r.jobAttributes {
s = s + fmt.Sprintf(" %v (%v)\n", a, a.valueTag())
}
return s
} }
func (r Response) Header() ippResponseHeader {
return r.header
}
// Marshal converts the response object to a wire formatted byte stream.
func (r *Response) Marshal() []byte { func (r *Response) Marshal() []byte {
a := make([]byte, 0, 20) a := make([]byte, 0, 20)
a = append(a, r.header.marshal()...) a = append(a, r.header.marshal()...)
if len(r.a.operation) > 0 { if len(r.operationAttributes) > 0 {
a = append(a, byte(operationAttributes)) a = append(a, byte(operationAttributes))
for _, e := range r.a.operation { for _, e := range r.operationAttributes {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
if len(r.a.job) > 0 { if len(r.jobAttributes) > 0 {
a = append(a, byte(jobAttributes)) a = append(a, byte(jobAttributes))
for _, e := range r.a.job { for _, e := range r.jobAttributes {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
if len(r.a.printer) > 0 { if len(r.printerAttributes) > 0 {
a = append(a, byte(printerAttributes)) a = append(a, byte(printerAttributes))
for _, e := range r.a.printer { for _, e := range r.printerAttributes {
a = append(a, e.marshal()...) a = append(a, e.marshal()...)
} }
} }
a = append(a, byte(endOfAttributes)) a = append(a, byte(endOfAttributes))
return a return a
} }
// UnMarshal unmarshals a ipp response into a response object func (r *Response) AddPrinterAttribute(a Attribute) {
func (r *Response) UnMarshal(body io.Reader) { r.printerAttributes = append(r.printerAttributes, a)
buffbody := bufio.NewReader(body)
r.header.unmarshal(buffbody)
r.a = UnMarshalAttributes(buffbody)
} }
// AddPrinterAttribute adds a printer attribute to the response object func (r *Response) AddOperatonAttribute(a Attribute) {
func (r *Response) AddPrinterAttribute(a Attribute) error { r.operationAttributes = append(r.operationAttributes, a)
if a != nil {
r.a.addAttribute(printerAttributes, a)
return nil
}
return ErrNilAttribute
} }
// AddOperatonAttribute adds a printer attribute to the response object func (r *Response) AddJobAttribute(a Attribute) {
func (r *Response) AddOperatonAttribute(a Attribute) error { r.jobAttributes = append(r.jobAttributes, a)
if a != nil {
r.a.addAttribute(operationAttributes, a)
return nil
}
return ErrNilAttribute
}
// AddJobAttribute adds a printer attribute to the response object
func (r *Response) AddJobAttribute(a Attribute) error {
if a != nil {
r.a.addAttribute(jobAttributes, a)
return nil
}
return ErrNilAttribute
}
// GetAttribute retreives a attribute by name from the response object
// returns nil a atribute with the provided name can be found.
func (r *Response) GetAttribute(name string) Attribute {
for _, a := range r.a.operation {
if a.Name() == name {
return a
}
}
for _, a := range r.a.job {
if a.Name() == name {
return a
}
}
for _, a := range r.a.printer {
if a.Name() == name {
return a
}
}
for _, a := range r.a.unsupported {
if a.Name() == name {
return a
}
}
return nil
} }

View File

@@ -10,7 +10,7 @@ func TestMarshalResponseHeader(T *testing.T) {
h.versionNumber = 0x0101 h.versionNumber = 0x0101
h.statusCode = SuccessfulOk h.statusCode = SuccessfulOk
h.requestID = 0xdeadbeef h.requestId = 0xdeadbeef
b := h.marshal() b := h.marshal()
fmt.Printf("% x\n", b) fmt.Printf("% x\n", b)

View File

@@ -2,31 +2,28 @@ package ipp
import "encoding/binary" import "encoding/binary"
// SetOfStrings is the strings attribute type setOfStrings struct {
type SetOfStrings struct {
name string name string
values []string values []string
vTag tag vTag tag
} }
// NewSetOfStrings creates a new strings attribute func NewSetOfStrings(name string, t tag, values []string) *setOfStrings {
func NewSetOfStrings(name string, t tag, values []string) *SetOfStrings { s := new(setOfStrings)
s := new(SetOfStrings)
s.name = name s.name = name
s.vTag = t s.vTag = t
s.values = values //make([]string, 0) s.values = values //make([]string, 0)
return s return s
} }
func (s SetOfStrings) String() string { func (s setOfStrings) String() string {
r := s.name + " :" r := s.name + " :"
for _, v := range s.values { for _, v := range s.values {
r = r + " " + v r = r + " " + v
} }
return r return r
} }
func (s *setOfStrings) valueTag() tag {
func (s *SetOfStrings) valueTag() tag {
return s.vTag return s.vTag
} }
@@ -42,15 +39,15 @@ func (s *SetOfStrings) valueTag() tag {
// } // }
// } // }
func (s *SetOfStrings) marshal() []byte { func (s *setOfStrings) marshal() []byte {
l := 5 + len(s.name) + len(s.values[0]) l := 5 + len(s.name) + len(s.values[0])
for i := range s.values[1:] { for i := range s.values[1:] {
l += 5 + len(s.values[i+1]) l += 5 + len(s.values[i+1])
} }
res := make([]byte, l) res := make([]byte, l, l)
p := 0 p := 0
res[p] = byte(s.vTag) res[p] = byte(s.vTag)
p++ p += 1
binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.name))) binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.name)))
p += 2 p += 2
copy(res[p:], []byte(s.name)) copy(res[p:], []byte(s.name))
@@ -61,7 +58,7 @@ func (s *SetOfStrings) marshal() []byte {
p += len(s.values[0]) p += len(s.values[0])
for i := range s.values[1:] { for i := range s.values[1:] {
res[p] = byte(s.vTag) res[p] = byte(s.vTag)
p++ p += 1
binary.BigEndian.PutUint16(res[p:p+2], uint16(0)) binary.BigEndian.PutUint16(res[p:p+2], uint16(0))
p = p + 2 p = p + 2
binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.values[i+1]))) binary.BigEndian.PutUint16(res[p:p+2], uint16(len(s.values[i+1])))
@@ -73,12 +70,11 @@ func (s *SetOfStrings) marshal() []byte {
return res return res
} }
// AddValue adds a new sring to the set func (s *setOfStrings) AddValue(v string) {
func (s *SetOfStrings) AddValue(v string) {
s.values = append(s.values, v) s.values = append(s.values, v)
} }
func (s *SetOfStrings) size() int { func (s *setOfStrings) size() int {
l := 1 + 2 // The value tag (0x44) + name-length field (2 bytes) l := 1 + 2 // The value tag (0x44) + name-length field (2 bytes)
l += len(s.name) l += len(s.name)
l += 2 // value-length field (2 bytes) l += 2 // value-length field (2 bytes)

View File

@@ -0,0 +1,41 @@
package ipp
import "io"
type textWithoutLanguage struct {
name string
value string
}
func NewtextWithoutLanguage(name, value string) *textWithoutLanguage {
c := new(textWithoutLanguage)
c.name = name
c.value = value
return c
}
func (c textWithoutLanguage) String() string {
return c.name + ":" + c.value
}
func (c *textWithoutLanguage) valueTag() tag {
return textWithoutLanguageValueTag
}
func (c *textWithoutLanguage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream)
}
func (c *textWithoutLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value)
b := make([]byte, l, l)
b[0] = byte(textWithoutLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:])
return b
}
func (c *textWithoutLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name)
l += len(c.value)
return l
}

View File

@@ -1,49 +0,0 @@
package ipp
import "io"
// TextWithoutLanguage is the TextWithoutLanguage attribute
type TextWithoutLanguage struct {
name string
value string
}
// NewtextWithoutLanguage creates a new textWithoutLanguage attribute
func NewtextWithoutLanguage(name, value string) *TextWithoutLanguage {
c := new(TextWithoutLanguage)
c.name = name
c.value = value
return c
}
// Name returns the name of the attribute
func (c TextWithoutLanguage) Name() string {
return c.name
}
func (c TextWithoutLanguage) String() string {
return c.name + ":" + c.value
}
func (c *TextWithoutLanguage) valueTag() tag {
return textWithoutLanguageValueTag
}
func (c *TextWithoutLanguage) unmarshal(byteStream io.Reader) {
c.name, c.value = unmarshalSingleValue(byteStream)
}
func (c *TextWithoutLanguage) marshal() []byte {
l := 5 + len(c.name) + len(c.value)
b := make([]byte, l)
b[0] = byte(textWithoutLanguageValueTag)
marshalNameValue(c.name, c.value, b[1:])
return b
}
func (c *TextWithoutLanguage) size() int {
l := 1 + 4 // The attribute tag + 2 lengths
l += len(c.name)
l += len(c.value)
return l
}

View File

@@ -1,40 +0,0 @@
package ipp
import (
"io"
log "github.com/sirupsen/logrus"
)
// UnSupportedValue represents a ipp unsupported attributes attribute
type UnSupportedValue struct {
name string
}
// NewUnsupportedValue creates a new unsupported attributes attribute
func NewUnsupportedValue() *UnSupportedValue {
c := new(UnSupportedValue)
return c
}
func (c *UnSupportedValue) unmarshal(byteStream io.Reader) {
c.name, _ = unmarshalSingleValue(byteStream)
}
// Name returns the name of the attribute
func (c *UnSupportedValue) Name() string {
return c.name
}
func (c *UnSupportedValue) marshal() (data []byte) {
log.Error("Unmarshal unsupported value is not implemented yet")
return
}
func (c *UnSupportedValue) valueTag() tag {
return unsupportedValueTag
}
func (c UnSupportedValue) String() string {
return c.name
}

View File

@@ -4,49 +4,37 @@ import (
"io" "io"
) )
// URIValue represents a ipp URI value attribute type uriValue struct {
type URIValue struct {
name string name string
value string value string
} }
// NewURIValue creates a new URIValue attribute func NewUriValue(name, value string) *uriValue {
func NewURIValue(name, value string) *URIValue { u := new(uriValue)
u := new(URIValue)
u.name = name u.name = name
u.value = value u.value = value
return u return u
} }
// Name returns the name of the attribute func (u uriValue) String() string {
func (u URIValue) Name() string {
return u.name
}
// Value returns the value of the attribute
func (u URIValue) Value() string {
return u.value
}
func (u URIValue) String() string {
return u.name + ":" + u.value return u.name + ":" + u.value
} }
func (u *URIValue) valueTag() tag { func (u *uriValue) valueTag() tag {
return uriValueTag return uriValueTag
} }
func (u *URIValue) unmarshal(byteStream io.Reader) { func (u *uriValue) unmarshal(byteStream io.Reader) {
u.name, u.value = unmarshalSingleValue(byteStream) u.name, u.value = unmarshalSingleValue(byteStream)
} }
func (u *URIValue) marshal() []byte { func (u *uriValue) marshal() []byte {
res := make([]byte, u.size()) res := make([]byte, u.size())
res[0] = byte(uriValueTag) res[0] = byte(uriValueTag)
marshalNameValue(u.name, u.value, res[1:]) marshalNameValue(u.name, u.value, res[1:])
return res return res
} }
func (u *URIValue) size() int { func (u *uriValue) size() int {
l := 1 + 4 // The attribute tag + 2 lengths l := 1 + 4 // The attribute tag + 2 lengths
l += len(u.name) l += len(u.name)
l += len(u.value) l += len(u.value)

View File

@@ -8,7 +8,7 @@ import (
) )
func TestMarshalUriValue(T *testing.T) { func TestMarshalUriValue(T *testing.T) {
var u URIValue var u uriValue
u.name = "foo" u.name = "foo"
u.value = "bar" u.value = "bar"
b := u.marshal() b := u.marshal()

View File

@@ -24,6 +24,11 @@ func Run(ctx context.Context) {
log.Fatalf("EntryGroupNew() failed: %v", err) log.Fatalf("EntryGroupNew() failed: %v", err)
} }
//hostname, err := a.GetHostName()
if err != nil {
log.Fatalf("GetHostName() failed: %v", err)
}
fqdn, err := a.GetHostNameFqdn() fqdn, err := a.GetHostNameFqdn()
if err != nil { if err != nil {
log.Fatalf("GetHostNameFqdn() failed: %v", err) log.Fatalf("GetHostNameFqdn() failed: %v", err)
@@ -31,11 +36,11 @@ func Run(ctx context.Context) {
var txt [][]byte var txt [][]byte
txt = append(txt, []byte("note=burken")) txt = append(txt, []byte("note=burken"))
txt = append(txt, []byte("product=ChroBroPrint V1")) txt = append(txt, []byte("product=coola-skrivaren"))
txt = append(txt, []byte("Color=T")) txt = append(txt, []byte("Color=T"))
txt = append(txt, []byte("rp=ipp/print")) txt = append(txt, []byte("rp=ipp/print"))
txt = append(txt, []byte("ty=ChroBroPrint")) txt = append(txt, []byte("ty=ChroBro 001"))
err = eg.AddService(avahi.InterfaceUnspec, avahi.ProtoUnspec, 0, "ChroBroPrint", "_ipp._tcp", "local", fqdn, 1234, txt) err = eg.AddService(avahi.InterfaceUnspec, avahi.ProtoUnspec, 0, "ChroBro 1000", "_ipp._tcp", "local", fqdn, 1234, txt)
if err != nil { if err != nil {
log.Fatalf("AddService() failed: %v", err) log.Fatalf("AddService() failed: %v", err)
} }

View File

@@ -1,10 +0,0 @@
package main
import "ippserver/packages/ipp"
func handleValidateJob(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
return response
}

View File

@@ -1,45 +0,0 @@
package main
import (
"bytes"
"ippserver/packages/ipp"
"net/http"
log "github.com/sirupsen/logrus"
)
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(r.GetAttribute("job-id"))
request.AddOperatonAttribute(r.GetAttribute("requesting-user-name"))
request.AddOperatonAttribute(r.GetAttribute("requested-attributes"))
log.Infof("Downstream request\n%v\n", request)
downStreamRequest := request.Marshal()
b := bytes.NewBuffer(downStreamRequest)
downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", b)
if err != nil {
return nil, err
}
rb := ipp.NewResponse(0, 0)
rb.UnMarshal(downStreamResponse.Body)
log.Infof("Downstream response\n%v\n", rb)
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
response.AddJobAttribute(rb.GetAttribute("job-id"))
response.AddJobAttribute(rb.GetAttribute("job-name"))
response.AddJobAttribute(rb.GetAttribute("job-originating-user-name"))
response.AddJobAttribute(rb.GetAttribute("job-state"))
response.AddJobAttribute(rb.GetAttribute("job-state-reasons"))
return response, nil
}

View File

@@ -1,46 +0,0 @@
package main
import (
"bytes"
"ippserver/packages/ipp"
"net/http"
log "github.com/sirupsen/logrus"
)
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(r.GetAttribute("requesting-user-name"))
request.AddOperatonAttribute(r.GetAttribute("requested-attributes"))
log.Infof("Downstream request\n%v\n", request)
downStreamRequest := request.Marshal()
b := bytes.NewBuffer(downStreamRequest)
downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", b)
if err != nil {
return nil, err
}
//log.Printf("response HTTP status: %v %v", downStreamResponse.StatusCode, downStreamResponse.Status)
rb := ipp.NewResponse(0, 0)
rb.UnMarshal(downStreamResponse.Body)
log.Infof("Downstream response\n%v\n", rb)
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
a := rb.GetAttribute("job-id")
response.AddJobAttribute(a)
response.AddJobAttribute(rb.GetAttribute("job-name"))
response.AddJobAttribute(rb.GetAttribute("job-originating-user-name"))
response.AddJobAttribute(rb.GetAttribute("job-state"))
response.AddJobAttribute(rb.GetAttribute("job-state-reasons"))
return response, nil
}

View File

@@ -1,36 +0,0 @@
package main
import "ippserver/packages/ipp"
func handleGetPrinterAttributes(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
response.AddPrinterAttribute(ipp.NewURIValue("printer-uri", "ipp://drpork:1234/ipp/print"))
response.AddPrinterAttribute(ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBroPrint"))
response.AddPrinterAttribute(ipp.NewEnum("printer-state", int32(ipp.Idle)))
response.AddPrinterAttribute(ipp.NewEnum("operations-supported", int32(ipp.PrintJob), int32(ipp.ValidateJob), int32(ipp.CancelJob), int32(ipp.GetJobAttributes), int32(ipp.GetJobs), int32(ipp.GetPrinterAttributes)))
response.AddPrinterAttribute(ipp.NewKeyWord("printer-state-reasons", "none"))
response.AddPrinterAttribute(ipp.NewKeyWord("ipp-versions-supported", "1.0", "1.1", "2.0"))
response.AddPrinterAttribute(ipp.NewKeyWord("ipp-features-supported", "wfds-print-1.0"))
response.AddPrinterAttribute(ipp.NewMimeMediaType("document-format-supported", "image/pwg-raster"))
response.AddPrinterAttribute(ipp.NewKeyWord("media-supported", "iso_a4_210x297mm"))
response.AddPrinterAttribute(ipp.NewKeyWord("media-default", "iso_a4_210x297mm"))
response.AddPrinterAttribute(ipp.NewKeyWord("output-mode-supported", "color", "auto", "monochrome"))
response.AddPrinterAttribute(ipp.NewKeyWord("output-mode-default", "color"))
response.AddPrinterAttribute(ipp.NewBoolean("color-supported", true))
response.AddPrinterAttribute(ipp.NewKeyWord("sides-supported", "one-sided", "two-sided-long-edge", "two-sided-short-edge"))
response.AddPrinterAttribute(ipp.NewKeyWord("print-color-mode-supported", "auto", "color", "monochrome"))
response.AddPrinterAttribute(ipp.NewKeyWord("job-creation-attributes-supported", "copies", "finishings", "ipp-attribute-fidelity",
"job-name", "media", "media-col", "orientation-requested", "output-bin", "output-mode", "print-quality", "printer-resolution",
"requesting-user-name", "sides", "print-color-mode"))
response.AddPrinterAttribute(ipp.NewEnum("print-quality-supported", int32(4), int32(5))) //normal,high
res1 := ipp.Resolution{CrossFeedResolution: 600, FeedResolution: 600, Unit: 3}
res2 := ipp.Resolution{CrossFeedResolution: 2400, FeedResolution: 600, Unit: 3}
response.AddPrinterAttribute(ipp.NewSetOfResolution("printer-resolution-default", res1))
response.AddPrinterAttribute(ipp.NewSetOfResolution("printer-resolution-supported", res1, res2))
response.AddPrinterAttribute(ipp.NewBoolean("printer-is-accepting-jobs", true))
return response
}

View File

@@ -1,67 +0,0 @@
package main
import (
"bytes"
"io"
"ippserver/packages/ipp"
"net/http"
"net/url"
"strings"
log "github.com/sirupsen/logrus"
)
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(r.GetAttribute("requesting-user-name"))
request.AddOperatonAttribute(r.GetAttribute("job-name"))
request.AddOperatonAttribute(ipp.NewMimeMediaType("document-format", "image/pwg-raster"))
request.AddJobAttribute(r.GetAttribute("sides"))
request.AddJobAttribute(r.GetAttribute("media"))
request.AddJobAttribute(r.GetAttribute("print-color-mode"))
request.AddJobAttribute(r.GetAttribute("printer-resolution"))
request.AddJobAttribute(r.GetAttribute("output-bin"))
log.Infof("Downstream request\n%v\n", request)
downStreamRequest := request.Marshal()
b := bytes.NewBuffer(downStreamRequest)
mr := io.MultiReader(b, byteStream)
downStreamResponse, err := http.Post("http://"+"brn30055cb5e3ae.local:631/ipp/print", "application/ipp", mr)
if err != nil {
return nil, err
}
rb := ipp.NewResponse(0, 0)
rb.UnMarshal(downStreamResponse.Body)
log.Infof("Downstream response\n%v\n", rb)
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
jua := rb.GetAttribute("job-uri").(*ipp.URIValue)
if jua != nil {
u, err := url.Parse(jua.Value())
if err != nil {
log.Error("could not parse job-uri")
}
l := strings.Split(u.Path, "/")
j := l[len(l)-1]
joburi := r.GetAttribute("printer-uri").(*ipp.URIValue).Value() + "/" + j
response.AddJobAttribute(ipp.NewURIValue("job-uri", joburi))
}
response.AddJobAttribute(rb.GetAttribute("job-id"))
response.AddJobAttribute(rb.GetAttribute("job-state"))
response.AddJobAttribute(rb.GetAttribute("job-state-reasons"))
return response, nil
}

View File

@@ -1,80 +0,0 @@
package main
import (
"context"
"flag"
"ippserver/packages/ipp"
"ippserver/packages/mdnsserver"
"net/http"
"sync"
log "github.com/sirupsen/logrus"
)
var (
loglevel string
location string
)
func init() {
flag.StringVar(&loglevel, "loglevel", "info", "The wanted loglevel error/info/debug")
}
func main() {
customFormatter := new(log.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
log.SetFormatter(customFormatter)
customFormatter.FullTimestamp = true
log.SetLevel(log.InfoLevel)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go mdnsserver.Run(ctx)
http.HandleFunc("/ipp/print", handle)
log.Info("http server started on :1234")
err := http.ListenAndServe(":1234", nil)
if err != nil {
log.Fatal("ListenAndServe: " + err.Error())
}
}
var mut sync.Mutex
var requestID uint32
func handle(w http.ResponseWriter, r *http.Request) {
mut.Lock()
requestID++
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: 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, err = handlePrintJob(request, r.Body, requestID)
case ipp.GetJobs:
response, err = handleGetJobs(request, requestID)
case ipp.ValidateJob:
response = handleValidateJob(request)
case ipp.GetJobAttributes:
response, err = handleGetJobAttributes(request, requestID)
default:
response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestID())
}
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,10 +0,0 @@
package main
import "ippserver/packages/ipp"
func handleValidateJob(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
return response
}

View File

@@ -3,6 +3,6 @@ package main
import "ippserver/packages/ipp" import "ippserver/packages/ipp"
func handleGetJobs(r *ipp.Request) *ipp.Response { func handleGetJobs(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID()) response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId())
return response return response
} }

View File

@@ -3,34 +3,26 @@ package main
import "ippserver/packages/ipp" import "ippserver/packages/ipp"
func handleGetPrinterAttributes(r *ipp.Request) *ipp.Response { func handleGetPrinterAttributes(r *ipp.Request) *ipp.Response {
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID()) response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId())
var a ipp.Attribute
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8")) a = ipp.NewCharSetValue("attributes-charset", "utf-8")
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en")) response.AddOperatonAttribute(a)
a = ipp.NewNaturalLanguage("attributes-natural-language", "en")
response.AddPrinterAttribute(ipp.NewURIValue("printer-uri", "ipp://drpork:1234/ipp/print")) response.AddOperatonAttribute(a)
response.AddPrinterAttribute(ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBroPrint")) a = ipp.NewUriValue("printer-uri", "ipp://drpork:1234/ipp/print")
response.AddPrinterAttribute(ipp.NewEnum("printer-state", int32(ipp.Idle))) response.AddOperatonAttribute(a)
response.AddPrinterAttribute(ipp.NewEnum("operations-supported", int32(ipp.PrintJob), int32(ipp.ValidateJob), int32(ipp.CancelJob), int32(ipp.GetJobAttributes), int32(ipp.GetJobs), int32(ipp.GetPrinterAttributes))) a = ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBro 001")
response.AddPrinterAttribute(ipp.NewKeyWord("printer-state-reasons", "none")) response.AddOperatonAttribute(a)
response.AddPrinterAttribute(ipp.NewKeyWord("ipp-versions-supported", "1.0", "1.1", "2.0")) a = ipp.NewEnum("printer-state", int32(ipp.Idle))
response.AddPrinterAttribute(ipp.NewKeyWord("ipp-features-supported", "wfds-print-1.0")) response.AddOperatonAttribute(a)
response.AddPrinterAttribute(ipp.NewMimeMediaType("document-format-supported", "image/pwg-raster")) a = ipp.NewKeyWord("ipp-versions-supported", "1.0", "1.1", "2.0")
response.AddPrinterAttribute(ipp.NewKeyWord("media-supported", "iso_a4_210x297mm")) response.AddOperatonAttribute(a)
response.AddPrinterAttribute(ipp.NewKeyWord("media-default", "iso_a4_210x297mm")) response.AddOperatonAttribute(ipp.NewKeyWord("ipp-features-supported", "wfds-print-1.0"))
response.AddPrinterAttribute(ipp.NewKeyWord("output-mode-supported", "color", "auto", "monochrome")) response.AddOperatonAttribute(ipp.NewMimeMediaType("document-format-supported", "image/pwg-raster"))
response.AddPrinterAttribute(ipp.NewKeyWord("output-mode-default", "color")) response.AddOperatonAttribute(ipp.NewKeyWord("media-supported", "iso_a4_210x297mm"))
response.AddPrinterAttribute(ipp.NewBoolean("color-supported", true)) response.AddOperatonAttribute(ipp.NewKeyWord("sides-supported", "one-sided", "two-sided-long-edge", "two-sided-short-edge"))
response.AddPrinterAttribute(ipp.NewKeyWord("sides-supported", "one-sided", "two-sided-long-edge", "two-sided-short-edge")) response.AddOperatonAttribute(ipp.NewKeyWord("print-color-mode-supported", "auto", "color", "monochrome"))
response.AddPrinterAttribute(ipp.NewKeyWord("print-color-mode-supported", "auto", "color", "monochrome")) response.AddOperatonAttribute(ipp.NewResolution("printer-resolution-default", 600, 600))
response.AddPrinterAttribute(ipp.NewKeyWord("job-creation-attributes-supported", "copies", "finishings", "ipp-attribute-fidelity", response.AddOperatonAttribute(ipp.NewBoolean("printer-is-accepting-jobs", true))
"job-name", "media", "media-col", "orientation-requested", "output-bin", "output-mode", "print-quality", "printer-resolution",
"requesting-user-name", "sides", "print-color-mode"))
response.AddPrinterAttribute(ipp.NewEnum("print-quality-supported", int32(4), int32(5))) //normal,high
res1 := ipp.Resolution{CrossFeedResolution: 600, FeedResolution: 600, Unit: 3}
res2 := ipp.Resolution{CrossFeedResolution: 2400, FeedResolution: 600, Unit: 3}
response.AddPrinterAttribute(ipp.NewSetOfResolution("printer-resolution-default", res1))
response.AddPrinterAttribute(ipp.NewSetOfResolution("printer-resolution-supported", res1, res2))
response.AddPrinterAttribute(ipp.NewBoolean("printer-is-accepting-jobs", true))
return response return response
} }

View File

@@ -16,9 +16,7 @@ func handlePrintJob(r *ipp.Request, byteStream io.Reader) *ipp.Response {
} }
defer f.Close() defer f.Close()
io.Copy(f, byteStream) io.Copy(f, byteStream)
response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestID()) response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId())
response.AddOperatonAttribute(ipp.NewCharSetValue("attributes-charset", "utf-8"))
response.AddOperatonAttribute(ipp.NewNaturalLanguage("attributes-natural-language", "en"))
return response return response
} }

View File

@@ -30,6 +30,7 @@ func main() {
} }
func handle(w http.ResponseWriter, r *http.Request) { func handle(w http.ResponseWriter, r *http.Request) {
log.Infoln("handle")
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
http.Error(w, "Unsupported method", http.StatusMethodNotAllowed) http.Error(w, "Unsupported method", http.StatusMethodNotAllowed)
@@ -55,10 +56,8 @@ func handle(w http.ResponseWriter, r *http.Request) {
response = handlePrintJob(request, r.Body) response = handlePrintJob(request, r.Body)
case ipp.GetJobs: case ipp.GetJobs:
response = handleGetJobs(request) response = handleGetJobs(request)
case ipp.ValidateJob:
response = handleValidateJob(request)
default: default:
response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestID()) response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestId())
} }
log.Infof("Response:\n%v\n", response) log.Infof("Response:\n%v\n", response)