This file is indexed.

/etc/gnuspool/cupspy/conf.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
# 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 re, socket, string, copy, time, ipp, sys, os, stat

class ConfError(Exception):
    """Error report in config file"""
    pass

class ConfEOF(Exception):
    """Just exception to raise when we read EOF"""
    pass

class Confdef:
    def __init__(self, name, *istrings):
        self.descr = name
        self.attrlist = list()
        self.attrs = dict()
        self.defs = dict()
        for i in istrings:
            self.defs[i] = ''

    def setdef(self, name, value):
        """Set a default name to given value"""
        if name not in self.defs:
            raise ConfError("Unknown keyword " + name)
        self.defs[name] = value;

    def setattr(self, name, typ, value):
        """Set up attribute name/type/value"""
        if typ not in ipp.name_to_tag:
            raise ConfError("Unknown type " + typ)
        self.attrlist.append(name)
        self.attrs[name] = (typ, value)

    def check_complete(self):
        """Check we've got all the bits we should have"""
        for k,v in dict.items(self.defs):
            if len(v) == 0:
                raise ConfError("Definition of " + k + " missing in " + self.descr)

    def write_defs(self, outfile):
        """Write out definitions to file"""

        outfile.write("# Definitions for %s\n\n%s:\n\n" % (self.descr, self.descr))

        for k,v in dict.items(self.defs):
            outfile.write("\t%s=%s\n" % (k, v))

        outfile.write("\n")
        for a in self.attrlist:
            typ, val = self.attrs[a]
            outfile.write("\t%s %s %s\n" % (a, typ, val))
        outfile.write("\n")
        

def readuncommline(f):
    """Read a non-empty line from file
Also strip comments"""
    while 1:
        result = f.readline()
        if len(result) == 0:
            raise ConfEOF
        m = re.match("([^#]*)#", result)
        if m:
            result = m.group(1)
        result = string.strip(result)
        if  len(result) != 0:
            return  result

def readfullline(f):
    """Read a line in joining lines ending with \\ to the next one"""
    result = readuncommline(f)
    while result[-1] == '\\':
        result = result[0:-1]
        r2 = readuncommline(f)
        result += r2
    return result

def procparam(line, cdef):
    """Add the parameter list to the given entry
This might be the defaults or the entry for a given printer"""
    # Split line into attribute name, type and args
    try:
        name, typ, args = re.split("\s+", line, 2)
    except ValueError:
        raise ConfError("Expecting name/type/args in ")
    cdef.setattr(name, typ, args)

def parse_loop(f, cdef):
    """Loop to read definitions from file"""
    while 1:
        # Remember where we were
        where = f.tell()
        line = readfullline(f)

        # If start of next section rewind and drop out
        if re.match("\w+\s*:$", line):
            f.seek(where)
            return

        try:
            # Def specs have name=string in
            m = re.match("(\w+)\s*=\s*(.*)", line)
            if m:
                cdef.setdef(m.group(1), m.group(2))
            else:
                procparam(line, cdef)
        except ConfError as Err:
            raise ConfError(Err.args[0] + " in " + line)

class Conf:
    """Content of config file"""

    def __init__(self):
        self.defaults = Confdef("DEFAULTS", "Prefix", "Form", "GSPrinter", "Copies", "Title", "User", "Printer")
        self.printers = dict()
        self.plist = []
        self.loglevel = 1
        self.timeouts = 20
        self.conf_dir = os.getcwd()
        self.ppddir = self.conf_dir

    def parse_conf_params(self, f):
        """Parse parameters section of a config file"""

        hadppd = False
        while 1:
            # Remember where we wre
            where = f.tell()
            line = readfullline(f)
            # If start of next section rewind and drop out
            if re.match("\w+\s*:$", line):
                f.seek(where)
                break
            m = re.match("(\w+)\s*=\s*(.*)", line)
            if not m:
                break
            opt = m.group(1)
            arg = m.group(2)
            if opt == "LOGLEVEL":
                try:
                    self.loglevel = int(arg)
                    if self.loglevel < 0 or self.loglevel > 4:
                        raise ValueError
                except ValueError:
                    raise ConfError("Invalid LOGLEVEL " + arg + " (should be int < 5)")
            elif opt == "PPDDIR":
                self.ppddir = arg
                if  len(arg) == 0:
                    raise ConfError("Invalid null PPDDIR")
                hadppd = True
            elif opt == "TIMEOUT":
                try:
                    self.timeouts = float(arg)
                    if self.timeouts <= 0:
                        raise ValueError
                except ValueError:
                    raise ConfError("Invalid TIMEOUT " + arg + " (should be >0)")

        # If ppdir was specified and not absolute, put confdir in front

        if hadppd:
            if self.ppddir[0] != '/':
                self.ppddir = os.path.join(self.conf_dir, self.ppddir)
            try:
                s = os.stat(self.ppddir)
                if not stat.S_ISDIR(s[0]):
                    raise ConfError("PPDDIR " + self.ppddir + " not directory")
            except OSError:
                raise ConfError("Invalid PPDDIR " + self.ppddir)

    def parse_conf_defaults(self, f):
        """Parse the defaults section of a config file"""
        parse_loop(f, self.defaults)

    def parse_conf_printer(self, pname, f):
        """Parse printer definition in a config file"""
        if pname in self.printers:
            raise ConfError("Already had definition of " + pname)
        self.plist.append(pname)
        pd = Confdef(pname, "GSPrinter", "Form", "Command")
        pd.defs["Command"] = ':'
        self.printers[pname] = pd
        parse_loop(f, pd)

    def parse_conf_file(self, fname):
        """Parse a config file and set up defaults"""

        # Set base directory for conf stuff to be the directory we are reading the file from
        # That might reset it later

        confd, filen = os.path.split(os.path.abspath(fname))
        self.conf_dir = confd
        self.ppddir = confd

        # Open the silly thing

        try:
            cfile = open(fname, 'rb')
        except IOError:
            raise ConfError("Cannot open " + fname)
        try:
            while 1:
                line = readfullline(cfile)
                m = re.match("\s*(\w+)\s*:$", line)
                if not m:
                    raise ConfError("Unrecognised section start - " + line)
                if m.group(1) == "PARAMS":
                    self.parse_conf_params(cfile)
                elif m.group(1) == "DEFAULTS":
                    self.parse_conf_defaults(cfile)
                else:
                    self.parse_conf_printer(m.group(1), cfile)
        except ConfEOF:
            pass
        cfile.close()

        # Check things make sense

        self.defaults.check_complete()
        for ptr in self.printers.values():
            ptr.check_complete()
        if self.defaults.defs['Printer'] not in self.printers:
            raise ConfError("Default printer " + self.defaults['Printer'] + " not set up")

    def write_config(self, fname, plist=None):
        """Write out a config file to the specified file
Optionally write printers out in the order given"""
        try:
            outfile = open(fname, "wb")
        except IOError:
            raise ConfError("Cannot open " + fname)

        outd, filen = os.path.split(os.path.abspath(fname))
     
        # Write parameters
        
        outfile.write("# CUPSPY configuration file written on %s\n\n" % time.ctime())
        outfile.write("# Parameters section\n\nPARAMS:\n\tLOGLEVEL=%d\n\tTIMEOUT=%d\n" % (self.loglevel, self.timeouts))

        # Write out ppddir as relative or not at all if it's the same as outd

        ppd = self.ppddir
        if outd != ppd:
            lout = len(outd)
            if lout < len(ppd) and outd == ppd[0:lout]  and  ppd[lout] == '/':
                ppd = ppd[lout+1:]
            outfile.write("\tPPDDIR=%s\n" % ppd)

        outfile.write("\n")
        
        # Write defaults
        
        self.defaults.write_defs(outfile)
        if not plist:
            plist = self.plist
        for p in plist:
            if p in self.printers:
                self.printers[p].write_defs(outfile)
        outfile.close()

    # The following things are enquiries on the config data
    # Not too many errors now

    def list_printers(self):
        """Get a list of printers"""
        return self.plist

    def default_printer(self):
        """Get default printer name"""
        return  self.defaults.defs['Printer']

    def set_default_printer(self, pname):
        """Set default printer"""
        self.defaults.defs['Printer'] = pname

    def print_command(self, pname, copies=1, user='root', title='No title'):
        """Get print command for printer"""

        # If specific command given (not ':') use that

        try:
            pdefs = self.printers[pname].defs
        except (KeyError, AttributeError):
            return None
        
        try:
            cmd = pdefs['Command']
        except KeyError:
            cmd = ':'

        if cmd != ':':
            return cmd

        # Put quotes round title if needed

        title = re.sub("[^\w\s.]+", "_", title)
        if re.search("\s", title):
            title = "'" + title + "'"

        # Create copies, printer, user, title args

        try:
            cmd = self.defaults.defs['Prefix']
            cpsarg = self.defaults.defs['Copies'] % copies
            titlearg = self.defaults.defs['Title'] % title
            userarg = self.defaults.defs['User'] % user
            if len(pdefs['GSPrinter']) != 0 and  pdefs['GSPrinter'] != ':':
                printerarg = self.defaults.defs['GSPrinter'] % pdefs['GSPrinter']
            else:
                printerarg = ""
            formarg = self.defaults.defs['Form'] % pdefs['Form']
        except (TypeError, KeyError, AttributeError):
            return None

        return string.join([cmd, cpsarg, titlearg, userarg, printerarg, formarg], ' ')

    def get_attnames(self, pname):
        """Get list of applicable attributes for specified printer
in order that they are supposed to be"""
        try:
            attlist = copy.deepcopy(self.defaults.attrlist)
            for att in self.printers[pname].attrlist:
                if att not in attlist:
                    attlist.append(att)
            return attlist
        except KeyError:
            return None

    def get_config(self, pname, al):
        """Get specified configuration information for specified printer"""

        if pname not in self.printers:
            return None

        # First get Default attributes
        # We want a copy not a reference as we're going to overwrite bits

        datts = copy.deepcopy(self.defaults.attrs)
        for k,v in dict.items(self.printers[pname].attrs):
            datts[k] = v

        pendresult = []
        for att in al:
            if att in datts:
                pendresult.append((att, datts[att]))

        result = []

        # Now decode any symbolic names and argument lists

        for r in pendresult:
            name, att = r
            typestr, argstring = att
            typenum = ipp.name_to_tag[typestr]

            # Deal with "" strings as a special case - only one of them

            m = re.match('"(.*)"', argstring)
            if  m:
                res = m.group(1)
            elif re.match("NOW", argstring):
                # Simple expressions involving current time
                m = re.match("NOW(([-+])(\d+))?", argstring)
                if not m:
                    raise ConfError("Cannot decipher " + argstring)
                when = time.time()
                if m.group(1):
                    offset = int(m.group(3))
                    if m.group(2) == '-':
                        when -= offset
                    else:
                        when += offset
                res = when
            elif re.match("MAKE\w+URI", argstring):
                if argstring == "MAKELOCALURI":
                    res = "ipp://localhost:631/printers/" + pname
                elif argstring == "MAKENETURI":
                    res = "ipp://" + socket.gethostname() + ":631/printers/" + pname
                elif argstring == "MAKEDEVURI":
                    res = "hal:///org/freedesktop/Hal/devices/" + pname
                else:
                    raise ConfError("Cannot decipher " + argstring)
            elif argstring == "PRINTERNAME":
                res = pname
            else:
                res = re.split("\s+", argstring)
                if typenum == ipp.IPP_TAG_INTEGER or typenum == ipp.IPP_TAG_ENUM or typenum == ipp.IPP_TAG_BOOLEAN or typenum == ipp.IPP_TAG_DATE:
                    try:
                        res = map(int, res)
                    except ValueError:
                        raise ConfError("Invalid number for " + typestr + " in attr " + name)
                elif typenum == ipp.IPP_TAG_RESOLUTION:
                    if len(res) != 3:
                        raise ConfError("Invalid resolution " + argstring)
                    units = res.pop();
                    try:
                        res = map(int, res)
                    except ValueError:
                        raise ConfError("Invalid number for resolution " + name)
                    res.append(units);
                    res = tuple(res)
                elif typenum == ipp.IPP_TAG_RANGE:
                    if len(res) != 2:
                        raise ConfError("Invalid range " + argstring)
                    try:
                        res = map(int, res)
                    except ValueError:
                        raise ConfError("Invalid number for range " + name)
                    res = tuple(res)

            if not isinstance(res, list):
                res = [res]

            result.append((name, typenum, res))

        return result

    def get_default_attribute(self, attr):
        """Get default attribute value as a list"""
        if attr not in self.defaults.attrs:
            return None
        return  re.split('\s+', self.defaults.attrs[attr][1])

    def get_attribute_value(self, pname, attr):
        """Get specified attribute value as single item"""
        if pname not in self.printers:
            return None
        atts = self.get_config(pname, [attr])
        if len(atts) == 0:
            return None
        atts = atts[0][2]
        if len(atts) == 1:
            return atts[0]
        return atts

    def set_attribute_value(self, pname, attrname, value):
        """Set specified attribute value"""
        if pname not in self.printers:
            raise ConfError("No such printer - " + pname)
        ptrdef = self.printers[pname]
        pattrs = ptrdef.attrs

        # Set value to list if it isn't

        if isinstance(value, list):
            value = string.join(map(list, str), ' ')
        
        # If something is already defined just update it otherwise
        # get type from default or throw wobbly
        
        if attrname in pattrs:
            ty, ev = pattrs[attrname]
            if ty == "IPP_TAG_TEXT":
                value = '"' + value + '"'
            pattrs[attrname] = (ty, value)
        else:
            if attrname not in self.defaults.attrs:
                raise ConfError("Unknown attribute " + attrname)
            # Need first level copy as we're overwriting value part
            ty, ev = self.defaults.attrs[attrname]
            if ty == "IPP_TAG_TEXT":
                value = '"' + value + '"'
            newatt = (ty, value)
            pattrs[attrname] = newatt
            ptrdef.attrlist.append(attrname)

    def get_param_value(self, pname, param):
        """Get specified printer parameter"""
        if pname not in self.printers:
            raise ConfError("No such printer - " + pname)
        pdefs = self.printers[pname].defs
        if param not in pdefs:
            raise ConfError("No such parameter - " + param)
        return pdefs[param]
        
    def set_param_value(self, pname, param, value):
        """Set specified printer parameter"""
        if pname not in self.printers:
            raise ConfError("No such printer - " + pname)
        pdefs = self.printers[pname].defs
        if param not in pdefs:
            raise ConfError("No such parameter - " + param)
        pdefs[param] = value
        
    def add_printer(self, pname):
        """Add a new printer to the list"""
        if pname in self.printers:
            raise ConfError("Duplicate printer " + pname)
        self.plist.append(pname)
        pd = Confdef(pname, "GSPrinter", "Form", "Command")
        pd.defs["Command"] = ':'
        self.printers[pname] = pd

    def rename_printer(self, pname, newpname):
        """Rename a printer"""
        if newpname in self.printers:
            raise ConfError("Duplicate printer name " + newpname)
        self.printers[newpname] = self.printers[pname]
        del self.printers[pname]
        self.plist[self.plist.index(pname)] = newpname
        # If it's the default printer rename that
        if self.defaults.defs['Printer'] == pname:
            self.defaults.defs['Printer'] = newpname
        
    def del_printer(self, pname):
        """Delete a printer from the list"""
        if pname not in self.printers:
            raise ConfError("Unknown printer " + pname)
        del self.printers[pname]
        try:
            del self.plist[self.plist.index(pname)]
        except:
            pass
        if self.defaults.defs['Printer'] == pname:
            self.defaults.defs['Printer'] = ""