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 22 29 0a 0a 09 64 65 66 20 5f 63 75 tag")...def _cu
0360: 72 73 28 73 65 6c 66 29 3a 0a 09 09 69 66 20 6e rs(self):...if n
0370: 6f 74 20 73 65 6c 66 2e 5f 64 62 3a 0a 09 09 09 ot self._db:....
0380: 63 6f 6e 66 69 67 2e 73 65 63 74 69 6f 6e 28 27 config.section('
0390: 64 61 74 61 62 61 73 65 27 29 0a 09 09 09 73 65 database')....se
03a0: 6c 66 2e 5f 64 62 20 3d 20 70 6f 73 74 67 72 65 lf._db = postgre
03b0: 73 71 6c 2e 6f 70 65 6e 28 0a 09 09 09 09 27 70 sql.open(.....'p
03c0: 71 3a 2f 2f 7b 7d 3a 7b 7d 40 7b 7d 2f 7b 7d 27 q://{}:{}@{}/{}'
03d0: 2e 66 6f 72 6d 61 74 28 0a 09 09 09 09 09 63 6f .format(......co
03e0: 6e 66 69 67 5b 27 75 73 65 72 27 5d 2c 0a 09 09 nfig['user'],...
03f0: 09 09 09 63 6f 6e 66 69 67 5b 27 70 61 73 73 77 ...config['passw
0400: 6f 72 64 27 5d 2c 0a 09 09 09 09 09 63 6f 6e 66 ord'],......conf
0410: 69 67 5b 27 68 6f 73 74 27 5d 2c 0a 09 09 09 09 ig['host'],.....
0420: 09 63 6f 6e 66 69 67 5b 27 64 61 74 61 62 61 73 .config['databas
0430: 65 27 5d 2c 0a 09 09 09 29 20 29 0a 09 09 72 65 e'],....) )...re
0440: 74 75 72 6e 28 73 65 6c 66 2e 5f 64 62 29 0a 0a turn(self._db)..
0450: 09 64 65 66 20 64 75 6d 70 28 73 65 6c 66 29 3a .def dump(self):
0460: 0a 09 09 72 65 74 75 72 6e 28 73 65 6c 66 2e 5f ...return(self._
0470: 64 75 6d 70 5f 73 74 6d 74 28 29 29 0a 0a 23 20 dump_stmt())..#
0480: 74 68 69 73 20 63 6c 61 73 73 65 73 20 70 72 6f this classes pro
0490: 63 65 73 73 65 73 20 63 6f 6e 66 69 67 20 66 69 cesses config fi
04a0: 6c 65 20 61 6e 64 20 73 75 62 73 74 69 74 75 74 le and substitut
04b0: 65 73 20 64 65 66 61 75 6c 74 20 76 61 6c 75 65 es default value
04c0: 73 0a 63 6c 61 73 73 20 43 6f 6e 66 69 67 3a 0a s.class Config:.
04d0: 09 5f 5f 73 6c 6f 74 73 5f 5f 20 3d 20 66 72 6f .__slots__ = fro
04e0: 7a 65 6e 73 65 74 28 5b 27 5f 63 6f 6e 66 69 67 zenset(['_config
04f0: 27 2c 20 27 5f 64 65 66 61 75 6c 74 27 2c 20 27 ', '_default', '
0500: 5f 73 65 63 74 69 6f 6e 27 5d 29 0a 09 5f 64 65 _section']).._de
0510: 66 61 75 6c 74 20 3d 20 7b 0a 09 09 27 72 65 61 fault = {...'rea
0520: 63 74 6f 72 27 3a 20 7b 0a 09 09 09 27 72 65 61 ctor': {....'rea
0530: 63 74 6f 72 27 3a 20 27 74 68 72 65 61 64 27 2c ctor': 'thread',
0540: 0a 09 09 7d 2c 0a 09 09 27 6c 6f 67 27 3a 20 7b ...},...'log': {
0550: 0a 09 09 09 27 73 69 6c 65 6e 74 27 3a 20 27 6e ....'silent': 'n
0560: 6f 27 2c 0a 09 09 7d 2c 0a 09 09 27 64 61 74 61 o',...},...'data
0570: 62 61 73 65 27 3a 20 7b 0a 09 09 09 27 68 6f 73 base': {....'hos
0580: 74 27 3a 20 27 6c 6f 63 61 6c 68 6f 73 74 27 2c t': 'localhost',
0590: 0a 09 09 09 27 64 61 74 61 62 61 73 65 27 3a 20 ....'database':
05a0: 27 73 71 75 69 64 54 61 67 27 2c 0a 09 7d 2c 7d 'squidTag',..},}
05b0: 0a 0a 09 23 20 66 75 6e 63 74 69 6f 6e 20 74 6f ...# function to
05c0: 20 72 65 61 64 20 69 6e 20 63 6f 6e 66 69 67 20 read in config
05d0: 66 69 6c 65 0a 09 64 65 66 20 5f 5f 69 6e 69 74 file..def __init
05e0: 5f 5f 28 73 65 6c 66 29 3a 0a 09 09 70 61 72 73 __(self):...pars
05f0: 65 72 20 3d 20 6f 70 74 70 61 72 73 65 2e 4f 70 er = optparse.Op
0600: 74 69 6f 6e 50 61 72 73 65 72 28 29 0a 09 09 70 tionParser()...p
0610: 61 72 73 65 72 2e 61 64 64 5f 6f 70 74 69 6f 6e arser.add_option
0620: 28 27 2d 63 27 2c 20 27 2d 2d 63 6f 6e 66 69 67 ('-c', '--config
0630: 27 2c 20 64 65 73 74 20 3d 20 27 63 6f 6e 66 69 ', dest = 'confi
0640: 67 27 2c 0a 09 09 09 68 65 6c 70 20 3d 20 27 63 g',....help = 'c
0650: 6f 6e 66 69 67 20 66 69 6c 65 20 6c 6f 63 61 74 onfig file locat
0660: 69 6f 6e 27 2c 20 6d 65 74 61 76 61 72 20 3d 20 ion', metavar =
0670: 27 46 49 4c 45 27 2c 0a 09 09 09 64 65 66 61 75 'FILE',....defau
0680: 6c 74 20 3d 20 27 2f 75 73 72 2f 6c 6f 63 61 6c lt = '/usr/local
0690: 2f 65 74 63 2f 73 71 75 69 64 2d 74 61 67 67 65 /etc/squid-tagge
06a0: 72 2e 63 6f 6e 66 27 29 0a 0a 09 09 28 6f 70 74 r.conf')....(opt
06b0: 69 6f 6e 73 2c 20 61 72 67 73 29 20 3d 20 70 61 ions, args) = pa
06c0: 72 73 65 72 2e 70 61 72 73 65 5f 61 72 67 73 28 rser.parse_args(
06d0: 29 0a 0a 09 09 69 66 20 6e 6f 74 20 6f 73 2e 61 )....if not os.a
06e0: 63 63 65 73 73 28 6f 70 74 69 6f 6e 73 2e 63 6f ccess(options.co
06f0: 6e 66 69 67 2c 20 6f 73 2e 52 5f 4f 4b 29 3a 0a nfig, os.R_OK):.
0700: 09 09 09 70 72 69 6e 74 28 22 43 61 6e 27 74 20 ...print("Can't
0710: 72 65 61 64 20 7b 7d 3a 20 65 78 69 74 74 69 6e read {}: exittin
0720: 67 22 2e 66 6f 72 6d 61 74 28 6f 70 74 69 6f 6e g".format(option
0730: 73 2e 63 6f 6e 66 69 67 29 29 0a 09 09 09 73 79 s.config))....sy
0740: 73 2e 65 78 69 74 28 32 29 0a 0a 09 09 73 65 6c s.exit(2)....sel
0750: 66 2e 5f 63 6f 6e 66 69 67 20 3d 20 63 6f 6e 66 f._config = conf
0760: 69 67 70 61 72 73 65 72 2e 43 6f 6e 66 69 67 50 igparser.ConfigP
0770: 61 72 73 65 72 28 29 0a 09 09 73 65 6c 66 2e 5f arser()...self._
0780: 63 6f 6e 66 69 67 2e 72 65 61 64 66 70 28 6f 70 config.readfp(op
0790: 65 6e 28 6f 70 74 69 6f 6e 73 2e 63 6f 6e 66 69 en(options.confi
07a0: 67 29 29 0a 0a 09 23 20 66 75 6e 63 74 69 6f 6e g))...# function
07b0: 20 74 6f 20 73 65 6c 65 63 74 20 63 6f 6e 66 69 to select confi
07c0: 67 20 66 69 6c 65 20 73 65 63 74 69 6f 6e 20 6f g file section o
07d0: 72 20 63 72 65 61 74 65 20 6f 6e 65 0a 09 64 65 r create one..de
07e0: 66 20 73 65 63 74 69 6f 6e 28 73 65 6c 66 2c 20 f section(self,
07f0: 73 65 63 74 69 6f 6e 29 3a 0a 09 09 69 66 20 6e section):...if n
0800: 6f 74 20 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e ot self._config.
0810: 68 61 73 5f 73 65 63 74 69 6f 6e 28 73 65 63 74 has_section(sect
0820: 69 6f 6e 29 3a 0a 09 09 09 73 65 6c 66 2e 5f 63 ion):....self._c
0830: 6f 6e 66 69 67 2e 61 64 64 5f 73 65 63 74 69 6f onfig.add_sectio
0840: 6e 28 73 65 63 74 69 6f 6e 29 0a 09 09 73 65 6c n(section)...sel
0850: 66 2e 5f 73 65 63 74 69 6f 6e 20 3d 20 73 65 63 f._section = sec
0860: 74 69 6f 6e 0a 0a 09 23 20 66 75 6e 63 74 69 6f tion...# functio
0870: 6e 20 74 6f 20 67 65 74 20 63 6f 6e 66 69 67 20 n to get config
0880: 70 61 72 61 6d 65 74 65 72 2c 20 69 66 20 70 61 parameter, if pa
0890: 72 61 6d 65 74 65 72 20 64 6f 65 73 6e 27 74 20 rameter doesn't
08a0: 65 78 69 73 74 73 20 74 68 65 20 64 65 66 61 75 exists the defau
08b0: 6c 74 0a 09 23 20 76 61 6c 75 65 20 6f 72 20 4e lt..# value or N
08c0: 6f 6e 65 20 69 73 20 73 75 62 73 74 69 74 75 74 one is substitut
08d0: 65 64 0a 09 64 65 66 20 5f 5f 67 65 74 69 74 65 ed..def __getite
08e0: 6d 5f 5f 28 73 65 6c 66 2c 20 6e 61 6d 65 29 3a m__(self, name):
08f0: 0a 09 09 69 66 20 6e 6f 74 20 73 65 6c 66 2e 5f ...if not self._
0900: 63 6f 6e 66 69 67 2e 68 61 73 5f 6f 70 74 69 6f config.has_optio
0910: 6e 28 73 65 6c 66 2e 5f 73 65 63 74 69 6f 6e 2c n(self._section,
0920: 20 6e 61 6d 65 29 3a 0a 09 09 09 69 66 20 73 65 name):....if se
0930: 6c 66 2e 5f 73 65 63 74 69 6f 6e 20 69 6e 20 73 lf._section in s
0940: 65 6c 66 2e 5f 64 65 66 61 75 6c 74 3a 0a 09 09 elf._default:...
0950: 09 09 69 66 20 6e 61 6d 65 20 69 6e 20 73 65 6c ..if name in sel
0960: 66 2e 5f 64 65 66 61 75 6c 74 5b 73 65 6c 66 2e f._default[self.
0970: 5f 73 65 63 74 69 6f 6e 5d 3a 0a 09 09 09 09 09 _section]:......
0980: 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e 73 65 74 self._config.set
0990: 28 73 65 6c 66 2e 5f 73 65 63 74 69 6f 6e 2c 20 (self._section,
09a0: 6e 61 6d 65 2c 20 73 65 6c 66 2e 5f 64 65 66 61 name, self._defa
09b0: 75 6c 74 5b 73 65 6c 66 2e 5f 73 65 63 74 69 6f ult[self._sectio
09c0: 6e 5d 5b 6e 61 6d 65 5d 29 0a 09 09 09 09 65 6c n][name]).....el
09d0: 73 65 3a 0a 09 09 09 09 09 73 65 6c 66 2e 5f 63 se:......self._c
09e0: 6f 6e 66 69 67 2e 73 65 74 28 73 65 6c 66 2e 5f onfig.set(self._
09f0: 73 65 63 74 69 6f 6e 2c 20 6e 61 6d 65 2c 20 4e section, name, N
0a00: 6f 6e 65 29 0a 09 09 09 65 6c 73 65 3a 0a 09 09 one)....else:...
0a10: 09 09 73 65 6c 66 2e 5f 63 6f 6e 66 69 67 2e 73 ..self._config.s
0a20: 65 74 28 73 65 6c 66 2e 5f 73 65 63 74 69 6f 6e et(self._section
0a30: 2c 20 6e 61 6d 65 2c 20 4e 6f 6e 65 29 0a 09 09 , name, None)...
0a40: 72 65 74 75 72 6e 28 73 65 6c 66 2e 5f 63 6f 6e return(self._con
0a50: 66 69 67 2e 67 65 74 28 73 65 6c 66 2e 5f 73 65 fig.get(self._se
0a60: 63 74 69 6f 6e 2c 20 6e 61 6d 65 29 29 0a 0a 23 ction, name))..#
0a70: 20 69 6e 69 74 69 61 6c 69 7a 69 6e 67 20 61 6e initializing an
0a80: 64 20 72 65 61 64 69 6e 67 20 69 6e 20 63 6f 6e d reading in con
0a90: 66 69 67 20 66 69 6c 65 0a 63 6f 6e 66 69 67 20 fig file.config
0aa0: 3d 20 43 6f 6e 66 69 67 28 29 0a 0a 74 61 67 64 = Config()..tagd
0ab0: 62 20 3d 20 74 61 67 44 42 28 29 0a 0a 63 73 76 b = tagDB()..csv
0ac0: 5f 77 72 69 74 65 72 20 3d 20 63 73 76 2e 77 72 _writer = csv.wr
0ad0: 69 74 65 72 28 73 79 73 2e 73 74 64 6f 75 74 29 iter(sys.stdout)
0ae0: 0a 63 73 76 5f 77 72 69 74 65 72 2e 77 72 69 74 .csv_writer.writ
0af0: 65 72 6f 77 28 5b 27 73 69 74 65 27 2c 20 27 74 erow(['site', 't
0b00: 61 67 73 27 2c 20 27 72 65 67 65 78 70 27 5d 29 ags', 'regexp'])
0b10: 0a 66 6f 72 20 72 6f 77 20 69 6e 20 74 61 67 64 .for row in tagd
0b20: 62 2e 64 75 6d 70 28 29 3a 0a 09 63 73 76 5f 77 b.dump():..csv_w
0b30: 72 69 74 65 72 2e 77 72 69 74 65 72 6f 77 28 5b riter.writerow([
0b40: 72 6f 77 5b 30 5d 2c 20 27 7b 27 20 2b 20 27 2c row[0], '{' + ',
0b50: 27 2e 6a 6f 69 6e 28 72 6f 77 5b 31 5d 29 20 2b '.join(row[1]) +
0b60: 20 27 7d 27 2c 20 72 6f 77 5b 32 5d 5d 29 0a '}', row[2]]).