Very early IPP support, works with cups on unix

This commit is contained in:
NaitLee 2021-09-09 17:33:19 +08:00
parent 3123548ab5
commit 64d1c3528b

View File

@ -1,6 +1,6 @@
from http.server import HTTPServer, BaseHTTPRequestHandler
import socketserver, threading, urllib, os, asyncio
import socketserver, threading, urllib, os, asyncio, tempfile, platform
from printer import PrinterDriver
import bleak
@ -76,6 +76,10 @@ class PrinterServer(BaseHTTPRequestHandler):
self.wfile.write(b'Not Found')
return
def do_POST(self):
if self.headers.get('Content-Type', '') == 'application/ipp':
# https://datatracker.ietf.org/doc/html/rfc8010
self.handle_ipp()
return
path = urllib.parse.unquote(self.path)
v = urlvar(path)
path = path.split('?')[0]
@ -90,7 +94,6 @@ class PrinterServer(BaseHTTPRequestHandler):
try:
content_length = int(self.headers.get('Content-Length'))
data = self.rfile.read(content_length)
assert content_length == len(data), 'Post data not fully read'
asyncio.run(self.driver.print_data(data, v['address']))
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
@ -106,6 +109,92 @@ class PrinterServer(BaseHTTPRequestHandler):
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'Bad Request')
def handle_ipp(self):
path = urllib.parse.unquote(self.path)
printer_name = path[1:]
data = self.rfile.read(int(self.headers.get('Content-Length', 0)))
# len_data = len(data)
# ipp_version_number = data[0:2]
# ipp_operation_id = data[2:4]
# ipp_request_id = data[4:8]
ipp_operation_attributes_tag = data[8]
attributes = {}
data_to_print = b''
# b'\x01'[0] == int(1)
if ipp_operation_attributes_tag == b'\x01'[0]:
pointer = 9
next_name_length_at = 10
next_value_length_at = 10
name = b''
value = b''
while data[pointer] != b'\x03'[0]:
tag = data[pointer:pointer + 1]
pointer += 1
if tag[0] < 0x10: # delimiter-tag
continue
next_name_length_at = pointer + data[pointer] * 0x0100 + data[pointer + 1] + 2
pointer += 2
while pointer < next_name_length_at:
name = name + data[pointer:pointer + 1]
pointer += 1
next_value_length_at = pointer + data[pointer] * 0x0100 + data[pointer + 1] + 2
pointer += 2
while pointer < next_value_length_at:
value = value + data[pointer:pointer + 1]
pointer += 1
attributes[name] = (tag, value)
name = b''
value = b''
pointer += 1
data_to_print = data[pointer:]
if data_to_print == b'':
self.send_response(200)
self.send_header('Content-Type', 'application/ipp')
self.end_headers()
self.wfile.write(
b'\x01\x01\x00\x00\x00\x00\x00\x01\x01\x03'
)
return
try:
devices = asyncio.run(bleak.BleakScanner.discover())
target_device = ''
for i in devices:
if i.name == printer_name:
target_device = i.address
if target_device != '':
platform_system = platform.system()
temp_dir = tempfile.mkdtemp()
temp_file_ps = os.path.join(temp_dir, 'temp.ps')
temp_file_pbm = os.path.join(temp_dir, 'temp.pbm')
f = open(temp_file_ps, 'wb')
f.write(data_to_print)
f.close()
# https://ghostscript.com/doc/9.54.0/Use.htm#Output_device
ghostscript_exe = 'gs'
if platform_system == 'Windows':
ghostscript_exe = 'gswin32c.exe'
elif platform_system == 'Linux':
ghostscript_exe = 'gs'
elif platform_system == 'OS/2':
ghostscript_exe = 'gsos2'
return_code = os.system('%s -q -sDEVICE=pbmraw -dNOPAUSE -dBATCH -dSAFER -dFIXEDMEDIA -g384x543 -r46.4441219158x46.4441219158 -dFitPage -sOutputFile="%s" "%s"' % (ghostscript_exe, temp_file_pbm, temp_file_ps))
if return_code == 0:
asyncio.run(self.driver.print_file(temp_file_pbm, target_device))
else:
raise Exception('Error on invoking Ghostscript')
# print(data_to_print)
self.send_response(200)
self.send_header('Content-Type', 'application/ipp')
self.end_headers()
self.wfile.write(
b'\x01\x01\x00\x00\x00\x00\x00\x01\x01\x03'
)
except Exception as _:
self.send_response(500)
self.send_header('Content-Type', 'application/ipp')
self.end_headers()
self.wfile.write(b'')
class ThreadedHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
""" Handle requests in a separate thread. """