0ef24b1937 2011-04-06 1: #!/usr/bin/env python
0ef24b1937 2011-04-06 2:
0ef24b1937 2011-04-06 3: from __future__ import division, print_function, unicode_literals
0ef24b1937 2011-04-06 4:
0ef24b1937 2011-04-06 5: import gevent.monkey
0ef24b1937 2011-04-06 6: gevent.monkey.patch_all()
0ef24b1937 2011-04-06 7:
0ef24b1937 2011-04-06 8: import fcntl, gevent.core, gevent.pool, gevent.queue, gevent.socket, os, psycopg2, re, sys
0ef24b1937 2011-04-06 9:
0ef24b1937 2011-04-06 10: # //inclusion start
0ef24b1937 2011-04-06 11: # Copyright (C) 2010 Daniele Varrazzo <daniele.varrazzo@gmail.com>
0ef24b1937 2011-04-06 12: # and licensed under the MIT license:
0ef24b1937 2011-04-06 13:
0ef24b1937 2011-04-06 14: def gevent_wait_callback(conn, timeout=None):
0ef24b1937 2011-04-06 15: """A wait callback useful to allow gevent to work with Psycopg."""
0ef24b1937 2011-04-06 16: while 1:
0ef24b1937 2011-04-06 17: state = conn.poll()
0ef24b1937 2011-04-06 18: if state == psycopg2.extensions.POLL_OK:
0ef24b1937 2011-04-06 19: break
0ef24b1937 2011-04-06 20: elif state == psycopg2.extensions.POLL_READ:
0ef24b1937 2011-04-06 21: gevent.socket.wait_read(conn.fileno(), timeout=timeout)
0ef24b1937 2011-04-06 22: elif state == psycopg2.extensions.POLL_WRITE:
0ef24b1937 2011-04-06 23: gevent.socket.wait_write(conn.fileno(), timeout=timeout)
0ef24b1937 2011-04-06 24: else:
0ef24b1937 2011-04-06 25: raise psycopg2.OperationalError("Bad result from poll: %r" % state)
0ef24b1937 2011-04-06 26:
0ef24b1937 2011-04-06 27: if not hasattr(psycopg2.extensions, 'set_wait_callback'):
0ef24b1937 2011-04-06 28: raise ImportError("support for coroutines not available in this Psycopg version (%s)" % psycopg2.__version__)
0ef24b1937 2011-04-06 29: psycopg2.extensions.set_wait_callback(gevent_wait_callback)
0ef24b1937 2011-04-06 30:
0ef24b1937 2011-04-06 31: # //inclusion end
fc934cead1 2009-10-13 32:
fc934cead1 2009-10-13 33: # this classes processes config file and substitutes default values
fc934cead1 2009-10-13 34: class Config:
ae30851739 2010-08-12 35: __slots__ = frozenset(['_config', '_default', '_section', 'options'])
b93dc49210 2009-10-13 36: _default = {
fc934cead1 2009-10-13 37: 'log': {
fc934cead1 2009-10-13 38: 'silent': 'no',
fc934cead1 2009-10-13 39: },
fc934cead1 2009-10-13 40: 'database': {
fc934cead1 2009-10-13 41: 'host': 'localhost',
fc934cead1 2009-10-13 42: 'database': 'squidTag',
fc934cead1 2009-10-13 43: },}
fc934cead1 2009-10-13 44:
fc934cead1 2009-10-13 45: # function to read in config file
fc934cead1 2009-10-13 46: def __init__(self):
0ef24b1937 2011-04-06 47: import ConfigParser, optparse, os
ae30851739 2010-08-12 48:
fc934cead1 2009-10-13 49: parser = optparse.OptionParser()
fc934cead1 2009-10-13 50: parser.add_option('-c', '--config', dest = 'config',
fc934cead1 2009-10-13 51: help = 'config file location', metavar = 'FILE',
fc934cead1 2009-10-13 52: default = '/usr/local/etc/squid-tagger.conf')
ae30851739 2010-08-12 53: parser.add_option('-d', '--dump', dest = 'dump',
ae30851739 2010-08-12 54: help = 'dump database', action = 'store_true', metavar = 'bool',
ae30851739 2010-08-12 55: default = False)
31e69c4237 2010-08-12 56: parser.add_option('-f', '--flush-database', dest = 'flush_db',
31e69c4237 2010-08-12 57: help = 'flush previous database on load', default = False,
31e69c4237 2010-08-12 58: action = 'store_true', metavar = 'bool')
31e69c4237 2010-08-12 59: parser.add_option('-l', '--load', dest = 'load',
31e69c4237 2010-08-12 60: help = 'load database', action = 'store_true', metavar = 'bool',
31e69c4237 2010-08-12 61: default = False)
d301d9adc6 2010-08-13 62: parser.add_option('-D', '--dump-conf', dest = 'dump_conf',
d301d9adc6 2010-08-13 63: help = 'dump filtering rules', default = False, metavar = 'bool',
d301d9adc6 2010-08-13 64: action = 'store_true')
d301d9adc6 2010-08-13 65: parser.add_option('-L', '--load-conf', dest = 'load_conf',
d301d9adc6 2010-08-13 66: help = 'load filtering rules', default = False, metavar = 'bool',
d301d9adc6 2010-08-13 67: action = 'store_true')
7c13294e9f 2010-08-07 68:
ae30851739 2010-08-12 69: (self.options, args) = parser.parse_args()
7c13294e9f 2010-08-07 70:
ae30851739 2010-08-12 71: assert os.access(self.options.config, os.R_OK), "Fatal error: can't read {}".format(self.options.config)
7c13294e9f 2010-08-07 72:
0ef24b1937 2011-04-06 73: self._config = ConfigParser.ConfigParser()
ae30851739 2010-08-12 74: self._config.readfp(open(self.options.config))
fc934cead1 2009-10-13 75:
fc934cead1 2009-10-13 76: # function to select config file section or create one
fc934cead1 2009-10-13 77: def section(self, section):
fc934cead1 2009-10-13 78: if not self._config.has_section(section):
fc934cead1 2009-10-13 79: self._config.add_section(section)
fc934cead1 2009-10-13 80: self._section = section
fc934cead1 2009-10-13 81:
fc934cead1 2009-10-13 82: # function to get config parameter, if parameter doesn't exists the default
fc934cead1 2009-10-13 83: # value or None is substituted
fc934cead1 2009-10-13 84: def __getitem__(self, name):
fc934cead1 2009-10-13 85: if not self._config.has_option(self._section, name):
b93dc49210 2009-10-13 86: if self._section in self._default:
b93dc49210 2009-10-13 87: if name in self._default[self._section]:
fc934cead1 2009-10-13 88: self._config.set(self._section, name, self._default[self._section][name])
fc934cead1 2009-10-13 89: else:
fc934cead1 2009-10-13 90: self._config.set(self._section, name, None)
fc934cead1 2009-10-13 91: else:
fc934cead1 2009-10-13 92: self._config.set(self._section, name, None)
b93dc49210 2009-10-13 93: return(self._config.get(self._section, name))
d500448801 2009-10-05 94:
fc934cead1 2009-10-13 95: # initializing and reading in config file
fc934cead1 2009-10-13 96: config = Config()
d500448801 2009-10-05 97:
39b97ced92 2011-06-05 98: # wrapper around syslog, can be muted
39b97ced92 2011-06-05 99: class Logger(object):
39b97ced92 2011-06-05 100: __slots__ = frozenset(['_syslog'])
39b97ced92 2011-06-05 101:
39b97ced92 2011-06-05 102: def __init__(self):
39b97ced92 2011-06-05 103: config.section('log')
39b97ced92 2011-06-05 104: if config['silent'] == 'yes':
39b97ced92 2011-06-05 105: self._syslog = None
39b97ced92 2011-06-05 106: else:
39b97ced92 2011-06-05 107: import syslog
39b97ced92 2011-06-05 108: self._syslog = syslog
39b97ced92 2011-06-05 109: self._syslog.openlog(str('squidTag'))
39b97ced92 2011-06-05 110:
39b97ced92 2011-06-05 111: def info(self, message):
39b97ced92 2011-06-05 112: if self._syslog != None:
39b97ced92 2011-06-05 113: self._syslog.syslog(self._syslog.LOG_INFO, message)
39b97ced92 2011-06-05 114:
39b97ced92 2011-06-05 115: def notice(self, message):
39b97ced92 2011-06-05 116: if self._syslog != None:
39b97ced92 2011-06-05 117: self._syslog.syslog(self._syslog.LOG_NOTICE, message)
39b97ced92 2011-06-05 118:
39b97ced92 2011-06-05 119: logger = Logger()
39b97ced92 2011-06-05 120:
39b97ced92 2011-06-05 121: # tiny wrapper around a file to make reads from it geventable
39b97ced92 2011-06-05 122: # or should i move this somewhere?
39b97ced92 2011-06-05 123:
39b97ced92 2011-06-05 124: class FReadlineQueue(gevent.queue.Queue):
39b97ced92 2011-06-05 125: # storing file descriptor, leftover
39b97ced92 2011-06-05 126: __slots__ = frozenset(['_fd', '_tail'])
39b97ced92 2011-06-05 127:
39b97ced92 2011-06-05 128: def __init__(self, fd):
39b97ced92 2011-06-05 129: # initialising class
39b97ced92 2011-06-05 130: gevent.queue.Queue.__init__(self)
39b97ced92 2011-06-05 131: # storing file descriptor
39b97ced92 2011-06-05 132: self._fd = fd
39b97ced92 2011-06-05 133: # using empty tail
39b97ced92 2011-06-05 134: self._tail = ''
39b97ced92 2011-06-05 135: # setting up event
39b97ced92 2011-06-05 136: self._install_wait()
39b97ced92 2011-06-05 137:
39b97ced92 2011-06-05 138: def _install_wait(self):
39b97ced92 2011-06-05 139: fileno = self._fd.fileno()
39b97ced92 2011-06-05 140: # putting file to nonblocking mode
39b97ced92 2011-06-05 141: fcntl.fcntl(fileno, fcntl.F_SETFL, fcntl.fcntl(fileno, fcntl.F_GETFL) | os.O_NONBLOCK)
39b97ced92 2011-06-05 142: # installing event handler
39b97ced92 2011-06-05 143: gevent.core.read_event(fileno, self._wait_helper)
39b97ced92 2011-06-05 144:
39b97ced92 2011-06-05 145: def _wait_helper(self, ev, evtype):
39b97ced92 2011-06-05 146: # reading one buffer from stream
39b97ced92 2011-06-05 147: buf = self._fd.read(4096)
39b97ced92 2011-06-05 148: # splitting stream by line ends
39b97ced92 2011-06-05 149: rows = buf.decode('l1').split('\n')
39b97ced92 2011-06-05 150: # adding tail to the first element if there is some tail
39b97ced92 2011-06-05 151: if len(self._tail) > 0:
39b97ced92 2011-06-05 152: rows[0] = self._tail + rows[0]
39b97ced92 2011-06-05 153: # popping out last (incomplete) element
39b97ced92 2011-06-05 154: self._tail = rows.pop(-1)
39b97ced92 2011-06-05 155: # dropping all complete elements to the queue
39b97ced92 2011-06-05 156: for row in rows:
39b97ced92 2011-06-05 157: self.put_nowait(row)
39b97ced92 2011-06-05 158: logger.info('request: ' + row)
39b97ced92 2011-06-05 159: if len(buf) > 0:
39b97ced92 2011-06-05 160: # no EOF, reinstalling event handler
39b97ced92 2011-06-05 161: gevent.core.read_event(self._fd.fileno(), self._wait_helper)
39b97ced92 2011-06-05 162: else:
39b97ced92 2011-06-05 163: # EOF found, sending EOF to queue
39b97ced92 2011-06-05 164: self.put_nowait(None)
39b97ced92 2011-06-05 165:
39b97ced92 2011-06-05 166: stdin = FReadlineQueue(sys.stdin)
39b97ced92 2011-06-05 167:
d823fa83dd 2012-07-07 168: class FWritelineQueue(gevent.queue.JoinableQueue):
d823fa83dd 2012-07-07 169: # storing fileno, io interface, leftover
d823fa83dd 2012-07-07 170: __slots__ = frozenset(['_fileno', '_io', '_tail'])
d823fa83dd 2012-07-07 171:
d823fa83dd 2012-07-07 172: def __init__(self, fd, closefd = True):
d823fa83dd 2012-07-07 173: import io
d823fa83dd 2012-07-07 174: # initialising class
d823fa83dd 2012-07-07 175: gevent.queue.JoinableQueue.__init__(self)
d823fa83dd 2012-07-07 176: # storing fileno
d823fa83dd 2012-07-07 177: self._fileno = fd.fileno()
d823fa83dd 2012-07-07 178: # creating interface
d823fa83dd 2012-07-07 179: self._io = io.FileIO(self._fileno, 'w', closefd)
d823fa83dd 2012-07-07 180: # using empty tail
d823fa83dd 2012-07-07 181: self._tail = None
d823fa83dd 2012-07-07 182: # putting file to nonblocking mode
d823fa83dd 2012-07-07 183: fcntl.fcntl(self._fileno, fcntl.F_SETFL, fcntl.fcntl(self._fileno, fcntl.F_GETFL) | os.O_NONBLOCK)
d823fa83dd 2012-07-07 184:
d823fa83dd 2012-07-07 185: def __del__(self):
d823fa83dd 2012-07-07 186: # purge queue before deleting
d823fa83dd 2012-07-07 187: if not self.empty():
d823fa83dd 2012-07-07 188: self.join()
d823fa83dd 2012-07-07 189:
d823fa83dd 2012-07-07 190: def put(self, item, block=True, timeout=None):
d823fa83dd 2012-07-07 191: # calling real put
d823fa83dd 2012-07-07 192: gevent.queue.JoinableQueue.put(self, item, block, timeout)
d823fa83dd 2012-07-07 193: # installing event handler
d823fa83dd 2012-07-07 194: gevent.core.write_event(self._fileno, self._wait_helper)
d823fa83dd 2012-07-07 195:
d823fa83dd 2012-07-07 196: def _wait_helper(self, ev, evtype):
d823fa83dd 2012-07-07 197: # XXX ev, evtype checking?
d823fa83dd 2012-07-07 198: # checking leftover
d823fa83dd 2012-07-07 199: while True:
d823fa83dd 2012-07-07 200: if self._tail == None:
d823fa83dd 2012-07-07 201: try:
d823fa83dd 2012-07-07 202: self._tail = str(self.get_nowait()).encode('utf-8') + '\n'
d823fa83dd 2012-07-07 203: except gevent.queue.Empty:
d823fa83dd 2012-07-07 204: self._tail = None
d823fa83dd 2012-07-07 205: return
d823fa83dd 2012-07-07 206: # writing tail
d823fa83dd 2012-07-07 207: written = self._io.write(self._tail)
d823fa83dd 2012-07-07 208: length = len(self._tail)
d823fa83dd 2012-07-07 209: if written == length:
d823fa83dd 2012-07-07 210: self._tail = None
d823fa83dd 2012-07-07 211: elif written < length:
d823fa83dd 2012-07-07 212: self._tail = self._tail[written:]
d823fa83dd 2012-07-07 213: break
d823fa83dd 2012-07-07 214: else:
d823fa83dd 2012-07-07 215: break
d823fa83dd 2012-07-07 216: # reinstalling event handler
d823fa83dd 2012-07-07 217: gevent.core.write_event(self._fileno, self._wait_helper)
d823fa83dd 2012-07-07 218:
39b97ced92 2011-06-05 219: # wrapper around database
39b97ced92 2011-06-05 220: class tagDB(object):
39b97ced92 2011-06-05 221: __slots__ = frozenset(['_cursor', '_db'])
39b97ced92 2011-06-05 222:
39b97ced92 2011-06-05 223: def __init__(self):
39b97ced92 2011-06-05 224: config.section('database')
d2c7ba18a4 2011-09-14 225: if config['host'] == None:
d2c7ba18a4 2011-09-14 226: self._db = psycopg2.connect(
d2c7ba18a4 2011-09-14 227: database = config['database'],
d2c7ba18a4 2011-09-14 228: user = config['user'],
d2c7ba18a4 2011-09-14 229: password = config['password']
d2c7ba18a4 2011-09-14 230: )
d2c7ba18a4 2011-09-14 231: else:
d2c7ba18a4 2011-09-14 232: self._db = psycopg2.connect(
d2c7ba18a4 2011-09-14 233: database = config['database'],
d2c7ba18a4 2011-09-14 234: host = config['host'],
d2c7ba18a4 2011-09-14 235: user = config['user'],
d2c7ba18a4 2011-09-14 236: password = config['password']
d2c7ba18a4 2011-09-14 237: )
39b97ced92 2011-06-05 238: self._cursor = self._db.cursor()
39b97ced92 2011-06-05 239:
39b97ced92 2011-06-05 240: def _field_names(self):
39b97ced92 2011-06-05 241: names = []
39b97ced92 2011-06-05 242: for record in self._cursor.description:
39b97ced92 2011-06-05 243: names.append(record.name)
39b97ced92 2011-06-05 244: return(names)
39b97ced92 2011-06-05 245:
39b97ced92 2011-06-05 246: def check(self, site, ip_address):
39b97ced92 2011-06-05 247: self._cursor.execute("select * from (select redirect_url, regexp from site_rule where site <@ tripdomain(%s) and netmask >>= %s order by array_length(site, 1) desc) a group by redirect_url, regexp", [site, ip_address])
39b97ced92 2011-06-05 248: return(self._cursor.fetchall())
39b97ced92 2011-06-05 249:
39b97ced92 2011-06-05 250: def dump(self):
39b97ced92 2011-06-05 251: self._cursor.execute("select untrip(site) as site, tag::text, regexp from urls order by site, tag")
39b97ced92 2011-06-05 252: return(self._field_names(), self._cursor.fetchall())
39b97ced92 2011-06-05 253:
39b97ced92 2011-06-05 254: def load(self, data):
39b97ced92 2011-06-05 255: if config.options.flush_db:
39b97ced92 2011-06-05 256: self._cursor.execute('delete from urls;')
39b97ced92 2011-06-05 257: bundle = []
39b97ced92 2011-06-05 258: for row in data:
39b97ced92 2011-06-05 259: if len(row) == 2:
39b97ced92 2011-06-05 260: bundle.append([row[0], row[1], None])
39b97ced92 2011-06-05 261: else:
39b97ced92 2011-06-05 262: bundle.append([row[0], row[1], row[2]])
39b97ced92 2011-06-05 263: self._cursor.executemany("insert into urls (site, tag, regexp) values (tripdomain(%s), %s, %s)", bundle)
39b97ced92 2011-06-05 264: self._cursor.execute("update urls set regexp = NULL where regexp = ''")
39b97ced92 2011-06-05 265: self._db.commit()
39b97ced92 2011-06-05 266:
39b97ced92 2011-06-05 267: def load_conf(self, csv_data):
39b97ced92 2011-06-05 268: self._cursor.execute('delete from rules;')
39b97ced92 2011-06-05 269: bundle = []
39b97ced92 2011-06-05 270: for row in csv_data:
39b97ced92 2011-06-05 271: bundle.append([row[0], row[1], int(row[2]), int(row[3]), row[4], row[5], row[6]])
39b97ced92 2011-06-05 272: self._cursor.executemany("insert into rules (netmask, redirect_url, from_weekday, to_weekday, from_time, to_time, tag) values (%s::text::cidr, %s, %s, %s, %s::text::time, %s::text::time, %s::text::text[])", bundle)
39b97ced92 2011-06-05 273: self._db.commit()
39b97ced92 2011-06-05 274:
39b97ced92 2011-06-05 275: def dump_conf(self):
39b97ced92 2011-06-05 276: self._cursor.execute("select netmask, redirect_url, from_weekday, to_weekday, from_time, to_time, tag::text from rules")
39b97ced92 2011-06-05 277: return(self._field_names(), self._cursor.fetchall())
39b97ced92 2011-06-05 278:
39b97ced92 2011-06-05 279: # abstract class with basic checking functionality
39b97ced92 2011-06-05 280: class Checker(object):
d823fa83dd 2012-07-07 281: __slots__ = frozenset(['_db', '_log', '_queue', '_request', '_stdout'])
39b97ced92 2011-06-05 282:
39b97ced92 2011-06-05 283: def __init__(self, queue, logger):
39b97ced92 2011-06-05 284: self._db = tagDB()
39b97ced92 2011-06-05 285: self._log = logger
d823fa83dd 2012-07-07 286: self._log.info('started')
39b97ced92 2011-06-05 287: self._request = re.compile('^([0-9]+)\ (http|ftp):\/\/([-\w.:]+)\/([^ ]*)\ ([0-9.]+)\/(-|[\w\.]+)\ (-|\w+)\ (-|GET|HEAD|POST).*$')
39b97ced92 2011-06-05 288: self._queue = queue
d823fa83dd 2012-07-07 289: self._stdout = FWritelineQueue(sys.stdout, False)
39b97ced92 2011-06-05 290:
39b97ced92 2011-06-05 291: def process(self, id, site, ip_address, url_path, line = None):
d823fa83dd 2012-07-07 292: #self._log.info('trying {}'.format(site))
39b97ced92 2011-06-05 293: result = self._db.check(site, ip_address)
39b97ced92 2011-06-05 294: reply = None
39b97ced92 2011-06-05 295: #self._log.info('got {} lines from database'.format(len(result)))
39b97ced92 2011-06-05 296: for row in result:
39b97ced92 2011-06-05 297: if row != None and row[0] != None:
39b97ced92 2011-06-05 298: if row[1] != None:
d823fa83dd 2012-07-07 299: self._log.info('trying regexp "{}" versus "{}"'.format(row[1], url_path))
39b97ced92 2011-06-05 300: try:
39b97ced92 2011-06-05 301: if re.compile(row[1]).match(url_path):
39b97ced92 2011-06-05 302: reply = row[0].format(url_path)
39b97ced92 2011-06-05 303: else:
39b97ced92 2011-06-05 304: continue
39b97ced92 2011-06-05 305: except:
39b97ced92 2011-06-05 306: self._log.info("can't compile regexp")
39b97ced92 2011-06-05 307: else:
39b97ced92 2011-06-05 308: reply = row[0].format(url_path)
39b97ced92 2011-06-05 309: if reply != None:
d823fa83dd 2012-07-07 310: self.writeline('{} {}'.format(id, reply))
39b97ced92 2011-06-05 311: return(True)
d823fa83dd 2012-07-07 312: self.writeline('{}'.format(id))
39b97ced92 2011-06-05 313:
39b97ced92 2011-06-05 314: def check(self):
39b97ced92 2011-06-05 315: while True:
39b97ced92 2011-06-05 316: line = self._queue.get()
39b97ced92 2011-06-05 317: if line == None:
39b97ced92 2011-06-05 318: break
39b97ced92 2011-06-05 319: #self._log.info('request: ' + line)
39b97ced92 2011-06-05 320: request = self._request.match(line)
39b97ced92 2011-06-05 321: if request:
39b97ced92 2011-06-05 322: id = request.group(1)
39b97ced92 2011-06-05 323: #proto = request.group(2)
39b97ced92 2011-06-05 324: site = request.group(3)
39b97ced92 2011-06-05 325: url_path = request.group(4)
39b97ced92 2011-06-05 326: ip_address = request.group(5)
39b97ced92 2011-06-05 327: self.process(id, site, ip_address, url_path, line)
39b97ced92 2011-06-05 328: else:
39b97ced92 2011-06-05 329: self._log.info('bad request\n')
d823fa83dd 2012-07-07 330: self.writeline(line)
39b97ced92 2011-06-05 331:
39b97ced92 2011-06-05 332: def writeline(self, string):
39b97ced92 2011-06-05 333: self._log.info('sending: ' + string)
d823fa83dd 2012-07-07 334: self._stdout.put(string)
39b97ced92 2011-06-05 335:
39b97ced92 2011-06-05 336: def loop(self):
39b97ced92 2011-06-05 337: pool = gevent.pool.Pool()
39b97ced92 2011-06-05 338: pool.spawn(self.check)
39b97ced92 2011-06-05 339: pool.join()
39b97ced92 2011-06-05 340:
d301d9adc6 2010-08-13 341: if config.options.dump or config.options.load or config.options.dump_conf or config.options.load_conf:
d301d9adc6 2010-08-13 342: import csv
d301d9adc6 2010-08-13 343:
d301d9adc6 2010-08-13 344: tagdb = tagDB()
bde51dc0c7 2010-08-26 345: data_fields = ['site', 'tag', 'regexp']
d301d9adc6 2010-08-13 346: conf_fields = ['netmask', 'redirect_url', 'from_weekday', 'to_weekday', 'from_time', 'to_time', 'tag']
d301d9adc6 2010-08-13 347:
d301d9adc6 2010-08-13 348: if config.options.dump or config.options.dump_conf:
0ef24b1937 2011-04-06 349: csv_writer = csv.writer(sys.stdout)
d301d9adc6 2010-08-13 350: if config.options.dump:
bde51dc0c7 2010-08-26 351: dump = tagdb.dump()
bde51dc0c7 2010-08-26 352: elif config.options.dump_conf:
bde51dc0c7 2010-08-26 353: dump = tagdb.dump_conf()
bde51dc0c7 2010-08-26 354:
0ef24b1937 2011-04-06 355: csv_writer.writerow(dump[0])
0ef24b1937 2011-04-06 356: for line in dump[1]:
0ef24b1937 2011-04-06 357: csv_writer.writerow(line)
d301d9adc6 2010-08-13 358:
d301d9adc6 2010-08-13 359: elif config.options.load or config.options.load_conf:
d301d9adc6 2010-08-13 360: csv_reader = csv.reader(sys.stdin)
d301d9adc6 2010-08-13 361: first_row = next(csv_reader)
d301d9adc6 2010-08-13 362:
d301d9adc6 2010-08-13 363: if config.options.load:
bde51dc0c7 2010-08-26 364: fields = data_fields
bde51dc0c7 2010-08-26 365: load = tagdb.load
bde51dc0c7 2010-08-26 366: elif config.options.load_conf:
bde51dc0c7 2010-08-26 367: fields = conf_fields
bde51dc0c7 2010-08-26 368: load = tagdb.load_conf
bde51dc0c7 2010-08-26 369:
bde51dc0c7 2010-08-26 370: assert first_row == fields, 'File must contain csv data with theese columns: ' + repr(fields)
bde51dc0c7 2010-08-26 371: load(csv_reader)
d301d9adc6 2010-08-13 372:
d301d9adc6 2010-08-13 373: else:
d301d9adc6 2010-08-13 374: # main loop
39b97ced92 2011-06-05 375: Checker(stdin, logger).loop()