Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions ippserver/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ def parse_args(args=None):
parser.add_argument('-v', '--verbose', action='count', help='Add debugging')
parser.add_argument('-H', '--host', type=str, default='localhost', metavar='HOST', help='Address to listen on')
parser.add_argument('-p', '--port', type=int, required=True, metavar='PORT', help='Port to listen on')
parser.add_argument('-i', '--uri', type=str, default='ipp://localhost:1234/', metavar='URI', help='Printer URI')
parser.add_argument('-u', '--uuid', type=str, default='884d7c0a-f449-45a7-8bbe-095e2943d313', metavar='UUID', help='Printer UUID')
parser.add_argument('-n', '--name', type=str, default='Virtual Printer', metavar='NAME', help='Printer name')

parser_action = parser.add_subparsers(help='Actions', dest='action')

Expand Down Expand Up @@ -57,28 +60,44 @@ def behaviour_from_parsed_args(args):
if args.action == 'save':
return behaviour.SaveFilePrinter(
directory=args.directory,
filename_ext='pdf' if args.pdf else 'ps')
filename_ext='pdf' if args.pdf else 'ps',
uri=args.uri,
name=args.name,
printer_uuid=args.uuid)
if args.action == 'run':
return behaviour.RunCommandPrinter(
command=args.command,
use_env=args.env,
filename_ext='pdf' if args.pdf else 'ps')
filename_ext='pdf' if args.pdf else 'ps',
uri=args.uri,
name=args.name,
printer_uuid=args.uuid)
if args.action == 'saveandrun':
return behaviour.SaveAndRunPrinter(
command=args.command,
use_env=args.env,
directory=args.directory,
filename_ext='pdf' if args.pdf else 'ps')
filename_ext='pdf' if args.pdf else 'ps',
uri=args.uri,
name=args.name,
printer_uuid=args.uuid)
if args.action == 'pc2paper':
pc2paper_config = Pc2Paper.from_config_file(args.config)
return behaviour.PostageServicePrinter(
service_api=pc2paper_config,
filename_ext='pdf' if args.pdf else 'ps')
filename_ext='pdf' if args.pdf else 'ps',
uri=args.uri,
name=args.name,
printer_uuid=args.uuid)
if args.action == 'load':
module, name = args.path[0].rsplit(".", 1)
return getattr(importlib.import_module(module), name)(*args.command)
if args.action == 'reject':
return behaviour.RejectAllPrinter()
return behaviour.RejectAllPrinter(
uri=args.uri,
name=args.name,
printer_uuid=args.uuid)

raise RuntimeError(args)


Expand Down
61 changes: 46 additions & 15 deletions ippserver/behaviour.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,21 @@ def prepare_environment(ipp_request):
)
return env

DEFAULT_PRINTER_UUID = '884d7c0a-f449-45a7-8bbe-095e2943d313'
DEFAULT_PRINTER_NAME = 'Virtual Printer'
DEFAULT_PRINTER_URI = 'ipp://localhost:1234/'

class Behaviour(object):
"""Do anything in response to IPP requests"""
version = (1, 1)
base_uri = b'ipp://localhost:1234/'
printer_uri = b'ipp://localhost:1234/printer'

def __init__(self, ppd=BasicPostscriptPPD()):
def __init__(self, ppd=BasicPostscriptPPD(), uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
self.ppd = ppd
if not uri.endswith('/'): uri += '/'
self.base_uri = uri.encode('ascii')
self.printer_uri = (uri + 'ipp/print').encode('ascii')
self.printer_name = name.encode('ascii')
self.printer_uuid = ('urn:uuid:' + printer_uuid).encode('ascii')

def expect_page_data_follows(self, ipp_request):
return ipp_request.opid_or_status == OperationEnum.print_job
Expand All @@ -79,6 +85,10 @@ class AllCommandsReturnNotImplemented(Behaviour):

There's no real use for this, it's just an example.
"""

def __init__(self, ppd=BasicPostscriptPPD(), uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
super().__init__(ppd=ppd, uri=uri, name=name, printer_uuid=printer_uuid)

def get_handle_command_function(self, _opid_or_status):
return self.operation_not_implemented_response

Expand All @@ -98,6 +108,9 @@ class StatelessPrinter(Behaviour):
It says all print jobs succeed immediately: there are some stub functions like create_job() which subclasses could use to keep track of jobs, eg: if operation_get_jobs_response wants to return something sensible.
"""

def __init__(self, ppd=BasicPostscriptPPD(), uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
super().__init__(ppd=ppd, uri=uri, name=name, printer_uuid=printer_uuid)

def get_handle_command_function(self, opid_or_status):
commands = {
OperationEnum.get_printer_attributes: self.operation_printer_list_response,
Expand Down Expand Up @@ -225,17 +238,17 @@ def printer_list_attributes(self):
SectionEnum.printer,
b'printer-name',
TagEnum.name_without_language
): [b'ipp-printer.py'],
): [self.printer_name],
(
SectionEnum.printer,
b'printer-info',
TagEnum.text_without_language
): [b'Printer using ipp-printer.py'],
): [b'Virtual Printer'],
(
SectionEnum.printer,
b'printer-make-and-model',
TagEnum.text_without_language
): [b'h2g2bob\'s ipp-printer.py 0.00'],
): [b'Virtual Printer'],
(
SectionEnum.printer,
b'printer-state',
Expand Down Expand Up @@ -324,6 +337,21 @@ def printer_list_attributes(self):
b'compression-supported',
TagEnum.keyword
): [b'none'],
(
SectionEnum.printer,
b'media-supported',
TagEnum.keyword
): [b'iso_a4_210x297mm'],
(
SectionEnum.printer,
b'media-default',
TagEnum.keyword
): [b'iso_a4_210x297mm'],
(
SectionEnum.printer,
b'printer-uuid',
TagEnum.uri
): [self.printer_uuid]
}
attr.update(self.minimal_attributes())
return attr
Expand Down Expand Up @@ -429,6 +457,9 @@ class RejectAllPrinter(StatelessPrinter):
send ipp aborted
"""

def __init__(self, ppd=BasicPostscriptPPD(), uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
super().__init__(ppd=ppd, uri=uri, name=name, printer_uuid=printer_uuid)

def operation_print_job_response(self, req, _psfile):
job_id = self.create_job(req)
attributes = self.print_job_attributes(
Expand All @@ -453,7 +484,7 @@ def operation_get_job_attributes_response(self, req, _psfile):


class SaveFilePrinter(StatelessPrinter):
def __init__(self, directory, filename_ext):
def __init__(self, directory, filename_ext, uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
self.directory = directory
self.filename_ext = filename_ext

Expand All @@ -462,7 +493,7 @@ def __init__(self, directory, filename_ext):
'pdf': BasicPdfPPD(),
}[filename_ext]

super(SaveFilePrinter, self).__init__(ppd=ppd)
super().__init__(ppd=ppd, uri=uri, name=name, printer_uuid=printer_uuid)

def handle_postscript(self, ipp_request, postscript_file):
filename = self.filename(ipp_request)
Expand All @@ -485,11 +516,11 @@ def leaf_filename(self, _ipp_request):


class SaveAndRunPrinter(SaveFilePrinter):
def __init__(self, directory, use_env, filename_ext, command):
def __init__(self, directory, use_env, filename_ext, command, uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
self.command = command
self.use_env = use_env
super(SaveAndRunPrinter, self).__init__(
directory=directory, filename_ext=filename_ext
super().__init__(
directory=directory, filename_ext=filename_ext, uri=uri, name=name, printer_uuid=printer_uuid
)

def run_after_saving(self, filename, ipp_request):
Expand All @@ -506,7 +537,7 @@ def run_after_saving(self, filename, ipp_request):


class RunCommandPrinter(StatelessPrinter):
def __init__(self, command, use_env, filename_ext):
def __init__(self, command, use_env, filename_ext, uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
self.command = command
self.use_env = use_env

Expand All @@ -515,7 +546,7 @@ def __init__(self, command, use_env, filename_ext):
'pdf': BasicPdfPPD(),
}[filename_ext]

super(RunCommandPrinter, self).__init__(ppd=ppd)
super().__init__(ppd=ppd, uri=uri, name=name, printer_uuid=printer_uuid)

def handle_postscript(self, ipp_request, postscript_file):
logging.info('Running command for job')
Expand All @@ -534,7 +565,7 @@ def handle_postscript(self, ipp_request, postscript_file):


class PostageServicePrinter(StatelessPrinter):
def __init__(self, service_api, filename_ext):
def __init__(self, service_api, filename_ext, uri=DEFAULT_PRINTER_URI, name=DEFAULT_PRINTER_NAME, printer_uuid=DEFAULT_PRINTER_UUID):
self.service_api = service_api
self.filename_ext = filename_ext

Expand All @@ -543,7 +574,7 @@ def __init__(self, service_api, filename_ext):
'pdf': BasicPdfPPD(),
}[filename_ext]

super(PostageServicePrinter, self).__init__(ppd=ppd)
super().__init__(ppd=ppd, uri=uri, name=name, printer_uuid=printer_uuid)

def handle_postscript(self, _ipp_request, postscript_file):
filename = b'ipp-server-{}.{}'.format(
Expand Down
1 change: 0 additions & 1 deletion ippserver/data/404.txt

This file was deleted.

1 change: 0 additions & 1 deletion ippserver/data/error.txt

This file was deleted.

1 change: 0 additions & 1 deletion ippserver/data/homepage.txt

This file was deleted.

2 changes: 1 addition & 1 deletion ippserver/ppd.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class BasicPdfPPD(BasicPostscriptPPD):
model = 'ipp-server-pdf'

def text(self):
return super(BasicPdfPPD, self).text() + b'''
return super().text() + b'''
*% The printer can only handle PDF files, so get CUPS to send that
*% https://en.wikipedia.org/wiki/CUPS#Filter_system
*% https://www.cups.org/doc/spec-ppd.html
Expand Down
10 changes: 2 additions & 8 deletions ippserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
from . import request


def local_file_location(filename):
return os.path.join(os.path.dirname(__file__), 'data', filename)


def _get_next_chunk(rfile):
while True:
chunk_size_s = rfile.readline()
Expand Down Expand Up @@ -108,8 +104,7 @@ def handle_www(self):
self.send_headers(
status=200, content_type='text/plain'
)
with open(local_file_location('homepage.txt'), 'rb') as wwwfile:
self.wfile.write(wwwfile.read())
self.wfile.write(b'IPP server is running ...')
elif self.path.endswith('.ppd'):
self.send_headers(
status=200, content_type='text/plain'
Expand All @@ -119,8 +114,7 @@ def handle_www(self):
self.send_headers(
status=404, content_type='text/plain'
)
with open(local_file_location('404.txt'), 'rb') as wwwfile:
self.wfile.write(wwwfile.read())
self.wfile.write(b'404 Not Found')

def handle_expect_100(self):
""" Disable """
Expand Down
5 changes: 1 addition & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,10 @@
license="BSD2",
description='An IPP server which acts like a printer',
long_description='A module which implements enough of IPP to fool CUPS into thinking it is a real printer.',
version='0.2',
version='0.3',
url='http://github.com/h2g2bob/ipp-server',
packages=find_packages(exclude=["tests"]),
test_suite="tests.test_request",
package_data={
'ippserver': ['data/*'],
},
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
Expand Down