'''
Advanced logger for sagator, version 0.4
 
(c) 2003-2004 Jan ONDREJ (SAL) <ondrejj(at)salstar.sk>

 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 2 of the License, or
 (at your option) any later version.
                                                                                
'''
                                                                                
from avlib import *
from match import match_any
from report import report
                                                                                
__all__=['log','log_mysql','log_pgsql','log_sqlite']

class log(report):
  '''
  Advanced logger interscanner.
  
  usage: log(logto,format,scanners...)
  
  where: logto is a string, which defines filename to store these data
           If logto is an integer, it defines log level in standard
           log file.
         format is a string, which defines data format

  This scanner can be used to log some special data.
  You can use these variables in format string:
    $LEVEL		- detected virus level
    $VIRNAME		- name of detected virus, empty if CLEAN
    $STATUS		- if mail is dropped, rejected, ...
    $QNAME		- quarantine file name
    $SCANNER_NAME	- scanner which reported this virus
    $SENDER		- scanned message sender
    $RECIPIENTS		- scanned message recipients
    $SIZE               - email size
    $VERSION		- sagator's version
  
  You also can use log.FORMAT for default format.
  '''
  name='log()'
  # default format
  FORMAT="level='$LEVEL', virname='$VIRNAME', size='$SIZE', sender='$SENDER', recipients='$RECIPIENTS'\n"
  def __init__(self,logto,format,*scanners):
      self.logto=logto
      self.format=format
      match_any.__init__(self,scanners)
  def quote(self,s):
      return quote(s)
  def repl_vars(self,level,detected):
      return self.replace_tmpl(self.format,{
        'LEVEL':	str(level),
        'VIRNAME':	self.quote(detected),
        'STATUS':	globals.action(level),
        'QNAME':	self.quote(globals.QFNAME),
        'SCANNER_NAME':	globals.found_by.name,
        'SENDER':	self.quote(mail.sender.strip()),
        'RECIPIENTS':	self.quote(string.join(mail.recip,',')),
        'SIZE':		str(len(mail.data)),
        'VERSION':	SG_VER_REL
      })
  def scanstream(self,stream):
      level,detected,virlist=match_any.scanstream(self,stream)
      if type(self.logto)==type(1):
        debug.echo(self.logto,self.repl_vars(level,detected).strip())
      else:
        open(self.logto,'a').write(self.repl_vars(level,detected))
      return level,detected,virlist

class _log_sql(log):
  '''
  SQL interscanner for python DB-API 2.0 compatible DB modules.
  
  This scanner is used as parent for various SQL based log_scanners.
  '''
  FORMAT="INSERT INTO sagator " \
    +"(datetime,level,virname,status,qname,sender,recipients,size)" \
    +" VALUES " \
    +"(now(),'$LEVEL','$VIRNAME','$STATUS','$QNAME','$SENDER','$RECIPIENTS','$SIZE');"
  def __init__(self,sqldb,format,*scanners):
      self.sqldb=sqldb
      self.format=format
      self.dbc=None
      self.connect()
      match_any.__init__(self,scanners)
  def __del__(self):
      try:
        self.dbc.commit()
      except:
        pass
  def quote(self,s):
      return s.replace("'","''").replace('\\','\\\\')
  def connect(self):
      raise 'You need to define connect() for SQL logger!'
  def scanstream(self,stream):
      level,detected,virlist=match_any.scanstream(self,stream)
      for TRY in [1,2]:
        try:
          self.connect()
          cur=self.dbc.cursor()
          insert=self.repl_vars(level,detected)
          debug.echo(5,"log_sql(): ",str([insert]))
          cur.execute(insert)
          self.dbc.commit()
        except:
          if TRY==1:
            debug.echo(3,"log_sql(): WARNING: Problem logging into database!")
            debug.traceback(5,"log_sql()")
          else:
            debug.echo(1,"log_sql(): ERROR: Error logging into database!")
            debug.traceback(4,"log_sql()")
          # next connect will be created on next email
          self.dbc=None
      return level,detected,virlist

class log_mysql(_log_sql):
  '''
  MySQL logger interscanner.
  
  usage: log_mysql(host, port, dbname,dbuser,dbpass,format,scanners...)
  
  where: host is a string, which defines MySQL database hostname
           (you can use "127.0.0.1", UNIX socket can't be used in chroot)
         port is an integer, which defines MySQL database TCP port number
           (it is by default 3306)
         dbuser is a string, which defines username which can log to DB
         dbname is a string, which defines database name
         dbpass is a string, which defines user password
         format is a string, which defines data format.
           For detailed format description see log() scanner documentation.
           You also can use log.FORMAT for default format.
  '''
  def __init__(self,host,port,dbname,dbuser,dbpasswd,format,*scanners):
      try:
        import MySQLdb
      except ImportError,es:
        sys.exit("Error importing MySQLdb module for python: "+str(es))
      self.dbhost=host
      self.dbport=port
      self.dbuser=dbuser
      self.dbname=dbname
      if dbpasswd:
        self.dbpasswd=dbpasswd
      else:
        self.dbpasswd=''
      _log_sql.__init__(self,MySQLdb,format,scanners)
  def connect(self):
      if not self.dbc:
        # make new connection to database
        debug.echo(7,"log_mysql: Connecting to DB...")
        self.dbc=self.sqldb.connect(
          host=self.dbhost,
          user=self.dbuser,
          db=self.dbname,
          port=self.dbport,
          passwd=self.dbpasswd,
          connect_timeout=5
        )

class log_pgsql(_log_sql):
  '''
  PosgreSQL logger interscanner.
  
  usage: log_pgsql(host, port, dbname,dbuser,dbpass,format,scanners...)
  
  where: host is a string, which defines pgsql database hostname
           (you can use "localhost", UNIX socket can't be used in chroot)
         port is an integer, which defines pgsql database TCP port number
           (it is by default 5432)
         dbuser is a string, which defines username which can log to DB
         dbname is a string, which defines database name
         dbpass is a string, which defines user password
         format is a string, which defines data format.
           For detailed format description see log() scanner documentation.
           You also can use log.FORMAT for default format.
  '''
  FORMAT="INSERT INTO sagator VALUES ( now(), '$LEVEL', '$VIRNAME', '$STATUS', '$QNAME', '$SENDER', '$RECIPIENTS', '$SIZE' )"
  def __init__(self,host,port,dbname,dbuser,dbpasswd,format,*scanners):
      try:
        import pg
      except ImportError,es:
        sys.exit("Error importing PostgreSQL module for python: "+str(es))
      self.dbhost=host
      self.dbport=port
      self.dbuser=dbuser
      self.dbname=dbname
      self.dbpasswd=dbpasswd
      _log_sql.__init__(self,pg,format,scanners)
  def connect(self):
      if not self.dbc:
        # make new connection to database
        self.dbc=self.sqldb.connect(
          dbname=self.dbname,
          host=self.dbhost,
          port=self.dbport,
          user=self.dbuser,
          passwd=self.dbpasswd
        )

class log_sqlite(_log_sql):
  '''
  SQLite logger interscanner.
  
  usage: log_sqlite(dbname,format,scanners...)
  
  where: dbname is a string, which defines, which database to use
         format is a string, which defines data format.
           For detailed format description see log() scanner documentation.
           You also can use log.FORMAT for default format.
  '''
  def __init__(self,dbname,format,*scanners):
      try:
        import sqlite
      except ImportError,es:
        sys.exit("Error importing SQLite module for python: "+str(es))
      self.dbname=dbname
      _log_sql.__init__(self,sqlite,re.sub("now\(\)","0",format),scanners)
  def connect(self):
      if not self.dbc:
        self.dbc=self.sqldb.connect(safe.fn(self.dbname))
