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 CONFIG = JSON.parse(process.env.PRINTSERVANT_CONFIG || 'null')
const SECRET = process.env.PRINTSERVANT_SECRET || ''
const MAX_SIZE_MB = Number(process.env.PRINTSERVANT_MAX_SIZE_MB) || 10
if (!(PORT && CONFIG)) {
throw new Error("PRINTSERVANT_PORT and PRINTSERVANT_CONFIG environment variables must be set")
@ -59,10 +60,11 @@ app.get('/', (req, res) => {
- Prints IPP attributes
- GET /printers/:name/jobs
- Prints IPP jobs
- POST /print?printer=:name (body: application/pdf or image/png)
- POST /print?printer=:name
- Prints a PDF
- ?printer= printer name or alias (case insensitive)
- ?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
</pre>`)
})
@ -107,9 +109,9 @@ app.get('/printers/:name/jobs', (req, res) => {
// --- Print API ---
app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res) => {
const { body, query } = req
console.log("Received print job: ", { body, query })
app.post('/print', bodyParser.raw({ type: '*/*', limit: MAX_SIZE_MB * 1_000_000 }), (req, res) => {
const { body, query, headers } = req
console.log("Received print job: ", { body, query, headers })
// validate query params
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
if (!body || !body.length) {
res.status(400).send("No data received, please send a PDF")
res.status(400).send("No data received")
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 isPNG = body.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
if (!isPDF && !isPNG) {
res.status(415).send("Unsupported media type, only PDF and PNG are supported")
const hasValidFormat = isPDF || isPNG || hasValidContentType
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
}
@ -152,7 +160,12 @@ app.post('/print', bodyParser.raw({ type: '*/*', limit: 10_000_000 }), (req, res
// send print job
const msg = {
"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": {
"copies": copies,