'''
Filesystem scanners for sagator.

(c) 2003-2007 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 *

__all__=['filesys', 'dazuko']

class filesys(ascanner):
  '''
  Scanner which uses filesystem scanners.
  
  Usage: filesys(prefix='/tmp/fs',logfile='',regexp='')

  Where: '/prefix' is a string, which defines directory
            and a part of filename to store scanned files,
            by default '/tmp/fs'
         'logfile' is a string, which defines full path and filename
            of logfile, by default not logfile used
         'regexp' is a string, which defines how to parse logfile

  New in version 0.7.0.
  '''
  name='filesys()'
  req_lines=1
  def __init__(self,prefix='/tmp/fs',logfile='',regexp=''):
      self.prefix=prefix
      self.logfile=logfile
      self.reg=re.compile(regexp,re.IGNORECASE)
      self.virname_to_return_value={}
      self.hdr=fromhdr()
  def scanbuffer(self,buffer,args={}):
      level=0.0
      detected=''
      virlist=[]
      # save email
      t=mktemp(self.prefix, '.eml', 'w', 0600).autorm()
      # open logfile
      log=None
      if self.logfile:
        try:
          log=open(self.logfile, 'r')
          log.seek(0, 2) # end of file
        except IOError:
          pass
      try:
        os.write(t.fd, self.hdr+buffer)
        t.f.close()
        ts=os.open(t.root_name, os.O_RDONLY) # OSError 13 raised when virus found
        if os.fstat(ts)[6]!=len(self.hdr+buffer):
          level,detected,virlist=self.get_vir_info(
            log,(t.root_name,t.name),
            13,'File truncated %d:%d' % (os.fstat(ts)[6],len(self.hdr+buffer)))
        os.close(ts)
      except OSError,(ec,es):
        debug.traceback(9, 'filesys(): ')
        if ec==13: # permission denied
          level,detected,virlist = self.get_vir_info(
            log,(t.root_name,t.name),ec,es)
        else:
          raise
      # close log
      if log:
        log.close()
      return level,detected,virlist
  def scanfile(self,files,dirname='',args={}):
      # open logfile
      if self.logfile:
        log=open(self.logfile,'r')
        log.seek(0,2) # end of file
      else:
        log=None
      for fname in files:
        try:
          ts=safe.osopen(fname,os.O_RDONLY)
          os.close(ts)
        except OSError,(ec,es):
          if ec==13: # permission denied
            return self.get_vir_info(log,(safe.fn(fname),fname),ec,es)
          else:
            raise
      return 0.0,'',[]
  def shortname(self,filename):
      return filename
  def get_vir_info(self,log,filenames,ec,es):
      level=0.0
      virlist=[]
      debug.echo(7,"%s: %d: %s" % (self.name,ec,es))
      if ec==13:
        detected,info='',''
        level+=1.0
        if log:
          req_lines = self.req_lines
          l='x'
          while (req_lines>0) and l:
            l=log.readline()
            reg1=self.reg.search(l)
            if reg1:
              try:
                req_lines+=int(reg1.group('lines'))
              except:
                pass
              req_lines-=1
              if self.shortname(reg1.group('filename')) in filenames:
                if not detected:
                  detected=reg1.group('virus')
                if not info:
                  info=reg1.group('info')
                virlist.append(l)
                debug.echo(6,"A[%d]: %s: %s" % (req_lines,self.name,l.strip()))
                continue
            debug.echo(9,"I[%d]: %s: %s" % (req_lines,self.name,l.strip()))
        if detected in self.virname_to_return_value.keys():
          level,detected=self.virname_to_return_value[detected]
        if (not detected) and info:
          return level/2.0,info,virlist
        if not virlist:
          raise ScannerError,"Can't decode virname."
        return level,detected or 'UNKNOWN',virlist
      else:
        raise

class dazuko(ascanner):
  '''
  Scanner which uses dazuko directory to scan for viruses.
  
  Usage: dazuko('/dazuko_prefix','logfile')

  Where: '/dazuko_prefix' is a string, which defines directory
            and a part of filename to store scanned files,
            by default '/tmp/dazuko'
         'logfile' is a string, which defines full path and filename
            of logfile, by default not logfile used
         'regexp' is a string, which defines how to parse logfile
  
  Obsolete since 0.7.0, use filesys() instead.
  '''
  name='dazuko()'
  def __init__(self,dazuko_prefix,logfile='',regexp=''):
      self.prefix=dazuko_prefix
      self.logfile=logfile
      self.reg=re.compile(regexp,re.IGNORECASE)
  def scanbuffer(self,buffer,args={}):
      level=0.0
      detected=''
      virlist=[]
      # open logfile
      if self.logfile:
        log=open(safe.fn(self.logfile),'r')
        log.seek(0,2) # end of file
      # save email
      t=mktemp(self.prefix,'.eml','w',0600).autorm()
      try:
        t.f.write(fromhdr())
        t.f.write(buffer)
        t.f.flush()
        open(t.root_name,'r') # IOError1 raised when virus found
      except IOError,(ec,es):
        if ec==1:
          detected='UNKNOWN'
          level+=1.0
          if log:
            l=1
            while l:
              l=log.readline()
              reg1=self.reg.search(l)
              if reg1:
                if reg1.group(1) in [t.root_name,t.name]:
                  detected=reg1.group(2)
                  virlist.append(l)
                  debug.echo(6,"dazuko(): ",l)
        else:
          raise
      # close log
      if self.logfile:
        log.close()
      return level,detected,virlist
