ipp-server
Version:
Create a printer on the network
167 lines (127 loc) • 4.39 kB
JavaScript
;
var util = require("util");
var os = require("os");
var http = require("http");
var ipp = require("ipp-encoder");
var debug = require("debug")(require("../package").name);
var groups = require("./groups");
var operations = require("./operations");
var C = ipp.CONSTANTS;
module.exports = function (printer) {
var server = printer.server;
if (server) {
server.on("request", onrequest);
if (server.address()) {
onlistening();
} else {
server.on("listening", onlistening);
}
} else {
server = printer.server = http.createServer(onrequest);
server.listen(printer.port, onlistening);
}
return printer;
function onrequest(req, res) {
debug("HTTP request: %s %s", req.method, req.url);
if (req.method !== "POST") {
res.writeHead(405);
return res.end();
}
if (req.headers["content-type"] !== "application/ipp") {
res.writeHead(400);
return res.end();
}
req.on("data", consumeAttrGroups);
req.on("end", fail);
function consumeAttrGroups(chunk) {
req._body = req._body ? Buffer.concat([req._body, chunk]) : chunk;
try {
req._body = ipp.request.decode(req._body);
} catch (e) {
return debug("incomplete IPP body - waiting for more data...");
}
req.removeListener("data", consumeAttrGroups);
req.removeListener("end", fail);
printer.emit("operation", req._body);
router(printer, req, res);
}
function fail() {
// decode only the most essential part of the IPP request header to allow
// best possible response
if (req._body.length >= 8) {
var body = {
version : {
major : req._body.readInt8(0),
minor : req._body.readInt8(1)
},
operationId : req._body.readInt16BE(2),
requestId : req._body.readInt32BE(4)
};
}
send(printer, body, res, C.CLIENT_ERROR_BAD_REQUEST);
}
}
function onlistening() {
printer.port = server.address().port;
if (!printer.uri) {
printer.uri = "ipp://" + os.hostname() + ":" + printer.port + "/";
}
debug("printer \"%s\" is listening on %s", printer.name, printer.uri);
printer.start();
if (printer._zeroconf) {
debug("advertising printer \"%s\" on network on port %s", printer.name, printer.port);
}
}
}
function router(printer, req, res) {
var body = req._body;
var checkAuthorization = typeof printer.authorize == "function"
? printer.authorize
: function (req, res, body, next) { return next(); };
debug("IPP/%d.%d operation %d (request #%d)",
body.version.major,
body.version.minor,
body.operationId,
body.requestId,
util.inspect(body.groups, { depth: null }));
res.send = send.bind(null, printer, body, res);
if (body.version.major !== 1) {
return res.send(C.SERVER_ERROR_VERSION_NOT_SUPPORTED);
}
checkAuthorization(req, res, body, function () {
switch (body.operationId) {
// Printer Operations
case C.PRINT_JOB: return operations.printJob(printer, req, res);
case C.VALIDATE_JOB: return operations.validateJob(printer, req, res);
case C.GET_PRINTER_ATTRIBUTES: return operations.getPrinterAttributes(printer, req, res);
case C.GET_JOBS: return operations.getJobs(printer, req, res);
// Job Operations
case C.CANCEL_JOB: return operations.cancelJob(printer, req, res);
case C.GET_JOB_ATTRIBUTES: return operations.getJobAttributes(printer, req, res);
default: res.send(C.SERVER_ERROR_OPERATION_NOT_SUPPORTED);
}
});
}
function send(printer, req, res, statusCode, _groups) {
if (typeof statusCode === "object") {
return send(printer, req, res, C.SUCCESSFUL_OK, statusCode);
}
if (statusCode === undefined) {
statusCode = C.SUCCESSFUL_OK;
}
var obj = {};
if (printer.fallback && req && req.version.major === 1 && req.version.minor === 0) {
obj.version = { major: 1, minor: 0 };
}
obj.statusCode = statusCode;
obj.requestId = req ? req.requestId : 0;
obj.groups = [ groups.operationAttributesTag(ipp.STATUS_CODES[statusCode]) ];
if (_groups) obj.groups = obj.groups.concat(_groups);
debug("responding to request #%d", obj.requestId, util.inspect(obj, { depth: null }));
var buf = ipp.response.encode(obj);
res.writeHead(200, {
"Content-Length" : buf.length,
"Content-Type" : "application/ipp"
});
res.end(buf);
}