Index: server/lib/gutenbach/ipp/attributes/operation.py
===================================================================
--- server/lib/gutenbach/ipp/attributes/operation.py	(revision 793432f59cc3f6d9a34ba213d3911b9cb3b86b05)
+++ server/lib/gutenbach/ipp/attributes/operation.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -18,2 +18,5 @@
         'requesting-user-name',
         [Value(CharacterStringTags.NAME_WITHOUT_LANGUAGE, val)])
+
+def IppAttributeFidelity(val):
+    raise errors.ClientErrorAttributes, 'ipp-attribute-fidelity'
Index: server/lib/gutenbach/ipp/core/constants.py
===================================================================
--- server/lib/gutenbach/ipp/core/constants.py	(revision 793432f59cc3f6d9a34ba213d3911b9cb3b86b05)
+++ server/lib/gutenbach/ipp/core/constants.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -31,5 +31,5 @@
     
     """
-    
+
     PENDING    = 3
     HELD       = 4
Index: server/lib/gutenbach/server/__init__.py
===================================================================
--- server/lib/gutenbach/server/__init__.py	(revision 6effd500f1a343da7fc04b1dff40e47d90db5b3a)
+++ server/lib/gutenbach/server/__init__.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -3,9 +3,14 @@
 import logging
 import sys
+import traceback
 
 # configure logging
 logging.basicConfig(level=logging.DEBUG)
 
+# initialize logger
+logger = logging.getLogger(__name__)
+
 def error(self, request, client_address):
+    logger.fatal(traceback.format_exc())
     sys.exit(1)
 
Index: server/lib/gutenbach/server/job.py
===================================================================
--- server/lib/gutenbach/server/job.py	(revision 793432f59cc3f6d9a34ba213d3911b9cb3b86b05)
+++ server/lib/gutenbach/server/job.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -2,4 +2,5 @@
 import os
 import gutenbach.ipp as ipp
+import logging
 
 # initialize logger
@@ -18,5 +19,5 @@
     ]
 
-    def __init__(self, document=None):
+    def __init__(self, jid, printer, creator="", name="", size=0):
 	"""Initialize a Gutenbach job.
 
@@ -24,26 +25,27 @@
 	document to print to the value of document.
 	"""
-	 
-	self.jid = None
-        self.name = document
-	self.status = None
-	self.document = document
-	self.printer = None
+
+	self.jid      = jid
+	self.printer  = printer
+
+        self.creator  = creator
+        self.name     = name
+        self.size     = size
+
+	self.status   = ipp.JobStates.PENDING
 
     def __getattr__(self, attr):
         try:
-            return super(Job, self).__getattr__(attr)
+            return self.__getattribute__(attr)
         except AttributeError:
             pass
-
-        return super(Job, self).__getattr__(
-            attr.replace("-", "_"))
+        return self.__getattribute__(attr.replace("-", "_"))
 
     def __hasattr__(self, attr):
-        has = super(Job, self).__hasattr__(attr)
-        if not has:
-            has = super(Job, self).__hasattr__(
-                attr.replace("-", "_"))
-        return has
+        try:
+            getattr(self, attr)
+            return True
+        except AttributeError:
+            return False
 
     #### Job attributes
@@ -60,10 +62,10 @@
     @property
     def job_originating_user_name(self):
-        return ipp.JobOriginatingUserName("jhamrick")
+        return ipp.JobOriginatingUserName(self.creator)
 
     # XXX: we need to actually calculate this!
     @property
     def job_k_octets(self):
-        return ipp.JobKOctets(1)
+        return ipp.JobKOctets(self.size)
 
     @property
@@ -75,18 +77,17 @@
         return ipp.JobPrinterUri(self.printer.uri)
 
-    def get_job_attributes(self, request):
-        attributes = [getattr(self, attr) for attr in self.attributes]
+    def get_job_attributes(self, request=None):
+        if request and 'requested-attributes' in request:
+            requested = []
+            for value in request['requested-attributes'].values:
+                if value.value in self.attributes:
+                    requested.append(value.value)
+        else:
+            requested = self.attributes
+            
+        attributes = [getattr(self, attr) for attr in requested]
         return attributes
     
     #######
-
-    def enqueue(self, printer, job_id):
-	if self.status != None:
-	    raise InvalidJobException(
-		"Cannot enqueue a job that has " + \
-		"already been initialized!")
-	self.printer = printer
-        self.jid = job_id
-	self.status = const.JobStates.PENDING
 
     def play(self):
@@ -107,5 +108,3 @@
 
     def __str__(self):
-        return "<Job %d '%s'>" % \
-               (self.jid if self.jid is not None else -1, \
-                self.document)
+        return "<Job %d '%s'>" % (self.jid if self.jid is not None else -1, self.name)
Index: server/lib/gutenbach/server/printer.py
===================================================================
--- server/lib/gutenbach/server/printer.py	(revision 793432f59cc3f6d9a34ba213d3911b9cb3b86b05)
+++ server/lib/gutenbach/server/printer.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -1,4 +1,5 @@
 #import alsaaudio as aa
 from .exceptions import InvalidJobException, InvalidPrinterStateException
+from .job import Job
 import gutenbach.ipp as ipp
 import logging
@@ -169,17 +170,57 @@
         return ipp.MultipleDocumentJobsSupported(False)
 
-    def get_printer_attributes(self, request):
-        attributes = [getattr(self, attr) for attr in self.attributes]
+    ## Printer operations
+
+    def get_printer_attributes(self, request=None):
+        if request and 'requested-attributes' in request:
+            requested = []
+            for value in request['requested-attributes'].values:
+                if value.value in self.attributes:
+                    requested.append(value.value)
+        else:
+            requested = self.attributes
+            
+        attributes = [getattr(self, attr) for attr in requested]
         return attributes
 
-    ## Printer operations
+    def create_job(self, request):
+        operation = request.attribute_groups[0]
+        kwargs = {}
+        
+        # requesting username
+        if 'requesting-user-name' in operation:
+            username_attr = operation['requesting-user-name']
+            username = username_attr.values[0].value
+            if username_attr != ipp.RequestingUserName(username):
+                raise ipp.errors.ClientErrorBadRequest(str(username_attr))
+            kwargs['creator'] = username
+
+        # job name
+        if 'job-name' in operation:
+            job_name_attr = operation['job-name']
+            job_name = job_name_attr.values[0].value
+            if job_name_attr != ipp.JobName(job_name):
+                raise ipp.errors.ClientErrorBadRequest(str(job_name_attr))
+            kwargs['name'] = job_name
+
+        # job size
+        if 'job-k-octets' in operation:
+            job_k_octets_attr = operation['job-k-octets']
+            job_k_octets = job_k_octets_attr.values[0].value
+            if job_k_octets_attr != ipp.JobKOctets(job_k_octets):
+                raise ipp.errors.ClientErrorBadRequest(str(job_k_octets_attr))
+            kwargs['size'] = job_k_octets
+
+        job_id = self._next_jobid
+        self._next_jobid += 1
+        
+        job = Job(job_id, self, **kwargs)
+        self.jobs[job_id] = job
+        self.active_jobs.append(job_id)
+        print self.active_jobs
+        return job
 
     def print_job(self, job):
-	jobid = self._next_jobid
-        self._next_jobid += 1
-	self.active_jobs.append(jobid)
-	self.jobs[jobid] = job
-	job.enqueue(self, jobid)
-	return jobid
+        pass
 
     def complete_job(self, jobid):
@@ -214,10 +255,11 @@
 
     def get_jobs(self):
+        print self.active_jobs
         jobs = [self.jobs[job_id] for job_id in self.active_jobs]
-        return jobs            
-
-    def __repr__(self):
-	return str(self)
-
-    def __str__(self):
-	return "<Printer '%s'>" % self.name
+        return jobs
+
+    # def __repr__(self):
+    #     return str(self)
+
+    # def __str__(self):
+    #     return "<Printer '%s'>" % self.name
Index: server/lib/gutenbach/server/requests.py
===================================================================
--- server/lib/gutenbach/server/requests.py	(revision 103711508848c1db8aa0dfc6d977ac327ed6fb21)
+++ server/lib/gutenbach/server/requests.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -41,4 +41,5 @@
 
     def __init__(self):
+        print "init"
         self.printers = {
             "test": GutenbachPrinter(name="test")
@@ -227,14 +228,7 @@
         jobs = self.printers[printer_name].get_jobs()
 
-        # requesting username
-        if 'requesting-user-name' in operation:
-            username_attr = operation['requesting-user-name']
-            username = username_attr.values[0].value
-            if username_attr != ipp.RequestingUserName(username):
-                raise ipp.errors.ClientErrorBadRequest(str(username_attr))
-
         # get the job attributes and add them to the response
         for job in self.printers[printer_name].get_jobs():
-            attrs = job.get_job_attributes(request)
+            attrs = job.get_job_attributes(operation)
             response.attribute_groups.append(ipp.AttributeGroup(
                 ipp.AttributeTags.JOB, attrs))
@@ -260,9 +254,53 @@
         in the multi-document Job object.
 
-        Group 1: Operation Attributes
+        Request
+        -------
+        Group 1: Operation Attributes
+            REQUIRED 'attributes-charset' 
+            REQUIRED 'attributes-natural-language' 
+            REQUIRED 'printer-uri' (uri) 
+            OPTIONAL 'requesting-user-name' (name(MAX))
+            OPTIONAL 'job-name' (name(MAX))
+            OPTIONAL 'ipp-attribute-fidelity' (boolean)
+            OPTIONAL 'job-k-octets' (integer(0:MAX))
+            OPTIONAL 'job-impressions' (integer(0:MAX))
+            OPTIONAL 'job-media-sheets' (integer(0:MAX))
+        Group 2: Job Template Attributes
+
+        Response
+        --------
+        Group 1: Operation Attributes
+            OPTIONAL 'status-message' (text(255))
+            OPTIONAL 'detailed-status-message' (text(MAX))
+            REQUIRED 'attributes-charset'
+            REQUIRED 'attributes-natural-language'
+        Group 2: Unsupported Attributes
+        Group 3: Job Object Attributes
+            REQUIRED 'job-uri' (uri)
+            REQUIRED 'job-id' (integer(1:MAX))
+            REQUIRED 'job-state' (type1 enum)
+            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
+            OPTIONAL 'job-state-message' (text(MAX))
+            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
         
         """
 
-        raise ipp.errors.ServerErrorOperationNotSupported
+
+        operation = request.attribute_groups[0]
+
+        # requested printer uri
+        if 'printer-uri' not in operation:
+            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
+        uri_attr = operation['printer-uri']
+        printer_name = uri_attr.values[0].value.split("/")[-1]
+        if uri_attr != ipp.PrinterUri(uri_attr.values[0].value):
+            raise ipp.errors.ClientErrorBadRequest(str(uri_attr))
+        if printer_name not in self.printers:
+            raise ipp.errors.ClientErrorAttributes(str(uri_attr), uri_attr)
+
+        # get attributes from the printer and add to response
+        job = self.printers[printer_name].create_job(request)
+        response.attribute_groups.append(ipp.AttributeGroup(
+            ipp.AttributeTags.JOB, job.get_job_attributes(operation)))
     
     @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
@@ -296,6 +334,6 @@
             OPTIONAL 'requesting-user-name' (name(MAX))
             OPTIONAL 'requested-attributes' (1setOf type2 keyword) 
-            OPTIONAL 'document-format' (mimeMediaType):
-
+            OPTIONAL 'document-format' (mimeMediaType)
+            
         Response
         --------
@@ -324,14 +362,7 @@
         printer = self.printers[printer_name]
 
-        # requesting username
-        if 'requesting-user-name' in operation:
-            username_attr = operation['requesting-user-name']
-            username = username_attr.values[0].value
-            if username_attr != ipp.RequestingUserName(username):
-                raise ipp.errors.ClientErrorBadRequest(str(username_attr))
-
         # get attributes from the printer and add to response
         response.attribute_groups.append(ipp.AttributeGroup(
-            ipp.AttributeTags.PRINTER, printer.get_printer_attributes(request)))
+            ipp.AttributeTags.PRINTER, printer.get_printer_attributes(operation)))
 
     @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
@@ -379,13 +410,6 @@
         job = printer.get_job(job_id)
 
-        # requesting username
-        if 'requesting-user-name' in operation:
-            username_attr = operation['requesting-user-name']
-            username = username_attr.values[0].value
-            if username_attr != ipp.RequestingUserName(username):
-                raise ipp.errors.ClientErrorBadRequest(str(username_attr))
-
         # get the job attributes and add them to the response
-        attrs = job.get_job_attributes(request)
+        attrs = job.get_job_attributes(operation)
         response.attribute_groups.append(ipp.AttributeGroup(
             ipp.AttributeTags.JOB, attrs))
@@ -438,14 +462,7 @@
         printer = self.printers[self.default]
 
-        # requesting username
-        if 'requesting-user-name' in operation:
-            username_attr = operation['requesting-user-name']
-            username = username_attr.values[0].value
-            if username_attr != ipp.RequestingUserName(username):
-                raise ipp.errors.ClientErrorBadRequest(str(username_attr))
-
         # get attributes from the printer and add to response
         response.attribute_groups.append(ipp.AttributeGroup(
-            ipp.AttributeTags.PRINTER, printer.get_printer_attributes(request)))
+            ipp.AttributeTags.PRINTER, printer.get_printer_attributes(operation)))
 
     @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
@@ -462,15 +479,8 @@
         operation = request.attribute_groups[0]
 
-        # requesting username
-        if 'requesting-user-name' in operation:
-            username_attr = operation['requesting-user-name']
-            username = username_attr.values[0].value
-            if username_attr != ipp.RequestingUserName(username):
-                raise ipp.errors.ClientErrorBadRequest(str(username_attr))
-
         # get attributes from the printer and add to response
         for printer in self.printers.values():
             response.attribute_groups.append(ipp.AttributeGroup(
-                ipp.AttributeTags.PRINTER, printer.get_printer_attributes(request)))
+                ipp.AttributeTags.PRINTER, printer.get_printer_attributes(operation)))
 
     @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
Index: server/lib/gutenbach/server/server.py
===================================================================
--- server/lib/gutenbach/server/server.py	(revision aef164ab99e07d5dba2dc91548e544a2064ea3c7)
+++ server/lib/gutenbach/server/server.py	(revision ee8e6d08f0bd530f50e1298fc1b78a5923e5eb10)
@@ -9,21 +9,14 @@
 logger = logging.getLogger(__name__)
 
+# initialize handler
+handler = GutenbachRequestHandler()
+
 class GutenbachIPPServer(BaseHTTPServer.BaseHTTPRequestHandler):
-    def setup(self):
-        self.root = GutenbachRequestHandler()
-        BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
-
-    def handle_one_request(self):
-        self.raw_requestline = self.rfile.readline()
-        if not self.raw_requestline:
-            self.close_connection = 1
-            return
-        if not self.parse_request(): # An error code has been sent, just exit
-            return
-        self.handle_ipp()
-
-    def handle_ipp(self):
+    def do_POST(self):
         # Receive a request
         length = int(self.headers.getheader('content-length', 0))
+        if length == 0:
+            logger.warning("content-length == 0")
+            return
         request = ipp.Request(request=self.rfile, length=length)
 
@@ -33,5 +26,5 @@
         logger.debug("Received request: %s" % repr(request))
         try:
-            response = self.root.handle(request)
+            response = handler.handle(request)
         except:
             logger.fatal(traceback.format_exc())
