from gutenbach.server.printer import GutenbachPrinter import gutenbach.ipp as ipp import gutenbach.ipp.constants as const from gutenbach.ipp.constants import job_attribute_value_tags, printer_attribute_value_tags import logging # initialize logger logger = logging.getLogger(__name__) def handler_for(operation): """A decorator method to mark a function with the operation id that it handles. This value will be stored in 'func.ipp_operation'. """ def f(func): func.ipp_operation = operation return func return f class GutenbachRequestHandler(object): def __init__(self): self.printers = { "test": GutenbachPrinter(name="test") } self.default = "test" def handle(self, request, response): # look up the handler handler = None handler_name = None for d in dir(self): if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id: handler_name = d break # we couldn't find a handler, so default to unknown operation if handler_name is None: handler_name = "unknown_operation" # call the handler handler = getattr(self, handler_name) logger.info("Handling request of type '%s'" % handler_name) handler(request, response) def unknown_operation(self, request, response): logger.warning("Received unknown operation 0x%x" % request.operation_id) response.operation_id = const.StatusCodes.OPERATION_NOT_SUPPORTED ##### Helper functions def _get_printer_attributes(self, printer, request, response): attrs = printer.get_printer_attributes(request) ipp_attrs = [] for attr, vals in attrs: ipp_vals = [ipp.Value( tag=printer_attribute_value_tags[attr], value=val) for val in vals] ipp_attrs.append(ipp.Attribute(name=attr, values=ipp_vals)) response.attribute_groups.append(ipp.AttributeGroup( const.AttributeTags.PRINTER, ipp_attrs)) def _get_job_attributes(self, job, request, response): attrs = job.get_job_attributes(request) ipp_attrs = [] for attr, vals in attrs: ipp_vals = [ipp.Value( tag=job_attribute_value_tags[attr], value=val) for val in vals] ipp_attrs.append(ipp.Attribute(name=attr, values=ipp_vals)) response.attribute_groups.append(ipp.AttributeGroup( const.AttributeTags.JOB, ipp_attrs)) def _get_job_id(self, request): pass ##### Printer Commands def print_job(self, request, response): pass def validate_job(self, request, response): pass @handler_for(const.Operations.GET_JOBS) def get_jobs(self, request, response): """RFC 2911: 3.2.6 Get-Jobs Operation This REQUIRED operation allows a client to retrieve the list of Job objects belonging to the target Printer object. The client may also supply a list of Job attribute names and/or attribute group names. A group of Job object attributes will be returned for each Job object that is returned. This operation is similar to the Get-Job-Attributes operation, except that this Get-Jobs operation returns attributes from possibly more than one object. """ reqdict = ipp.ops.verify_get_jobs_request(request) printer_name = reqdict['printer-uri'] if printer_name not in self.printers: raise ipp.errors.Attributes( "Invalid printer uri: %s" % printer_name, [request.attribute_groups[0].attributes[2]]) # Each job will append a new job attribute group. # XXX: we need to honor the things that the request actually asks for for job in self.printers[printer_name].get_jobs(): self._get_job_attributes(job, request, response) def print_uri(self, request, response): pass def create_job(self, request, response): pass def pause_printer(self, request, response): pass def resume_printer(self, request, response): pass @handler_for(const.Operations.GET_PRINTER_ATTRIBUTES) def get_printer_attributes(self, request, response): """RFC 2911: 3.2.5 Get-Printer-Attributes Operation This REQUIRED operation allows a client to request the values of the attributes of a Printer object. In the request, the client supplies the set of Printer attribute names and/or attribute group names in which the requester is interested. In the response, the Printer object returns a corresponding attribute set with the appropriate attribute values filled in. For Printer objects, the possible names of attribute groups are: - 'job-template': the subset of the Job Template attributes that apply to a Printer object (the last two columns of the table in Section 4.2) that the implementation supports for Printer objects. - 'printer-description': the subset of the attributes specified in Section 4.4 that the implementation supports for Printer objects. - 'all': the special group 'all' that includes all attributes that the implementation supports for Printer objects. Since a client MAY request specific attributes or named groups, there is a potential that there is some overlap. For example, if a client requests, 'printer-name' and 'all', the client is actually requesting the 'printer-name' attribute twice: once by naming it explicitly, and once by inclusion in the 'all' group. In such cases, the Printer object NEED NOT return each attribute only once in the response even if it is requested multiple times. The client SHOULD NOT request the same attribute in multiple ways. It is NOT REQUIRED that a Printer object support all attributes belonging to a group (since some attributes are OPTIONAL). However, it is REQUIRED that each Printer object support all group names. """ # this is just like cups_get_default, except the printer name # is given reqdict = ipp.ops.verify_get_printer_attributes_request(request) printer_name = reqdict['printer-uri'] if printer_name not in self.printers: raise ipp.errors.Attributes( "Invalid printer uri: %s" % printer_name, [request.attribute_groups[0].attributes[2]]) self._get_printer_attributes(self.printers[printer_name], request, response) def set_printer_attributes(self, request, response): pass ##### Job Commands def cancel_job(self, request, response): pass def send_document(self, request, response): pass def send_uri(self, request, response): pass def get_job_attributes(self, request, response): reqdict = ipp.ops.verify_get_jobs_request(request) printer_name = reqdict['printer-uri'] job_id = reqdict['job-id'] if printer_name not in self.printers: raise ipp.errors.Attributes( "Invalid printer uri: %s" % printer_name, [request.attribute_groups[0].attributes[2]]) try: job = self.printers[printer_name].get_job(job_id) except InvalidJobException: raise ipp.errors.Attributes( "Invalid job id: %d" % job_id, [request.attribute_groups[0].attributes[2]]) # XXX: this is wrong # Each job will append a new job attribute group. # XXX: we need to honor the things that the request actually asks for self._get_job_attributes(job, request, response) def set_job_attributes(self, request, response): pass def cups_get_document(self, request, response): pass def restart_job(self, request, response): pass def promote_job(self, request, response): pass ##### CUPS Specific Commands @handler_for(const.Operations.CUPS_GET_DEFAULT) def cups_get_default(self, request, response): """The CUPS-Get-Default operation (0x4001) returns the default printer URI and attributes. CUPS-Get-Default Request ------------------------ The following groups of attributes are supplied as part of the CUPS-Get-Default request: Group 1: Operation Attributes Natural Language and Character Set: The 'attributes-charset' and 'attributes-natural-language' attributes as described in section 3.1.4.1 of the IPP Model and Semantics document. 'requested-attributes' (1setOf keyword): The client OPTIONALLY supplies a set of attribute names and/or attribute group names in whose values the requester is interested. If the client omits this attribute, the server responds as if this attribute had been supplied with a value of 'all'. CUPS-Get-Default Response ------------------------- The following groups of attributes are send as part of the CUPS-Get-Default Response: Group 1: Operation Attributes Status Message: The standard response status message. Natural Language and Character Set: The 'attributes-charset' and 'attributes-natural-language' attributes as described in section 3.1.4.2 of the IPP Model and Semantics document. Group 2: Printer Object Attributes The set of requested attributes and their current values. (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT ) """ self._get_printer_attributes(self.printers[self.default], request, response) @handler_for(const.Operations.CUPS_GET_PRINTERS) def cups_get_printers(self, request, response): """ The CUPS-Get-Printers operation (0x4002) returns the printer attributes for every printer known to the system. This may include printers that are not served directly by the server. CUPS-Get-Printers Request ------------------------- The following groups of attributes are supplied as part of the CUPS-Get-Printers request: Group 1: Operation Attributes Natural Language and Character Set: The 'attributes-charset' and 'attributes-natural-language' attributes as described in section 3.1.4.1 of the IPP Model and Semantics document. 'first-printer-name' (name(127)):CUPS 1.2/Mac OS X 10.5 The client OPTIONALLY supplies this attribute to select the first printer that is returned. 'limit' (integer (1:MAX)): The client OPTIONALLY supplies this attribute limiting the number of printers that are returned. 'printer-location' (text(127)): CUPS 1.1.7 The client OPTIONALLY supplies this attribute to select which printers are returned. 'printer-type' (type2 enum): CUPS 1.1.7 The client OPTIONALLY supplies a printer type enumeration to select which printers are returned. 'printer-type-mask' (type2 enum): CUPS 1.1.7 The client OPTIONALLY supplies a printer type mask enumeration to select which bits are used in the 'printer-type' attribute. 'requested-attributes' (1setOf keyword) : The client OPTIONALLY supplies a set of attribute names and/or attribute group names in whose values the requester is interested. If the client omits this attribute, the server responds as if this attribute had been supplied with a value of 'all'. 'requested-user-name' (name(127)) : CUPS 1.2/Mac OS X 10.5 The client OPTIONALLY supplies a user name that is used to filter the returned printers. CUPS-Get-Printers Response -------------------------- The following groups of attributes are send as part of the CUPS-Get-Printers Response: Group 1: Operation Attributes Status Message: The standard response status message. Natural Language and Character Set: The 'attributes-charset' and 'attributes-natural-language' attributes as described in section 3.1.4.2 of the IPP Model and Semantics document. Group 2: Printer Object Attributes The set of requested attributes and their current values for each printer. (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS ) """ # Each printer will append a new printer attribute group. for printer in self.printers: self._get_printer_attributes(self.printers[printer], request, response) @handler_for(const.Operations.CUPS_GET_CLASSES) def cups_get_classes(self, request, response): """The CUPS-Get-Classes operation (0x4005) returns the printer attributes for every printer class known to the system. This may include printer classes that are not served directly by the server. CUPS-Get-Classes Request ------------------------ The following groups of attributes are supplied as part of the CUPS-Get-Classes request: Group 1: Operation Attributes Natural Language and Character Set: The 'attributes-charset' and 'attributes-natural-language' attributes as described in section 3.1.4.1 of the IPP Model and Semantics document. 'first-printer-name' (name(127)):CUPS 1.2/Mac OS X 10.5 The client OPTIONALLY supplies this attribute to select the first printer that is returned. 'limit' (integer (1:MAX)): The client OPTIONALLY supplies this attribute limiting the number of printer classes that are returned. 'printer-location' (text(127)): CUPS 1.1.7 The client OPTIONALLY supplies this attribute to select which printer classes are returned. 'printer-type' (type2 enum): CUPS 1.1.7 The client OPTIONALLY supplies a printer type enumeration to select which printer classes are returned. 'printer-type-mask' (type2 enum): CUPS 1.1.7 The client OPTIONALLY supplies a printer type mask enumeration to select which bits are used in the 'printer-type' attribute. 'requested-attributes' (1setOf keyword) : The client OPTIONALLY supplies a set of attribute names and/or attribute group names in whose values the requester is interested. If the client omits this attribute, the server responds as if this attribute had been supplied with a value of 'all'. 'requested-user-name' (name(127)) : CUPS 1.2/Mac OS X 10.5 The client OPTIONALLY supplies a user name that is used to filter the returned printers. CUPS-Get-Classes Response ------------------------- The following groups of attributes are send as part of the CUPS-Get-Classes Response: Group 1: Operation Attributes Status Message: The standard response status message. Natural Language and Character Set: The 'attributes-charset' and 'attributes-natural-language' attributes as described in section 3.1.4.2 of the IPP Model and Semantics document. Group 2: Printer Class Object Attributes The set of requested attributes and their current values for each printer class. (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES ) """ # We have no printer classes, so we don't need to do anything pass