'''
Basic interscanners for sagator, version 0.6.1

(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 *
import string

__all__=['match_all','alternatives','match_any','recover','nothing']

#####################################################################
### match classes

def get_only_scanners(scanners):
    ret=[]
    for scanner in scanners:
      if type(scanner) in [type(()),type([])]:
        ret.extend(get_only_scanners(scanner))
      else:
        try:
          if scanner.is_scanner:
            ret.append(scanner)
        except:
          pass
    return ret

class match_class(interscanner):
  '''parent class for all match clases'''
  def __init__(self,*scanners):
      self.scanners=get_only_scanners(scanners)
      self.rename(self.scanners)
  def destroy(self):
      for scnr in self.scanners:
        scnr.destroy()
  def param(self,p):
      n=0
      for scnr in self.scanners:
        n+=scnr.param(p)
      return n
  def reinit(self):
      for scnr in self.scanners:
        scnr.reinit()
  def get(self,var):
      try:
        r=eval("self."+var)
        if not r:
          raise AttributeError
      except AttributeError:
        for scnr in self.scanners:
          try:
            r+=scnr.get(var)
          except NameError:
            r=scnr.get(var)
          except AttributeError:
            r=''
      return r

class match_all(match_class):
  '''
  match_all() interscanner
  
  Usage: match_all(scanner1(),scanner2(),...)
  
  Where: scanner1(),... are scanners, which must find a virus
  
  Returns a virus only if all scanners returns a virus.
  '''
  name='match_all()'
  def scanstream(self,stream):
      level,vir,ret=0.0,'',[]
      for self.scanner in self.scanners:
        self.scanner.prescan()
        l,v,r=self.scanner.scanstream(stream)
        self.scanner.postscan(l,v,r)
        if v:
          if vir:
            vir+=','+v
          else:
            vir=v
          ret+=r
          level+=l
        else:
          return 0.0,'',[]
      return level,vir,ret

class alternatives(match_class):
  '''
  alternatives() interscanner
  
  Usage: alternatives(scanner1(),scanner2(),...)
  
  Where: scanner1(),... are scanners, which may find a virus
  
  Returns a virus, if one scanner returns a virus.
  Scannig is ended if one scanner returns virus or clean.
  Next scanner from the list is used, if previous scannes fails
  (raises an error). Raises an ScannerError, if all scanners failed.
  '''
  name='alternatives()'
  def scanstream(self,stream):
      level,vir,ret=0.0,'',[]
      for self.scanner in self.scanners:
        try:
          self.scanner.prescan()
          level,vir,ret=self.scanner.scanstream(stream)
          self.scanner.postscan(level,vir,ret)
          return level,vir,ret
        except:
          debug.echo(3,self.name,": scanner ",self.scanner.name," failed: ",
                string.rstrip(debug.traceback_value_str()))
      debug.echo(3,self.name,": All scanners failed")
      raise ScannerError,''

class match_any(match_class):
  '''
  match_any() interscanner
  
  Usage: match_any(scanner1(),scanner2(),...)
  
  Where: scanner1(),... are scanners, which may find a virus
  
  Returns a virus, if one scanner returns a virus.
  Next scanner from list is used, if previous scanner returns
  no virus, or failed (raised an error).
  '''
  name='match_any()'
  def scanstream(self,stream):
      level,vir,ret=0.0,'',[]
      for self.scanner in self.scanners:
        try:
          self.scanner.prescan()
          level,vir,ret=self.scanner.scanstream(stream)
          self.scanner.postscan(level,vir,ret)
          if is_infected(level):
            return level,vir,ret
        except:
          debug.echo(3,self.name,": scanner ",self.scanner.name," failed: ",
                string.rstrip(debug.traceback_value_str()))
          raise
      return 0.0,'',[]

class recover(match_any):
  '''
  recover() interscanner
  
  Usage: recover(scanner1(),scanner2(),...)
  
  Where: scanner1... are scanners
  
  This scanner can be used to recover from scanner fail. If an scanner
  fails (raises an error), this scanner return no virus.
  '''
  name='recover()'
  def scanstream(self,stream):
      level,vir,ret=0.0,'',[]
      for self.scanner in self.scanners:
        try:
          self.scanner.prescan()
          level,vir,ret=self.scanner.scanstream(stream)
          self.scanner.postscan(level,vir,ret)
          if is_infected(level):
            return level,vir,ret
        except:
          debug.echo(3,self.name,": scanner ",self.scanner.name," failed: ",
                string.rstrip(debug.traceback_value_str()))
      return 0.0,'',[]

class nothing(match_any):
  '''
  nothing interscanner
  
  Usage: nothing([some parameters...])
  
  Where: [some parameters...] are ignored :)
  
  This scanner does nothing. :-)
  '''
  name='N()'
  def scanstream(self,stream):
      return match_any.scanstream(self,stream)
