@c_phillips/ipp-browser
Version:
IPP for the browse and node
195 lines • 6.66 kB
JavaScript
import enums from "./enums";
import { status } from "./status-codes";
import { tags } from "./tags";
const operations = enums["operations-supported"];
const RS = "\u001e";
export default function (buf) {
let obj = {};
let position = 0;
let encoding = "utf8";
function read1() {
return buf[position++];
}
function read2() {
let val = buf.readInt16BE(position, true);
position += 2;
return val;
}
function read4() {
let val = buf.readInt32BE(position, true);
position += 4;
return val;
}
function read(length, enc) {
if (length == 0)
return "";
return buf.toString(enc || encoding, position, (position += length));
}
function readGroups() {
let group;
while (position < buf.length && (group = read1()) !== 0x03) {
//end-of-attributes-tag
readGroup(group);
}
}
function readGroup(group) {
let name = tags.lookup[group];
group = {};
if (obj[name]) {
if (!Array.isArray(obj[name]))
obj[name] = [obj[name]];
obj[name].push(group);
}
else
obj[name] = group;
while (buf[position] >= 0x0f) {
// delimiters are between 0x00 to 0x0F
readAttr(group);
}
}
function readAttr(group) {
let tag = read1();
//TODO: find a test for this
if (tag === 0x7f) {
//tags.extension
tag = read4();
}
let name = read(read2());
group[name] = readValues(tag, name);
}
function hasAdditionalValue() {
let current = buf[position];
return (current !== 0x4a && //tags.memberAttrName
current !== 0x37 && //tags.endCollection
current !== 0x03 && //tags.end-of-attributes-tag
buf[position + 1] === 0x00 &&
buf[position + 2] === 0x00);
}
function readValues(type, name) {
let value = readValue(type, name);
if (hasAdditionalValue()) {
value = [value];
do {
type = read1();
read2(); //empty name
value.push(readValue(type, name));
} while (hasAdditionalValue());
}
return value;
}
function readValue(tag, name) {
let length = read2();
//http://tools.ietf.org/html/rfc2910#section-3.9
switch (tag) {
case tags.enum:
let val = read4();
return (enums[name] && enums[name].lookup[val]) || val;
case tags.integer:
return read4();
case tags.boolean:
return !!read1();
case tags.rangeOfInteger:
return [read4(), read4()];
case tags.resolution:
return [read4(), read4(), read1() === 0x03 ? "dpi" : "dpcm"];
case tags.dateTime:
// http://tools.ietf.org/html/rfc1903 page 17
let date = new Date(read2(), read1(), read1(), read1(), read1(), read1(), read1());
//silly way to add on the timezone
return new Date(date.toISOString().substr(0, 23).replace("T", ",") +
"," +
String.fromCharCode(read(1)) +
read(1) +
":" +
read(1));
case tags.textWithLanguage:
case tags.nameWithLanguage:
let lang = read(read2());
let subval = read(read2());
return lang + RS + subval;
case tags.nameWithoutLanguage:
case tags.textWithoutLanguage:
case tags.octetString:
case tags.memberAttrName:
return read(length);
case tags.keyword:
case tags.uri:
case tags.uriScheme:
case tags.charset:
case tags.naturalLanguage:
case tags.mimeMediaType:
return read(length, "ascii");
case tags.begCollection:
//the spec says a value could be present- but can be ignored
read(length);
return readCollection();
case tags["no-value"]:
// default:
// debugger;
// return handleUnknownTag(tag, name, length, read);
}
}
function readCollection() {
let tag;
let collection = {};
while ((tag = read1()) !== 0x37) {
//tags.endCollection
if (tag !== 0x4a) {
console.log("unexpected:", tags.lookup[tag]);
return;
}
//read nametag name and discard it
read(read2());
let name = readValue(0x4a);
let values = readCollectionItemValue();
collection[name] = values;
}
//Read endCollection name & value and discard it.
//The spec says that they MAY have contents in the
// future- so we can't assume they are empty.
read(read2());
read(read2());
return collection;
}
function readCollectionItemValue(name) {
let tag = read1();
//TODO: find a test for this
if (tag === 0x7f) {
//tags.extension
tag = read4();
}
//read valuetag name and discard it
read(read2());
return readValues(tag, name);
}
obj.version = read1() + "." + read1();
let bytes2and3 = read2();
//byte[2] and byte[3] are used to define the 'operation' on
//requests, but used to hold the statusCode on responses. We
//can almost detect if it is a req or a res- but sadly, six
//values overlap. In these cases, the parser will give both and
//the consumer can ignore (or delete) whichever they don't want.
if (bytes2and3 >= 0x02 || bytes2and3 <= 0x3d)
obj.operation = operations.lookup[bytes2and3];
if (bytes2and3 <= 0x0007 || bytes2and3 >= 0x0400)
obj.statusCode = status.lookup[bytes2and3];
obj.id = read4();
readGroups();
if (position < buf.length)
obj.data = buf.toString(encoding, position);
return obj;
}
;
export const handleUnknownTag = function log(tag, name, length, read) {
let value = length ? read(length) : undefined;
console.log("The spec is not clear on how to handle tag " +
tag +
": " +
name +
"=" +
String(value) +
". " +
"Please open a github issue to help find a solution!");
return value;
};
//# sourceMappingURL=parser.js.map