diff --git a/main.go b/main.go deleted file mode 100644 index 4010e48..0000000 --- a/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "context" - "fmt" - "ippserver/packages/ipp" - "ippserver/packages/mdnsserver" - "net/http" - - log "github.com/sirupsen/logrus" -) - -func main() { - customFormatter := new(log.TextFormatter) - customFormatter.TimestampFormat = "2006-01-02 15:04:05" - log.SetFormatter(customFormatter) - customFormatter.FullTimestamp = true - log.SetLevel(log.DebugLevel) - - 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()) - } -} - -func handle(w http.ResponseWriter, r *http.Request) { - log.Infoln("handle") - if r.Method != http.MethodPost { - http.Error(w, "Unsupported method", http.StatusMethodNotAllowed) - - } - //body := make([]byte, r.ContentLength) - //io.ReadFull(r.Body, body) - //log.Infof("Body %x", body) - - request := ipp.NewRequest() - //rdata := make([]byte, r.ContentLength) - //io.ReadFull(r.Body, rdata) - //log.Printf("Data %x", rdata) - //fmt.Printf("data % #0x", rdata) - //buf := bytes.NewBuffer(rdata) - request.UnMarshal(r.Body) - fmt.Printf("Request: \n%v\n", request) - response := ipp.NewResponse(ipp.SuccessfulOk, request.RequestId()) - var a ipp.Attribute - a = ipp.NewCharSetValue("attributes-charset", "utf-8") - response.AddOperatonAttribute(a) - a = ipp.NewNaturalLanguage("attributes-natural-language", "en") - response.AddOperatonAttribute(a) - a = ipp.NewUriValue("printer-uri", "ipp://drpork:1234/ipp/print") - response.AddOperatonAttribute(a) - a = ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBro 001") - response.AddOperatonAttribute(a) - a = ipp.NewEnum("printer-state", int32(ipp.Idle)) - response.AddOperatonAttribute(a) - a = ipp.NewKeyWord("ipp-versions-supported", "1.0", "1.1", "2.0") - response.AddOperatonAttribute(a) - response.AddOperatonAttribute(ipp.NewKeyWord("ipp-features-supported", "wfds-print-1.0")) - response.AddOperatonAttribute(ipp.NewMimeMediaType("document-format-supported", "image/pwg-raster")) - response.AddOperatonAttribute(ipp.NewKeyWord("media-supported", "iso_a4_210x297mm")) - response.AddOperatonAttribute(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")) - - fmt.Printf("Response:\n%v\n", response) - data := response.Marshal() - - log.Debugf("% x", data) - w.Write(data) - -} diff --git a/packages/ipp/enum.go b/packages/ipp/enum.go index 89b5309..4123774 100644 --- a/packages/ipp/enum.go +++ b/packages/ipp/enum.go @@ -5,6 +5,8 @@ import ( "encoding/binary" "fmt" "io" + + log "github.com/sirupsen/logrus" ) type enum struct { @@ -27,8 +29,7 @@ func (e *enum) valueTag() tag { } func (e *enum) unmarshal(byteStream io.Reader) { - //e.name, e.value = unmarshalSingleValue(byteStream) - + log.Warn("Unmarshal of enum is not implemented yet") } func (e *enum) marshal() []byte { diff --git a/packages/ipp/messages.go b/packages/ipp/messages.go index 1aa6a7c..39985f6 100644 --- a/packages/ipp/messages.go +++ b/packages/ipp/messages.go @@ -55,33 +55,33 @@ const ( ) // Operation-id, defined in rfc8011 -type operationId uint16 +type OperationId uint16 const ( - PrintJob operationId = 0x0002 - PrintURI operationId = 0x0003 - ValidateJob operationId = 0x0004 - CreateJob operationId = 0x0005 - SendDocument operationId = 0x0006 - SendURI operationId = 0x0007 - CancelJob operationId = 0x0008 - GetJobAttributes operationId = 0x0009 - GetJobs operationId = 0x000a - GetPrinterAttributes operationId = 0x000b - HoldJob operationId = 0x000c - ReleaseJob operationId = 0x000d - RestartJob operationId = 0x000e - PausePrinter operationId = 0x0010 - ResumePrinter operationId = 0x0011 - PurgeJobs operationId = 0x0012 + PrintJob OperationId = 0x0002 + PrintURI OperationId = 0x0003 + ValidateJob OperationId = 0x0004 + CreateJob OperationId = 0x0005 + SendDocument OperationId = 0x0006 + SendURI OperationId = 0x0007 + CancelJob OperationId = 0x0008 + GetJobAttributes OperationId = 0x0009 + GetJobs OperationId = 0x000a + GetPrinterAttributes OperationId = 0x000b + HoldJob OperationId = 0x000c + ReleaseJob OperationId = 0x000d + RestartJob OperationId = 0x000e + PausePrinter OperationId = 0x0010 + ResumePrinter OperationId = 0x0011 + PurgeJobs OperationId = 0x0012 ) type printerState int32 const ( - Idle printerState = 3 - Preocessing printerState = 4 - Stopped printerState = 5 + Idle printerState = 3 + Processing printerState = 4 + Stopped printerState = 5 ) type statusCode uint16 @@ -101,17 +101,13 @@ func (v versionNumber) String() string { func unmarshalSingleValue(byteStream io.Reader) (string, string) { var length uint16 binary.Read(byteStream, binary.BigEndian, &length) - //log.Infof("Length %v", length) attributeName := make([]byte, length) if length > 0 { binary.Read(byteStream, binary.BigEndian, attributeName) - //log.Infof("Valuename %v", string(attributeName)) } binary.Read(byteStream, binary.BigEndian, &length) - //log.Infof("Length %v", length) attributeValue := make([]byte, length) binary.Read(byteStream, binary.BigEndian, attributeValue) - //log.Infof("Value %v", string(attributeValue)) return string(attributeName), string(attributeValue) } diff --git a/packages/ipp/namewithoutlanguage.go b/packages/ipp/namewithoutlanguage.go index 6888a5f..fdaac16 100644 --- a/packages/ipp/namewithoutlanguage.go +++ b/packages/ipp/namewithoutlanguage.go @@ -2,30 +2,30 @@ package ipp import "io" -type nameWithoutLanguage struct { +type NameWithoutLanguage struct { name string value string } -func NewNameWithoutLanguage(name, value string) *nameWithoutLanguage { - c := new(nameWithoutLanguage) +func NewNameWithoutLanguage(name, value string) *NameWithoutLanguage { + c := new(NameWithoutLanguage) c.name = name c.value = value return c } -func (c nameWithoutLanguage) String() string { +func (c NameWithoutLanguage) String() string { return c.name + ":" + c.value } -func (c *nameWithoutLanguage) valueTag() tag { +func (c *NameWithoutLanguage) valueTag() tag { return nameWithoutLanguageValueTag } -func (c *nameWithoutLanguage) unmarshal(byteStream io.Reader) { +func (c *NameWithoutLanguage) unmarshal(byteStream io.Reader) { c.name, c.value = unmarshalSingleValue(byteStream) } -func (c *nameWithoutLanguage) marshal() []byte { +func (c *NameWithoutLanguage) marshal() []byte { l := 5 + len(c.name) + len(c.value) b := make([]byte, l, l) b[0] = byte(nameWithoutLanguageValueTag) @@ -33,9 +33,13 @@ func (c *nameWithoutLanguage) marshal() []byte { return b } -func (c *nameWithoutLanguage) size() int { +func (c *NameWithoutLanguage) size() int { l := 1 + 4 // The attribute tag + 2 lengths l += len(c.name) l += len(c.value) return l } + +func (n NameWithoutLanguage) Value() string { + return n.value +} diff --git a/packages/ipp/operationid_string.go b/packages/ipp/operationid_string.go index efb1b35..2d90b82 100644 --- a/packages/ipp/operationid_string.go +++ b/packages/ipp/operationid_string.go @@ -36,7 +36,7 @@ var ( _operationId_index_1 = [...]uint8{0, 12, 25, 34} ) -func (i operationId) String() string { +func (i OperationId) String() string { switch { case 2 <= i && i <= 14: i -= 2 diff --git a/packages/ipp/printerstate_string.go b/packages/ipp/printerstate_string.go index 4f13146..fb82ddd 100644 --- a/packages/ipp/printerstate_string.go +++ b/packages/ipp/printerstate_string.go @@ -9,7 +9,7 @@ func _() { // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Idle-3] - _ = x[Preocessing-4] + _ = x[Processing-4] _ = x[Stopped-5] } diff --git a/packages/ipp/request.go b/packages/ipp/request.go index 42d5a60..c9ad66a 100644 --- a/packages/ipp/request.go +++ b/packages/ipp/request.go @@ -1,6 +1,7 @@ package ipp import ( + "bytes" "encoding/binary" "fmt" "io" @@ -10,16 +11,25 @@ import ( type ippRequestHeader struct { versionNumber versionNumber - operationId operationId + operationId OperationId requestId uint32 } -func (h *ippRequestHeader) marshal(byteStream io.Reader) { +func (h *ippRequestHeader) unmarshal(byteStream io.Reader) { binary.Read(byteStream, binary.BigEndian, &h.versionNumber) binary.Read(byteStream, binary.BigEndian, &h.operationId) binary.Read(byteStream, binary.BigEndian, &h.requestId) } +func (h *ippRequestHeader) marshal() []byte { + b := make([]byte, 0, 8) + buf := bytes.NewBuffer(b) + binary.Write(buf, binary.BigEndian, h.versionNumber) + binary.Write(buf, binary.BigEndian, h.operationId) + binary.Write(buf, binary.BigEndian, h.requestId) + return buf.Bytes() +} + func (h ippRequestHeader) String() string { return fmt.Sprintf("Version number: %v Operation Id: %v Request Id: %v", h.versionNumber, h.operationId, h.requestId) } @@ -41,8 +51,11 @@ type Request struct { header ippRequestHeader } -func NewRequest() *Request { +func NewRequest(op OperationId, requestId uint32) *Request { r := new(Request) + r.header.operationId = op + r.header.requestId = requestId + r.header.versionNumber = 0x0200 r.operationAttributes = make(map[string]Attribute) r.jobAttributes = make(map[string]Attribute) r.printerAttributes = make(map[string]Attribute) @@ -67,7 +80,7 @@ func (r Request) String() string { } func (r *Request) UnMarshal(body io.Reader) { - r.header.marshal(body) + r.header.unmarshal(body) log.Debugf("Header %v", r.header) var tag tag err := binary.Read(body, binary.BigEndian, &tag) @@ -75,95 +88,116 @@ func (r *Request) UnMarshal(body io.Reader) { 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) + } - if tag == operationAttributes { - var lastAddValuer AddValuer - nextoperationattr: - for tag != endOfAttributes { - - err = binary.Read(body, binary.BigEndian, &tag) - if err != nil { - log.Error(err.Error()) - } - log.Debugf("Value tag - %v", tag) - switch tag { - case endOfAttributes: - err = binary.Read(body, binary.BigEndian, &tag) - if err == io.EOF { - // No more data - return - } - if err != nil { - log.Error(err.Error()) - } - log.Debugf("got tag - %v", tag) - err = binary.Read(body, binary.BigEndian, &tag) - if err != nil { - log.Error(err.Error()) - } - log.Debugf("got tag - %v", tag) - - break nextoperationattr - case charsetValueTag: - c := NewCharSetValue("", "") - c.unmarshal(body) - r.operationAttributes[c.name] = c - log.Debugf("%v %v", c.name, c.value) - case uriValueTag: - u := NewUriValue("", "") - u.unmarshal(body) - r.operationAttributes[u.name] = u - log.Debugf("%v %v", u.name, u.value) - case naturalLanguageValueTag: - n := NewNaturalLanguage("", "") - n.unmarshal(body) - r.operationAttributes[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) - r.operationAttributes[name] = k - lastAddValuer = k - } - log.Debugf("%v : %v", name, value) - case nameWithoutLanguageValueTag: - n := NewNameWithoutLanguage("", "") - n.unmarshal(body) - r.operationAttributes[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) - r.operationAttributes[name] = m - lastAddValuer = m - } - case jobAttributes: - log.Debug("Start job attributes") - case resolutionValueTag: - res := NewResolution() - res.unmarshal(body) - r.operationAttributes["resolution"] = res - log.Debugf("Resolution %v", res) - - default: - log.Errorf("Unsupported 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) } - log.Infof("Value tag %v", tag) - } else { - log.Error("unexpected tag") - // TODO Return something sensible here - } } func (r *Request) RequestId() uint32 { return r.header.requestId } + +func (r *Request) Operation() OperationId { + return r.header.operationId +} + +func (r *Request) GetAttribute(name string) Attribute { + return r.operationAttributes[name] +} + +func (r *Request) Marshal() []byte { + // //s = r.size + var buf bytes.Buffer + buf.Write(r.header.marshal()) + if len(r.operationAttributes) > 0 { + buf.WriteByte(byte(operationAttributes)) + for _, e := range r.operationAttributes { + buf.Write(e.marshal()) + } + } + if len(r.jobAttributes) > 0 { + buf.WriteByte(byte(jobAttributes)) + for _, e := range r.jobAttributes { + buf.Write(e.marshal()) + } + } + if len(r.printerAttributes) > 0 { + buf.WriteByte(byte(printerAttributes)) + for _, e := range r.printerAttributes { + buf.Write(e.marshal()) + } + } + buf.WriteByte(byte(endOfAttributes)) + return buf.Bytes() +} diff --git a/packages/ipp/request_test.go b/packages/ipp/request_test.go index 3e522de..296f7af 100644 --- a/packages/ipp/request_test.go +++ b/packages/ipp/request_test.go @@ -59,7 +59,7 @@ var testRequest = []byte{0x01, 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x11, func TestUnmarshalRequestPrinterAttributes(T *testing.T) { buf := bytes.NewBuffer(testRequest) - req := NewRequest() + req := NewRequest(GetPrinterAttributes, 17) req.UnMarshal(buf) fmt.Print(req) diff --git a/packages/ipp/resolution.go b/packages/ipp/resolution.go index 4bc2fa4..9ba8fe9 100644 --- a/packages/ipp/resolution.go +++ b/packages/ipp/resolution.go @@ -1,6 +1,7 @@ package ipp import ( + "bytes" "encoding/binary" "fmt" "io" @@ -13,8 +14,12 @@ type resolution struct { units int8 } -func NewResolution() *resolution { +func NewResolution(name string, xfres int32, fres int32) *resolution { r := new(resolution) + r.name = name + r.crossFeedResolution = xfres + r.feedResolution = fres + r.units = 3 // 3 seems to mean dpi (rfc3805) return r } @@ -36,11 +41,21 @@ func (r *resolution) unmarshal(byteStream io.Reader) { } func (r resolution) String() string { - return fmt.Sprintf("%v:%v:%v", r.crossFeedResolution, r.feedResolution, r.units) + return fmt.Sprintf("%v:%v,%v,%v", r.name, r.crossFeedResolution, r.feedResolution, r.units) } func (r *resolution) marshal() []byte { - return []byte{} + + b := make([]byte, 0, 14+len(r.name)) + buf := bytes.NewBuffer(b) + buf.WriteByte(byte(resolutionValueTag)) + binary.Write(buf, binary.BigEndian, uint16(len(r.name))) + buf.WriteString(r.name) + binary.Write(buf, binary.BigEndian, uint16(9)) + binary.Write(buf, binary.BigEndian, int32(r.crossFeedResolution)) + binary.Write(buf, binary.BigEndian, int32(r.feedResolution)) + buf.WriteByte(byte(r.units)) + return buf.Bytes() } func (r *resolution) valueTag() tag { diff --git a/packages/ipp/response.go b/packages/ipp/response.go index 39c3b6c..fca8e1c 100644 --- a/packages/ipp/response.go +++ b/packages/ipp/response.go @@ -25,10 +25,10 @@ func (h *ippResponseHeader) marshal() []byte { } type Response struct { - header ippResponseHeader operationAttributes []Attribute jobAttributes []Attribute printerAttributes []Attribute + header ippResponseHeader } func NewResponse(code statusCode, requestId uint32) *Response { diff --git a/packages/ipp/setofstrings.go b/packages/ipp/setofstrings.go index 485da7a..241ff24 100644 --- a/packages/ipp/setofstrings.go +++ b/packages/ipp/setofstrings.go @@ -3,9 +3,9 @@ package ipp import "encoding/binary" type setOfStrings struct { - vTag tag name string values []string + vTag tag } func NewSetOfStrings(name string, t tag, values []string) *setOfStrings { diff --git a/server/handlegetjobs.go b/server/handlegetjobs.go new file mode 100644 index 0000000..5ba3aed --- /dev/null +++ b/server/handlegetjobs.go @@ -0,0 +1,8 @@ +package main + +import "ippserver/packages/ipp" + +func handleGetJobs(r *ipp.Request) *ipp.Response { + response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId()) + return response +} diff --git a/server/handlegetprinterattributes.go b/server/handlegetprinterattributes.go new file mode 100644 index 0000000..1b0c6dd --- /dev/null +++ b/server/handlegetprinterattributes.go @@ -0,0 +1,28 @@ +package main + +import "ippserver/packages/ipp" + +func handleGetPrinterAttributes(r *ipp.Request) *ipp.Response { + response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId()) + var a ipp.Attribute + a = ipp.NewCharSetValue("attributes-charset", "utf-8") + response.AddOperatonAttribute(a) + a = ipp.NewNaturalLanguage("attributes-natural-language", "en") + response.AddOperatonAttribute(a) + a = ipp.NewUriValue("printer-uri", "ipp://drpork:1234/ipp/print") + response.AddOperatonAttribute(a) + a = ipp.NewtextWithoutLanguage("printer-make-and-model", "ChroBro 001") + response.AddOperatonAttribute(a) + a = ipp.NewEnum("printer-state", int32(ipp.Idle)) + response.AddOperatonAttribute(a) + a = ipp.NewKeyWord("ipp-versions-supported", "1.0", "1.1", "2.0") + response.AddOperatonAttribute(a) + response.AddOperatonAttribute(ipp.NewKeyWord("ipp-features-supported", "wfds-print-1.0")) + response.AddOperatonAttribute(ipp.NewMimeMediaType("document-format-supported", "image/pwg-raster")) + response.AddOperatonAttribute(ipp.NewKeyWord("media-supported", "iso_a4_210x297mm")) + response.AddOperatonAttribute(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.AddOperatonAttribute(ipp.NewResolution("printer-resolution-default", 600, 600)) + response.AddOperatonAttribute(ipp.NewBoolean("printer-is-accepting-jobs", true)) + return response +} diff --git a/server/handleprintjob.go b/server/handleprintjob.go new file mode 100644 index 0000000..b44f03d --- /dev/null +++ b/server/handleprintjob.go @@ -0,0 +1,22 @@ +package main + +import ( + "io" + "ippserver/packages/ipp" + "os" +) + +func handlePrintJob(r *ipp.Request, byteStream io.Reader) *ipp.Response { + + a := r.GetAttribute("job-name") + //a.(nameWithoutLanguage).Value + f, err := os.Create(a.(*ipp.NameWithoutLanguage).Value()) + if err != nil { + panic("fail") + } + defer f.Close() + io.Copy(f, byteStream) + response := ipp.NewResponse(ipp.SuccessfulOk, r.RequestId()) + + return response +} diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..6e2e98f --- /dev/null +++ b/server/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "context" + "ippserver/packages/ipp" + "ippserver/packages/mdnsserver" + "net/http" + + log "github.com/sirupsen/logrus" +) + +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()) + } +} + +func handle(w http.ResponseWriter, r *http.Request) { + log.Infoln("handle") + if r.Method != http.MethodPost { + http.Error(w, "Unsupported method", http.StatusMethodNotAllowed) + + } + log.Info(r.Header) + //body := make([]byte, r.ContentLength) + //io.ReadFull(r.Body, body) + //log.Infof("Body %x", body) + + request := ipp.NewRequest(0, 0) + //rdata := make([]byte, r.ContentLength) + //io.ReadFull(r.Body, rdata) + //log.Printf("Data %x", rdata) + //fmt.Printf("data % #0x", rdata) + //buf := bytes.NewBuffer(rdata) + request.UnMarshal(r.Body) + log.Infof("Request: \n%v\n", request) + var response *ipp.Response + switch request.Operation() { + case ipp.GetPrinterAttributes: + response = handleGetPrinterAttributes(request) + case ipp.PrintJob: + response = handlePrintJob(request, r.Body) + case ipp.GetJobs: + response = handleGetJobs(request) + default: + response = ipp.NewResponse(ipp.ClientErrorBadRequest, request.RequestId()) + } + + log.Infof("Response:\n%v\n", response) + data := response.Marshal() + + //log.Debugf("% x", data) + w.Write(data) + +}