add configurable size limit, allow arbitrary data if content-type is passed

main
radex 2024-02-02 12:00:10 +01:00
parent 64ff461da5
commit 8b1857bc3b
Signed by: radex
SSH Key Fingerprint: SHA256:hvqRXAGG1h89yqnS+cyFTLKQbzjWD4uXIqw7Y+0ws30
1 changed files with 21 additions and 8 deletions

View File

@ -9,6 +9,7 @@ const bodyParser = require('body-parser')
const PORT = Number(process.env.PRINTSERVANT_PORT) || 3199 const PORT = Number(process.env.PRINTSERVANT_PORT) || 3199
const CONFIG = JSON.parse(process.env.PRINTSERVANT_CONFIG || 'null') const CONFIG = JSON.parse(process.env.PRINTSERVANT_CONFIG || 'null')
const SECRET = process.env.PRINTSERVANT_SECRET || '' const SECRET = process.env.PRINTSERVANT_SECRET || ''
const MAX_SIZE_MB = Number(process.env.PRINTSERVANT_MAX_SIZE_MB) || 10
if (!(PORT && CONFIG)) { if (!(PORT && CONFIG)) {
throw new Error("PRINTSERVANT_PORT and PRINTSERVANT_CONFIG environment variables must be set") throw new Error("PRINTSERVANT_PORT and PRINTSERVANT_CONFIG environment variables must be set")
@ -59,10 +60,11 @@ app.get('/', (req, res) => {
- Prints IPP attributes - Prints IPP attributes
- GET /printers/:name/jobs - GET /printers/:name/jobs
- Prints IPP jobs - Prints IPP jobs
- POST /print?printer=:name (body: application/pdf or image/png) - POST /print?printer=:name
- Prints a PDF - Prints a PDF
- ?printer= printer name or alias (case insensitive) - ?printer= printer name or alias (case insensitive)
- ?copies= (optional) number of copies (1-10 allowed) - ?copies= (optional) number of copies (1-10 allowed)
- Body: PDF or PNG data; *or* any data if Content-Type header is passed
- Response: 200 OK if sent to printer successfully (not necessarily printed), 4xx/5xx otherwise - Response: 200 OK if sent to printer successfully (not necessarily printed), 4xx/5xx otherwise
</pre>`) </pre>`)
}) })
@ -107,9 +109,9 @@ app.get('/printers/:name/jobs', (req, res) => {
// --- Print API --- // --- Print API ---
app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res) => { app.post('/print', bodyParser.raw({ type: '*/*', limit: MAX_SIZE_MB * 1_000_000 }), (req, res) => {
const { body, query } = req const { body, query, headers } = req
console.log("Received print job: ", { body, query }) console.log("Received print job: ", { body, query, headers })
// validate query params // validate query params
const { printer: printerName, copies: copiesParam, ...otherParams } = query const { printer: printerName, copies: copiesParam, ...otherParams } = query
@ -130,15 +132,21 @@ app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res
// validate body // validate body
if (!body || !body.length) { if (!body || !body.length) {
res.status(400).send("No data received, please send a PDF") res.status(400).send("No data received")
return return
} }
const contentType = headers['content-type']
const hasValidContentType = contentType
&& contentType !== 'application/octet-stream'
&& contentType !== 'application/x-www-form-urlencoded'
const isPDF = body.subarray(0, 4).toString() === "%PDF" const isPDF = body.subarray(0, 4).toString() === "%PDF"
const isPNG = body.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) const isPNG = body.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
if (!isPDF && !isPNG) { const hasValidFormat = isPDF || isPNG || hasValidContentType
res.status(415).send("Unsupported media type, only PDF and PNG are supported") if (!hasValidFormat) {
res.status(415).send("Unknown media type. Pass a PDF or PNG file, or set Content-Type header to print something else.")
return return
} }
@ -152,7 +160,12 @@ app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res
// send print job // send print job
const msg = { const msg = {
"operation-attributes-tag": { "operation-attributes-tag": {
"document-format": isPNG ? "image/png" : "application/pdf", "document-format": (() => {
if (isPDF) return "application/pdf"
if (isPNG) return "image/png"
if (contentType) return contentType
throw new Error('unreachable')
})(),
}, },
"job-attributes-tag": { "job-attributes-tag": {
"copies": copies, "copies": copies,