This file is indexed.

/etc/gnuspool/cupspy/ipp.py is in gnuspool-cupspy 1.7.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# Copyright (C) 2009  Free Software Foundation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

# Originally written by John Collins <jmc@xisl.com>.

import filebuf, struct, re, string, time, random, syslog

name_to_op = dict(IPP_PRINT_JOB=2,                      # Print a single file
                  IPP_PRINT_URI=3,                      # Print a single URL @private@
                  IPP_VALIDATE_JOB=4,                   # Validate job options
                  IPP_CREATE_JOB=5,                     # Create an empty print job
                  IPP_SEND_DOCUMENT=6,              	# Add a file to a job
                  IPP_SEND_URI=7,           		# Add a URL to a job @private@
                  IPP_CANCEL_JOB=8,                 	# Cancel a job
                  IPP_GET_JOB_ATTRIBUTES=9,             # Get job attributes
                  IPP_GET_JOBS=10,			# Get a list of jobs
                  IPP_GET_PRINTER_ATTRIBUTES=11,        # Get printer attributes
                  IPP_HOLD_JOB=12,                      # Hold a job for printing
                  IPP_RELEASE_JOB=13,			# Release a job for printing
                  IPP_RESTART_JOB=14,			# Reprint a job
                  IPP_PAUSE_PRINTER=16,                 # Stop a printer
                  IPP_RESUME_PRINTER=17,		# Start a printer
                  IPP_PURGE_JOBS=18,			# Cancel all jobs
                  IPP_SET_PRINTER_ATTRIBUTES=19,	# Set printer attributes @private@
                  IPP_SET_JOB_ATTRIBUTES=20,		# Set job attributes
                  IPP_GET_PRINTER_SUPPORTED_VALUES=21,	# Get supported attribute values
                  IPP_CREATE_PRINTER_SUBSCRIPTION=22,	# Create a printer subscription @since CUPS 1.2@
                  IPP_CREATE_JOB_SUBSCRIPTION=23,	# Create a job subscription @since CUPS 1.2@
                  IPP_GET_SUBSCRIPTION_ATTRIBUTES=24,	# Get subscription attributes @since CUPS 1.2@
                  IPP_GET_SUBSCRIPTIONS=25,             # Get list of subscriptions @since CUPS 1.2@
                  IPP_RENEW_SUBSCRIPTION=26,		# Renew a printer subscription @since CUPS 1.2@
                  IPP_CANCEL_SUBSCRIPTION=27,		# Cancel a subscription @since CUPS 1.2@
                  IPP_GET_NOTIFICATIONS=28,             # Get notification events @since CUPS 1.2@
                  IPP_SEND_NOTIFICATIONS=29,		# Send notification events @private@
                  IPP_GET_PRINT_SUPPORT_FILES=33,       # Get printer support files @private@
                  IPP_ENABLE_PRINTER=34,                # Start a printer
                  IPP_DISABLE_PRINTER=35,               # Stop a printer
                  IPP_PAUSE_PRINTER_AFTER_CURRENT_JOB=36,# Stop printer after the current job @private@
                  IPP_HOLD_NEW_JOBS=37,                 # Hold new jobs @private@
                  IPP_RELEASE_HELD_NEW_JOBS=38,         # Release new jobs @private@
                  IPP_DEACTIVATE_PRINTER=39,            # Stop a printer @private@
                  IPP_ACTIVATE_PRINTER=40,              # Start a printer @private@
                  IPP_RESTART_PRINTER=41,		# Restart a printer @private@
                  IPP_SHUTDOWN_PRINTER=42,		# Turn a printer off @private@
                  IPP_STARTUP_PRINTER=43,		# Turn a printer on @private@
                  IPP_REPROCESS_JOB=44,			# Reprint a job @private@
                  IPP_CANCEL_CURRENT_JOB=45,		# Cancel the current job @private@
                  IPP_SUSPEND_CURRENT_JOB=46,		# Suspend the current job @private@
                  IPP_RESUME_JOB=47,			# Resume the current job @private@
                  IPP_PROMOTE_JOB=48,			# Promote a job to print sooner @private@
                  IPP_SCHEDULE_JOB_AFTER=49,		# Schedule a job to print after another @private@
                  IPP_PRIVATE=0x4000,			# Reserved @private@
                  CUPS_GET_DEFAULT=0x4001,		# Get the default printer
                  CUPS_GET_PRINTERS=0x4002,		# Get a list of printers and/or classes
                  CUPS_ADD_MODIFY_PRINTER=0x4003,	# Add or modify a printer
                  CUPS_DELETE_PRINTER=0x4004,		# Delete a printer
                  CUPS_GET_CLASSES=0x4005,		# Get a list of classes @deprecated@
                  CUPS_ADD_MODIFY_CLASS=0x4006,		# Add or modify a class
                  CUPS_DELETE_CLASS=0x4007,		# Delete a class
                  CUPS_ACCEPT_JOBS=0x4008,		# Accept new jobs on a printer
                  CUPS_REJECT_JOBS=0x4009,		# Reject new jobs on a printer
                  CUPS_SET_DEFAULT=0x400A,		# Set the default printer
                  CUPS_GET_DEVICES=0x400B,		# Get a list of supported devices
                  CUPS_GET_PPDS=0x400C,			# Get a list of supported drivers
                  CUPS_MOVE_JOB=0x400D,			# Move a job to a different printer
                  CUPS_AUTHENTICATE_JOB=0x400E,		# Authenticate a job @since CUPS 1.2@
                  CUPS_GET_PPD=0x400E)			# Get a PPD file @since CUPS 1.3@

name_to_resp = dict(IPP_OK=0,                           # successful-ok
                    IPP_OK_SUBST=1,			# successful-ok-ignored-or-substituted-attributes
                    IPP_OK_CONFLICT=2,			# successful-ok-conflicting-attributes
                    IPP_OK_IGNORED_SUBSCRIPTIONS=3,	# successful-ok-ignored-subscriptions
                    IPP_OK_IGNORED_NOTIFICATIONS=4,	# successful-ok-ignored-notifications
                    IPP_OK_TOO_MANY_EVENTS=5,		# successful-ok-too-many-events
                    IPP_OK_BUT_CANCEL_SUBSCRIPTION=6,	# successful-ok-but-cancel-subscription
                    IPP_OK_EVENTS_COMPLETE=7,		# successful-ok-events-complete
                    IPP_REDIRECTION_OTHER_SITE=0x200,	#
                    CUPS_SEE_OTHER=0x280,               # cups-see-other
                    IPP_BAD_REQUEST=0x0400,             # client-error-bad-request
                    IPP_FORBIDDEN=0x401,		# client-error-forbidden
                    IPP_NOT_AUTHENTICATED=0x402,	# client-error-not-authenticated
                    IPP_NOT_AUTHORIZED=0x403,		# client-error-not-authorized
                    IPP_NOT_POSSIBLE=0x404,		# client-error-not-possible
                    IPP_TIMEOUT=0x405,			# client-error-timeout
                    IPP_NOT_FOUND=0x406,		# client-error-not-found
                    IPP_GONE=0x407,			# client-error-gone
                    IPP_REQUEST_ENTITY=0x408,		# client-error-request-entity-too-large
                    IPP_REQUEST_VALUE=0x409,		# client-error-request-value-too-long
                    IPP_DOCUMENT_FORMAT=0x40A,		# client-error-document-format-not-supported
                    IPP_ATTRIBUTES=0x40B,		# client-error-attributes-or-values-not-supported
                    IPP_URI_SCHEME=0x40C,		# client-error-uri-scheme-not-supported
                    IPP_CHARSET=0x40D,			# client-error-charset-not-supported
                    IPP_CONFLICT=0x40E,			# client-error-conflicting-attributes
                    IPP_COMPRESSION_NOT_SUPPORTED=0x40F,# client-error-compression-not-supported
                    IPP_COMPRESSION_ERROR=0x410,	# client-error-compression-error
                    IPP_DOCUMENT_FORMAT_ERROR=0x411,	# client-error-document-format-error
                    IPP_DOCUMENT_ACCESS_ERROR=0x412,	# client-error-document-access-error
                    IPP_ATTRIBUTES_NOT_SETTABLE=0x413,	# client-error-attributes-not-settable
                    IPP_IGNORED_ALL_SUBSCRIPTIONS=0x414,# client-error-ignored-all-subscriptions
                    IPP_TOO_MANY_SUBSCRIPTIONS=0x415,	# client-error-too-many-subscriptions
                    IPP_IGNORED_ALL_NOTIFICATIONS=0x416,# client-error-ignored-all-notifications
                    IPP_PRINT_SUPPORT_FILE_NOT_FOUND=0x417,# client-error-print-support-file-not-found
                    IPP_INTERNAL_ERROR=0x0500,		# server-error-internal-error
                    IPP_OPERATION_NOT_SUPPORTED=0x501,	# server-error-operation-not-supported
                    IPP_SERVICE_UNAVAILABLE=0x502,	# server-error-service-unavailable
                    IPP_VERSION_NOT_SUPPORTED=0x503,	# server-error-version-not-supported
                    IPP_DEVICE_ERROR=0x504,		# server-error-device-error
                    IPP_TEMPORARY_ERROR=0x505,		# server-error-temporary-error
                    IPP_NOT_ACCEPTING=0x506,		# server-error-not-accepting-jobs
                    IPP_PRINTER_BUSY=0x507,		# server-error-busy
                    IPP_ERROR_JOB_CANCELED=0x508,	# server-error-job-canceled
                    IPP_MULTIPLE_JOBS_NOT_SUPPORTED=0x509,# server-error-multiple-document-jobs-not-supported
                    IPP_PRINTER_IS_DEACTIVATED=0x50A)	# server-error-printer-is-deactivated

name_to_tag = dict(IPP_TAG_ZERO=0,                      # Zero tag - used for separators
                   IPP_TAG_OPERATION=1,			# Operation group
                   IPP_TAG_JOB=2,			# Job group
                   IPP_TAG_END=3,			# End-of-attributes
                   IPP_TAG_PRINTER=4,			# Printer group
                   IPP_TAG_UNSUPPORTED_GROUP=5,		# Unsupported attributes group
                   IPP_TAG_SUBSCRIPTION=6,		# Subscription group
                   IPP_TAG_EVENT_NOTIFICATION=7,	# Event group
                   IPP_TAG_UNSUPPORTED_VALUE=16,        # Unsupported value
                   IPP_TAG_DEFAULT=17,			# Default value
                   IPP_TAG_UNKNOWN=18,			# Unknown value
                   IPP_TAG_NOVALUE=19,			# No-value value
                   IPP_TAG_NOTSETTABLE=0x15,		# Not-settable value
                   IPP_TAG_DELETEATTR=0x16,             # Delete-attribute value
                   IPP_TAG_ADMINDEFINE=0x17,		# Admin-defined value
                   IPP_TAG_INTEGER=0x21,                # Integer value
                   IPP_TAG_BOOLEAN=0x22,                # Boolean value
                   IPP_TAG_ENUM=0x23,			# Enumeration value
                   IPP_TAG_STRING=0x30,                 # Octet string value
                   IPP_TAG_DATE=0x31,			# Date/time value
                   IPP_TAG_RESOLUTION=0x32,             # Resolution value
                   IPP_TAG_RANGE=0x33,			# Range value
                   IPP_TAG_BEGIN_COLLECTION=0x34,       # Beginning of collection value
                   IPP_TAG_TEXTLANG=0x35,               # Text-with-language value
                   IPP_TAG_NAMELANG=0x36,               # Name-with-language value
                   IPP_TAG_END_COLLECTION=0x37,		# End of collection value
                   IPP_TAG_TEXT=0x41,			# Text value
                   IPP_TAG_NAME=0x42,			# Name value
                   IPP_TAG_KEYWORD=0x44,                # Keyword value
                   IPP_TAG_URI=0x45,			# URI value
                   IPP_TAG_URISCHEME=0x46,              # URI scheme value
                   IPP_TAG_CHARSET=0x47,                # Character set value
                   IPP_TAG_LANGUAGE=0x48,               # Language value
                   IPP_TAG_MIMETYPE=0x49,               # MIME media type value
                   IPP_TAG_MEMBERNAME=0x4A,             # Collection member name value
                   IPP_TAG_MASK=0x7fffffff,             # Mask for copied attribute values
                   IPP_TAG_COPY=-0x7fffffff-1)          # Bitflag for copied attribute values

name_to_res = dict(IPP_RES_PER_INCH=3,                  # Pixels per inch
                   IPP_RES_PER_CM=4)			# Pixels per centimeter

name_to_finish = dict(IPP_FINISHINGS_NONE=3,            # No finishing
                      IPP_FINISHINGS_STAPLE=4,		# Staple (any location)
                      IPP_FINISHINGS_PUNCH=5,		# Punch (any location/count)
                      IPP_FINISHINGS_COVER=6,		# Add cover
                      IPP_FINISHINGS_BIND=7,		# Bind
                      IPP_FINISHINGS_SADDLE_STITCH=8,	# Staple interior
                      IPP_FINISHINGS_EDGE_STITCH=9,	# Stitch along any side
                      IPP_FINISHINGS_FOLD=10,		# Fold (any type)
                      IPP_FINISHINGS_TRIM=11,		# Trim (any type)
                      IPP_FINISHINGS_BALE=12,		# Bale (any type)
                      IPP_FINISHINGS_BOOKLET_MAKER=13,	# Fold to make booklet
                      IPP_FINISHINGS_JOB_OFFSET=14,	# Offset for binding (any type)
                      IPP_FINISHINGS_STAPLE_TOP_LEFT=20,# Staple top left corner
                      IPP_FINISHINGS_STAPLE_BOTTOM_LEFT=21,	# Staple bottom left corner
                      IPP_FINISHINGS_STAPLE_TOP_RIGHT=22,	# Staple top right corner
                      IPP_FINISHINGS_STAPLE_BOTTOM_RIGHT=23,	# Staple bottom right corner
                      IPP_FINISHINGS_EDGE_STITCH_LEFT=24,	# Stitch along left side
                      IPP_FINISHINGS_EDGE_STITCH_TOP=25,	# Stitch along top edge
                      IPP_FINISHINGS_EDGE_STITCH_RIGHT=26,	# Stitch along right side
                      IPP_FINISHINGS_EDGE_STITCH_BOTTOM=27,	# Stitch along bottom edge
                      IPP_FINISHINGS_STAPLE_DUAL_LEFT=28,	# Two staples on left
                      IPP_FINISHINGS_STAPLE_DUAL_TOP=29,	# Two staples on top
                      IPP_FINISHINGS_STAPLE_DUAL_RIGHT=30,	# Two staples on right
                      IPP_FINISHINGS_STAPLE_DUAL_BOTTOM=31,	# Two staples on bottom
                      IPP_FINISHINGS_BIND_LEFT=50,      # Bind on left
                      IPP_FINISHINGS_BIND_TOP=51,       # Bind on top
                      IPP_FINISHINGS_BIND_RIGHT=52,     # Bind on right
                      IPP_FINISHINGS_BIND_BOTTOM=53)    # Bind on bottom

name_to_orient = dict(IPP_PORTRAIT=3,                	# No rotation
                      IPP_LANDSCAPE=4,                       # 90 degrees counter-clockwise
                      IPP_REVERSE_LANDSCAPE=5,               # 90 degrees clockwise
                      IPP_REVERSE_PORTRAIT=6)                # 180 degrees

name_to_qual = dict(IPP_QUALITY_DRAFT=3,                  # Draft quality
                    IPP_QUALITY_NORMAL=4,                 # Normal quality
                    IPP_QUALITY_HIGH=5)			# High quality

name_to_jstate = dict(IPP_JOB_PENDING=3,                     # Job is waiting to be printed
                      IPP_JOB_HELD=4,                        # Job is held for printing
                      IPP_JOB_PROCESSING=5,                  # Job is currently printing
                      IPP_JOB_STOPPED=6,                     # Job has been stopped
                      IPP_JOB_CANCELED=7,                    # Job has been canceled
                      IPP_JOB_ABORTED=8,                     # Job has aborted due to error
                      IPP_JOB_COMPLETED=9)                   # Job has completed successfully

name_to_pstate = dict(IPP_PRINTER_IDLE=3,                    # Printer is idle
                      IPP_PRINTER_PROCESSING=4,              # Printer is working
                      IPP_PRINTER_STOPPED=5)		     # Printer is stopped

name_to_ippstate = dict(IPP_ERROR=-1,                        # An error occurred
                        IPP_IDLE=0,                          # Nothing is happening/request completed
                        IPP_HEADER=1,                        # The request header needs to be sent/received
                        IPP_ATTRIBUTE=2,                     # One or more attributes need to be sent/received
                        IPP_DATA=3)                          # IPP request data needs to be sent/received

# Set up reverse dics

def inverse_dic(dic):
    idic={}
    for n,v in dic.iteritems():
        idic[v] = n
    return idic

op_to_name=inverse_dic(name_to_op)
resp_to_name=inverse_dic(name_to_resp)
tag_to_name=inverse_dic(name_to_tag)
res_to_name=inverse_dic(name_to_res)
finish_to_name=inverse_dic(name_to_finish)
orient_to_name=inverse_dic(name_to_orient)
qual_to_name=inverse_dic(name_to_qual)
jstate_to_name=inverse_dic(name_to_jstate)
pstate_to_name=inverse_dic(name_to_pstate)
ippstate_to_name=inverse_dic(name_to_ippstate)

# Common tags

IPP_TAG_INTEGER = name_to_tag['IPP_TAG_INTEGER']
IPP_TAG_ENUM = name_to_tag['IPP_TAG_ENUM']
IPP_TAG_BOOLEAN = name_to_tag['IPP_TAG_BOOLEAN']
IPP_TAG_DATE = name_to_tag['IPP_TAG_DATE']
IPP_TAG_RESOLUTION = name_to_tag['IPP_TAG_RESOLUTION']
IPP_TAG_RANGE = name_to_tag['IPP_TAG_RANGE']
IPP_TAG_TEXTLANG = name_to_tag['IPP_TAG_TEXTLANG']
IPP_TAG_NAMELANG = name_to_tag['IPP_TAG_NAMELANG']
IPP_TAG_BEGIN_COLLECTION = name_to_tag['IPP_TAG_BEGIN_COLLECTION']
IPP_TAG_UNSUPPORTED_GROUP = name_to_tag['IPP_TAG_UNSUPPORTED_GROUP']
IPP_TAG_UNSUPPORTED_VALUE = name_to_tag['IPP_TAG_UNSUPPORTED_VALUE']
IPP_TAG_END = name_to_tag['IPP_TAG_END']

# Common tags

IPP_TAG_TEXT=0x41
IPP_TAG_URI=0x45
IPP_TAG_CHARSET=0x47
IPP_TAG_LANGUAGE=0x48

# Might as well have this

IPP_OK = 0

IPP_JOB_PENDING=3
IPP_JOB_HELD=4
IPP_JOB_PROCESSING=5

# Also have handy group names

IPP_TAG_OPERATION=1
IPP_TAG_JOB=2
IPP_TAG_PRINTER=4

class IppError(Exception):
    """Exception class for IPP errors"""
    pass

def generate_value(name, valuetup):
    """Generate value length field and value"""
    tag = valuetup[0]
    value = valuetup[1]
    if tag == IPP_TAG_INTEGER or tag == IPP_TAG_ENUM:
        resfld = struct.pack('!I', int(value))
    elif tag == IPP_TAG_BOOLEAN:
        resfld = chr(value)
    elif tag == IPP_TAG_DATE:
        year,month,day,hours,minutes,seconds,wday,yday,isdst = time.gmtime(value)
        resfld = struct.pack('!H6Bc2B', year, month, day, hours, minutes, seconds, 0, '+', 0, 0)
    elif tag == IPP_TAG_RESOLUTION:
        resfld = struct.pack('!2Ic', *value)
    elif tag == IPP_TAG_RANGE:
        resfld = struct.pack('!2I', *value)
    elif tag == IPP_TAG_TEXTLANG or tag == IPP_TAG_NAMELANG:
        cs, t = value
        resfld = struct.pack('!H', len(cs)) + cs + struct.pack('!H', len(t)) + t
    elif tag == IPP_TAG_BEGIN_COLLECTION:
        raise IppError("Sorry cannot do collections yet")
    else:
        resfld = value
    return struct.pack('!BH', tag, len(name)) + name + struct.pack('!H', len(resfld)) + resfld

class ippvalue:
    """Representation of value field in IPP"""
    def __init__(self, tagval):
        if isinstance(tagval, str):
            try:
                self.tag = name_to_tag[tagval]
            except KeyError:
                raise IppError("Unknown tag value " + tagval)
        else:
            self.tag = tagval
        self.name = ''
        self.value = []

    def setname(self, name):
        """Set value name"""
        self.name = name

    def setvalue(self, value):
        """Set a value to given item"""
        self.value.append(value)

    def setnamevalues(self, name, values):
        """Initialise name and values"""
        self.name = name;
        if isinstance(values,str) or isinstance(values,int):
            self.value.append((self.tag, values))
        else:
            for v in values:
                self.value.append((self.tag, v))

    def display(self, indent):
        """Display/trace the value"""
        syslog.syslog(syslog.LOG_DEBUG, "Value:" + tag_to_name[self.tag])
        syslog.syslog(syslog.LOG_DEBUG, "Name:" + self.name)
        for i,v in enumerate(self.value):
            syslog.syslog(syslog.LOG_DEBUG, str(i) + ' ' + tag_to_name[v[0]] + ' ' + str(v[1]))

    def generate(self):
        """Turn value into IPP string"""
        name = self.name
        result = ''
        for v in self.value:
            result += generate_value(name, v)
            name = ''
        return  result

    def find_attribute(self, name):
        """See if the attribute has the given name"""
        if  self.name == name:
            return self
        return  None

    def get_value(self, ind=0):
        """Get the actual indth value of an ippvalue"""
        return self.value[ind][1]

class ippgroup:
    """Representation of group field in IPP"""
    def __init__(self, tagval):
        self.tag = tagval
        self.values = []

    def setvalue(self, value):
        """Set a group value"""
        self.values.append(value)

    def display(self, indent):
        """Display/trace of group"""
        syslog.syslog(syslog.LOG_DEBUG, "Group:" + tag_to_name[self.tag])
        for i,v in enumerate(self.values):
            syslog.syslog(syslog.LOG_DEBUG, "Group value " + str(i) + ":")
            v.display(indent)

    def generate(self):
        """Generate an IPP string for the group"""
        result = chr(self.tag)
        for v in self.values:
            result += v.generate()
        return  result

    def find_attribute(self, name):
        """Find the first attribute with the given name"""
        for v in self.values:
            fv = v.find_attribute(name)
            if  fv:
                return  fv
        return  None

class ipp:
    """Representation of IPP"""
    def __init__(self, fb = None):
        """Initialise with optional parse string"""
        self.filb = fb
        self.values = []
        self.majv = 1;
        self.minv = 1;
        self.statuscode = IPP_OK
        self.id = 0

    def setrespcode(self, code, ident = 0):
        """Set up response code - code may be string or tag number"""
        if isinstance(code, str):
            try:
                self.statuscode = name_to_resp[code]
            except KeyError:
                raise IppError("Unknown response code " + code)
        else:
            self.statuscode = code
        if  ident == 0:
            ident = random.randint(1, 1000000)
        self.id = ident

    def pushvalue(self, value):
        """Append value to structure"""
        self.values.append(value)

    def parsehdr(self):
        """Parse header of IPP string"""
        return struct.unpack('!2BHI', self.filb.get(8))

    def parsevalue(self, tag, length):
        """Parse value structure making conversions where appropriate"""
        field = self.filb.get(length)
        if tag == IPP_TAG_INTEGER or tag == IPP_TAG_ENUM:
            result = struct.unpack('!I', field)[0]
        elif tag == IPP_TAG_BOOLEAN:
            result = ord(field)
        elif tag == IPP_TAG_DATE:
            year, month, day, hours, minutes, seconds, decisec, plusmin, utchr, utcmin = struct.unpack('!H6Bc2B', field)
            t = time.mktime((year, month, day, hours, minutes, seconds, 0, 0, -1))
            if plusmin == '-':
                t += utchr * 3600 + utcmin * 60
            else:
                t -= utchr * 3600 + utcmin * 60
            result = t
        elif tag == IPP_TAG_RESOLUTION:
            result = struct.unpack('!2Ic', field)
        elif tag == IPP_TAG_RANGE:
            result = struct.unpack('!2I', field);
        elif tag == IPP_TAG_TEXTLANG or tag == IPP_TAG_NAMELANG:
            csleng = struct.unpack('!H', field[0:2])[0]
            field = field[2:]
            cs = field[0:csleng]
            field = field[csleng:]
            tleng = struct.unpack('!H', field[0:2])[0]
            field = field[2:]
            t = field[0:tleng]
            result = (cs, t)
        elif tag == IPP_TAG_BEGIN_COLLECTION:
            raise IppError("Sorry cannot do collections yet")
        else:
            result = field
        return (tag, result)

    def parseattr(self):
        """Parse attribute sequence"""
        tag = self.filb.getch()
        resval = ippvalue(tag)
        namelength = struct.unpack('!H', self.filb.get(2))[0]
        resval.setname(self.filb.get(namelength))
        valuelength = struct.unpack('!H', self.filb.get(2))[0]
        resval.setvalue(self.parsevalue(tag, valuelength))
        while self.filb.peekch() >= IPP_TAG_UNSUPPORTED_VALUE:
            newtag, namelength = struct.unpack('!BH', self.filb.peek(3))
            if namelength != 0:
                break
            self.filb.skip(3)
            valuelength = struct.unpack('!H', self.filb.get(2))[0]
            resval.setvalue(self.parsevalue(newtag, valuelength))
        return resval

    def parsegrp(self, tag):
        """Parse a group of attributes"""
        grp = ippgroup(tag)
        while 1:
            attr = self.parseattr()
            grp.setvalue(attr)
            if self.filb.peekch() < IPP_TAG_UNSUPPORTED_VALUE:
                return  grp

    def parseipp(self):
        """Parse an IPP string"""
        self.majv, self.minv, self.statuscode, self.id = self.parsehdr()
        while 1:
            if self.filb.peekch() < IPP_TAG_UNSUPPORTED_VALUE:
                # Reading groups
                grptag = self.filb.getch()
                if grptag == IPP_TAG_END:
                    break
                item = self.parsegrp(grptag)
            else:
                item = self.parseattr()
            self.values.append(item)

    def parse(self):
        """Parse an IPP structure and catch errors"""
        try:
            self.parseipp()
        except (TypeError, struct.error, filebuf.filebufEOF):
            raise IppError("Problem parsing input")

    def display(self, resp=0):
        """Display/trace an IPP structure
Argument is non-zero to specify it's a response structure"""
        try:
            if resp:
                code = resp_to_name[self.statuscode]
            else:
                code = op_to_name[self.statuscode]
        except KeyError:
            if resp:
                code = "Unknown status did you really mean resp"
            else:
                code = "Unknown code didn't you mean resp"
        syslog.syslog(syslog.LOG_DEBUG, "Version=" + str(self.majv) + "." + str(self.minv))
        syslog.syslog(syslog.LOG_DEBUG, "Code=" + str(code) + " Id=" + str(self.id))
        if len(self.values) > 0:
            syslog.syslog(syslog.LOG_DEBUG, "Items:")
            for v in self.values:
                v.display(0)

    def generate(self):
        """Generate an IPP string from the contents"""
        result = struct.pack('!2BHI', self.majv, self.minv, self.statuscode, self.id)
        for v in self.values:
            result += v.generate()
        return  result + chr(IPP_TAG_END)

    def find_attribute(self, name):
        """Find and return the first value field with the given name"""
        for v in self.values:
            fv = v.find_attribute(name)
            if  fv:
                return fv
        return None