Index: server/lib/ipp/constants.py
===================================================================
--- server/lib/ipp/constants.py	(revision 91abb7fec8a1f6995d6c5f691039529c42c22894)
+++ server/lib/ipp/constants.py	(revision 1176f8b7b194ba6ef8b67441c731da5624cb71d4)
@@ -104,5 +104,5 @@
     #CUPS_ADD_PRINTER      = 0x4003
     #CUPS_DELETE_PRINTER   = 0x4004
-    #CUPS_GET_CLASSES      = 0x4005
+    CUPS_GET_CLASSES       = 0x4005
     #CUPS_ADD_CLASS        = 0x4006
     #CUPS_DELETE_CLASS     = 0x4007
Index: server/lib/server.py
===================================================================
--- server/lib/server.py	(revision dc37ed0272f2cd2f1808068cd6b613bf7063e7a2)
+++ server/lib/server.py	(revision 1176f8b7b194ba6ef8b67441c731da5624cb71d4)
@@ -2,10 +2,166 @@
 
 import logging, BaseHTTPServer
+import time
 import ipp
 import ipp.constants as const
+from ipp.constants import Operations
 
 logging.basicConfig(level=logging.DEBUG)
 
+def handler_for(operation):
+    def f(func):
+        func.ipp_operation = operation
+        return func
+    return f
+
+class IPPRequestHandler(object):
+    def _ipp_dispatch(self, request):
+        for d in dir(self):
+            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
+                return getattr(self, d)
+        return self.unknown_operation
+
+    def unknown_operation(self, request, response):
+        print "Received UNKNOWN OPERATION %x" % request.operation_id
+        response.operation_id = const.StatusCodes.OPERATION_NOT_SUPPORTED
+
+class PrinterRequestHandler(IPPRequestHandler):
+    def __init__(self, name):
+        self.name = name
+
+    @handler_for(Operations.GET_PRINTER_ATTRIBUTES)
+    def get_printer_attributes(self, request, response):
+        printer_attributes = ipp.AttributeGroup(const.AttributeTags.PRINTER)
+        printer_attributes[:] = \
+            ipp.Attribute("printer-uri-supported",
+                          [ipp.Value(ipp.Tags.URI,
+                                     "ipp://localhost:8000/printers/"+self.name)]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("uri-authentication-supported",
+                          [ipp.Value(ipp.Tags.KEYWORD,
+                                     "none")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("uri-security-supported",
+                          [ipp.Value(ipp.Tags.KEYWORD,
+                                     "none")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("printer-name",
+                          [ipp.Value(ipp.Tags.NAME_WITHOUT_LANGUAGE,
+                                     self.name)]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("printer-state",
+                          [ipp.Value(ipp.Tags.ENUM,
+                                     const.PrinterStates.IDLE)]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("printer-state-reasons",
+                          [ipp.Value(ipp.Tags.KEYWORD,
+                                     "none")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("ipp-versions-supported",
+                          [ipp.Value(ipp.Tags.KEYWORD,
+                                     "1.0"),
+                           ipp.Value(ipp.Tags.KEYWORD,
+                                     "1.1")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("operations-supported",
+                          [ipp.Value(ipp.Tags.ENUM,
+                                     Operations.GET_JOBS)]
+                          ) #XXX: We should query ourself for the supported operations
+        printer_attributes[:] = \
+            ipp.Attribute("charset-configured",
+                          [ipp.Value(ipp.Tags.CHARSET,
+                                     "utf-8")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("charset-supported",
+                          [ipp.Value(ipp.Tags.CHARSET,
+                                     "utf-8")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("natural-language-configured",
+                          [ipp.Value(ipp.Tags.NATURAL_LANGUAGE,
+                                     "en-us")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("generated-natural-language-supported",
+                          [ipp.Value(ipp.Tags.NATURAL_LANGUAGE,
+                                     "en-us")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("document-format-default",
+                          [ipp.Value(ipp.Tags.MIME_MEDIA_TYPE,
+                                     "application/octet-stream")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("document-format-supported",
+                          [ipp.Value(ipp.Tags.MIME_MEDIA_TYPE,
+                                     "application/octet-stream"),
+                           ipp.Value(ipp.Tags.MIME_MEDIA_TYPE,
+                                     "audio/mp3")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("printer-is-accepting-jobs",
+                          [ipp.Value(ipp.Tags.BOOLEAN,
+                                     True)]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("queued-job-count",
+                          [ipp.Value(ipp.Tags.INTEGER,
+                                     1)]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("pdl-override-supported",
+                          [ipp.Value(ipp.Tags.KEYWORD,
+                                     "not-attempted")]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("printer-up-time",
+                          [ipp.Value(ipp.Tags.INTEGER,
+                                     int(time.time()))]
+                          )
+        printer_attributes[:] = \
+            ipp.Attribute("compression-supported",
+                          [ipp.Value(ipp.Tags.KEYWORD,
+                                     "none")]
+                          )
+
+        response.attribute_groups.append(printer_attributes)
+        response.operation_id = const.StatusCodes.OK
+        print "get_printer_attributes called"
+
+class RootRequestHandler(IPPRequestHandler):
+    printers = [PrinterRequestHandler(name="sipbmp3")]
+
+    @handler_for(Operations.CUPS_GET_DEFAULT)
+    def cups_get_default(self, request, response):
+        print "get_default called"
+        return self.printers[0].get_printer_attributes(request, response)
+
+    @handler_for(Operations.CUPS_GET_PRINTERS)
+    def cups_get_printers(self, request, response):
+        print "get_printers called"
+        response.operation_id = const.StatusCodes.OK
+        for p in self.printers:
+            # Each printer will append a new printer attribute group.
+            p.get_printer_attributes(request, response)
+
+    @handler_for(Operations.CUPS_GET_CLASSES)
+    def cups_get_classes(self, request, response):
+        print "get_classes called"
+        response.operation_id = const.StatusCodes.OK
+        # We have no printer classes, so nothing to return.
+
 class GutenbachIPPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    def setup(self):
+        self.root = RootRequestHandler()
+        BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
+
     def handle_one_request(self):
         self.raw_requestline = self.rfile.readline()
@@ -25,7 +181,21 @@
         response_kwargs = {}
         response_kwargs['version'] = request.version
+        response_kwargs['operation_id'] = const.StatusCodes.INTERNAL_ERROR
         response_kwargs['request_id'] = request.request_id
-        response_kwargs = self.get_jobs(request, response_kwargs)
+        response_kwargs['attribute_groups'] = [
+            ipp.AttributeGroup(const.AttributeTags.OPERATION,
+                               [ipp.Attribute('attributes-charset',
+                                              [ipp.Value(ipp.Tags.CHARSET,
+                                                         'utf-8'
+                                                         )]),
+                                ipp.Attribute('attributes-natural-language',
+                                              [ipp.Value(ipp.Tags.NATURAL_LANGUAGE,
+                                                         'en-us'
+                                                         )])
+                                ])]
+
         response = ipp.Request(**response_kwargs)
+        handler = self.root._ipp_dispatch(request)
+        handler(request, response)
         print "Sending response:", repr(response)
 
@@ -61,5 +231,5 @@
                           ipp.Attribute('job-printer-uri',
                                        [ipp.Value(ipp.Tags.URI,
-                                                 'http://localhost:8000/printers/foo',
+                                                 'ipp://localhost:8000/printers/foo',
                                                  )])]
 
@@ -95,7 +265,4 @@
         pass
 
-    def get_printer_attributes(self, request):
-        pass
-
     #def get_jobs(self, request):
     #    pass
@@ -105,10 +272,4 @@
 
     def create_job(self, request):
-        pass
-
-    def cups_get_default(self, request):
-        pass
-
-    def cups_get_printers(self, request):
         pass
 
