[97f20dd] | 1 | from . import sync |
---|
[33ea505] | 2 | from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException |
---|
| 3 | from .job import GutenbachJob |
---|
[b01b6d1] | 4 | from gutenbach.ipp import PrinterStates as States |
---|
[b2e077a] | 5 | import gutenbach.ipp as ipp |
---|
| 6 | import logging |
---|
[97f20dd] | 7 | import math |
---|
[eee389a] | 8 | import sys |
---|
[57bc2dc] | 9 | import tempfile |
---|
[97f20dd] | 10 | import threading |
---|
| 11 | import time |
---|
| 12 | import traceback |
---|
[d04a689] | 13 | |
---|
| 14 | # initialize logger |
---|
| 15 | logger = logging.getLogger(__name__) |
---|
[776a659] | 16 | |
---|
[eee389a] | 17 | class GutenbachPrinter(threading.Thread): |
---|
[b2e077a] | 18 | |
---|
[1a63bf7] | 19 | # for IPP |
---|
[33ea505] | 20 | printer_attributes = [ |
---|
[b2e077a] | 21 | "printer-uri-supported", |
---|
| 22 | "uri-authentication-supported", |
---|
| 23 | "uri-security-supported", |
---|
| 24 | "printer-name", |
---|
| 25 | "printer-state", |
---|
| 26 | "printer-state-reasons", |
---|
| 27 | "ipp-versions-supported", |
---|
| 28 | "operations-supported", |
---|
| 29 | "charset-configured", |
---|
| 30 | "charset-supported", |
---|
| 31 | "natural-language-configured", |
---|
| 32 | "generated-natural-language-supported", |
---|
| 33 | "document-format-default", |
---|
| 34 | "document-format-supported", |
---|
| 35 | "printer-is-accepting-jobs", |
---|
| 36 | "queued-job-count", |
---|
| 37 | "pdl-override-supported", |
---|
| 38 | "printer-up-time", |
---|
[f6e2532] | 39 | "compression-supported", |
---|
| 40 | "multiple-operation-time-out", |
---|
| 41 | "multiple-document-jobs-supported", |
---|
[1a63bf7] | 42 | ] |
---|
[b2e077a] | 43 | |
---|
[33ea505] | 44 | job_attributes = [ |
---|
| 45 | "job-id", |
---|
| 46 | "job-name", |
---|
| 47 | "job-originating-user-name", |
---|
| 48 | "job-k-octets", |
---|
| 49 | "job-state", |
---|
| 50 | "job-printer-uri" |
---|
| 51 | ] |
---|
| 52 | |
---|
[f6e2532] | 53 | operations = [ |
---|
| 54 | "print-job", |
---|
[33ea505] | 55 | "validate-job", |
---|
[f6e2532] | 56 | "get-jobs", |
---|
[33ea505] | 57 | "print-uri", |
---|
| 58 | "create-job", |
---|
| 59 | "pause-printer", |
---|
| 60 | "resume-printer", |
---|
| 61 | "get-printer-attributes", |
---|
| 62 | "set-printer-attributes", |
---|
| 63 | "cancel-job", |
---|
| 64 | "send-document", |
---|
| 65 | "send-uri", |
---|
| 66 | "get-job-attributes", |
---|
| 67 | "set-job-attributes", |
---|
| 68 | "restart-job", |
---|
| 69 | "promote-job" |
---|
[f6e2532] | 70 | ] |
---|
| 71 | |
---|
[609a9b0] | 72 | def __init__(self, name, config, *args, **kwargs): |
---|
[eee389a] | 73 | |
---|
[d21198f] | 74 | super(GutenbachPrinter, self).__init__(*args, **kwargs) |
---|
| 75 | |
---|
| 76 | self.name = name |
---|
[609a9b0] | 77 | self.config = config |
---|
[d21198f] | 78 | self.time_created = int(time.time()) |
---|
[b2e077a] | 79 | |
---|
[d21198f] | 80 | self.finished_jobs = [] |
---|
| 81 | self.pending_jobs = [] |
---|
| 82 | self.current_job = None |
---|
| 83 | self.jobs = {} |
---|
[b2e077a] | 84 | |
---|
[d21198f] | 85 | self.lock = threading.RLock() |
---|
[33528b4] | 86 | self._running = False |
---|
[d21198f] | 87 | self.paused = False |
---|
[776a659] | 88 | |
---|
[d21198f] | 89 | # CUPS ignores jobs with id 0, so we have to start at 1 |
---|
| 90 | self._next_job_id = 1 |
---|
[b01b6d1] | 91 | |
---|
[33528b4] | 92 | @sync |
---|
[b01b6d1] | 93 | def __repr__(self): |
---|
| 94 | return str(self) |
---|
| 95 | |
---|
[33528b4] | 96 | @sync |
---|
[b01b6d1] | 97 | def __str__(self): |
---|
[33528b4] | 98 | return "<Printer '%s'>" % self.name |
---|
[b01b6d1] | 99 | |
---|
[33ea505] | 100 | def run(self): |
---|
[33528b4] | 101 | self._running = True |
---|
| 102 | while self._running: |
---|
[d42236e] | 103 | with self.lock: |
---|
| 104 | try: |
---|
[33528b4] | 105 | if self.current_job is None: |
---|
[33ea505] | 106 | self.start_job() |
---|
[345c476] | 107 | elif self.current_job.is_done: |
---|
[33ea505] | 108 | self.complete_job() |
---|
[d42236e] | 109 | except: |
---|
| 110 | self._running = False |
---|
| 111 | logger.fatal(traceback.format_exc()) |
---|
| 112 | break |
---|
[33ea505] | 113 | time.sleep(0.1) |
---|
| 114 | |
---|
[33528b4] | 115 | def stop(self): |
---|
| 116 | with self.lock: |
---|
[d42236e] | 117 | for job in self.jobs.keys(): |
---|
| 118 | try: |
---|
| 119 | self.jobs[job].abort() |
---|
| 120 | del self.jobs[job] |
---|
| 121 | except InvalidJobStateException: |
---|
| 122 | pass |
---|
| 123 | |
---|
[33528b4] | 124 | self._running = False |
---|
| 125 | if self.ident is not None and self.isAlive(): |
---|
| 126 | self.join() |
---|
| 127 | |
---|
[b01b6d1] | 128 | ###################################################################### |
---|
| 129 | ### Properties ### |
---|
| 130 | ###################################################################### |
---|
| 131 | |
---|
| 132 | @property |
---|
[7e29e6a] | 133 | def name(self): |
---|
| 134 | return self._name |
---|
| 135 | @name.setter |
---|
| 136 | def name(self, val): |
---|
| 137 | try: |
---|
| 138 | self._name = str(val) |
---|
| 139 | except: |
---|
| 140 | self._name = "gutenbach-printer" |
---|
| 141 | |
---|
| 142 | @property |
---|
| 143 | def config(self): |
---|
| 144 | return self._config |
---|
| 145 | @config.setter |
---|
| 146 | def config(self, val): |
---|
| 147 | try: |
---|
| 148 | _config = dict(val).copy() |
---|
| 149 | except: |
---|
| 150 | raise ValueError, "not a dictionary" |
---|
| 151 | if 'ipp-versions' not in _config: |
---|
| 152 | raise ValueError, "missing ipp-versions" |
---|
| 153 | self._config = _config |
---|
| 154 | |
---|
| 155 | @property |
---|
[b01b6d1] | 156 | def uris(self): |
---|
| 157 | uris = ["ipp://localhost:8000/printers/" + self.name, |
---|
| 158 | "ipp://localhost/printers/" + self.name] |
---|
| 159 | return uris |
---|
| 160 | |
---|
| 161 | @property |
---|
| 162 | def uri(self): |
---|
| 163 | return self.uris[0] |
---|
| 164 | |
---|
| 165 | @property |
---|
[cf0d7e8] | 166 | @sync |
---|
[eee389a] | 167 | def state(self): |
---|
[d42236e] | 168 | if self.is_running and not self.paused: |
---|
| 169 | if len(self.active_jobs) > 0: |
---|
| 170 | state = States.PROCESSING |
---|
| 171 | else: |
---|
| 172 | state = States.IDLE |
---|
[cf0d7e8] | 173 | else: |
---|
[5e70cc2] | 174 | state = States.STOPPED |
---|
[d42236e] | 175 | |
---|
[5e70cc2] | 176 | return state |
---|
[eee389a] | 177 | |
---|
| 178 | @property |
---|
[cf0d7e8] | 179 | @sync |
---|
[eee389a] | 180 | def active_jobs(self): |
---|
[cf0d7e8] | 181 | jobs = self.pending_jobs[:] |
---|
| 182 | if self.current_job is not None: |
---|
| 183 | jobs.insert(0, self.current_job.id) |
---|
[eee389a] | 184 | return jobs |
---|
[b01b6d1] | 185 | |
---|
[33528b4] | 186 | @property |
---|
| 187 | def is_running(self): |
---|
| 188 | running = self.ident is not None and self.isAlive() and self._running |
---|
| 189 | return running |
---|
| 190 | |
---|
[b01b6d1] | 191 | ###################################################################### |
---|
| 192 | ### Methods ### |
---|
| 193 | ###################################################################### |
---|
| 194 | |
---|
[cf0d7e8] | 195 | @sync |
---|
[5e70cc2] | 196 | def assert_running(self): |
---|
| 197 | if not self.is_running: |
---|
| 198 | raise RuntimeError, "%s not started" % str(self) |
---|
| 199 | |
---|
| 200 | @sync |
---|
[eee389a] | 201 | def start_job(self): |
---|
[5e70cc2] | 202 | self.assert_running() |
---|
[33528b4] | 203 | if not self.paused and self.current_job is None: |
---|
[cf0d7e8] | 204 | try: |
---|
[d42236e] | 205 | job_id = self.pending_jobs.pop(0) |
---|
[cf0d7e8] | 206 | self.current_job = self.get_job(job_id) |
---|
| 207 | self.current_job.play() |
---|
| 208 | except IndexError: |
---|
| 209 | self.current_job = None |
---|
[eee389a] | 210 | |
---|
[cf0d7e8] | 211 | @sync |
---|
[eee389a] | 212 | def complete_job(self): |
---|
[5e70cc2] | 213 | self.assert_running() |
---|
[33528b4] | 214 | if not self.paused and self.current_job is not None: |
---|
| 215 | try: |
---|
| 216 | if not self.current_job.is_done: |
---|
| 217 | self.current_job.stop() |
---|
| 218 | finally: |
---|
| 219 | self.finished_jobs.append(self.current_job.id) |
---|
| 220 | self.current_job = None |
---|
[1a63bf7] | 221 | |
---|
[cf0d7e8] | 222 | @sync |
---|
[eee389a] | 223 | def get_job(self, job_id): |
---|
[5e70cc2] | 224 | self.assert_running() |
---|
[cf0d7e8] | 225 | if job_id not in self.jobs: |
---|
| 226 | raise InvalidJobException(job_id) |
---|
| 227 | return self.jobs[job_id] |
---|
[b01b6d1] | 228 | |
---|
| 229 | ###################################################################### |
---|
| 230 | ### IPP Attributes ### |
---|
| 231 | ###################################################################### |
---|
[1a63bf7] | 232 | |
---|
[b2e077a] | 233 | @property |
---|
| 234 | def printer_uri_supported(self): |
---|
[97f20dd] | 235 | """RFC 2911: 4.4.1 printer-uri-supported (1setOf uri) |
---|
| 236 | |
---|
| 237 | This REQUIRED Printer attribute contains at least one URI for |
---|
| 238 | the Printer object. It OPTIONALLY contains more than one URI |
---|
| 239 | for the Printer object. An administrator determines a Printer |
---|
| 240 | object's URI(s) and configures this attribute to contain those |
---|
| 241 | URIs by some means outside the scope of this IPP/1.1 document. |
---|
| 242 | The precise format of this URI is implementation dependent and |
---|
| 243 | depends on the protocol. See the next two sections for a |
---|
| 244 | description of the 'uri-security-supported' and |
---|
| 245 | 'uri-authentication-supported' attributes, both of which are |
---|
| 246 | the REQUIRED companion attributes to this 'printer-uri- |
---|
| 247 | supported' attribute. See section 2.4 on Printer object |
---|
| 248 | identity and section 8.2 on security and URIs for more |
---|
| 249 | information. |
---|
| 250 | |
---|
| 251 | """ |
---|
[5e70cc2] | 252 | self.assert_running() |
---|
[793432f] | 253 | return ipp.PrinterUriSupported(self.uri) |
---|
[9da7428] | 254 | @printer_uri_supported.setter |
---|
| 255 | def printer_uri_supported(self, val): |
---|
[5e70cc2] | 256 | self.assert_running() |
---|
[9da7428] | 257 | raise ipp.errors.AttributesNotSettable("printer-uri-supported") |
---|
[1a63bf7] | 258 | |
---|
[b2e077a] | 259 | @property |
---|
| 260 | def uri_authentication_supported(self): |
---|
[97f20dd] | 261 | """RFC 2911: 4.4.2 uri-authentication-supported (1setOf type2 |
---|
| 262 | keyword) |
---|
| 263 | |
---|
| 264 | This REQUIRED Printer attribute MUST have the same cardinality |
---|
| 265 | (contain the same number of values) as the |
---|
| 266 | 'printer-uri-supported' attribute. This attribute identifies |
---|
| 267 | the Client Authentication mechanism associated with each URI |
---|
| 268 | listed in the 'printer-uri- supported' attribute. The Printer |
---|
| 269 | object uses the specified mechanism to identify the |
---|
| 270 | authenticated user (see section 8.3). The 'i th' value in |
---|
| 271 | 'uri-authentication-supported' corresponds to the 'i th' value |
---|
| 272 | in 'printer-uri-supported' and it describes the authentication |
---|
| 273 | mechanisms used by the Printer when accessed via that URI. |
---|
| 274 | See [RFC2910] for more details on Client Authentication. |
---|
| 275 | |
---|
| 276 | The following standard keyword values are defined: |
---|
| 277 | |
---|
| 278 | 'none': There is no authentication mechanism associated with |
---|
| 279 | the URI. The Printer object assumes that the |
---|
| 280 | authenticated user is 'anonymous'. |
---|
| 281 | |
---|
| 282 | 'requesting-user-name': When a client performs an operation |
---|
| 283 | whose target is the associated URI, the Printer object |
---|
| 284 | assumes that the authenticated user is specified by the |
---|
| 285 | 'requesting-user- name' Operation attribute (see section |
---|
| 286 | 8.3). If the 'requesting-user-name' attribute is absent in |
---|
| 287 | a request, the Printer object assumes that the |
---|
| 288 | authenticated user is 'anonymous'. |
---|
| 289 | |
---|
| 290 | 'basic': When a client performs an operation whose target is |
---|
| 291 | the associated URI, the Printer object challenges the |
---|
| 292 | client with HTTP basic authentication [RFC2617]. The |
---|
| 293 | Printer object assumes that the authenticated user is the |
---|
| 294 | name received via the basic authentication mechanism. |
---|
| 295 | |
---|
| 296 | 'digest': When a client performs an operation whose target is |
---|
| 297 | the associated URI, the Printer object challenges the |
---|
| 298 | client with HTTP digest authentication [RFC2617]. The |
---|
| 299 | Printer object assumes that the authenticated user is the |
---|
| 300 | name received via the digest authentication mechanism. |
---|
| 301 | |
---|
| 302 | 'certificate': When a client performs an operation whose |
---|
| 303 | target is the associated URI, the Printer object expects |
---|
| 304 | the client to provide a certificate. The Printer object |
---|
| 305 | assumes that the authenticated user is the textual name |
---|
| 306 | contained within the certificate. |
---|
| 307 | |
---|
| 308 | """ |
---|
[5e70cc2] | 309 | self.assert_running() |
---|
[793432f] | 310 | return ipp.UriAuthenticationSupported("none") |
---|
[9da7428] | 311 | @uri_authentication_supported.setter |
---|
| 312 | def uri_authentication_supported(self, val): |
---|
[5e70cc2] | 313 | self.assert_running() |
---|
[9da7428] | 314 | raise ipp.errors.AttributesNotSettable("uri-authentication-supported") |
---|
[b2e077a] | 315 | |
---|
| 316 | @property |
---|
| 317 | def uri_security_supported(self): |
---|
[97f20dd] | 318 | """RFC 2911: 4.4.3 uri-security-supported (1setOf type2 |
---|
| 319 | keyword) |
---|
| 320 | |
---|
| 321 | This REQUIRED Printer attribute MUST have the same cardinality |
---|
| 322 | (contain the same number of values) as the |
---|
| 323 | 'printer-uri-supported' attribute. This attribute identifies |
---|
| 324 | the security mechanisms used for each URI listed in the |
---|
| 325 | 'printer-uri-supported' attribute. The 'i th' value in |
---|
| 326 | 'uri-security-supported' corresponds to the 'i th' value in |
---|
| 327 | 'printer-uri-supported' and it describes the security |
---|
| 328 | mechanisms used for accessing the Printer object via that |
---|
| 329 | URI. See [RFC2910] for more details on security mechanisms. |
---|
| 330 | |
---|
| 331 | The following standard keyword values are defined: |
---|
| 332 | |
---|
| 333 | 'none': There are no secure communication channel |
---|
| 334 | protocols in use for the given URI. |
---|
| 335 | |
---|
| 336 | 'ssl3': SSL3 [SSL] is the secure communications channel |
---|
| 337 | protocol in use for the given URI. |
---|
| 338 | |
---|
| 339 | 'tls': TLS [RFC2246] is the secure communications channel |
---|
| 340 | protocol in use for the given URI. |
---|
| 341 | |
---|
| 342 | This attribute is orthogonal to the definition of a Client |
---|
| 343 | Authentication mechanism. Specifically, 'none' does not |
---|
| 344 | exclude Client Authentication. See section 4.4.2. |
---|
| 345 | |
---|
| 346 | Consider the following example. For a single Printer object, |
---|
| 347 | an administrator configures the 'printer-uri-supported', 'uri- |
---|
| 348 | authentication-supported' and 'uri-security-supported' |
---|
| 349 | attributes as follows: |
---|
| 350 | |
---|
| 351 | 'printer-uri-supported': 'xxx://acme.com/open-use-printer', |
---|
| 352 | 'xxx://acme.com/restricted-use-printer', |
---|
| 353 | 'xxx://acme.com/private-printer' |
---|
| 354 | 'uri-authentication-supported': 'none', 'digest', 'basic' |
---|
| 355 | 'uri-security-supported': 'none', 'none', 'tls' |
---|
| 356 | |
---|
| 357 | Note: 'xxx' is not a valid scheme. See the IPP/1.1 'Transport |
---|
| 358 | and Encoding' document [RFC2910] for the actual URI schemes to |
---|
| 359 | be used in object target attributes. |
---|
| 360 | |
---|
| 361 | In this case, one Printer object has three URIs. |
---|
| 362 | |
---|
| 363 | - For the first URI, 'xxx://acme.com/open-use-printer', the |
---|
| 364 | value 'none' in 'uri-security-supported' indicates that |
---|
| 365 | there is no secure channel protocol configured to run under |
---|
| 366 | HTTP. The value of 'none' in 'uri-authentication-supported' |
---|
| 367 | indicates that all users are 'anonymous'. There will be no |
---|
| 368 | challenge and the Printer will ignore |
---|
| 369 | 'requesting-user-name'. |
---|
| 370 | |
---|
| 371 | - For the second URI, 'xxx://acme.com/restricted-use-printer', |
---|
| 372 | the value 'none' in 'uri-security-supported' indicates that |
---|
| 373 | there is no secure channel protocol configured to run under |
---|
| 374 | HTTP. The value of 'digest' in |
---|
| 375 | 'uri-authentication-supported' indicates that the Printer |
---|
| 376 | will issue a challenge and that the Printer will use the |
---|
| 377 | name supplied by the digest mechanism to determine the |
---|
| 378 | authenticated user (see section 8.3). |
---|
| 379 | |
---|
| 380 | - For the third URI, 'xxx://acme.com/private-printer', the |
---|
| 381 | value 'tls' in 'uri-security-supported' indicates that TLS |
---|
| 382 | is being used to secure the channel. The client SHOULD be |
---|
| 383 | prepared to use TLS framing to negotiate an acceptable |
---|
| 384 | ciphersuite to use while communicating with the Printer |
---|
| 385 | object. In this case, the name implies the use of a secure |
---|
| 386 | communications channel, but the fact is made explicit by the |
---|
| 387 | presence of the 'tls' value in 'uri-security-supported'. The |
---|
| 388 | client does not need to resort to understanding which |
---|
| 389 | security it must use by following naming conventions or by |
---|
| 390 | parsing the URI to determine which security mechanisms are |
---|
| 391 | implied. The value of 'basic' in 'uri- |
---|
| 392 | authentication-supported' indicates that the Printer will |
---|
| 393 | issue a challenge and that the Printer will use the name |
---|
| 394 | supplied by the digest mechanism to determine the |
---|
| 395 | authenticated user (see section 8.3). Because this challenge |
---|
| 396 | occurs in a tls session, the channel is secure. |
---|
| 397 | |
---|
| 398 | It is expected that many IPP Printer objects will be |
---|
| 399 | configured to support only one channel (either configured to |
---|
| 400 | use TLS access or not) and only one authentication |
---|
| 401 | mechanism. Such Printer objects only have one URI listed in |
---|
| 402 | the 'printer-uri-supported' attribute. No matter the |
---|
| 403 | configuration of the Printer object (whether it has only one |
---|
| 404 | URI or more than one URI), a client MUST supply only one URI |
---|
| 405 | in the target 'printer-uri' operation attribute. |
---|
| 406 | |
---|
| 407 | """ |
---|
| 408 | |
---|
[5e70cc2] | 409 | self.assert_running() |
---|
[793432f] | 410 | return ipp.UriSecuritySupported("none") |
---|
[9da7428] | 411 | @uri_security_supported.setter |
---|
| 412 | def uri_security_supported(self, val): |
---|
[5e70cc2] | 413 | self.assert_running() |
---|
[9da7428] | 414 | raise ipp.errors.AttributesNotSettable("uri-security-supported") |
---|
[b2e077a] | 415 | |
---|
| 416 | @property |
---|
| 417 | def printer_name(self): |
---|
[97f20dd] | 418 | """RFC 2911: 4.4.4 printer-name (name(127)) |
---|
| 419 | |
---|
| 420 | This REQUIRED Printer attribute contains the name of the |
---|
| 421 | Printer object. It is a name that is more end-user friendly |
---|
| 422 | than a URI. An administrator determines a printer's name and |
---|
| 423 | sets this attribute to that name. This name may be the last |
---|
| 424 | part of the printer's URI or it may be unrelated. In |
---|
| 425 | non-US-English locales, a name may contain characters that are |
---|
| 426 | not allowed in a URI. |
---|
| 427 | |
---|
| 428 | """ |
---|
[5e70cc2] | 429 | self.assert_running() |
---|
[793432f] | 430 | return ipp.PrinterName(self.name) |
---|
[9da7428] | 431 | @printer_name.setter |
---|
| 432 | def printer_name(self, val): |
---|
[5e70cc2] | 433 | self.assert_running() |
---|
[9da7428] | 434 | raise ipp.errors.AttributesNotSettable("printer-name") |
---|
[1a63bf7] | 435 | |
---|
[b2e077a] | 436 | @property |
---|
[97f20dd] | 437 | def printer_location(self): |
---|
| 438 | """RFC 2911: 4.4.5 printer-location (text(127)) |
---|
| 439 | |
---|
| 440 | This Printer attribute identifies the location of the |
---|
| 441 | device. This could include things like: 'in Room 123A, second |
---|
| 442 | floor of building XYZ'. |
---|
| 443 | |
---|
| 444 | """ |
---|
| 445 | raise AttributeError # XXX |
---|
| 446 | |
---|
| 447 | @property |
---|
| 448 | def printer_info(self): |
---|
| 449 | """RFC 2911: 4.4.6 printer-info (text(127)) |
---|
| 450 | |
---|
| 451 | This Printer attribute identifies the descriptive information |
---|
| 452 | about this Printer object. This could include things like: |
---|
| 453 | 'This printer can be used for printing color transparencies |
---|
| 454 | for HR presentations', or 'Out of courtesy for others, please |
---|
| 455 | print only small (1-5 page) jobs at this printer', or even |
---|
| 456 | 'This printer is going away on July 1, 1997, please find a new |
---|
| 457 | printer'. |
---|
| 458 | |
---|
| 459 | """ |
---|
| 460 | raise AttributeError # XXX |
---|
| 461 | |
---|
| 462 | @property |
---|
| 463 | def printer_more_info(self): |
---|
| 464 | """RFC 2911: 4.4.7 printer-more-info (uri) |
---|
| 465 | |
---|
| 466 | This Printer attribute contains a URI used to obtain more |
---|
| 467 | information about this specific Printer object. For example, |
---|
| 468 | this could be an HTTP type URI referencing an HTML page |
---|
| 469 | accessible to a Web Browser. The information obtained from |
---|
| 470 | this URI is intended for end user consumption. Features |
---|
| 471 | outside the scope of IPP can be accessed from this URI. The |
---|
| 472 | information is intended to be specific to this printer |
---|
| 473 | instance and site specific services (e.g. job pricing, |
---|
| 474 | services offered, end user assistance). The device |
---|
| 475 | manufacturer may initially populate this attribute. |
---|
| 476 | |
---|
| 477 | """ |
---|
| 478 | raise AttributeError # XXX |
---|
| 479 | |
---|
| 480 | @property |
---|
[b2e077a] | 481 | def printer_state(self): |
---|
[97f20dd] | 482 | """RFC 2911: 4.4.11 printer-state (type1 enum) |
---|
| 483 | |
---|
| 484 | This REQUIRED Printer attribute identifies the current state |
---|
| 485 | of the device. The 'printer-state reasons' attribute augments |
---|
| 486 | the 'printer-state' attribute to give more detailed |
---|
| 487 | information about the Printer in the given printer state. |
---|
| 488 | |
---|
| 489 | A Printer object need only update this attribute before |
---|
| 490 | responding to an operation which requests the attribute; the |
---|
| 491 | Printer object NEED NOT update this attribute continually, |
---|
| 492 | since asynchronous event notification is not part of |
---|
| 493 | IPP/1.1. A Printer NEED NOT implement all values if they are |
---|
| 494 | not applicable to a given implementation. The following |
---|
| 495 | standard enum values are defined: |
---|
| 496 | |
---|
| 497 | Value Symbolic Name and Description |
---|
| 498 | --------------------------------------------------------------- |
---|
| 499 | '3' 'idle': Indicates that new jobs can start processing |
---|
| 500 | without waiting. |
---|
| 501 | '4' 'processing': Indicates that jobs are processing; new |
---|
| 502 | jobs will wait before processing. |
---|
| 503 | '5' 'stopped': Indicates that no jobs can be processed and |
---|
| 504 | intervention is required. |
---|
| 505 | |
---|
| 506 | Values of 'printer-state-reasons', such as 'spool-area-full' |
---|
| 507 | and 'stopped-partly', MAY be used to provide further |
---|
| 508 | information. |
---|
| 509 | |
---|
| 510 | """ |
---|
[5e70cc2] | 511 | self.assert_running() |
---|
[b01b6d1] | 512 | return ipp.PrinterState(self.state) |
---|
[9da7428] | 513 | @printer_state.setter |
---|
| 514 | def printer_state(self, val): |
---|
[5e70cc2] | 515 | self.assert_running() |
---|
[9da7428] | 516 | raise ipp.errors.AttributesNotSettable("printer-state") |
---|
[1a63bf7] | 517 | |
---|
[b2e077a] | 518 | @property |
---|
| 519 | def printer_state_reasons(self): |
---|
[97f20dd] | 520 | """RFC 2911: 4.4.12 printer-state-reasons (1setOf type2 |
---|
| 521 | keyword) |
---|
| 522 | |
---|
| 523 | This REQUIRED Printer attribute supplies additional detail |
---|
| 524 | about the device's state. Some of the these value definitions |
---|
| 525 | indicate conformance requirements; the rest are OPTIONAL. |
---|
| 526 | Each keyword value MAY have a suffix to indicate its level of |
---|
| 527 | severity. The three levels are: report (least severe), |
---|
| 528 | warning, and error (most severe). |
---|
| 529 | |
---|
| 530 | - '-report': This suffix indicates that the reason is a |
---|
| 531 | 'report'. An implementation may choose to omit some or all |
---|
| 532 | reports. Some reports specify finer granularity about the |
---|
| 533 | printer state; others serve as a precursor to a warning. A |
---|
| 534 | report MUST contain nothing that could affect the printed |
---|
| 535 | output. |
---|
| 536 | |
---|
| 537 | - '-warning': This suffix indicates that the reason is a |
---|
| 538 | 'warning'. An implementation may choose to omit some or all |
---|
| 539 | warnings. Warnings serve as a precursor to an error. A |
---|
| 540 | warning MUST contain nothing that prevents a job from |
---|
| 541 | completing, though in some cases the output may be of lower |
---|
| 542 | quality. |
---|
| 543 | |
---|
| 544 | - '-error': This suffix indicates that the reason is an |
---|
| 545 | 'error'. An implementation MUST include all errors. If this |
---|
| 546 | attribute contains one or more errors, printer MUST be in |
---|
| 547 | the stopped state. |
---|
| 548 | |
---|
| 549 | If the implementation does not add any one of the three |
---|
| 550 | suffixes, all parties MUST assume that the reason is an |
---|
| 551 | 'error'. |
---|
| 552 | |
---|
| 553 | If a Printer object controls more than one output device, each |
---|
| 554 | value of this attribute MAY apply to one or more of the output |
---|
| 555 | devices. An error on one output device that does not stop the |
---|
| 556 | Printer object as a whole MAY appear as a warning in the |
---|
| 557 | Printer's 'printer-state-reasons attribute'. If the |
---|
| 558 | 'printer-state' for such a Printer has a value of 'stopped', |
---|
| 559 | then there MUST be an error reason among the values in the |
---|
| 560 | 'printer-state-reasons' attribute. |
---|
| 561 | |
---|
| 562 | """ |
---|
[5e70cc2] | 563 | self.assert_running() |
---|
[793432f] | 564 | return ipp.PrinterStateReasons("none") |
---|
[9da7428] | 565 | @printer_state_reasons.setter |
---|
| 566 | def printer_state_reasons(self, val): |
---|
[5e70cc2] | 567 | self.assert_running() |
---|
[9da7428] | 568 | raise ipp.errors.AttributesNotSettable("printer-state-reasons") |
---|
[b2e077a] | 569 | |
---|
| 570 | @property |
---|
| 571 | def ipp_versions_supported(self): |
---|
[97f20dd] | 572 | """RFC 2911: 4.4.14 ipp-versions-supported (1setOf type2 |
---|
| 573 | keyword) |
---|
| 574 | |
---|
| 575 | This REQUIRED attribute identifies the IPP protocol version(s) |
---|
| 576 | that this Printer supports, including major and minor |
---|
| 577 | versions, i.e., the version numbers for which this Printer |
---|
| 578 | implementation meets the conformance requirements. For version |
---|
| 579 | number validation, the Printer matches the (two-octet binary) |
---|
| 580 | 'version-number' parameter supplied by the client in each |
---|
| 581 | request (see sections 3.1.1 and 3.1.8) with the (US-ASCII) |
---|
| 582 | keyword values of this attribute. |
---|
| 583 | |
---|
| 584 | The following standard keyword values are defined: |
---|
| 585 | '1.0': Meets the conformance requirement of IPP version 1.0 |
---|
| 586 | as specified in RFC 2566 [RFC2566] and RFC 2565 |
---|
| 587 | [RFC2565] including any extensions registered |
---|
| 588 | according to Section 6 and any extension defined in |
---|
| 589 | this version or any future version of the IPP 'Model |
---|
| 590 | and Semantics' document or the IPP 'Encoding and |
---|
| 591 | Transport' document following the rules, if any, when |
---|
| 592 | the 'version-number' parameter is '1.0'. |
---|
| 593 | '1.1': Meets the conformance requirement of IPP version 1.1 |
---|
| 594 | as specified in this document and [RFC2910] including |
---|
| 595 | any extensions registered according to Section 6 and |
---|
| 596 | any extension defined in any future versions of the |
---|
| 597 | IPP 'Model and Semantics' document or the IPP |
---|
| 598 | Encoding and Transport document following the rules, |
---|
| 599 | if any, when the 'version-number' parameter is '1.1'. |
---|
| 600 | |
---|
| 601 | """ |
---|
[5e70cc2] | 602 | self.assert_running() |
---|
[609a9b0] | 603 | return ipp.IppVersionsSupported(*self.config['ipp-versions']) |
---|
[9da7428] | 604 | @ipp_versions_supported.setter |
---|
| 605 | def ipp_versions_supported(self, val): |
---|
[5e70cc2] | 606 | self.assert_running() |
---|
[9da7428] | 607 | raise ipp.errors.AttributesNotSettable("ipp-versions-supported") |
---|
[1a63bf7] | 608 | |
---|
[f6e2532] | 609 | # XXX: We should query ourself for the supported operations |
---|
[b2e077a] | 610 | @property |
---|
| 611 | def operations_supported(self): |
---|
[97f20dd] | 612 | """RFC 2911: 4.4.15 operations-supported (1setOf type2 enum) |
---|
| 613 | |
---|
| 614 | This REQUIRED Printer attribute specifies the set of supported |
---|
| 615 | operations for this Printer object and contained Job objects. |
---|
| 616 | This attribute is encoded as any other enum attribute syntax |
---|
| 617 | according to [RFC2910] as 32-bits. However, all 32-bit enum |
---|
| 618 | values for this attribute MUST NOT exceed 0x00008FFF, since |
---|
| 619 | these same values are also passed in two octets in the |
---|
| 620 | 'operation-id' parameter (see section 3.1.1) in each Protocol |
---|
| 621 | request with the two high order octets omitted in order to |
---|
| 622 | indicate the operation being performed [RFC2910]. |
---|
| 623 | |
---|
| 624 | """ |
---|
[5e70cc2] | 625 | self.assert_running() |
---|
[793432f] | 626 | return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS) |
---|
[9da7428] | 627 | @operations_supported.setter |
---|
| 628 | def operations_supported(self, val): |
---|
[5e70cc2] | 629 | self.assert_running() |
---|
[9da7428] | 630 | raise ipp.errors.AttributesNotSettable("operations-supported") |
---|
[b2e077a] | 631 | |
---|
| 632 | @property |
---|
[97f20dd] | 633 | def multiple_document_jobs_supported(self): |
---|
| 634 | """RFC 2911: 4.4.16 multiple-document-jobs-supported (boolean) |
---|
| 635 | |
---|
| 636 | This Printer attribute indicates whether or not the Printer |
---|
| 637 | supports more than one document per job, i.e., more than one |
---|
| 638 | Send-Document or Send-Data operation with document data. If |
---|
| 639 | the Printer supports the Create-Job and Send-Document |
---|
| 640 | operations (see section 3.2.4 and 3.3.1), it MUST support this |
---|
| 641 | attribute. |
---|
| 642 | |
---|
| 643 | """ |
---|
| 644 | self.assert_running() |
---|
| 645 | return ipp.MultipleDocumentJobsSupported(False) |
---|
| 646 | @multiple_document_jobs_supported.setter |
---|
| 647 | def multiple_document_jobs_supported(self, val): |
---|
| 648 | self.assert_running() |
---|
| 649 | raise ipp.errors.AttributesNotSettable("multiple-document-jobs-supported") |
---|
| 650 | |
---|
| 651 | @property |
---|
[b2e077a] | 652 | def charset_configured(self): |
---|
[97f20dd] | 653 | """RFC 2911: 4.4.17 charset-configured (charset) |
---|
| 654 | |
---|
| 655 | This REQUIRED Printer attribute identifies the charset that |
---|
| 656 | the Printer object has been configured to represent 'text' and |
---|
| 657 | 'name' Printer attributes that are set by the operator, system |
---|
| 658 | administrator, or manufacturer, i.e., for 'printer-name' |
---|
| 659 | (name), 'printer-location' (text), 'printer-info' (text), and |
---|
| 660 | 'printer-make- and-model' (text). Therefore, the value of the |
---|
| 661 | Printer object's 'charset-configured' attribute MUST also be |
---|
| 662 | among the values of the Printer object's 'charset-supported' |
---|
| 663 | attribute. |
---|
| 664 | |
---|
| 665 | """ |
---|
[5e70cc2] | 666 | self.assert_running() |
---|
[9da7428] | 667 | return ipp.CharsetConfigured("utf-8") # XXX |
---|
| 668 | @charset_configured.setter |
---|
| 669 | def charset_configured(self, val): |
---|
[5e70cc2] | 670 | self.assert_running() |
---|
[9da7428] | 671 | raise ipp.errors.AttributesNotSettable("charset-configured") |
---|
| 672 | |
---|
[b2e077a] | 673 | @property |
---|
| 674 | def charset_supported(self): |
---|
[97f20dd] | 675 | """RFC 2911: 4.4.18 charset-supported (1setOf charset) |
---|
| 676 | |
---|
| 677 | This REQUIRED Printer attribute identifies the set of charsets |
---|
| 678 | that the Printer and contained Job objects support in |
---|
| 679 | attributes with attribute syntax 'text' and 'name'. At least |
---|
| 680 | the value 'utf-8' MUST be present, since IPP objects MUST |
---|
| 681 | support the UTF-8 [RFC2279] charset. If a Printer object |
---|
| 682 | supports a charset, it means that for all attributes of syntax |
---|
| 683 | 'text' and 'name' the IPP object MUST (1) accept the charset |
---|
| 684 | in requests and return the charset in responses as needed. |
---|
| 685 | |
---|
| 686 | If more charsets than UTF-8 are supported, the IPP object MUST |
---|
| 687 | perform charset conversion between the charsets as described |
---|
| 688 | in Section 3.1.4.2. |
---|
| 689 | |
---|
| 690 | """ |
---|
[5e70cc2] | 691 | self.assert_running() |
---|
[9da7428] | 692 | return ipp.CharsetSupported("utf-8") # XXX |
---|
| 693 | @charset_supported.setter |
---|
| 694 | def charset_supported(self, val): |
---|
[5e70cc2] | 695 | self.assert_running() |
---|
[9da7428] | 696 | raise ipp.errors.AttributesNotSettable("charset-supported") |
---|
[b2e077a] | 697 | |
---|
| 698 | @property |
---|
| 699 | def natural_language_configured(self): |
---|
[97f20dd] | 700 | """RFC 2911: 4.4.19 natural-language-configured (naturalLanguage) |
---|
| 701 | |
---|
| 702 | This REQUIRED Printer attribute identifies the natural |
---|
| 703 | language that the Printer object has been configured to |
---|
| 704 | represent 'text' and 'name' Printer attributes that are set by |
---|
| 705 | the operator, system administrator, or manufacturer, i.e., for |
---|
| 706 | 'printer-name' (name), 'printer-location' (text), |
---|
| 707 | 'printer-info' (text), and 'printer-make- and-model' |
---|
| 708 | (text). When returning these Printer attributes, the Printer |
---|
| 709 | object MAY return them in the configured natural language |
---|
| 710 | specified by this attribute, instead of the natural language |
---|
| 711 | requested by the client in the 'attributes-natural-language' |
---|
| 712 | operation attribute. See Section 3.1.4.1 for the specification |
---|
| 713 | of the OPTIONAL multiple natural language support. Therefore, |
---|
| 714 | the value of the Printer object's |
---|
| 715 | 'natural-language-configured' attribute MUST also be among the |
---|
| 716 | values of the Printer object's 'generated-natural- |
---|
| 717 | language-supported' attribute. |
---|
| 718 | |
---|
| 719 | """ |
---|
[5e70cc2] | 720 | self.assert_running() |
---|
[793432f] | 721 | return ipp.NaturalLanguageConfigured("en-us") |
---|
[9da7428] | 722 | @natural_language_configured.setter |
---|
| 723 | def natural_language_configured(self, val): |
---|
[5e70cc2] | 724 | self.assert_running() |
---|
[9da7428] | 725 | raise ipp.errors.AttributesNotSettable("natural-language-configured") |
---|
[b2e077a] | 726 | |
---|
| 727 | @property |
---|
| 728 | def generated_natural_language_supported(self): |
---|
[97f20dd] | 729 | """RFC 2911: 4.4.20 generated-natural-language-supported |
---|
| 730 | (1setOf naturalLanguage) |
---|
| 731 | |
---|
| 732 | This REQUIRED Printer attribute identifies the natural |
---|
| 733 | language(s) that the Printer object and contained Job objects |
---|
| 734 | support in attributes with attribute syntax 'text' and |
---|
| 735 | 'name'. The natural language(s) supported depends on |
---|
| 736 | implementation and/or configuration. Unlike charsets, IPP |
---|
| 737 | objects MUST accept requests with any natural language or any |
---|
| 738 | Natural Language Override whether the natural language is |
---|
| 739 | supported or not. |
---|
| 740 | |
---|
| 741 | If a Printer object supports a natural language, it means that |
---|
| 742 | for any of the attributes for which the Printer or Job object |
---|
| 743 | generates messages, i.e., for the 'job-state-message' and |
---|
| 744 | 'printer-state- message' attributes and Operation Messages |
---|
| 745 | (see Section 3.1.5) in operation responses, the Printer and |
---|
| 746 | Job objects MUST be able to generate messages in any of the |
---|
| 747 | Printer's supported natural languages. See section 3.1.4 for |
---|
| 748 | the definition of 'text' and 'name' attributes in operation |
---|
| 749 | requests and responses. |
---|
| 750 | |
---|
| 751 | Note: A Printer object that supports multiple natural |
---|
| 752 | languages, often has separate catalogs of messages, one for |
---|
| 753 | each natural language supported. |
---|
| 754 | |
---|
| 755 | """ |
---|
[5e70cc2] | 756 | self.assert_running() |
---|
[793432f] | 757 | return ipp.GeneratedNaturalLanguageSupported("en-us") |
---|
[9da7428] | 758 | @generated_natural_language_supported.setter |
---|
| 759 | def generated_natural_language_supported(self, val): |
---|
[5e70cc2] | 760 | self.assert_running() |
---|
[9da7428] | 761 | raise ipp.errors.AttributesNotSettable("generated-natural-language-supported") |
---|
[b2e077a] | 762 | |
---|
| 763 | @property |
---|
| 764 | def document_format_default(self): |
---|
[97f20dd] | 765 | """RFC 2911: 4.4.21 document-format-default (mimeMediaType) |
---|
| 766 | |
---|
| 767 | This REQUIRED Printer attribute identifies the document format |
---|
| 768 | that the Printer object has been configured to assume if the |
---|
| 769 | client does not supply a 'document-format' operation attribute |
---|
| 770 | in any of the operation requests that supply document |
---|
| 771 | data. The standard values for this attribute are Internet |
---|
| 772 | Media types (sometimes called MIME types). For further details |
---|
| 773 | see the description of the 'mimeMediaType' attribute syntax in |
---|
| 774 | Section 4.1.9. |
---|
| 775 | |
---|
| 776 | """ |
---|
[5e70cc2] | 777 | self.assert_running() |
---|
[793432f] | 778 | return ipp.DocumentFormatDefault("application/octet-stream") |
---|
[9da7428] | 779 | @document_format_default.setter |
---|
| 780 | def document_format_default(self, val): |
---|
[5e70cc2] | 781 | self.assert_running() |
---|
[9da7428] | 782 | raise ipp.errors.AttributesNotSettable("document-format-default") |
---|
[b2e077a] | 783 | |
---|
| 784 | @property |
---|
| 785 | def document_format_supported(self): |
---|
[97f20dd] | 786 | """RFC 2911: 4.4.22 document-format-supported (1setOf mimeMediaType) |
---|
| 787 | |
---|
| 788 | This REQUIRED Printer attribute identifies the set of document |
---|
| 789 | formats that the Printer object and contained Job objects can |
---|
| 790 | support. For further details see the description of the |
---|
| 791 | 'mimeMediaType' attribute syntax in Section 4.1.9. |
---|
| 792 | |
---|
| 793 | """ |
---|
[5e70cc2] | 794 | self.assert_running() |
---|
[793432f] | 795 | return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3") |
---|
[9da7428] | 796 | @document_format_supported.setter |
---|
| 797 | def document_format_supported(self, val): |
---|
[5e70cc2] | 798 | self.assert_running() |
---|
[9da7428] | 799 | raise ipp.errors.AttributesNotSettable("document-format-supported") |
---|
[b2e077a] | 800 | |
---|
| 801 | @property |
---|
| 802 | def printer_is_accepting_jobs(self): |
---|
[97f20dd] | 803 | """RFC 2911: 4.4.23 printer-is-accepting-jobs (boolean) |
---|
| 804 | |
---|
| 805 | This REQUIRED Printer attribute indicates whether the printer |
---|
| 806 | is currently able to accept jobs, i.e., is accepting |
---|
| 807 | Print-Job, Print- URI, and Create-Job requests. If the value |
---|
| 808 | is 'true', the printer is accepting jobs. If the value is |
---|
| 809 | 'false', the Printer object is currently rejecting any jobs |
---|
| 810 | submitted to it. In this case, the Printer object returns the |
---|
| 811 | 'server-error-not-accepting-jobs' status code. |
---|
| 812 | |
---|
| 813 | This value is independent of the 'printer-state' and |
---|
| 814 | 'printer-state- reasons' attributes because its value does not |
---|
| 815 | affect the current job; rather it affects future jobs. This |
---|
| 816 | attribute, when 'false', causes the Printer to reject jobs |
---|
| 817 | even when the 'printer-state' is 'idle' or, when 'true', |
---|
| 818 | causes the Printer object to accepts jobs even when the |
---|
| 819 | 'printer-state' is 'stopped'. |
---|
| 820 | |
---|
| 821 | """ |
---|
[5e70cc2] | 822 | self.assert_running() |
---|
[97f20dd] | 823 | return ipp.PrinterIsAcceptingJobs(True) # XXX |
---|
[9da7428] | 824 | @printer_is_accepting_jobs.setter |
---|
| 825 | def printer_is_accepting_jobs(self, val): |
---|
[5e70cc2] | 826 | self.assert_running() |
---|
[9da7428] | 827 | raise ipp.errors.AttributesNotSettable("printer-is-accepting-jobs") |
---|
[b2e077a] | 828 | |
---|
| 829 | @property |
---|
| 830 | def queued_job_count(self): |
---|
[97f20dd] | 831 | """RFC 2911: 4.4.24 queued-job-count (integer(0:MAX)) |
---|
| 832 | |
---|
| 833 | This REQUIRED Printer attribute contains a count of the number |
---|
| 834 | of jobs that are either 'pending', 'processing', |
---|
| 835 | 'pending-held', or 'processing-stopped' and is set by the |
---|
| 836 | Printer object. |
---|
| 837 | |
---|
| 838 | """ |
---|
[5e70cc2] | 839 | self.assert_running() |
---|
[793432f] | 840 | return ipp.QueuedJobCount(len(self.active_jobs)) |
---|
[9da7428] | 841 | @queued_job_count.setter |
---|
| 842 | def queued_job_count(self, val): |
---|
[5e70cc2] | 843 | self.assert_running() |
---|
[9da7428] | 844 | raise ipp.errors.AttributesNotSettable("queued-job-count") |
---|
[b2e077a] | 845 | |
---|
| 846 | @property |
---|
| 847 | def pdl_override_supported(self): |
---|
[97f20dd] | 848 | """RFC 2911: 4.4.28 pdl-override-supported (type2 keyword) |
---|
| 849 | |
---|
| 850 | This REQUIRED Printer attribute expresses the ability for a |
---|
| 851 | particular Printer implementation to either attempt to |
---|
| 852 | override document data instructions with IPP attributes or |
---|
| 853 | not. |
---|
| 854 | |
---|
| 855 | This attribute takes on the following keyword values: |
---|
| 856 | |
---|
| 857 | - 'attempted': This value indicates that the Printer object |
---|
| 858 | attempts to make the IPP attribute values take precedence |
---|
| 859 | over embedded instructions in the document data, however |
---|
| 860 | there is no guarantee. |
---|
| 861 | |
---|
| 862 | - 'not-attempted': This value indicates that the Printer |
---|
| 863 | object makes no attempt to make the IPP attribute values |
---|
| 864 | take precedence over embedded instructions in the document |
---|
| 865 | data. |
---|
| 866 | |
---|
| 867 | Section 15 contains a full description of how this attribute |
---|
| 868 | interacts with and affects other IPP attributes, especially |
---|
| 869 | the 'ipp-attribute-fidelity' attribute. |
---|
| 870 | |
---|
| 871 | """ |
---|
[5e70cc2] | 872 | self.assert_running() |
---|
[793432f] | 873 | return ipp.PdlOverrideSupported("not-attempted") |
---|
[9da7428] | 874 | @pdl_override_supported.setter |
---|
| 875 | def pdl_override_supported(self, val): |
---|
[5e70cc2] | 876 | self.assert_running() |
---|
[9da7428] | 877 | raise ipp.errors.AttributesNotSettable("pdl-override-supported") |
---|
[b2e077a] | 878 | |
---|
| 879 | @property |
---|
| 880 | def printer_up_time(self): |
---|
[97f20dd] | 881 | """RFC 2911: 4.4.29 printer-up-time (integer(1:MAX)) |
---|
| 882 | |
---|
| 883 | This REQUIRED Printer attribute indicates the amount of time |
---|
| 884 | (in seconds) that this Printer instance has been up and |
---|
| 885 | running. The value is a monotonically increasing value |
---|
| 886 | starting from 1 when the Printer object is started-up |
---|
| 887 | (initialized, booted, etc.). This value is used to populate |
---|
| 888 | the Event Time Job Description Job attributes |
---|
| 889 | 'time-at-creation', 'time-at-processing', and |
---|
| 890 | 'time-at-completed' (see section 4.3.14). |
---|
| 891 | |
---|
| 892 | If the Printer object goes down at some value 'n', and comes |
---|
| 893 | back up, the implementation MAY: |
---|
| 894 | |
---|
| 895 | 1. Know how long it has been down, and resume at some |
---|
| 896 | value greater than 'n', or |
---|
| 897 | 2. Restart from 1. |
---|
| 898 | |
---|
| 899 | In other words, if the device or devices that the Printer |
---|
| 900 | object is representing are restarted or power cycled, the |
---|
| 901 | Printer object MAY continue counting this value or MAY reset |
---|
| 902 | this value to 1 depending on implementation. However, if the |
---|
| 903 | Printer object software ceases running, and restarts without |
---|
| 904 | knowing the last value for 'printer- up-time', the |
---|
| 905 | implementation MUST reset this value to 1. If this value is |
---|
| 906 | reset and the Printer has persistent jobs, the Printer MUST |
---|
| 907 | reset the 'time-at-xxx(integer) Event Time Job Description |
---|
| 908 | attributes according to Section 4.3.14. An implementation MAY |
---|
| 909 | use both implementation alternatives, depending on warm versus |
---|
| 910 | cold start, respectively. |
---|
| 911 | |
---|
| 912 | """ |
---|
[5e70cc2] | 913 | self.assert_running() |
---|
[793432f] | 914 | return ipp.PrinterUpTime(int(time.time()) - self.time_created) |
---|
[9da7428] | 915 | @printer_up_time.setter |
---|
| 916 | def printer_up_time(self, val): |
---|
[5e70cc2] | 917 | self.assert_running() |
---|
[9da7428] | 918 | raise ipp.errors.AttributesNotSettable("printer-up-time") |
---|
[b2e077a] | 919 | |
---|
| 920 | @property |
---|
[97f20dd] | 921 | def printer_current_time(self): |
---|
| 922 | """RFC 2911: 4.4.30 printer-current-time (dateTime) |
---|
| 923 | |
---|
| 924 | This Printer attribute indicates the current date and |
---|
| 925 | time. This value is used to populate the Event Time Job |
---|
| 926 | Description attributes: 'date-time-at-creation', |
---|
| 927 | 'date-time-at-processing', and 'date-time- at-completed' (see |
---|
| 928 | Section 4.3.14). |
---|
| 929 | |
---|
| 930 | The date and time is obtained on a 'best efforts basis' and |
---|
| 931 | does not have to be that precise in order to work in |
---|
| 932 | practice. A Printer implementation sets the value of this |
---|
| 933 | attribute by obtaining the date and time via some |
---|
| 934 | implementation-dependent means, such as getting the value from |
---|
| 935 | a network time server, initialization at time of manufacture, |
---|
| 936 | or setting by an administrator. See [IPP-IIG] for examples. If |
---|
| 937 | an implementation supports this attribute and the |
---|
| 938 | implementation knows that it has not yet been set, then the |
---|
| 939 | implementation MUST return the value of this attribute using |
---|
| 940 | the out-of-band 'no-value' meaning not configured. See the |
---|
| 941 | beginning of section 4.1. |
---|
| 942 | |
---|
| 943 | The time zone of this attribute NEED NOT be the time zone used |
---|
| 944 | by people located near the Printer object or device. The |
---|
| 945 | client MUST NOT expect that the time zone of any received |
---|
| 946 | 'dateTime' value to be in the time zone of the client or in |
---|
| 947 | the time zone of the people located near the printer. |
---|
| 948 | |
---|
| 949 | The client SHOULD display any dateTime attributes to the user |
---|
| 950 | in client local time by converting the 'dateTime' value |
---|
| 951 | returned by the server to the time zone of the client, rather |
---|
| 952 | than using the time zone returned by the Printer in attributes |
---|
| 953 | that use the 'dateTime' attribute syntax. |
---|
| 954 | |
---|
| 955 | """ |
---|
| 956 | raise AttributeError # XXX |
---|
[b2e077a] | 957 | |
---|
[f6e2532] | 958 | @property |
---|
| 959 | def multiple_operation_time_out(self): |
---|
[97f20dd] | 960 | """RFC 2911: 4.4.31 multiple-operation-time-out |
---|
| 961 | (integer(1:MAX)) |
---|
| 962 | |
---|
| 963 | This Printer attributes identifies the minimum time (in |
---|
| 964 | seconds) that the Printer object waits for additional |
---|
| 965 | Send-Document or Send-URI operations to follow a still-open |
---|
| 966 | Job object before taking any recovery actions, such as the |
---|
| 967 | ones indicated in section 3.3.1. If the Printer object |
---|
| 968 | supports the Create-Job and Send-Document operations (see |
---|
| 969 | section 3.2.4 and 3.3.1), it MUST support this attribute. |
---|
| 970 | |
---|
| 971 | It is RECOMMENDED that vendors supply a value for this |
---|
| 972 | attribute that is between 60 and 240 seconds. An |
---|
| 973 | implementation MAY allow a system administrator to set this |
---|
| 974 | attribute (by means outside this IPP/1.1 document). If so, the |
---|
| 975 | system administrator MAY be able to set values outside this |
---|
| 976 | range. |
---|
| 977 | |
---|
| 978 | """ |
---|
[5e70cc2] | 979 | self.assert_running() |
---|
[793432f] | 980 | return ipp.MultipleOperationTimeOut(240) |
---|
[9da7428] | 981 | @multiple_operation_time_out.setter |
---|
| 982 | def multiple_operation_time_out(self, val): |
---|
[5e70cc2] | 983 | self.assert_running() |
---|
[9da7428] | 984 | raise ipp.errors.AttributesNotSettable("multiple-operation-time-out") |
---|
[f6e2532] | 985 | |
---|
| 986 | @property |
---|
[97f20dd] | 987 | def compression_supported(self): |
---|
| 988 | """RFC 2911: 4.4.32 compression-supported (1setOf type3 |
---|
| 989 | keyword) |
---|
| 990 | |
---|
| 991 | This REQUIRED Printer attribute identifies the set of |
---|
| 992 | supported compression algorithms for document |
---|
| 993 | data. Compression only applies to the document data; |
---|
| 994 | compression does not apply to the encoding of the IPP |
---|
| 995 | operation itself. The supported values are used to validate |
---|
| 996 | the client supplied 'compression' operation attributes in |
---|
| 997 | Print-Job, Send-Document, and Send-URI requests. |
---|
| 998 | |
---|
| 999 | Standard keyword values are : |
---|
| 1000 | 'none': no compression is used. |
---|
| 1001 | 'deflate': ZIP public domain inflate/deflate) compression |
---|
| 1002 | technology in RFC 1951 [RFC1951] |
---|
| 1003 | 'gzip' GNU zip compression technology described in RFC |
---|
| 1004 | 1952 [RFC1952]. |
---|
| 1005 | 'compress': UNIX compression technology in RFC 1977 [RFC1977] |
---|
| 1006 | |
---|
| 1007 | """ |
---|
[5e70cc2] | 1008 | self.assert_running() |
---|
[97f20dd] | 1009 | return ipp.CompressionSupported("none") |
---|
| 1010 | @compression_supported.setter |
---|
| 1011 | def compression_supported(self, val): |
---|
[5e70cc2] | 1012 | self.assert_running() |
---|
[97f20dd] | 1013 | raise ipp.errors.AttributesNotSettable("compression-supported") |
---|
[f6e2532] | 1014 | |
---|
[33ea505] | 1015 | ###################################################################### |
---|
| 1016 | ### Job IPP Attributes ### |
---|
| 1017 | ###################################################################### |
---|
| 1018 | |
---|
[97f20dd] | 1019 | def job_uri(self, job_id): |
---|
| 1020 | """RFC 2911: 4.3.1 job-uri (uri) |
---|
| 1021 | |
---|
| 1022 | This REQUIRED attribute contains the URI for the job. The |
---|
| 1023 | Printer object, on receipt of a new job, generates a URI which |
---|
| 1024 | identifies the new Job. The Printer object returns the value |
---|
| 1025 | of the 'job-uri' attribute as part of the response to a create |
---|
| 1026 | request. The precise format of a Job URI is implementation |
---|
| 1027 | dependent. If the Printer object supports more than one URI |
---|
| 1028 | and there is some relationship between the newly formed Job |
---|
| 1029 | URI and the Printer object's URI, the Printer object uses the |
---|
| 1030 | Printer URI supplied by the client in the create request. For |
---|
| 1031 | example, if the create request comes in over a secure channel, |
---|
| 1032 | the new Job URI MUST use the same secure channel. This can be |
---|
| 1033 | guaranteed because the Printer object is responsible for |
---|
| 1034 | generating the Job URI and the Printer object is aware of its |
---|
| 1035 | security configuration and policy as well as the Printer URI |
---|
| 1036 | used in the create request. |
---|
| 1037 | |
---|
| 1038 | For a description of this attribute and its relationship to |
---|
| 1039 | 'job-id' and 'job-printer-uri' attribute, see the discussion |
---|
| 1040 | in section 2.4 on 'Object Identity'. |
---|
| 1041 | |
---|
| 1042 | """ |
---|
| 1043 | raise AttributeError # XXX |
---|
| 1044 | |
---|
[33ea505] | 1045 | def job_id(self, job_id): |
---|
[97f20dd] | 1046 | """RFC 2911: 4.3.2 job-id (integer(1:MAX)) |
---|
| 1047 | |
---|
| 1048 | This REQUIRED attribute contains the ID of the job. The |
---|
| 1049 | Printer, on receipt of a new job, generates an ID which |
---|
| 1050 | identifies the new Job on that Printer. The Printer returns |
---|
| 1051 | the value of the 'job-id' attribute as part of the response to |
---|
| 1052 | a create request. The 0 value is not included to allow for |
---|
| 1053 | compatibility with SNMP index values which also cannot be 0. |
---|
| 1054 | |
---|
| 1055 | For a description of this attribute and its relationship to |
---|
| 1056 | 'job-uri' and 'job-printer-uri' attribute, see the discussion |
---|
| 1057 | in section 2.4 on 'Object Identity'. |
---|
| 1058 | |
---|
| 1059 | """ |
---|
[5e70cc2] | 1060 | self.assert_running() |
---|
[33ea505] | 1061 | job = self.get_job(job_id) |
---|
| 1062 | return ipp.JobId(job.id) |
---|
| 1063 | |
---|
[97f20dd] | 1064 | def job_printer_uri(self, job_id): |
---|
| 1065 | """RFC 2911: 4.3.3 job-printer-uri (uri) |
---|
| 1066 | |
---|
| 1067 | This REQUIRED attribute identifies the Printer object that |
---|
| 1068 | created this Job object. When a Printer object creates a Job |
---|
| 1069 | object, it populates this attribute with the Printer object |
---|
| 1070 | URI that was used in the create request. This attribute |
---|
| 1071 | permits a client to identify the Printer object that created |
---|
| 1072 | this Job object when only the Job object's URI is available to |
---|
| 1073 | the client. The client queries the creating Printer object to |
---|
| 1074 | determine which languages, charsets, operations, are supported |
---|
| 1075 | for this Job. |
---|
| 1076 | |
---|
| 1077 | For a description of this attribute and its relationship to |
---|
| 1078 | 'job-uri' and 'job-id' attribute, see the discussion in |
---|
| 1079 | section 2.4 on 'Object Identity'. |
---|
| 1080 | |
---|
| 1081 | """ |
---|
| 1082 | self.assert_running() |
---|
| 1083 | job = self.get_job(job_id) |
---|
| 1084 | return ipp.JobPrinterUri(self.uri) |
---|
| 1085 | |
---|
[33ea505] | 1086 | def job_name(self, job_id): |
---|
[97f20dd] | 1087 | """RFC 2911: 4.3.5 job-name (name(MAX)) |
---|
| 1088 | |
---|
| 1089 | This REQUIRED attribute is the name of the job. It is a name |
---|
| 1090 | that is more user friendly than the 'job-uri' attribute |
---|
| 1091 | value. It does not need to be unique between Jobs. The Job's |
---|
| 1092 | 'job-name' attribute is set to the value supplied by the |
---|
| 1093 | client in the 'job-name' operation attribute in the create |
---|
| 1094 | request (see Section 3.2.1.1). |
---|
| 1095 | |
---|
| 1096 | If, however, the 'job-name' operation attribute is not |
---|
| 1097 | supplied by the client in the create request, the Printer |
---|
| 1098 | object, on creation of the Job, MUST generate a name. The |
---|
| 1099 | printer SHOULD generate the value of the Job's 'job-name' |
---|
| 1100 | attribute from the first of the following sources that |
---|
| 1101 | produces a value: 1) the 'document-name' operation attribute |
---|
| 1102 | of the first (or only) document, 2) the 'document-URI' |
---|
| 1103 | attribute of the first (or only) document, or 3) any other |
---|
| 1104 | piece of Job specific and/or Document Content information. |
---|
| 1105 | |
---|
| 1106 | """ |
---|
[5e70cc2] | 1107 | self.assert_running() |
---|
[33ea505] | 1108 | job = self.get_job(job_id) |
---|
| 1109 | return ipp.JobName(job.name) |
---|
| 1110 | |
---|
| 1111 | def job_originating_user_name(self, job_id): |
---|
[97f20dd] | 1112 | """RFC 2911: 4.3.6 job-originating-user-name (name(MAX)) |
---|
[33ea505] | 1113 | |
---|
[97f20dd] | 1114 | This REQUIRED attribute contains the name of the end user that |
---|
| 1115 | submitted the print job. The Printer object sets this |
---|
| 1116 | attribute to the most authenticated printable name that it can |
---|
| 1117 | obtain from the authentication service over which the IPP |
---|
| 1118 | operation was received. Only if such is not available, does |
---|
| 1119 | the Printer object use the value supplied by the client in the |
---|
| 1120 | 'requesting-user-name' operation attribute of the create |
---|
| 1121 | operation (see Sections 4.4.2, 4.4.3, and 8). |
---|
| 1122 | |
---|
| 1123 | Note: The Printer object needs to keep an internal originating |
---|
| 1124 | user id of some form, typically as a credential of a |
---|
| 1125 | principal, with the Job object. Since such an internal |
---|
| 1126 | attribute is implementation- dependent and not of interest to |
---|
| 1127 | clients, it is not specified as a Job Description |
---|
| 1128 | attribute. This originating user id is used for authorization |
---|
| 1129 | checks (if any) on all subsequent operations. |
---|
| 1130 | |
---|
| 1131 | """ |
---|
[5e70cc2] | 1132 | self.assert_running() |
---|
[33ea505] | 1133 | job = self.get_job(job_id) |
---|
[97f20dd] | 1134 | return ipp.JobOriginatingUserName(job.creator) |
---|
[33ea505] | 1135 | |
---|
| 1136 | def job_state(self, job_id): |
---|
[97f20dd] | 1137 | """RFC 2911: 4.3.7 job-state (type1 enum) |
---|
| 1138 | |
---|
| 1139 | This REQUIRED attribute identifies the current state of the |
---|
| 1140 | job. Even though the IPP protocol defines seven values for |
---|
| 1141 | job states (plus the out-of-band 'unknown' value - see Section |
---|
| 1142 | 4.1), implementations only need to support those states which |
---|
| 1143 | are appropriate for the particular implementation. In other |
---|
| 1144 | words, a Printer supports only those job states implemented by |
---|
| 1145 | the output device and available to the Printer object |
---|
| 1146 | implementation. Standard enum values are: |
---|
| 1147 | |
---|
| 1148 | '3' 'pending': The job is a candidate to start processing, |
---|
| 1149 | but is not yet processing. |
---|
| 1150 | |
---|
| 1151 | '4' 'pending-held': The job is not a candidate for |
---|
| 1152 | processing for any number of reasons but will return |
---|
| 1153 | to the 'pending' state as soon as the reasons are no |
---|
| 1154 | longer present. The job's 'job-state-reason' attribute |
---|
| 1155 | MUST indicate why the job is no longer a candidate for |
---|
| 1156 | processing. |
---|
| 1157 | |
---|
| 1158 | '5' 'processing': One or more of: |
---|
| 1159 | |
---|
| 1160 | 1. the job is using, or is attempting to use, one or |
---|
| 1161 | more purely software processes that are analyzing, |
---|
| 1162 | creating, or interpreting a PDL, etc., |
---|
| 1163 | 2. the job is using, or is attempting to use, one or |
---|
| 1164 | more hardware devices that are interpreting a PDL, |
---|
| 1165 | making marks on a medium, and/or performing |
---|
| 1166 | finishing, such as stapling, etc., |
---|
| 1167 | 3. the Printer object has made the job ready for |
---|
| 1168 | printing, but the output device is not yet printing |
---|
| 1169 | it, either because the job hasn't reached the |
---|
| 1170 | output device or because the job is queued in the |
---|
| 1171 | output device or some other spooler, awaiting the |
---|
| 1172 | output device to print it. |
---|
| 1173 | |
---|
| 1174 | When the job is in the 'processing' state, the entire |
---|
| 1175 | job state includes the detailed status represented in |
---|
| 1176 | the Printer object's 'printer-state', 'printer-state- |
---|
| 1177 | reasons', and 'printer-state-message' attributes. |
---|
| 1178 | |
---|
| 1179 | Implementations MAY, though they NEED NOT, include |
---|
| 1180 | additional values in the job's 'job-state-reasons' |
---|
| 1181 | attribute to indicate the progress of the job, such as |
---|
| 1182 | adding the 'job-printing' value to indicate when the |
---|
| 1183 | output device is actually making marks on paper and/or |
---|
| 1184 | the 'processing-to-stop-point' value to indicate that |
---|
| 1185 | the IPP object is in the process of canceling or |
---|
| 1186 | aborting the job. Most implementations won't bother |
---|
| 1187 | with this nuance. |
---|
| 1188 | |
---|
| 1189 | '6' 'processing-stopped': The job has stopped while |
---|
| 1190 | processing for any number of reasons and will return |
---|
| 1191 | to the 'processing' state as soon as the reasons are |
---|
| 1192 | no longer present. |
---|
| 1193 | |
---|
| 1194 | The job's 'job-state-reason' attribute MAY indicate |
---|
| 1195 | why the job has stopped processing. For example, if |
---|
| 1196 | the output device is stopped, the 'printer-stopped' |
---|
| 1197 | value MAY be included in the job's 'job-state-reasons' |
---|
| 1198 | attribute. |
---|
| 1199 | |
---|
| 1200 | Note: When an output device is stopped, the device |
---|
| 1201 | usually indicates its condition in human readable form |
---|
| 1202 | locally at the device. A client can obtain more |
---|
| 1203 | complete device status remotely by querying the |
---|
| 1204 | Printer object's 'printer-state', |
---|
| 1205 | 'printer-state-reasons' and 'printer- state-message' |
---|
| 1206 | attributes. |
---|
| 1207 | |
---|
| 1208 | '7' 'canceled': The job has been canceled by a Cancel-Job |
---|
| 1209 | operation and the Printer object has completed |
---|
| 1210 | canceling the job and all job status attributes have |
---|
| 1211 | reached their final values for the job. While the |
---|
| 1212 | Printer object is canceling the job, the job remains |
---|
| 1213 | in its current state, but the job's |
---|
| 1214 | 'job-state-reasons' attribute SHOULD contain the |
---|
| 1215 | 'processing-to-stop-point' value and one of the |
---|
| 1216 | 'canceled-by-user', 'canceled-by-operator', or |
---|
| 1217 | 'canceled-at-device' value. When the job moves to the |
---|
| 1218 | 'canceled' state, the 'processing-to-stop-point' |
---|
| 1219 | value, if present, MUST be removed, but the |
---|
| 1220 | 'canceled-by-xxx', if present, MUST remain. |
---|
| 1221 | |
---|
| 1222 | '8' 'aborted': The job has been aborted by the system, |
---|
| 1223 | usually while the job was in the 'processing' or |
---|
| 1224 | 'processing- stopped' state and the Printer has |
---|
| 1225 | completed aborting the job and all job status |
---|
| 1226 | attributes have reached their final values for the |
---|
| 1227 | job. While the Printer object is aborting the job, the |
---|
| 1228 | job remains in its current state, but the job's |
---|
| 1229 | 'job-state-reasons' attribute SHOULD contain the |
---|
| 1230 | 'processing-to-stop-point' and 'aborted-by- system' |
---|
| 1231 | values. When the job moves to the 'aborted' state, the |
---|
| 1232 | 'processing-to-stop-point' value, if present, MUST be |
---|
| 1233 | removed, but the 'aborted-by-system' value, if |
---|
| 1234 | present, MUST remain. |
---|
| 1235 | |
---|
| 1236 | '9' 'completed': The job has completed successfully or |
---|
| 1237 | with warnings or errors after processing and all of |
---|
| 1238 | the job media sheets have been successfully stacked in |
---|
| 1239 | the appropriate output bin(s) and all job status |
---|
| 1240 | attributes have reached their final values for the |
---|
| 1241 | job. The job's 'job-state-reasons' attribute SHOULD |
---|
| 1242 | contain one of: 'completed-successfully', |
---|
| 1243 | 'completed-with-warnings', or 'completed-with-errors' |
---|
| 1244 | values. |
---|
| 1245 | |
---|
| 1246 | The final value for this attribute MUST be one of: |
---|
| 1247 | 'completed', 'canceled', or 'aborted' before the Printer |
---|
| 1248 | removes the job altogether. The length of time that jobs |
---|
| 1249 | remain in the 'canceled', 'aborted', and 'completed' states |
---|
| 1250 | depends on implementation. See section 4.3.7.2. |
---|
| 1251 | |
---|
| 1252 | The following figure shows the normal job state transitions. |
---|
| 1253 | |
---|
| 1254 | +----> canceled |
---|
| 1255 | / |
---|
| 1256 | +----> pending --------> processing ---------+------> completed |
---|
| 1257 | | ^ ^ \ |
---|
| 1258 | --->+ | | +----> aborted |
---|
| 1259 | | v v / |
---|
| 1260 | +----> pending-held processing-stopped ---+ |
---|
| 1261 | |
---|
| 1262 | Normally a job progresses from left to right. Other state |
---|
| 1263 | transitions are unlikely, but are not forbidden. Not shown are |
---|
| 1264 | the transitions to the 'canceled' state from the 'pending', |
---|
| 1265 | 'pending- held', and 'processing-stopped' states. |
---|
| 1266 | |
---|
| 1267 | Jobs reach one of the three terminal states: 'completed', |
---|
| 1268 | 'canceled', or 'aborted', after the jobs have completed all |
---|
| 1269 | activity, including stacking output media, after the jobs have |
---|
| 1270 | completed all activity, and all job status attributes have |
---|
| 1271 | reached their final values for the job. |
---|
| 1272 | |
---|
| 1273 | """ |
---|
[5e70cc2] | 1274 | self.assert_running() |
---|
[33ea505] | 1275 | job = self.get_job(job_id) |
---|
| 1276 | return ipp.JobState(job.state) |
---|
| 1277 | |
---|
[97f20dd] | 1278 | def job_k_octets(self, job_id): |
---|
| 1279 | """RFC 2911: 4.3.17.1 job-k-octets (integer(0:MAX)) |
---|
| 1280 | |
---|
| 1281 | This attribute specifies the total size of the document(s) in |
---|
| 1282 | K octets, i.e., in units of 1024 octets requested to be |
---|
| 1283 | processed in the job. The value MUST be rounded up, so that a |
---|
| 1284 | job between 1 and 1024 octets MUST be indicated as being 1, |
---|
| 1285 | 1025 to 2048 MUST be 2, etc. |
---|
| 1286 | |
---|
| 1287 | This value MUST NOT include the multiplicative factors |
---|
| 1288 | contributed by the number of copies specified by the 'copies' |
---|
| 1289 | attribute, independent of whether the device can process |
---|
| 1290 | multiple copies without making multiple passes over the job or |
---|
| 1291 | document data and independent of whether the output is |
---|
| 1292 | collated or not. Thus the value is independent of the |
---|
| 1293 | implementation and indicates the size of the document(s) |
---|
| 1294 | measured in K octets independent of the number of copies. |
---|
| 1295 | |
---|
| 1296 | This value MUST also not include the multiplicative factor due |
---|
| 1297 | to a copies instruction embedded in the document data. If the |
---|
| 1298 | document data actually includes replications of the document |
---|
| 1299 | data, this value will include such replication. In other |
---|
| 1300 | words, this value is always the size of the source document |
---|
| 1301 | data, rather than a measure of the hardcopy output to be |
---|
| 1302 | produced. |
---|
| 1303 | |
---|
| 1304 | """ |
---|
[5e70cc2] | 1305 | self.assert_running() |
---|
[33ea505] | 1306 | job = self.get_job(job_id) |
---|
[97f20dd] | 1307 | return ipp.JobKOctets(int(math.ceil(job.size / 1024.))) |
---|
| 1308 | |
---|
| 1309 | def job_k_octets_completed(self, job_id): |
---|
| 1310 | """RFC 2911: 4.3.18.1 job-k-octets-processed (integer(0:MAX)) |
---|
| 1311 | |
---|
| 1312 | This attribute specifies the total number of octets processed |
---|
| 1313 | in K octets, i.e., in units of 1024 octets so far. The value |
---|
| 1314 | MUST be rounded up, so that a job between 1 and 1024 octets |
---|
| 1315 | inclusive MUST be indicated as being 1, 1025 to 2048 inclusive |
---|
| 1316 | MUST be 2, etc. For implementations where multiple copies are |
---|
| 1317 | produced by the interpreter with only a single pass over the |
---|
| 1318 | data, the final value MUST be equal to the value of the |
---|
| 1319 | 'job-k-octets' attribute. For implementations where multiple |
---|
| 1320 | copies are produced by the interpreter by processing the data |
---|
| 1321 | for each copy, the final value MUST be a multiple of the value |
---|
| 1322 | of the 'job-k-octets' attribute. |
---|
| 1323 | |
---|
| 1324 | """ |
---|
| 1325 | raise AttributeError # XXX |
---|
| 1326 | |
---|
| 1327 | def attributes_charset(self, job_id): |
---|
| 1328 | """RFC 2911: 4.3.19 attributes-charset (charset) |
---|
| 1329 | |
---|
| 1330 | This REQUIRED attribute is populated using the value in the |
---|
| 1331 | client supplied 'attributes-charset' attribute in the create |
---|
| 1332 | request. It identifies the charset (coded character set and |
---|
| 1333 | encoding method) used by any Job attributes with attribute |
---|
| 1334 | syntax 'text' and 'name' that were supplied by the client in |
---|
| 1335 | the create request. See Section 3.1.4 for a complete |
---|
| 1336 | description of the 'attributes-charset' operation attribute. |
---|
| 1337 | |
---|
| 1338 | This attribute does not indicate the charset in which the |
---|
| 1339 | 'text' and 'name' values are stored internally in the Job |
---|
| 1340 | object. The internal charset is implementation-defined. The |
---|
| 1341 | IPP object MUST convert from whatever the internal charset is |
---|
| 1342 | to that being requested in an operation as specified in |
---|
| 1343 | Section 3.1.4. |
---|
| 1344 | |
---|
| 1345 | """ |
---|
| 1346 | raise AttributeError # XXX |
---|
| 1347 | |
---|
| 1348 | def attributes_natural_language(self, job_id): |
---|
| 1349 | """RFC 2911: 4.3.20 attributes-natural-language |
---|
| 1350 | (naturalLanguage) |
---|
| 1351 | |
---|
| 1352 | This REQUIRED attribute is populated using the value in the |
---|
| 1353 | client supplied 'attributes-natural-language' attribute in the |
---|
| 1354 | create request. It identifies the natural language used for |
---|
| 1355 | any Job attributes with attribute syntax 'text' and 'name' |
---|
| 1356 | that were supplied by the client in the create request. See |
---|
| 1357 | Section 3.1.4 for a complete description of the |
---|
| 1358 | 'attributes-natural-language' operation attribute. See |
---|
| 1359 | Sections 4.1.1.2 and 4.1.2.2 for how a Natural Language |
---|
| 1360 | Override may be supplied explicitly for each 'text' and 'name' |
---|
| 1361 | attribute value that differs from the value identified by the |
---|
| 1362 | 'attributes-natural-language' attribute. |
---|
| 1363 | |
---|
| 1364 | """ |
---|
| 1365 | raise AttributeError # XXX |
---|
[ee8e6d0] | 1366 | |
---|
[b01b6d1] | 1367 | ###################################################################### |
---|
| 1368 | ### IPP Operations ### |
---|
| 1369 | ###################################################################### |
---|
| 1370 | |
---|
[5e70cc2] | 1371 | @sync |
---|
[57bc2dc] | 1372 | def print_job(self, document, document_name=None, document_format=None, |
---|
| 1373 | document_natural_language=None, requesting_user_name=None, |
---|
| 1374 | compression=None, job_name=None, job_k_octets=None): |
---|
[b3a56af] | 1375 | """RFC 2911: 3.2.1 Print-Job Operation |
---|
| 1376 | |
---|
| 1377 | This REQUIRED operation allows a client to submit a print job |
---|
| 1378 | with only one document and supply the document data (rather |
---|
| 1379 | than just a reference to the data). See Section 15 for the |
---|
| 1380 | suggested steps for processing create operations and their |
---|
| 1381 | Operation and Job Template attributes. |
---|
| 1382 | |
---|
| 1383 | Parameters |
---|
| 1384 | ---------- |
---|
| 1385 | document (file) |
---|
| 1386 | an open file handler to the document |
---|
| 1387 | document_name (string) |
---|
| 1388 | the name of the document |
---|
| 1389 | document_format (string) |
---|
| 1390 | the encoding/format of the document |
---|
| 1391 | document_natural_language (string) |
---|
| 1392 | if the document is a text file, what language it is in |
---|
| 1393 | requesting_user_name (string) |
---|
| 1394 | the user name of the job owner |
---|
| 1395 | compression (string) |
---|
| 1396 | the form of compression used on the file |
---|
| 1397 | job_name (string) |
---|
| 1398 | the name that the job should be called |
---|
| 1399 | job_k_octets (int) |
---|
| 1400 | the size of the job in bytes |
---|
[b01b6d1] | 1401 | |
---|
[b3a56af] | 1402 | """ |
---|
| 1403 | |
---|
[5e70cc2] | 1404 | self.assert_running() |
---|
| 1405 | |
---|
[57bc2dc] | 1406 | # create the job |
---|
| 1407 | job_id = self.create_job( |
---|
| 1408 | requesting_user_name=requesting_user_name, |
---|
| 1409 | job_name=job_name, |
---|
| 1410 | job_k_octets=job_k_octets) |
---|
| 1411 | |
---|
| 1412 | # send the document |
---|
| 1413 | self.send_document( |
---|
| 1414 | job_id, |
---|
| 1415 | document, |
---|
| 1416 | document_name=document_name, |
---|
| 1417 | document_format=document_format, |
---|
| 1418 | document_natural_language=document_natural_language, |
---|
| 1419 | requesting_user_name=requesting_user_name, |
---|
| 1420 | compression=compression, |
---|
| 1421 | last_document=False) |
---|
| 1422 | |
---|
| 1423 | return job_id |
---|
| 1424 | |
---|
[5e70cc2] | 1425 | @sync |
---|
[7e29e6a] | 1426 | def validate_job(self, document_name=None, document_format=None, |
---|
| 1427 | document_natural_language=None, requesting_user_name=None, |
---|
| 1428 | compression=None, job_name=None, job_k_octets=None): |
---|
[b3a56af] | 1429 | """RFC 2911: 3.2.3 Validate-Job Operation |
---|
| 1430 | |
---|
| 1431 | This REQUIRED operation is similar to the Print-Job operation |
---|
| 1432 | (section 3.2.1) except that a client supplies no document data |
---|
| 1433 | and the Printer allocates no resources (i.e., it does not |
---|
| 1434 | create a new Job object). This operation is used only to |
---|
| 1435 | verify capabilities of a printer object against whatever |
---|
| 1436 | attributes are supplied by the client in the Validate-Job |
---|
| 1437 | request. By using the Validate-Job operation a client can |
---|
| 1438 | validate that an identical Print-Job operation (with the |
---|
| 1439 | document data) would be accepted. The Validate-Job operation |
---|
| 1440 | also performs the same security negotiation as the Print-Job |
---|
| 1441 | operation (see section 8), so that a client can check that the |
---|
| 1442 | client and Printer object security requirements can be met |
---|
| 1443 | before performing a Print-Job operation. |
---|
| 1444 | |
---|
| 1445 | The Validate-Job operation does not accept a 'document-uri' |
---|
| 1446 | attribute in order to allow a client to check that the same |
---|
| 1447 | Print-URI operation will be accepted, since the client doesn't |
---|
| 1448 | send the data with the Print-URI operation. The client SHOULD |
---|
| 1449 | just issue the Print-URI request. |
---|
| 1450 | |
---|
| 1451 | Parameters |
---|
| 1452 | ---------- |
---|
| 1453 | document (file) |
---|
| 1454 | an open file handler to the document |
---|
| 1455 | document_name (string) |
---|
| 1456 | the name of the document |
---|
| 1457 | document_format (string) |
---|
| 1458 | the encoding/format of the document |
---|
| 1459 | document_natural_language (string) |
---|
| 1460 | if the document is a text file, what language it is in |
---|
| 1461 | requesting_user_name (string) |
---|
| 1462 | the user name of the job owner |
---|
| 1463 | compression (string) |
---|
| 1464 | the form of compression used on the file |
---|
| 1465 | job_name (string) |
---|
| 1466 | the name that the job should be called |
---|
| 1467 | job_k_octets (int) |
---|
| 1468 | the size of the job in bytes |
---|
[57bc2dc] | 1469 | |
---|
[b3a56af] | 1470 | """ |
---|
| 1471 | |
---|
[5e70cc2] | 1472 | self.assert_running() |
---|
| 1473 | |
---|
[57bc2dc] | 1474 | job_id = self._next_job_id |
---|
| 1475 | job = GutenbachJob( |
---|
| 1476 | job_id, |
---|
| 1477 | creator=requesting_user_name, |
---|
| 1478 | name=job_name) |
---|
| 1479 | job.spool(tempfile.TemporaryFile()) |
---|
| 1480 | job.abort() |
---|
| 1481 | del job |
---|
[b01b6d1] | 1482 | |
---|
[5e70cc2] | 1483 | @sync |
---|
[33ea505] | 1484 | def get_jobs(self, requesting_user_name=None, which_jobs=None, |
---|
| 1485 | requested_attributes=None): |
---|
[b3a56af] | 1486 | """RFC 2911: 3.2.6 Get-Jobs Operation |
---|
| 1487 | |
---|
| 1488 | This REQUIRED operation allows a client to retrieve the list |
---|
| 1489 | of Job objects belonging to the target Printer object. The |
---|
| 1490 | client may also supply a list of Job attribute names and/or |
---|
| 1491 | attribute group names. A group of Job object attributes will |
---|
| 1492 | be returned for each Job object that is returned. |
---|
| 1493 | |
---|
| 1494 | This operation is similar to the Get-Job-Attributes operation, |
---|
| 1495 | except that this Get-Jobs operation returns attributes from |
---|
| 1496 | possibly more than one object. |
---|
| 1497 | |
---|
| 1498 | Parameters |
---|
| 1499 | ---------- |
---|
| 1500 | requesting_user_name (string) |
---|
| 1501 | the user name of the job owner, used as a filter |
---|
| 1502 | which_jobs (string) |
---|
| 1503 | a filter for the types of jobs to return: |
---|
| 1504 | * 'completed' -- only jobs that have finished |
---|
| 1505 | * 'not-completed' -- processing or pending jobs |
---|
| 1506 | this defaults to 'not-completed' |
---|
| 1507 | requested_attributes (list) |
---|
| 1508 | the job attributes to return |
---|
| 1509 | |
---|
| 1510 | """ |
---|
[33ea505] | 1511 | |
---|
[5e70cc2] | 1512 | self.assert_running() |
---|
| 1513 | |
---|
[b01b6d1] | 1514 | # Filter by the which-jobs attribute |
---|
| 1515 | if which_jobs is None: |
---|
[34a4e5d] | 1516 | which_jobs = "not-completed" |
---|
| 1517 | |
---|
| 1518 | if which_jobs == "completed": |
---|
[b01b6d1] | 1519 | jobs = [self.jobs[job_id] for job_id in self.finished_jobs] |
---|
| 1520 | elif which_jobs == "not-completed": |
---|
| 1521 | jobs = [self.jobs[job_id] for job_id in self.active_jobs] |
---|
[ee8e6d0] | 1522 | else: |
---|
[b01b6d1] | 1523 | raise ipp.errors.ClientErrorAttributes( |
---|
| 1524 | which_jobs, ipp.WhichJobs(which_jobs)) |
---|
[b2e077a] | 1525 | |
---|
[b01b6d1] | 1526 | # Filter by username |
---|
| 1527 | if requesting_user_name is None: |
---|
| 1528 | user_jobs = jobs |
---|
| 1529 | else: |
---|
| 1530 | user_jobs = [job for job in jobs if job.creator == requesting_user_name] |
---|
[33ea505] | 1531 | |
---|
| 1532 | # Get the attributes of each job |
---|
| 1533 | job_attrs = [self.get_job_attributes( |
---|
| 1534 | job.id, requested_attributes=requested_attributes) for job in user_jobs] |
---|
[ee8e6d0] | 1535 | |
---|
[33ea505] | 1536 | return job_attrs |
---|
[ee8e6d0] | 1537 | |
---|
[5e70cc2] | 1538 | @sync |
---|
[b01b6d1] | 1539 | def print_uri(self): |
---|
[b3a56af] | 1540 | """RFC 2911: 3.2.2 Print-URI Operation |
---|
| 1541 | |
---|
| 1542 | This OPTIONAL operation is identical to the Print-Job |
---|
| 1543 | operation (section 3.2.1) except that a client supplies a URI |
---|
| 1544 | reference to the document data using the 'document-uri' (uri) |
---|
| 1545 | operation attribute (in Group 1) rather than including the |
---|
| 1546 | document data itself. Before returning the response, the |
---|
| 1547 | Printer MUST validate that the Printer supports the retrieval |
---|
| 1548 | method (e.g., http, ftp, etc.) implied by the URI, and MUST |
---|
| 1549 | check for valid URI syntax. If the client-supplied URI scheme |
---|
| 1550 | is not supported, i.e. the value is not in the Printer |
---|
| 1551 | object's 'referenced-uri-scheme-supported' attribute, the |
---|
| 1552 | Printer object MUST reject the request and return the |
---|
| 1553 | 'client-error-uri- scheme-not-supported' status code. |
---|
| 1554 | |
---|
| 1555 | If the Printer object supports this operation, it MUST support |
---|
| 1556 | the 'reference-uri-schemes-supported' Printer attribute (see |
---|
| 1557 | section 4.4.27). |
---|
| 1558 | |
---|
| 1559 | It is up to the IPP object to interpret the URI and |
---|
| 1560 | subsequently 'pull' the document from the source referenced by |
---|
| 1561 | the URI string. |
---|
| 1562 | |
---|
| 1563 | """ |
---|
| 1564 | |
---|
[5e70cc2] | 1565 | self.assert_running() |
---|
[b3a56af] | 1566 | # XXX: todo |
---|
[b01b6d1] | 1567 | |
---|
[5e70cc2] | 1568 | @sync |
---|
[d42236e] | 1569 | def create_job(self, requesting_user_name=None, |
---|
| 1570 | job_name=None, job_k_octets=None): |
---|
[b3a56af] | 1571 | """RFC 2911: 3.2.4 Create-Job Operation |
---|
| 1572 | |
---|
| 1573 | This OPTIONAL operation is similar to the Print-Job operation |
---|
| 1574 | (section 3.2.1) except that in the Create-Job request, a |
---|
| 1575 | client does not supply document data or any reference to |
---|
| 1576 | document data. Also, the client does not supply any of the |
---|
| 1577 | 'document-name', 'document- format', 'compression', or |
---|
| 1578 | 'document-natural-language' operation attributes. This |
---|
| 1579 | operation is followed by one or more Send-Document or Send-URI |
---|
| 1580 | operations. In each of those operation requests, the client |
---|
| 1581 | OPTIONALLY supplies the 'document-name', 'document-format', |
---|
| 1582 | and 'document-natural-language' attributes for each document |
---|
| 1583 | in the multi-document Job object. |
---|
| 1584 | |
---|
| 1585 | Parameters |
---|
| 1586 | ---------- |
---|
| 1587 | requesting_user_name (string) |
---|
| 1588 | the user name of the job owner |
---|
| 1589 | job_name (string) |
---|
| 1590 | the name that the job should be called |
---|
| 1591 | job_k_octets (int) |
---|
| 1592 | the size of the job in bytes |
---|
[57bc2dc] | 1593 | |
---|
[b3a56af] | 1594 | """ |
---|
| 1595 | |
---|
[5e70cc2] | 1596 | self.assert_running() |
---|
| 1597 | |
---|
[eee389a] | 1598 | job_id = self._next_job_id |
---|
| 1599 | self._next_job_id += 1 |
---|
[ee8e6d0] | 1600 | |
---|
[33ea505] | 1601 | job = GutenbachJob( |
---|
| 1602 | job_id, |
---|
| 1603 | creator=requesting_user_name, |
---|
| 1604 | name=job_name) |
---|
[57bc2dc] | 1605 | |
---|
[ee8e6d0] | 1606 | self.jobs[job_id] = job |
---|
[33ea505] | 1607 | return job_id |
---|
[776a659] | 1608 | |
---|
[fa3e2c6] | 1609 | @sync |
---|
[b01b6d1] | 1610 | def pause_printer(self): |
---|
[b3a56af] | 1611 | """RFC 2911: 3.2.7 Pause-Printer Operation |
---|
| 1612 | |
---|
| 1613 | This OPTIONAL operation allows a client to stop the Printer |
---|
| 1614 | object from scheduling jobs on all its devices. Depending on |
---|
| 1615 | implementation, the Pause-Printer operation MAY also stop the |
---|
| 1616 | Printer from processing the current job or jobs. Any job that |
---|
| 1617 | is currently being printed is either stopped as soon as the |
---|
| 1618 | implementation permits or is completed, depending on |
---|
| 1619 | implementation. The Printer object MUST still accept create |
---|
| 1620 | operations to create new jobs, but MUST prevent any jobs from |
---|
| 1621 | entering the 'processing' state. |
---|
| 1622 | |
---|
| 1623 | If the Pause-Printer operation is supported, then the |
---|
| 1624 | Resume-Printer operation MUST be supported, and vice-versa. |
---|
| 1625 | |
---|
| 1626 | The IPP Printer MUST accept the request in any state and |
---|
| 1627 | transition the Printer to the indicated new 'printer-state' |
---|
| 1628 | before returning as follows: |
---|
| 1629 | |
---|
| 1630 | Current New Reasons Reponse |
---|
| 1631 | -------------------------------------------------------------- |
---|
| 1632 | 'idle' 'stopped' 'paused' 'successful-ok' |
---|
| 1633 | 'processing' 'processing' 'moving-to-paused' 'successful-ok' |
---|
| 1634 | 'processing' 'stopped' 'paused' 'successful-ok' |
---|
| 1635 | 'stopped' 'stopped' 'paused' 'successful-ok' |
---|
[fa3e2c6] | 1636 | |
---|
| 1637 | """ |
---|
[33528b4] | 1638 | |
---|
[5e70cc2] | 1639 | self.assert_running() |
---|
[33528b4] | 1640 | if not self.paused: |
---|
| 1641 | if self.current_job is not None and self.current_job.is_playing: |
---|
| 1642 | self.current_job.pause() |
---|
| 1643 | self.paused = True |
---|
| 1644 | logger.info("%s paused", str(self)) |
---|
[fa3e2c6] | 1645 | |
---|
| 1646 | @sync |
---|
[b01b6d1] | 1647 | def resume_printer(self): |
---|
[b3a56af] | 1648 | """RFC 2911: 3.2.8 Resume-Printer Operation |
---|
| 1649 | |
---|
| 1650 | This operation allows a client to resume the Printer object |
---|
| 1651 | scheduling jobs on all its devices. The Printer object MUST |
---|
| 1652 | remove the 'paused' and 'moving-to-paused' values from the |
---|
| 1653 | Printer object's 'printer-state-reasons' attribute, if |
---|
| 1654 | present. If there are no other reasons to keep a device |
---|
| 1655 | paused (such as media-jam), the IPP Printer is free to |
---|
| 1656 | transition itself to the 'processing' or 'idle' states, |
---|
| 1657 | depending on whether there are jobs to be processed or not, |
---|
| 1658 | respectively, and the device(s) resume processing jobs. |
---|
| 1659 | |
---|
| 1660 | If the Pause-Printer operation is supported, then the |
---|
| 1661 | Resume-Printer operation MUST be supported, and vice-versa. |
---|
| 1662 | |
---|
| 1663 | The IPP Printer removes the 'printer-stopped' value from any |
---|
| 1664 | job's 'job-state-reasons' attributes contained in that |
---|
| 1665 | Printer. |
---|
| 1666 | |
---|
| 1667 | The IPP Printer MUST accept the request in any state, |
---|
| 1668 | transition the Printer object to the indicated new state as |
---|
| 1669 | follows: |
---|
| 1670 | |
---|
| 1671 | Current New Response |
---|
| 1672 | --------------------------------------------- |
---|
| 1673 | 'idle' 'idle' 'successful-ok' |
---|
| 1674 | 'processing' 'processing' 'successful-ok' |
---|
| 1675 | 'stopped' 'processing' 'successful-ok' |
---|
| 1676 | 'stopped' 'idle' 'successful-ok' |
---|
[fa3e2c6] | 1677 | |
---|
| 1678 | """ |
---|
[33528b4] | 1679 | |
---|
[5e70cc2] | 1680 | self.assert_running() |
---|
[33528b4] | 1681 | if self.paused: |
---|
| 1682 | if self.current_job is not None: |
---|
| 1683 | self.current_job.resume() |
---|
| 1684 | self.paused = False |
---|
| 1685 | logger.info("%s unpaused", str(self)) |
---|
[776a659] | 1686 | |
---|
[57bc2dc] | 1687 | @sync |
---|
[b01b6d1] | 1688 | def get_printer_attributes(self, requested_attributes=None): |
---|
[b3a56af] | 1689 | """RFC 2911: 3.2.5 Get-Printer-Attributes Operation |
---|
| 1690 | |
---|
| 1691 | This REQUIRED operation allows a client to request the values |
---|
| 1692 | of the attributes of a Printer object. |
---|
| 1693 | |
---|
| 1694 | In the request, the client supplies the set of Printer |
---|
| 1695 | attribute names and/or attribute group names in which the |
---|
| 1696 | requester is interested. In the response, the Printer object |
---|
| 1697 | returns a corresponding attribute set with the appropriate |
---|
| 1698 | attribute values filled in. |
---|
| 1699 | |
---|
| 1700 | Parameters |
---|
| 1701 | ---------- |
---|
| 1702 | requested_attributes (list) |
---|
| 1703 | the attributes to return |
---|
| 1704 | |
---|
| 1705 | """ |
---|
| 1706 | |
---|
[5e70cc2] | 1707 | self.assert_running() |
---|
[b01b6d1] | 1708 | if requested_attributes is None: |
---|
[33ea505] | 1709 | requested = self.printer_attributes |
---|
[e58af05] | 1710 | else: |
---|
[33ea505] | 1711 | requested = [a for a in self.printer_attributes \ |
---|
| 1712 | if a in requested_attributes] |
---|
[b2e077a] | 1713 | |
---|
[b01b6d1] | 1714 | _attributes = [attr.replace("-", "_") for attr in requested] |
---|
| 1715 | attributes = [getattr(self, attr) for attr in _attributes] |
---|
| 1716 | return attributes |
---|
[776a659] | 1717 | |
---|
[57bc2dc] | 1718 | @sync |
---|
[7e29e6a] | 1719 | def set_printer_attributes(self, attributes): |
---|
[5e70cc2] | 1720 | self.assert_running() |
---|
[9da7428] | 1721 | for attr in attributes: |
---|
| 1722 | try: |
---|
| 1723 | setattr(self, attr, attributes[attr]) |
---|
| 1724 | except AttributeError: |
---|
| 1725 | raise ipp.errors.ClientErrorAttributes |
---|
[33ea505] | 1726 | |
---|
[57bc2dc] | 1727 | @sync |
---|
[33ea505] | 1728 | def cancel_job(self, job_id, requesting_user_name=None): |
---|
[b3a56af] | 1729 | """RFC 2911: 3.3.3 Cancel-Job Operation |
---|
| 1730 | |
---|
| 1731 | This REQUIRED operation allows a client to cancel a Print Job |
---|
| 1732 | from the time the job is created up to the time it is |
---|
| 1733 | completed, canceled, or aborted. Since a Job might already be |
---|
| 1734 | printing by the time a Cancel-Job is received, some media |
---|
| 1735 | sheet pages might be printed before the job is actually |
---|
| 1736 | terminated. |
---|
| 1737 | |
---|
| 1738 | The IPP object MUST accept or reject the request based on the |
---|
| 1739 | job's current state and transition the job to the indicated |
---|
| 1740 | new state as follows: |
---|
| 1741 | |
---|
| 1742 | Current State New State Response |
---|
| 1743 | ----------------------------------------------------------------- |
---|
| 1744 | pending canceled successful-ok |
---|
| 1745 | pending-held canceled successful-ok |
---|
| 1746 | processing canceled successful-ok |
---|
| 1747 | processing processing successful-ok See Rule 1 |
---|
| 1748 | processing processing client-error-not-possible See Rule 2 |
---|
| 1749 | processing-stopped canceled successful-ok |
---|
| 1750 | processing-stopped processing-stopped successful-ok See Rule 1 |
---|
| 1751 | processing-stopped processing-stopped client-error-not-possible See Rule 2 |
---|
| 1752 | completed completed client-error-not-possible |
---|
| 1753 | canceled canceled client-error-not-possible |
---|
| 1754 | aborted aborted client-error-not-possible |
---|
| 1755 | |
---|
| 1756 | Rule 1: If the implementation requires some measurable time to |
---|
| 1757 | cancel the job in the 'processing' or 'processing-stopped' job |
---|
| 1758 | states, the IPP object MUST add the 'processing-to-stop-point' |
---|
| 1759 | value to the job's 'job-state-reasons' attribute and then |
---|
| 1760 | transition the job to the 'canceled' state when the processing |
---|
| 1761 | ceases (see section 4.3.8). |
---|
| 1762 | |
---|
| 1763 | Rule 2: If the Job object already has the |
---|
| 1764 | 'processing-to-stop-point' value in its 'job-state-reasons' |
---|
| 1765 | attribute, then the Printer object MUST reject a Cancel-Job |
---|
| 1766 | operation. |
---|
| 1767 | |
---|
| 1768 | Parameters |
---|
| 1769 | ---------- |
---|
| 1770 | job_id (integer) |
---|
| 1771 | the id of the job to cancel |
---|
| 1772 | requesting_user_name (string) |
---|
| 1773 | the name of the job's owner |
---|
| 1774 | |
---|
| 1775 | """ |
---|
| 1776 | |
---|
[5e70cc2] | 1777 | self.assert_running() |
---|
[33ea505] | 1778 | job = self.get_job(job_id) |
---|
| 1779 | try: |
---|
| 1780 | job.cancel() |
---|
| 1781 | except InvalidJobStateException: |
---|
| 1782 | # XXX |
---|
| 1783 | raise |
---|
| 1784 | |
---|
[57bc2dc] | 1785 | @sync |
---|
[33ea505] | 1786 | def send_document(self, job_id, document, document_name=None, |
---|
| 1787 | document_format=None, document_natural_language=None, |
---|
| 1788 | requesting_user_name=None, compression=None, |
---|
| 1789 | last_document=None): |
---|
[b3a56af] | 1790 | """RFC 2911: 3.3.1 Send-Document Operation |
---|
| 1791 | |
---|
| 1792 | This OPTIONAL operation allows a client to create a |
---|
| 1793 | multi-document Job object that is initially 'empty' (contains |
---|
| 1794 | no documents). In the Create-Job response, the Printer object |
---|
| 1795 | returns the Job object's URI (the 'job-uri' attribute) and the |
---|
| 1796 | Job object's 32-bit identifier (the 'job-id' attribute). For |
---|
| 1797 | each new document that the client desires to add, the client |
---|
| 1798 | uses a Send-Document operation. Each Send- Document Request |
---|
| 1799 | contains the entire stream of document data for one document. |
---|
| 1800 | |
---|
| 1801 | If the Printer supports this operation but does not support |
---|
| 1802 | multiple documents per job, the Printer MUST reject subsequent |
---|
| 1803 | Send-Document operations supplied with data and return the |
---|
| 1804 | 'server-error-multiple- document-jobs-not-supported'. However, |
---|
| 1805 | the Printer MUST accept the first document with a 'true' or |
---|
| 1806 | 'false' value for the 'last-document' operation attribute (see |
---|
| 1807 | below), so that clients MAY always submit one document jobs |
---|
| 1808 | with a 'false' value for 'last-document' in the first |
---|
| 1809 | Send-Document and a 'true' for 'last-document' in the second |
---|
| 1810 | Send-Document (with no data). |
---|
| 1811 | |
---|
| 1812 | Since the Create-Job and the send operations (Send-Document or |
---|
| 1813 | Send- URI operations) that follow could occur over an |
---|
| 1814 | arbitrarily long period of time for a particular job, a client |
---|
| 1815 | MUST send another send operation within an IPP Printer defined |
---|
| 1816 | minimum time interval after the receipt of the previous |
---|
| 1817 | request for the job. If a Printer object supports the |
---|
| 1818 | Create-Job and Send-Document operations, the Printer object |
---|
| 1819 | MUST support the 'multiple-operation-time-out' attribute (see |
---|
| 1820 | section 4.4.31). This attribute indicates the minimum number |
---|
| 1821 | of seconds the Printer object will wait for the next send |
---|
| 1822 | operation before taking some recovery action. |
---|
| 1823 | |
---|
| 1824 | An IPP object MUST recover from an errant client that does not |
---|
| 1825 | supply a send operation, sometime after the minimum time |
---|
| 1826 | interval specified by the Printer object's |
---|
| 1827 | 'multiple-operation-time-out' attribute. |
---|
| 1828 | |
---|
| 1829 | Parameters |
---|
| 1830 | ---------- |
---|
| 1831 | job_id (integer) |
---|
| 1832 | the id of the job to send the document |
---|
| 1833 | document (file) |
---|
| 1834 | an open file handler to the document |
---|
| 1835 | document_name (string) |
---|
| 1836 | the name of the document |
---|
| 1837 | document_format (string) |
---|
| 1838 | the encoding/format of the document |
---|
| 1839 | document_natural_language (string) |
---|
| 1840 | if the document is a text file, what language it is in |
---|
| 1841 | requesting_user_name (string) |
---|
| 1842 | the user name of the job owner |
---|
| 1843 | compression (string) |
---|
| 1844 | the form of compression used on the file |
---|
| 1845 | last_document (boolean) |
---|
| 1846 | whether or not this is the last document in this job |
---|
[33ea505] | 1847 | |
---|
[b3a56af] | 1848 | """ |
---|
| 1849 | |
---|
[5e70cc2] | 1850 | self.assert_running() |
---|
[33ea505] | 1851 | job = self.get_job(job_id) |
---|
[345c476] | 1852 | job.spool(document) |
---|
[d42236e] | 1853 | if 'dryrun' in self.config and self.config['dryrun']: |
---|
| 1854 | job.player._dryrun = True |
---|
| 1855 | self.pending_jobs.append(job_id) |
---|
| 1856 | |
---|
[57bc2dc] | 1857 | @sync |
---|
[c1dc25f] | 1858 | def send_uri(self, job_id, document_uri, document_name=None, |
---|
| 1859 | document_format=None, document_natural_language=None, |
---|
| 1860 | requesting_user_name=None, compression=None, |
---|
| 1861 | last_document=None): |
---|
[b3a56af] | 1862 | """RFC 2911: 3.2.2 Send URI |
---|
| 1863 | |
---|
| 1864 | This OPTIONAL operation is identical to the Send-Document |
---|
| 1865 | operation (see section 3.3.1) except that a client MUST supply |
---|
| 1866 | a URI reference ('document-uri' operation attribute) rather |
---|
| 1867 | than the document data itself. If a Printer object supports |
---|
| 1868 | this operation, clients can use both Send-URI or Send-Document |
---|
| 1869 | operations to add new documents to an existing multi-document |
---|
| 1870 | Job object. However, if a client needs to indicate that the |
---|
| 1871 | previous Send-URI or Send-Document was the last document, the |
---|
| 1872 | client MUST use the Send-Document operation with no document |
---|
| 1873 | data and the 'last-document' flag set to 'true' (rather than |
---|
| 1874 | using a Send-URI operation with no 'document-uri' operation |
---|
| 1875 | attribute). |
---|
| 1876 | |
---|
| 1877 | If a Printer object supports this operation, it MUST also |
---|
| 1878 | support the Print-URI operation (see section 3.2.2). |
---|
| 1879 | |
---|
| 1880 | The Printer object MUST validate the syntax and URI scheme of |
---|
| 1881 | the supplied URI before returning a response, just as in the |
---|
| 1882 | Print-URI operation. The IPP Printer MAY validate the |
---|
| 1883 | accessibility of the document as part of the operation or |
---|
| 1884 | subsequently (see section 3.2.2). |
---|
| 1885 | |
---|
| 1886 | Parameters |
---|
| 1887 | ---------- |
---|
| 1888 | job_id (integer) |
---|
| 1889 | the id of the job to send the uri |
---|
| 1890 | document_uri (string) |
---|
| 1891 | the uri of the document |
---|
| 1892 | document_name (string) |
---|
| 1893 | the name of the document |
---|
| 1894 | document_format (string) |
---|
| 1895 | the encoding/format of the document |
---|
| 1896 | document_natural_language (string) |
---|
| 1897 | if the document is a text file, what language it is in |
---|
| 1898 | requesting_user_name (string) |
---|
| 1899 | the user name of the job owner |
---|
| 1900 | compression (string) |
---|
| 1901 | the form of compression used on the file |
---|
| 1902 | last_document (boolean) |
---|
| 1903 | whether or not this is the last document in this job |
---|
| 1904 | |
---|
| 1905 | """ |
---|
[5e70cc2] | 1906 | |
---|
| 1907 | self.assert_running() |
---|
[c1dc25f] | 1908 | job = self.get_job(job_id) |
---|
| 1909 | # XXX: need to validate URI |
---|
| 1910 | # XXX: need to deal with the URI stream? |
---|
[33ea505] | 1911 | |
---|
[d42236e] | 1912 | #job.spool_uri(document_uri) |
---|
| 1913 | #if 'dryrun' in self.config and self.config['dryrun']: |
---|
| 1914 | # job.player._dryrun = True |
---|
| 1915 | #self.pending_jobs.append(job_id) |
---|
| 1916 | |
---|
[57bc2dc] | 1917 | @sync |
---|
[33ea505] | 1918 | def get_job_attributes(self, job_id, requested_attributes=None): |
---|
[b3a56af] | 1919 | """RFC 2911: 3.3.4 Get-Job-Attributes Operation |
---|
| 1920 | |
---|
| 1921 | This REQUIRED operation allows a client to request the values |
---|
| 1922 | of attributes of a Job object and it is almost identical to |
---|
| 1923 | the Get- Printer-Attributes operation (see section 3.2.5). The |
---|
| 1924 | only differences are that the operation is directed at a Job |
---|
| 1925 | object rather than a Printer object, there is no |
---|
| 1926 | 'document-format' operation attribute used when querying a Job |
---|
| 1927 | object, and the returned attribute group is a set of Job |
---|
| 1928 | object attributes rather than a set of Printer object |
---|
| 1929 | attributes. |
---|
| 1930 | |
---|
| 1931 | For Jobs, the possible names of attribute groups are: |
---|
| 1932 | - 'job-template': the subset of the Job Template attributes |
---|
| 1933 | that apply to a Job object (the first column of the table |
---|
| 1934 | in Section 4.2) that the implementation supports for Job |
---|
| 1935 | objects. |
---|
| 1936 | - 'job-description': the subset of the Job Description |
---|
| 1937 | attributes specified in Section 4.3 that the |
---|
| 1938 | implementation supports for Job objects. |
---|
| 1939 | - 'all': the special group 'all' that includes all |
---|
| 1940 | attributes that the implementation supports for Job |
---|
| 1941 | objects. |
---|
| 1942 | |
---|
| 1943 | Since a client MAY request specific attributes or named |
---|
| 1944 | groups, there is a potential that there is some overlap. For |
---|
| 1945 | example, if a client requests, 'job-name' and |
---|
| 1946 | 'job-description', the client is actually requesting the |
---|
| 1947 | 'job-name' attribute once by naming it explicitly, and once by |
---|
| 1948 | inclusion in the 'job-description' group. In such cases, the |
---|
| 1949 | Printer object NEED NOT return the attribute only once in the |
---|
| 1950 | response even if it is requested multiple times. The client |
---|
| 1951 | SHOULD NOT request the same attribute in multiple ways. |
---|
| 1952 | |
---|
| 1953 | It is NOT REQUIRED that a Job object support all attributes |
---|
| 1954 | belonging to a group (since some attributes are |
---|
| 1955 | OPTIONAL). However it is REQUIRED that each Job object support |
---|
| 1956 | all these group names. |
---|
| 1957 | |
---|
| 1958 | Parameters |
---|
| 1959 | ---------- |
---|
| 1960 | job_id (integer) |
---|
| 1961 | the id of the job to send the uri |
---|
| 1962 | requested_attributes (list) |
---|
| 1963 | the attributes to return |
---|
| 1964 | |
---|
| 1965 | """ |
---|
[5e70cc2] | 1966 | |
---|
| 1967 | self.assert_running() |
---|
[33ea505] | 1968 | if requested_attributes is None: |
---|
| 1969 | requested = self.job_attributes |
---|
| 1970 | else: |
---|
| 1971 | requested = [a for a in self.job_attributes \ |
---|
| 1972 | if a in requested_attributes] |
---|
| 1973 | |
---|
| 1974 | _attributes = [attr.replace("-", "_") for attr in requested] |
---|
| 1975 | attributes = [getattr(self, attr)(job_id) for attr in _attributes] |
---|
| 1976 | return attributes |
---|
| 1977 | |
---|
[57bc2dc] | 1978 | @sync |
---|
[9da7428] | 1979 | def set_job_attributes(self, job_id, attributes): |
---|
[5e70cc2] | 1980 | self.assert_running() |
---|
[9da7428] | 1981 | job = self.get_job(job_id) |
---|
| 1982 | for attr in attributes: |
---|
| 1983 | if attr in ("job-id", "job-k-octets", "job-state", "job-printer-uri"): |
---|
| 1984 | raise ipp.errors.ClientErrorAttributesNotSettable(attr) |
---|
| 1985 | elif attr == "job-name": |
---|
| 1986 | job.name = attributes[attr] |
---|
| 1987 | elif attr == "job-originating-user-name": |
---|
| 1988 | job.creator = attributes[attr] # XXX: do we want this? |
---|
[57bc2dc] | 1989 | |
---|
| 1990 | @sync |
---|
[c1cebbc] | 1991 | def restart_job(self, job_id, requesting_user_name=None): |
---|
[5e70cc2] | 1992 | self.assert_running() |
---|
[c1cebbc] | 1993 | job = self.get_job(job_id) |
---|
| 1994 | try: |
---|
| 1995 | job.restart() |
---|
| 1996 | except InvalidJobStateException: |
---|
| 1997 | # XXX |
---|
| 1998 | raise ipp.errors.ClientErrorNotPossible |
---|
[33ea505] | 1999 | |
---|
[57bc2dc] | 2000 | self.finished_jobs.remove(job_id) |
---|
| 2001 | self.pending_jobs.append(job_id) |
---|
[33ea505] | 2002 | |
---|
[57bc2dc] | 2003 | @sync |
---|
[c1cebbc] | 2004 | def promote_job(self, job_id, requesting_user_name=None): |
---|
[c500bc2] | 2005 | # According to RFC 3998, we need to put the job at the front |
---|
| 2006 | # of the queue (so that when the currently playing job |
---|
| 2007 | # completes, this one will go next |
---|
| 2008 | |
---|
[5e70cc2] | 2009 | self.assert_running() |
---|
[c500bc2] | 2010 | job = self.get_job(job_id) |
---|
| 2011 | job.priority = 1 # XXX we need to actually do something |
---|
| 2012 | # correct here |
---|