0000: 23 21 2f 75 73 72 2f 62 69 6e 2f 65 6e 76 20 70 #!/usr/bin/env p
0010: 79 74 68 6f 6e 33 2e 31 0a 0a 69 6d 70 6f 72 74 ython3.1..import
0020: 20 63 6f 6e 66 69 67 70 61 72 73 65 72 2c 20 63 configparser, c
0030: 73 76 2c 20 6f 70 74 70 61 72 73 65 2c 20 6f 73 sv, optparse, os
0040: 2c 20 70 6f 73 74 67 72 65 73 71 6c 2e 61 70 69 , postgresql.api
0050: 2c 20 73 79 73 0a 0a 23 20 77 72 61 70 70 65 72 , sys..# wrapper
0060: 20 61 72 6f 75 6e 64 20 73 79 73 6c 6f 67 2c 20 around syslog,
0070: 63 61 6e 20 62 65 20 6d 75 74 65 64 0a 63 6c 61 can be muted.cla
0080: 73 73 20 4c 6f 67 67 65 72 3a 0a 09 5f 5f 73 6c ss Logger:..__sl
0090: 6f 74 73 5f 5f 20 3d 20 66 72 6f 7a 65 6e 73 65 ots__ = frozense
00a0: 74 28 5b 27 5f 73 79 73 6c 6f 67 27 5d 29 0a 0a t(['_syslog'])..
00b0: 09 64 65 66 20 5f 5f 69 6e 69 74 5f 5f 28 73 65 .def __init__(se
00c0: 6c 66 29 3a 0a 09 09 63 6f 6e 66 69 67 2e 73 65 lf):...config.se
00d0: 63 74 69 6f 6e 28 27 6c 6f 67 27 29 0a 09 09 69 ction('log')...i
00e0: 66 20 63 6f 6e 66 69 67 5b 27 73 69 6c 65 6e 74 f config['silent
00f0: 27 5d 20 3d 3d 20 27 79 65 73 27 3a 0a 09 09 09 '] == 'yes':....
0100: 73 65 6c 66 2e 5f 73 79 73 6c 6f 67 20 3d 20 4e self._syslog = N
0110: 6f 6e 65 0a 09 09 65 6c 73 65 3a 0a 09 09 09 69 one...else:....i
0120: 6d 70 6f 72 74 20 73 79 73 6c 6f 67 0a 09 09 09 mport syslog....
0130: 73 65 6c 66 2e 5f 73 79 73 6c 6f 67 20 3d 20 73 self._syslog = s
0140: 79 73 6c 6f 67 0a 09 09 09 73 65 6c 66 2e 5f 73 yslog....self._s
0150: 79 73 6c 6f 67 2e 6f 70 65 6e 6c 6f 67 28 27 73 yslog.openlog('s
0160: 71 75 69 64 54 61 67 27 29 0a 0a 09 64 65 66 20 quidTag')...def
0170: 69 6e 66 6f 28 73 65 6c 66 2c 20 6d 65 73 73 61 info(self, messa
0180: 67 65 29 3a 0a 09 09 69 66 20 73 65 6c 66 2e 5f ge):...if self._
0190: 73 79 73 6c 6f 67 3a 0a 09 09 09 73 65 6c 66 2e syslog:....self.
01a0: 5f 73 79 73 6c 6f 67 2e 73 79 73 6c 6f 67 28 73 _syslog.syslog(s
01b0: 65 6c 66 2e 5f 73 79 73 6c 6f 67 2e 4c 4f 47 5f elf._syslog.LOG_
01c0: 49 4e 46 4f 2c 20 6d 65 73 73 61 67 65 29 0a 0a INFO, message)..
01d0: 09 64 65 66 20 6e 6f 74 69 63 65 28 73 65 6c 66 .def notice(self
01e0: 2c 20 6d 65 73 73 61 67 65 29 3a 0a 09 09 69 66 , message):...if
01f0: 20 73 65 6c 66 2e 5f 73 79 73 6c 6f 67 3a 0a 09 self._syslog:..
0200: 09 09 73 65 6c 66 2e 5f 73 79 73 6c 6f 67 2e 73 ..self._syslog.s
0210: 79 73 6c 6f 67 28 73 65 6c 66 2e 5f 73 79 73 6c yslog(self._sysl
0220: 6f 67 2e 4c 4f 47 5f 4e 4f 54 49 43 45 2c 20 6d og.LOG_NOTICE, m
0230: 65 73 73 61 67 65 29 0a 0a 23 20 77 72 61 70 70 essage)..# wrapp
0240: 65 72 20 61 72 6f 75 6e 64 20 64 61 74 61 62 61 er around databa
0250: 73 65 0a 63 6c 61 73 73 20 74 61 67 44 42 3a 0a se.class tagDB:.
0260: 09 5f 5f 73 6c 6f 74 73 5f 5f 20 3d 20 66 72 6f .__slots__ = fro
0270: 7a 65 6e 73 65 74 28 5b 27 5f 70 72 65 70 61 72 zenset(['_prepar
0280: 65 64 27 2c 20 27 5f 64 75 6d 70 5f 73 74 6d 74 ed', '_dump_stmt
0290: 27 2c 20 27 5f 64 62 27 5d 29 0a 0a 09 64 65 66 ', '_db'])...def
02a0: 20 5f 5f 69 6e 69 74 5f 5f 28 73 65 6c 66 29 3a __init__(self):
02b0: 0a 09 09 73 65 6c 66 2e 5f 70 72 65 70 61 72 65 ...self._prepare
02c0: 64 20 3d 20 73 65 74 28 29 0a 09 09 73 65 6c 66 d = set()...self
02d0: 2e 5f 64 62 20 3d 20 46 61 6c 73 65 0a 09 09 73 ._db = False...s
02e0: 65 6c 66 2e 5f 64 75 6d 70 5f 73 74 6d 74 20 3d elf._dump_stmt =
02f0: 20 73 65 6c 66 2e 5f 63 75 72 73 28 29 2e 70 72 self._curs().pr
0300: 65 70 61 72 65 28 22 73 65 6c 65 63 74 20 75 6e epare("select un
0310: 74 72 69 70 28 73 69 74 65 29 2c 20 74 61 67 2c trip(site), tag,
0320: 20 72 65 67 65 78 70 20 66 72 6f 6d 20 75 72 6c regexp from url
0330: 73 20 6e 61 74 75 72 61 6c 20 6a 6f 69 6e 20 73 s natural join s
0340: 69 74 65 20 6e 61 74 75 72 61 6c 20 6a 6f 69 6e ite natural join
0350: 20 74 61 67 20 6f 72 64 65 72 20 62 79 20 73 69 tag order by si
0360: 74 65 2c 20 74 61 67 22 29 0a 0a 09 64 65 66 20 te, tag")...def
0370: 5f 63 75 72 73 28 73 65 6c 66 29 3a 0a 09 09 69 _curs(self):...i
0380: 66 20 6e 6f 74 20 73 65 6c 66 2e 5f 64 62 3a 0a f not self._db:.
0390: 09 09 09 63 6f 6e 66 69 67 2e 73 65 63 74 69 6f ...config.sectio
03a0: 6e 28 27 64 61 74 61 62 61 73 65 27 29 0a 09 09 n('database')...
03b0: 09 73 65 6c 66 2e 5f 64 62 20 3d 20 70 6f 73 74 .self._db = post
03c0: 67 72 65 73 71 6c 2e 6f 70 65 6e 28 0a 09 09 09 gresql.open(....
03d0: 09 27 70 71 3a 2f 2f 7b 7d 3a 7b 7d 40 7b 7d 2f .'pq://{}:{}@{}/
03e0: 7b 7d 27 2e 66 6f 72 6d 61 74 28 0a 09 09 09 09 {}'.format(.....
03f0: 09 63 6f 6e 66 69 67 5b 27 75 73 65 72 27 5d 2c .config['user'],
0400: 0a 09 09 09 09 09 63 6f 6e 66 69 67 5b 27 70 61 ......config['pa
0410: 73 73 77 6f 72 64 27 5d 2c 0a 09 09 09 09 09 63 ssword'],......c
0420: 6f 6e 66 69 67 5b 27 68 6f 73 74 27 5d 2c 0a 09 onfig['host'],..
0430: 09 09 09 09 63 6f 6e 66 69 67 5b 27 64 61 74 61 ....config['data
0440: 62 61 73 65 27 5d 2c 0a 09 09 09 29 20 29 0a 09 base'],....) )..
0450: 09 72 65 74 75 72 6e 28 73 65 6c 66 2e 5f 64 62 .return(self._db
0460: 29 0a 0a 09 64 65 66 20 64 75 6d 70 28 73 65 6c )...def dump(sel
0470: 66 29 3a 0a 09 09 72 65 74 75 72 6e 28 73 65 6c f):...return(sel
0480: 66 2e 5f 64 75 6d 70 5f 73 74 6d 74 28 29 29 0a f._dump_stmt()).
0490: 0a 23 20 74 68 69 73 20 63 6c 61 73 73 65 73 20 .# this classes
04a0: 70 72 6f 63 65 73 73 65 73 20 63 6f 6e 66 69 67 processes config
04b0: 20 66 69 6c 65 20 61 6e 64 20 73 75 62 73 74 69 file and substi
04c0: 74 75 74 65 73 20 64 65 66 61 75 6c 74 20 76 61 tutes default va
04d0: 6c 75 65 73 0a 63 6c 61 73 73 20 43 6f 6e 66 69 lues.class Confi
04e0: 67 3a 0a 09 5f 5f 73 6c 6f 74 73 5f 5f 20 3d 20 g:..__slots__ =
04f0: 66 72 6f 7a 65 6e 73 65 74 28 5b 27 5f 63 6f 6e frozenset(['_con
0500: 66 69 67 27 2c 20 27 5f 64 65 66 61 75 6c 74 27 fig', '_default'
0510: 2c 20 27 5f 73 65 63 74 69 6f 6e 27 5d 29 0a 09 , '_section'])..
0520: 5f 64 65 66 61 75 6c 74 20 3d 20 7b 0a 09 09 27 _default = {...'
0530: 72 65 61 63 74 6f 72 27 3a 20 7b 0a 09 09 09 27 reactor': {....'
0540: 72 65 61 63 74 6f 72 27 3a 20 27 74 68 72 65 61 reactor': 'threa
0550: 64 27 2c 0a 09 09 7d 2c 0a 09 09 27 6c 6f 67 27 d',...},...'log'
0560: 3a 20 7b 0a 09 09 09 27 73 69 6c 65 6e 74 27 3a : {....'silent':
0570: 20 27 6e 6f 27 2c 0a 09 09 7d 2c 0a 09 09 27 64 'no',...},...'d
0580: 61 74 61 62 61 73 65 27 3a 20 7b 0a 09 09 09 27 atabase': {....'
0590: 68 6f 73 74 27 3a 20 27 6c 6f 63 61 6c 68 6f 73 host': 'localhos
05a0: 74 27 2c 0a 09 09 09 27 64 61 74 61 62 61 73 65 t',....'database
05b0: 27 3a 20 27 73 71 75 69 64 54 61 67 27 2c 0a 09 ': 'squidTag',..
05c0: 7d 2c 7d 0a 0a 09 23 20 66 75 6e 63 74 69 6f 6e },}...# function
05d0: 20 74 6f 20 72 65 61 64 20 69 6e 20 63 6f 6e 66 to read in conf
05e0: 69 67 20 66 69 6c 65 0a 09 64 65 66 20 5f 5f 69 ig file..def __i
05f0: 6e 69 74 5f 5f 28 73 65 6c 66 29 3a 0a 09 09 70 nit__(self):...p
0600: 61 72 73 65 72 20 3d 20 6f 70 74 70 61 72 73 65 arser = optparse
0610: 2e 4f 70 74 69 6f 6e 50 61 72 73 65 72 28 29 0a .OptionParser().
0620: 09 09 70 61 72 73 65 72 2e 61 64 64 5f 6f 70 74 ..parser.add_opt
0630: 69 6f 6e 28 27 2d 63 27 2c 20 27 2d 2d 63 6f 6e ion('-c', '--con
0640: 66 69 67 27 2c 20 64 65 73 74 20 3d 20 27 63 6f fig', dest = 'co
0650: 6e 66 69 67 27 2c 0a 09 09 09 68 65 6c 70 20 3d nfig',....help =
0660: 20 27 63 6f 6e 66 69 67 20 66 69 6c 65 20 6c 6f 'config file lo
0670: 63 61 74 69 6f 6e 27 2c 20 6d 65 74 61 76 61 72 cation', metavar
0680: 20 3d 20 27 46 49 4c 45 27 2c 0a 09 09 09 64 65 = 'FILE',....de
0690: 66 61 75 6c 74 20 3d 20 27 2f 75 73 72 2f 6c 6f fault = '/usr/lo
06a0: 63 61 6c 2f 65 74 63 2f 73 71 75 69 64 2d 74 61 cal/etc/squid-ta
06b0: 67 67 65 72 2e 63 6f 6e 66 27 29 0a 0a 09 09 28 gger.conf')....(
06c0: 6f 70 74 69 6f 6e 73 2c 20 61 72 67 73 29 20 3d options, args) =
06d0: 20 70 61 72 73 65 72 2e 70 61 72 73 65 5f 61 72 parser.parse_ar
06e0: 67 73 28 29 0a 0a 09 09 69 66 20 6e 6f 74 20 6f gs()....if not o
06f0: 73 2e 61 63 63 65 73 73 28 6f 70 74 69 6f 6e 73 s.access(options
0700: 2e 63 6f 6e 66 69 67 2c 20 6f 73 2e 52 5f 4f 4b .config, os.R_OK
0710: 29 3a 0a 09 09 09 70 72 69 6e 74 28 22 43 61 6e ):....print("Can
0720: 27 74 20 72 65 61 64 20 7b 7d 3a 20 65 78 69 74 't read {}: exit
0730: 74 69 6e 67 22 2e 66 6f 72 6d 61 74 28 6f 70 74 ting".format(opt
0740: 69 6f 6e 73 2e 63 6f 6e 66 69 67 29 29 0a 09 09 ions.config))...
0750: 09 73 79 73 2e 65 78 69 74 28 32 29 0a 0a 09 09 .sys.exit(2)....
0760: 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 20 3d 20 63 self._config = c
0770: 6f 6e 66 69 67 70 61 72 73 65 72 2e 43 6f 6e 66 onfigparser.Conf
0780: 69 67 50 61 72 73 65 72 28 29 0a 09 09 73 65 6c igParser()...sel
0790: 66 2e 5f 63 6f 6e 66 69 67 2e 72 65 61 64 66 70 f._config.readfp
07a0: 28 6f 70 65 6e 28 6f 70 74 69 6f 6e 73 2e 63 6f (open(options.co
07b0: 6e 66 69 67 29 29 0a 0a 09 23 20 66 75 6e 63 74 nfig))...# funct
07c0: 69 6f 6e 20 74 6f 20 73 65 6c 65 63 74 20 63 6f ion to select co
07d0: 6e 66 69 67 20 66 69 6c 65 20 73 65 63 74 69 6f nfig file sectio
07e0: 6e 20 6f 72 20 63 72 65 61 74 65 20 6f 6e 65 0a n or create one.
07f0: 09 64 65 66 20 73 65 63 74 69 6f 6e 28 73 65 6c .def section(sel
0800: 66 2c 20 73 65 63 74 69 6f 6e 29 3a 0a 09 09 69 f, section):...i
0810: 66 20 6e 6f 74 20 73 65 6c 66 2e 5f 63 6f 6e 66 f not self._conf
0820: 69 67 2e 68 61 73 5f 73 65 63 74 69 6f 6e 28 73 ig.has_section(s
0830: 65 63 74 69 6f 6e 29 3a 0a 09 09 09 73 65 6c 66 ection):....self
0840: 2e 5f 63 6f 6e 66 69 67 2e 61 64 64 5f 73 65 63 ._config.add_sec
0850: 74 69 6f 6e 28 73 65 63 74 69 6f 6e 29 0a 09 09 tion(section)...
0860: 73 65 6c 66 2e 5f 73 65 63 74 69 6f 6e 20 3d 20 self._section =
0870: 73 65 63 74 69 6f 6e 0a 0a 09 23 20 66 75 6e 63 section...# func
0880: 74 69 6f 6e 20 74 6f 20 67 65 74 20 63 6f 6e 66 tion to get conf
0890: 69 67 20 70 61 72 61 6d 65 74 65 72 2c 20 69 66 ig parameter, if
08a0: 20 70 61 72 61 6d 65 74 65 72 20 64 6f 65 73 6e parameter doesn
08b0: 27 74 20 65 78 69 73 74 73 20 74 68 65 20 64 65 't exists the de
08c0: 66 61 75 6c 74 0a 09 23 20 76 61 6c 75 65 20 6f fault..# value o
08d0: 72 20 4e 6f 6e 65 20 69 73 20 73 75 62 73 74 69 r None is substi
08e0: 74 75 74 65 64 0a 09 64 65 66 20 5f 5f 67 65 74 tuted..def __get
08f0: 69 74 65 6d 5f 5f 28 73 65 6c 66 2c 20 6e 61 6d item__(self, nam
0900: 65 29 3a 0a 09 09 69 66 20 6e 6f 74 20 73 65 6c e):...if not sel
0910: 66 2e 5f 63 6f 6e 66 69 67 2e 68 61 73 5f 6f 70 f._config.has_op
0920: 74 69 6f 6e 28 73 65 6c 66 2e 5f 73 65 63 74 69 tion(self._secti
0930: 6f 6e 2c 20 6e 61 6d 65 29 3a 0a 09 09 09 69 66 on, name):....if
0940: 20 73 65 6c 66 2e 5f 73 65 63 74 69 6f 6e 20 69 self._section i
0950: 6e 20 73 65 6c 66 2e 5f 64 65 66 61 75 6c 74 3a n self._default:
0960: 0a 09 09 09 09 69 66 20 6e 61 6d 65 20 69 6e 20 .....if name in
0970: 73 65 6c 66 2e 5f 64 65 66 61 75 6c 74 5b 73 65 self._default[se
0980: 6c 66 2e 5f 73 65 63 74 69 6f 6e 5d 3a 0a 09 09 lf._section]:...
0990: 09 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e ...self._config.
09a0: 73 65 74 28 73 65 6c 66 2e 5f 73 65 63 74 69 6f set(self._sectio
09b0: 6e 2c 20 6e 61 6d 65 2c 20 73 65 6c 66 2e 5f 64 n, name, self._d
09c0: 65 66 61 75 6c 74 5b 73 65 6c 66 2e 5f 73 65 63 efault[self._sec
09d0: 74 69 6f 6e 5d 5b 6e 61 6d 65 5d 29 0a 09 09 09 tion][name])....
09e0: 09 65 6c 73 65 3a 0a 09 09 09 09 09 73 65 6c 66 .else:......self
09f0: 2e 5f 63 6f 6e 66 69 67 2e 73 65 74 28 73 65 6c ._config.set(sel
0a00: 66 2e 5f 73 65 63 74 69 6f 6e 2c 20 6e 61 6d 65 f._section, name
0a10: 2c 20 4e 6f 6e 65 29 0a 09 09 09 65 6c 73 65 3a , None)....else:
0a20: 0a 09 09 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 .....self._confi
0a30: 67 2e 73 65 74 28 73 65 6c 66 2e 5f 73 65 63 74 g.set(self._sect
0a40: 69 6f 6e 2c 20 6e 61 6d 65 2c 20 4e 6f 6e 65 29 ion, name, None)
0a50: 0a 09 09 72 65 74 75 72 6e 28 73 65 6c 66 2e 5f ...return(self._
0a60: 63 6f 6e 66 69 67 2e 67 65 74 28 73 65 6c 66 2e config.get(self.
0a70: 5f 73 65 63 74 69 6f 6e 2c 20 6e 61 6d 65 29 29 _section, name))
0a80: 0a 0a 23 20 69 6e 69 74 69 61 6c 69 7a 69 6e 67 ..# initializing
0a90: 20 61 6e 64 20 72 65 61 64 69 6e 67 20 69 6e 20 and reading in
0aa0: 63 6f 6e 66 69 67 20 66 69 6c 65 0a 63 6f 6e 66 config file.conf
0ab0: 69 67 20 3d 20 43 6f 6e 66 69 67 28 29 0a 0a 74 ig = Config()..t
0ac0: 61 67 64 62 20 3d 20 74 61 67 44 42 28 29 0a 0a agdb = tagDB()..
0ad0: 63 73 76 5f 77 72 69 74 65 72 20 3d 20 63 73 76 csv_writer = csv
0ae0: 2e 77 72 69 74 65 72 28 73 79 73 2e 73 74 64 6f .writer(sys.stdo
0af0: 75 74 29 0a 63 73 76 5f 77 72 69 74 65 72 2e 77 ut).csv_writer.w
0b00: 72 69 74 65 72 6f 77 28 5b 27 73 69 74 65 27 2c riterow(['site',
0b10: 20 27 74 61 67 73 27 2c 20 27 72 65 67 65 78 70 'tags', 'regexp
0b20: 27 5d 29 0a 66 6f 72 20 72 6f 77 20 69 6e 20 74 ']).for row in t
0b30: 61 67 64 62 2e 64 75 6d 70 28 29 3a 0a 09 63 73 agdb.dump():..cs
0b40: 76 5f 77 72 69 74 65 72 2e 77 72 69 74 65 72 6f v_writer.writero
0b50: 77 28 5b 72 6f 77 5b 30 5d 2c 20 27 7b 27 20 2b w([row[0], '{' +
0b60: 20 27 2c 27 2e 6a 6f 69 6e 28 72 6f 77 5b 31 5d ','.join(row[1]
0b70: 29 20 2b 20 27 7d 27 2c 20 72 6f 77 5b 32 5d 5d ) + '}', row[2]]
0b80: 29 0a ).