ipp-printer
Version:
Create a printer on the network
142 lines (111 loc) • 4.22 kB
JavaScript
var util = require('util')
var os = require('os')
var http = require('http')
var Bonjour = require('bonjour')
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
var bonjour = Bonjour()
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)
res.end()
return
} else if (req.headers['content-type'] !== 'application/ipp') {
res.writeHead(400)
res.end()
return
}
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) {
debug('incomplete IPP body - waiting for more data...')
return
}
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)
bonjour.publish({ type: 'ipp', port: printer.port, name: printer.name })
}
}
}
function router (printer, req, res) {
var body = req._body
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)
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)
}