Index: server/lib/gutenbach/server/printer.py
===================================================================
--- server/lib/gutenbach/server/printer.py	(revision 34a4e5d730962d108163c14fafd961f93db9c5db)
+++ server/lib/gutenbach/server/printer.py	(revision 33ea5054308a544bb5f939b99ad15f59fa4b6d7a)
@@ -1,4 +1,4 @@
-from . import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
-from . import Job
+from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
+from .job import GutenbachJob
 from gutenbach.ipp import PrinterStates as States
 import gutenbach.ipp as ipp
@@ -17,5 +17,5 @@
 
     # for IPP
-    attributes = [
+    printer_attributes = [
         "printer-uri-supported",
         "uri-authentication-supported",
@@ -41,10 +41,30 @@
     ]
 
+    job_attributes = [
+        "job-id",
+        "job-name",
+        "job-originating-user-name",
+        "job-k-octets",
+        "job-state",
+        "job-printer-uri"
+    ]
+
     operations = [
         "print-job",
-        "complete-job",
-        "start-job",
-        "get-job",
+        "validate-job",
         "get-jobs",
+        "print-uri",
+        "create-job",
+        "pause-printer",
+        "resume-printer",
+        "get-printer-attributes",
+        "set-printer-attributes",
+        "cancel-job",
+        "send-document",
+        "send-uri",
+        "get-job-attributes",
+        "set-job-attributes",
+        "restart-job",
+        "promote-job"
     ]
         
@@ -73,41 +93,4 @@
     def __str__(self):
         return "<Printer '%s'>" % self.name 
-
-    ######################################################################
-    ###                          Properties                            ###
-    ######################################################################
-
-    @property
-    def uris(self):
-        uris = ["ipp://localhost:8000/printers/" + self.name,
-                "ipp://localhost/printers/" + self.name]
-        return uris
-    
-    @property
-    def uri(self):
-        return self.uris[0]
-
-    @property
-    def state(self):
-        with self.lock:
-            if self.current_job is not None:
-                val = States.PROCESSING
-            elif len(self.pending_jobs) == 0:
-                val = States.IDLE
-            else:
-                val = States.STOPPED
-        return val
-
-    @property
-    def active_jobs(self):
-        with self.lock:
-            jobs = self.pending_jobs[:]
-            if self.current_job is not None:
-                jobs.insert(0, self.current_job.id)
-        return jobs
-
-    ######################################################################
-    ###                            Methods                             ###
-    ######################################################################
 
     def run(self):
@@ -124,4 +107,41 @@
                     sys.exit(1)
             time.sleep(0.1)
+
+    ######################################################################
+    ###                          Properties                            ###
+    ######################################################################
+
+    @property
+    def uris(self):
+        uris = ["ipp://localhost:8000/printers/" + self.name,
+                "ipp://localhost/printers/" + self.name]
+        return uris
+    
+    @property
+    def uri(self):
+        return self.uris[0]
+
+    @property
+    def state(self):
+        with self.lock:
+            if self.current_job is not None:
+                val = States.PROCESSING
+            elif len(self.pending_jobs) == 0:
+                val = States.IDLE
+            else:
+                val = States.STOPPED
+        return val
+
+    @property
+    def active_jobs(self):
+        with self.lock:
+            jobs = self.pending_jobs[:]
+            if self.current_job is not None:
+                jobs.insert(0, self.current_job.id)
+        return jobs
+
+    ######################################################################
+    ###                            Methods                             ###
+    ######################################################################
 
     def start_job(self):
@@ -150,7 +170,4 @@
                 self.current_job = None
 
-    def stop(self):
-        pass
-
     def get_job(self, job_id):
         with self.lock:
@@ -249,4 +266,31 @@
         return ipp.MultipleDocumentJobsSupported(False)
 
+    ######################################################################
+    ###                      Job IPP Attributes                        ###
+    ######################################################################
+
+    def job_id(self, job_id):
+        job = self.get_job(job_id)
+        return ipp.JobId(job.id)
+
+    def job_name(self, job_id):
+        job = self.get_job(job_id)
+        return ipp.JobName(job.name)
+
+    def job_originating_user_name(self, job_id):
+        job = self.get_job(job_id)
+        return ipp.JobOriginatingUserName(job.creator)
+
+    def job_k_octets(self, job_id):
+        job = self.get_job(job_id)
+        return ipp.JobKOctets(job.size)
+
+    def job_state(self, job_id):
+        job = self.get_job(job_id)
+        return ipp.JobState(job.state)
+
+    def job_printer_uri(self, job_id):
+        job = self.get_job(job_id)
+        return ipp.JobPrinterUri(self.uri)
 
     ######################################################################
@@ -260,5 +304,7 @@
         pass
 
-    def get_jobs(self, requesting_user_name="", which_jobs=None):
+    def get_jobs(self, requesting_user_name=None, which_jobs=None,
+                 requested_attributes=None):
+        
         # Filter by the which-jobs attribute
         if which_jobs is None:
@@ -278,6 +324,10 @@
         else:
             user_jobs = [job for job in jobs if job.creator == requesting_user_name]
-        
-        return user_jobs
+
+        # Get the attributes of each job
+        job_attrs = [self.get_job_attributes(
+            job.id, requested_attributes=requested_attributes) for job in user_jobs]
+        
+        return job_attrs
 
     def print_uri(self):
@@ -288,14 +338,13 @@
         self._next_job_id += 1
         
-        job = Job(job_id,
-                  self,
-                  creator=requesting_user_name,
-                  name=job_name,
-                  size=job_k_octets)
+        job = GutenbachJob(
+            job_id,
+            creator=requesting_user_name,
+            name=job_name)
         
         self.jobs[job_id] = job
         self.pending_jobs.append(job_id)
         
-        return job
+        return job_id
 
     def pause_printer(self):
@@ -307,7 +356,8 @@
     def get_printer_attributes(self, requested_attributes=None):
         if requested_attributes is None:
-            requested = self.attributes
+            requested = self.printer_attributes
         else:
-            requested = [a for a in self.attributes if a in requested_attributes]
+            requested = [a for a in self.printer_attributes \
+                         if a in requested_attributes]
 
         _attributes = [attr.replace("-", "_") for attr in requested]
@@ -317,2 +367,41 @@
     def set_printer_attributes(self):
         pass
+
+    def cancel_job(self, job_id, requesting_user_name=None):
+        job = self.get_job(job_id)
+        try:
+            job.cancel()
+        except InvalidJobStateException:
+            # XXX
+            raise
+
+    def send_document(self, job_id, document, document_name=None,
+                      document_format=None, document_natural_language=None,
+                      requesting_user_name=None, compression=None,
+                      last_document=None):
+
+        job = self.get_job(job_id)
+        job.spool(document, username=requesting_user_name)
+
+    def send_uri(self):
+        pass
+
+    def get_job_attributes(self, job_id, requested_attributes=None):
+        if requested_attributes is None:
+            requested = self.job_attributes
+        else:
+            requested = [a for a in self.job_attributes \
+                         if a in requested_attributes]
+
+        _attributes = [attr.replace("-", "_") for attr in requested]
+        attributes = [getattr(self, attr)(job_id) for attr in _attributes]
+        return attributes
+
+    def set_job_attributes(self):
+        pass
+
+    def restart_job(self):
+        pass
+
+    def promote_job(self):
+        pass
