williamkapke / ipp

Internet Printing Protocol (IPP) for nodejs
414 stars 111 forks source link

Issue with collection serialization #43

Open WillCHansen opened 7 years ago

WillCHansen commented 7 years ago

This is linked to the other issues of not being able to select the tray.

var msg = { "job-attributes-tag": { "media-col": { "media-source": "tray-2" } }}

Serializes to:

'02'+ //job attributes tag '34'+ //begin collection '0009'+ //length '6d656469612d636f6c'+ //media-col '0000'+ //value length '4a'+ //collection entry '0000'+ //name length '000c'+ //value length '6d656469612d736f75726365'+ // media-source '4a'+ // collection entry '0000'+ //name length '0006'+ //value length '747261792d32'+ //tray-2 '37'+ //end of collection '00000000'+ //name length and value length '03'

According to the spec

The first value after the 'begCollection' value has the attribute syntax, 'memberAttrName' (0x4A), and its value holds the name of the first member attribute (e.g., "aaa"). The second value holds the first member's attribute value, which can be of any attribute syntax, except 'memberAttrName' or 'endCollection'.

The issue is that the "0x4a" tag is being applied to the second entry in the collection, but it should only be applied to the parent name. The correct message is below.

'02'+ //job attributes tag '34'+ //begin collection '0009'+ //length '6d656469612d636f6c'+ //media-col '0000'+ //value length '4a'+ //collection entry '0000'+ //name length '000c'+ //value length '6d656469612d736f75726365'+ // media-source '44'+ // keyword tag '0000'+ //name length '0006'+ //value length '747261792d32'+ //tray-2 '37'+ //end of collection '00000000'+ //name length and value length '03'

Please let me know if you need any additional information.

Batajus commented 7 years ago

Hey, i tried your solution, but it does not work :-(

I tried the following code:

var printer = ipp.Printer(MY_URL);

    var msg = new Buffer(
        '02000002' +
        '00000001' +    //reqi

        '01' +      //operation-attributes-tag
        //blah blah the required bloat of this protocol
        // '470012617474726962757465732d6368617273657400057574662d3848001b617474726962757465732d6e61747572616c2d6c616e67756167650002656e' +
        '03'        //end-of-attributes-tag
        +
        '02' + //job attributes tag
        '34' + //begin collection
        '0009' + //length
        '6d656469612d636f6c' + //media-col
        '0000' + //value length
        '4a' + //collection entry
        '0000' + //name length
        '000c' + //value length
        '6d656469612d736f75726365' + //media-source
        '44' + // keyword tag
        '0000' + //name length
        '0006' + //value length
        '747261792d32' + //tray-2
        '37' + //end of collection
        '00000000' + //name length and value length
        '03'
        , 'hex');

    var file = ipp.parse(msg);
    file.data = data;

    printer.execute("Print-Job", file, function (err, res) {
        console.log("Printed: " + res.statusCode);
    });

But the display on my printer still shows the message i need to put paper in the first paper tray. And where did you find the spec you mentioned?

WillCHansen commented 7 years ago

Running the message through "printer.execute" triggers the serialize function and causes the same error again. Use ipp.request. williamkapke references the spec throughout the package but here's a link to some helpful info https://tools.ietf.org/html/rfc2911

var PDFDocument = require("pdfkit");
let fs = require('fs')
var ipp = require('ipp');
var uri = "http://10.1.205.71";

var msg = new Buffer(
  '0200'+ //Version
  '000201e6d5f2'+
  '01'+ //Operation attributes tag (your information in the Operation attributes might be different)
    '47'+ //charset tag
    '0012'+ //length
    '617474726962757465732d63686172736574'+ //attributes-charset
    '0005'+ //length
    '7574662d38'+ //utf-8
    '48'+ //natural language tag
    '001b'+ //length
    '617474726962757465732d6e61747572616c2d6c616e6775616765'+//attributes-natural-language
    '0002'+//length
    '656e'+ //en
    '45'+ // URI tag
    '000b'+ //length
    '7072696e7465722d757269'+ //printer-uri
    '0012'+//length
    '687474703a2f2f31302e312e3230352e3731'+//http://10.1.205.71
    '49'+ //mimeMediaType tag
    '000f'+ //length
    '646f63756d656e742d666f726d6174'+ //document format
    '000f'+ //length
    '6170706c69636174696f6e2f706466'+ //application/pdf
  '02'+ //job attributes tag
    '34'+ //begin collection
      '0009'+ //length
      '6d656469612d636f6c'+ //media-col
      '0000'+ //value length
      '4a'+ //collection entry
      '0000'+ //name length
      '000c'+ //value length
      '6d656469612d736f75726365'+ //media-source
      '44'+ // collection entry
      '0000'+ //name length
      '0006'+ //value length
      '747261792d32'+ //tray-2
    '37'+ //end of collection
    '00000000'+ //name length and value length
  '03', 'hex');

var doc = new PDFDocument;
doc.text("Hello World");

var buffers = [];
doc.on('data', buffers.push.bind(buffers));
doc.on('end', function(){
  var buf = Buffer.concat(buffers);
  var catBuf = Buffer.concat([msg, buf]);
  ipp.request(uri, catBuf, function(err, res){
    if(err){
      return console.log(err);
    }
    console.log(JSON.stringify(res,null,2));
  });
});
doc.end();
Batajus commented 7 years ago

Thanks for the fast reply :-) I tried it, but now i got an error message in the console:

{ 
Error
    at new IppResponseError (/Users/alex/dev/print/printing/node_modules/ipp/lib/request.js:72:17)
    at ClientRequest.<anonymous> (/Users/alex/dev/print/printing/node_modules/ipp/lib/request.js:40:8)
    at Object.onceWrapper (events.js:293:19)
    at emitOne (events.js:96:13)
    at ClientRequest.emit (events.js:191:7)
    at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:522:21)
    at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)
    at Socket.socketOnData (_http_client.js:411:20)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:191:7)
  name: 'IppResponseError',
  statusCode: 400,
  message: 'Received unexpected response status 400 from the printer',
  stack: 'Error\n    at new IppResponseError (/Users/alex/dev/print/printing/node_modules/ipp/lib/request.js:72:17)\n    at ClientRequest.<anonymous> (/Users/alex/dev/print/printing/node_modules/ipp/lib/request.js:40:8)\n    at Object.onceWrapper (events.js:293:19)\n    at emitOne (events.js:96:13)\n    at ClientRequest.emit (events.js:191:7)\n    at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:522:21)\n    at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)\n    at Socket.socketOnData (_http_client.js:411:20)\n    at emitOne (events.js:96:13)\n    at Socket.emit (events.js:191:7)' }
400 'response'

To my setup: The printer itself does not support IPP directly. I shared the printer on my MacbookPro, which provided an IPP service for all shared printers. Could it be, that this setup causes this error? Or do you have another idea?

WillCHansen commented 7 years ago

Sorry, I'm not sure. I only have limited experience with my setup here at work.

I would try a get attributes request and make sure that the commands you're sending are supported.

Batajus commented 7 years ago

I also tried another printer, but i got the same error message.

I have totally forgotten to say I opened a Stackoverflow question for this problem a few days ago.

https://stackoverflow.com/questions/44144159/cannot-print-on-another-paper-tray-via-ipp

williamkapke commented 7 years ago

Ok- this took me forever to dive in to. My deepest apologies.

Such amazing input provided by ya'll. Thank you so much.

Yup: collection serialization has been been broken. There were 2 issues that have surfaced. 1) it was incorrectly using memberAttrName (0x4a) for all values. 2) media-col did not have it's members defined (see https://tools.ietf.org/html/rfc3382#section-5.1)

I pushed a fix c5821bb45b0ff12c6118ab251a84feeb3ff1b5e0 that I believe corrects the issues. I have tested as much as I can with my available resource- but, with so many printers & options out there; It's really hard to said 100% that it's bug free now. So please try to test again whenever you have the chance.

As a basic test, I used the example from the RFC and verified byte by byte (as you did!) the data.

Here's the test script:

var ipp = require('./');

var data = ipp.serialize({
  "operation-attributes-tag": {
    "attributes-charset": "utf-8",
    "attributes-natural-language": "en",
    "printer-uri": 'noooooo',
    "document-format": "application/pdf"
  },
  "job-attributes-tag": {
    "media-col":
      {
        "media-color":  'blue',
        "media-size":
          {
            "x-dimension": 6,
            "y-dimension": 4
          }
      },
      "copies": "2"
  },
  data: new Buffer([])
})

console.log(data.toString("hex"))

Here's what the RFC says the output should be:

Octets Symbolic Value Protocol field comments
0x34 begCollection value-tag beginning of the "media-col" collection attribute
0x0009 name-length length of (collection) attribute name
media-col media-col name name of (collection) attribute
0x0000 value-length defined to be 0 for this type
no value (since value-length was 0)
0x4A memberAttrName value-tag starts a new member attribute: "media-color" defined to be 0 for this type, so part of 1setOf
0x0000 name-length
no name (since name-length was 0)
0x000B value-length length of "media-color" keyword
media-color media-color value value is name of 1st member attribute
0x44 keyword type value-tag keyword type
0x0000 name-length 0 indicates 1setOf
no name (since name-length was 0)
0x0004 value-length
blue blue value value of 1st member attribute
0x4A memberAttrName value-tag starts a new member attribute: "media-size"
0x0000 name-length defined to be 0 for this type, so part of 1setOf
no name (since name-length was 0)
0x000A value-length length of "media-size" keyword
media-size media-size value Name of 2nd member attribute
0x34 begCollection value-tag Beginning of the "media-size" collection attribute which is a sub-collection
0x0000 name-length 0 indicates 1setOf
no name (since name-length was 0)
0x0000 value-length collection attribute names have no value
no value (since value-length was 0)
0x4A memberAttrName value-tag starts a new member attribute: "x-dimension"
no name (since name-length was 0)
0x000B value-length length of "x-dimension" keyword
x-dimension x-dimension value name of 1st sub-collection member attribute
0x21 integer type value-tag attribute type
0x0000 name-length 0 indicates 1setOf
no name (since name-length was 0)
0x0004 value-length length of an integer = 4
0x0006 value value of 1st sub-collection member attribute
0x4A memberAttrName value-tag starts a new member attribute: "y-dimension"
0x0000 name-length defined to be 0 for this type, so part of 1setOf
no name (since name-length was 0)
0x000B value-length length of the "y-dimension" keyword
y-dimension y-dimension value name of 2nd sub-collection member attribute
0x21 integer type value-tag attribute type
0x0000 name-length 0 indicates 1setOf
no name (since name-length was 0)
0x0004 value-length length of an integer = 4
0x0004 value value of 2nd sub-collection member attribute
0x37 endCollection value-tag end of the sub-collection
0x0000 name-length defined to be 0 for this type, so part of 1setOf
no name (since name-length was 0)
0x0000 value-length defined to be 0 for this type
no value (since value-length was 0)
0x37 endCollection value-tag end of the 1st collection value in 1setOf
0x0000 name-length defined to be 0 for this type, so part of 1setOf
no name (since name-length was 0)
0x0000 value-length defined to be 0 for this type
no value (since value-length was 0)