From 64d1c3528bfc0d5c098084a42540b615efe83483 Mon Sep 17 00:00:00 2001 From: NaitLee Date: Thu, 9 Sep 2021 17:33:19 +0800 Subject: [PATCH] Very early IPP support, works with cups on unix --- server.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index 333ddd2..c2617da 100644 --- a/server.py +++ b/server.py @@ -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. """