'''
Antivir library for Sagator

(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.
    
'''

import os,sys,re,string,time,random
import traceback,signal,resource,struct,socket
from cStringIO import StringIO
import version

SG_VER_REL=version.VERSION+'-'+version.RELEASE
BUFSIZE=10240
allowed_chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-'

False=0
True=1

#########################################################################
### Debuging support

class debug_class:
  '''
  Debug class. Used for debugging informations.
  '''
  debug_level=2
  trace_level=9
  chroot=''
  def __init__(self):
      self.set_logfile('-')
  def dup(self):
      if self.fd>2:
        os.dup2(self.fd,1)
        os.dup2(self.fd,2)
  def set_logfile(self,logfile):
      self.logfile=logfile
      if logfile!='-':
        try:
          self.fd=os.open(logfile,os.O_CREAT|os.O_WRONLY|os.O_APPEND,0640)
        except OSError,(ec,es):
          debug.echo(0,"Error, can't open logfile: ",logfile,": ",es) 
        self.logfd=os.fdopen(self.fd,'a',0)
        #os.chmod(logfile,0640)
      else:
        self.logfd=os.fdopen(os.dup(1),'w',0)
        self.fd=1
      sys.stdout=self.logfd
      sys.stderr=self.logfd
  def reopen(self):
      if (self.logfile!='-') and (self.logfile[0:len(self.chroot)]==self.chroot):
        lfn=self.logfile[len(self.chroot):]
        if lfn[0]!="/":
          lfn="/"+lfn
        try:
          fd=os.open(lfn,os.O_CREAT|os.O_WRONLY|os.O_APPEND,0640)
          self.logfd=os.fdopen(self.fd,'a',0)
          os.dup2(fd,self.fd)
          os.close(fd)
          self.dup()
        except:
          debug.echo(0,"Warning, log is not reopened! (permission denied?)")
      else:
        debug.echo(0,"Error: logrotation not possible, log isn't in chroot")
  def set_level(self,level):
      self.debug_level=level
  def echo(self,level,*s):
      if self.debug_level>=level:
        o=''
        for i in s:
          if type(i)==type([]):
            for x in i:
              o+=str(x)
          else:
            o+=str(i)
        p=os.getpid()
        while 1:
          try:
            self.logfd.write("%5d: %s\n" % (p,o))
            break
          except IOError,(ec,es):
            if ec!=4:
              break
          except UnicodeEncodeError:
            o=re.sub('[^\x00-\x7F]','_',o) # leave only ascii chars
            break
  def stack(self,level,arg):
      self.echo(level,arg,traceback.format_stack())
  def traceback(self,level,arg):
      self.echo(level,arg,traceback.format_exception(sys.exc_type,
                       sys.exc_value, sys.exc_traceback))
  def traceback_value_str(self):
      return string.join(traceback.format_exception_only(
                             sys.exc_type, sys.exc_value),', ')

debug=debug_class()

#########################################################################
### Useful functions

def is_infected(level,detected=''):
    if level>=1.0:
      return True
    else:
      return False

class safe_class:
  ROOT_PATH='/'
  def fn(self,f):
      if f[0]=='/':
        # f.lstrip('/')
        while f[0]=='/':
          f=f[1:]
        return os.path.join(self.ROOT_PATH,f)
      else:
        return f
  def open(self,name,mode='',buffering=-1):
      if buffering>=0:
        return open(self.fn(name),mode,buffering)
      if mode:
        return open(self.fn(name),mode)
      return open(self.fn(name))
  def osopen(self,filename,flag,mode=0777):
      return os.open(self.fn(filename),flag,mode)

safe=safe_class()

def normalize_filename(sfn):
    '''replace !allowed_chars from filename with _'''
    ofn=''
    for chp in range(len(sfn)):
      if not sfn[chp] in allowed_chars:
        ofn=ofn+'_'
      else:
        ofn=ofn+sfn[chp]
    return ofn

def quote(string):
    return re.sub("([\\\\'])","\\\\\\1",string)

class popen:
  '''
  POpen implementation (with some changes)
  '''
  def __init__(self, cmd, wd='', resource_limits={}):
      self.exitstatus,self.killsignal,self.execerror,self.pid=-1,-1,'',0
      self.pcr,self.pcw = os.pipe()
      self.cpr,self.cpw = os.pipe()
      self.oldsigchld=signal.getsignal(signal.SIGCHLD)
      signal.signal(signal.SIGUSR1,self.sighandler)
      signal.signal(signal.SIGCHLD,self.sighandler)
      self.pid = os.fork()
      if self.pid == 0: # child
        signal.signal(signal.SIGUSR1,signal.SIG_DFL)
        signal.signal(signal.SIGCHLD,signal.SIG_DFL)
        os.close(self.pcw)
        os.close(self.cpr)
        os.dup2(self.pcr,0); os.close(self.pcr)
        os.dup2(self.cpw,1)
        os.dup2(self.cpw,2); os.close(self.cpw)
        try:
          if wd:
            os.chdir(wd)
          for key,value in resource_limits.items():
            resource.setrlimit(key,(value,value))
          debug.echo(4,"POpen: Running: ",str(cmd))
          os.execvp(cmd[0],cmd)
        except os.error,(ec,es):
          debug.echo(3,"POpen: os.error: ",es+str([cmd[0]]))
          os.write(1,es+str([cmd[0]]))
        os._exit(10)
      os.close(self.pcr)
      os.close(self.cpw)
      self.tocmd=os.fdopen(self.pcw, 'w', 0)
      self.fromcmd=os.fdopen(self.cpr, 'r', 0)
  def sighandler(self,sn,stack):
      if sn==signal.SIGUSR1:
        self.execerror=os.read(self.cpr,1024)
      else:
        self.wait()
        debug.echo(4,"POpen SIG=",sn,", pid=",self.pid,
              ", exitstatus=",self.exitstatus,
              ", signal=",self.killsignal)
  def wait(self):
      try:
        pid,es=os.waitpid(self.pid,0)
        self.exitstatus=es/256
        self.killsignal=es%256
      except OSError:
        if self.exitstatus<0:
          self.exitstatus=0 # pid already exited, but no exit status
      signal.signal(signal.SIGCHLD,self.oldsigchld)
      return self.exitstatus
  def readlines(self):
      try:
        return self.fromcmd.readlines()
      except IOError,(ec,es):
        if ec==4: #Interrupted system call
          return []
        else:
          raise
  def readline(self):
      try:
        return self.fromcmd.readline()
      except IOError,(ec,es):
        if ec==4: #Interrupted system call
          return []
        else:
          raise
  def write(self,s):
      if self.exitstatus<0:
        return self.tocmd.write(s)
      else:
        return 0
  def close(self,rw=3):
      if rw&1:
        self.tocmd.flush()
        os.close(self.pcw)
      if rw&2:
        os.close(self.cpr)

def rlistdir(rootpath,path=''):
    '''
    list files and directories recursively
    
    return values: [filelist],size
      [filelist] is an array of file and directory names
      size is added filesizes
    '''
    # decode to plain string from unicode
    rootpath=str(rootpath)
    path=str(path)
    files,fsize=[],0L
    try:
      listdir=os.listdir(safe.fn(os.path.join(rootpath,path)))
    except:
      return [],0L
    for d in listdir:
      stat=os.lstat(safe.fn(os.path.join(rootpath,path,d)))
      if stat[0] & 0x4000: # is_dir
        rld=rlistdir(rootpath,os.path.join(path,d))
        files=files+rld[0]
        fsize=fsize+rld[1]
      else: # is_file or is_symlink or ...
        fsize=fsize+stat[6]
        files.append(os.path.join(path,d))
    return files,fsize

def randomchars(count=10,chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
    '''
    Return count random characters. Characters are randomly selected
    from chars array.
    '''
    s=''
    r=random.Random()
    for _ in range(count):
      s=s+chars[int(r.random()*len(chars))]
    return s

class mktemp:
      '''
      make a temporary file or directory
      Makeing of file:
        mktemp('filename-prefix','.suffix','w',0600).autorm()
      Makeinf od directory:
        mktemp('filename-prefix','.suffix','d',0600).autorm()
      '''
      def __init__(self,prefix,suffix='',flags='w',umode=-1):
          self.flag_autorm=0
          self.flags=flags
          while 1:
            s=randomchars()
            self.name=prefix+s+suffix
            self.name_no_prefix=s+suffix
            self.root_name=safe.fn(self.name)
            debug.echo(7,"mktemp: ",self.root_name)
            try:
              if flags=='d':
                if umode<0:
                  umode=0700
                os.mkdir(self.root_name)
                os.chmod(self.root_name,umode)
                if (os.stat(self.root_name)[0] & 0170000)==040000:
                  return
              else:
                if umode<0:
                  umode=0600
                self.fd=os.open(self.root_name,os.O_WRONLY|os.O_CREAT|os.O_EXCL,umode)
                self.f=os.fdopen(self.fd,'w',BUFSIZE)
                if (os.stat(self.root_name)[0] & 0170000)==0100000:
                  return
            except OSError,(ec,es):
              if not (ec in [17,21]):
                if ec in [2,13]:
                  debug.echo(1,"ERROR: Can't make temp file: ",self.root_name,": ",es)
                raise
      def __del__(self):
          if self.flag_autorm:
            self.rm()
      def rm(self):
          if self.flags=='d':
            os.rmdir(self.name)
          else:
            try:
              self.f.close()
            except:
              pass
            os.unlink(self.root_name)
      def autorm(self):
          self.flag_autorm=1
          return self

def tempcleanup(filename,s=''):
    '''
    Safe cleanup for a file.
    '''
    try:
      os.unlink(safe.fn(filename))
    except OSError,(er,es):
      debug.echo(0,s+": ERROR: OSError: ",es,filename)

def rdel(paths):
    '''
    deletes directory recursively
      paths is an array of directories or files
    '''
    for dirname in paths:
      try:
        if os.lstat(safe.fn(dirname))[0] & 0x4000: # is_dir
          for fname in os.listdir(safe.fn(dirname)):
            rdel([os.path.join(dirname,fname)])
          os.rmdir(safe.fn(dirname))
        else: # is fname or symlink
          os.unlink(safe.fn(dirname))
      except OSError,(ec,es):
        if not ec in [2]:
          debug.echo(3,"rdel: ",debug.traceback_value_str())

def fromhdr():
    '''
    Return a "From local@... DATE" string. String is by RFC.
    '''
    return "From local@local.local"+\
      time.strftime("  %c\r\n",time.localtime(time.time()))

def socket_settimeout(sock,seconds):
    '''
    Set a timeout for a socket. Timeout is in seconds.
    '''
    try:
      # try to use settimeout
      sock.settimeout(seconds)
    except AttributeError:
      # use this for python < 2.3
      sock.setsockopt(socket.SOL_SOCKET,socket.SO_RCVTIMEO,
        struct.pack('ll',seconds,0))

#########################################################################
### SMTP and mail classes

S_OK='SENT'
S_TEMPFAIL='TEMP-FAILED'
S_REJECT='REJECTED'
S_DROP='DROPPED'
S_FORCE_SEND='SEND-FORCED'

RECV_SMTP=0
RECV_HEADER=1 # obsolete
RECV_BODY=2
RECV_QUIT=3 # waiting for quit

class mail_class:
  '''
  EMail structure with some functions.
  '''
  reg_header=re.compile('^[!-9;-~]+:').search
  reg_header_cont=re.compile('^[ \t]+').search
  def __init__(self):
      self.addr='' # Connection address
      self.comm='' # SMTP communication
      self.header='' # Email Header
      self.data='' # Email Data (with header too)
      self.bodypos=0 # Body position
      self.xheader='' # Extended header
      self.xhdra={} # Extended header array
      self.recip=[] # recipient emails
      self.sender='' # sender
      self.df=StringIO()
  def close(self):
      self.df.flush()
      mail.data=self.df.getvalue()
      # check for header
      self.df.seek(0)
      lineno=0
      mail.header=''
      while True:
        lineno+=1
        l=self.df.readline()
        if (not l) | (l=='\r\n') | (l=='\n'):
          break
        mail.header+=l
        if self.reg_header(l):
          continue
        if lineno>1:
          if self.reg_header_cont(l):
            continue
      self.df.close()
      self.bodypos=len(mail.header)
  def normalize_header(self,lines,value=None):
      ''' normalize to <80 chars per line '''
      if value!=None:
        lines+=': '+value+'\r\n'
      SPACE8 = ' '*8
      MAXLINELEN2=78
      MAXLINELEN=MAXLINELEN2-len(SPACE8)
      sublines_semi=[]
      for line in lines.splitlines():
        if len(line.replace("\t", SPACE8)) <= MAXLINELEN2:
          sublines_semi.append(line.rstrip())
        else:
          # split line by semicolons
          while len(line) > MAXLINELEN:
            i=line.rfind(';', 0, MAXLINELEN)
            if i<0:
              break
            sublines_semi.append(line[:i+1].lstrip())
            line=line[i+1:]
          sublines_semi.append(line.lstrip())
      # now split sublines by spaces
      sublines_space=[]
      for line in sublines_semi:
        if len(line.replace("\t", SPACE8)) <= MAXLINELEN2:
          sublines_space.append(line)
        else:
          while len(line) > MAXLINELEN:
            i=line.rfind(' ', 0, MAXLINELEN)
            if i<0:
              break
            sublines_space.append(line[:i])
            line=line[i+1:]
          sublines_space.append(line)
      # and now join it
      sublines=''
      current=sublines_space.pop(0)
      while sublines_space:
        line=sublines_space.pop(0)
        nline=current+' '+line
        if len(nline.replace("\t", SPACE8)) <= MAXLINELEN2:
          current=nline
        else:
          sublines=sublines+current+"\r\n"
          current="\t"+line
      sublines=sublines+current+"\r\n"
      return sublines
  def addheader(self,desc,value=None):
      if value==None:
        hdr=desc
      else:
        hdr=desc+': '+value+'\r\n'
        self.xhdra[desc]=value
      self.xheader+=self.normalize_header(hdr)
  def modheader(self,frm,to):
      '''
      Modifies header by regular expression.
      Returns number of modifications (max. 1).
      '''
      h=re.compile(frm,re.MULTILINE).subn(to,self.header,1)
      if h[1]>0:
        self.header=h[0]
      return h[1]
  def hdrlen(self):
      return len(self.header)
  def addvirusinfo(self,detected,scannames=''):
      if not "X-Sagator-Scanner" in self.xhdra.keys():
        self.addheader("X-Sagator-Scanner",
          SG_VER_REL+' at '+socket.gethostname()+'; '+scannames)
      if not "X-Sagator-ID" in self.xhdra.keys():
        if globals.id:
          self.addheader("X-Sagator-id", globals.id)
      if detected:
        if not "X-Sagator-Status" in self.xhdra.keys():
          mail.addheader("X-Sagator-Status",
                         "%s [%s]" % (detected,globals.found_by.name))
  def getnamebyaddr(self,ip):
      try:
        return socket.gethostbyaddr(ip)[0]
      except:
        return ''

# mail object
mail=mail_class()

class SmtpcError(Exception):
  """SMTP Client Error"""
  ConnectError='ConnectError'
  WrongReturnCode='WrongReturnCode'
  SendmailError='SendmailError'

class smtp:
  '''
  Basic SMTP class. All SMTP clases are descended from this class.
  '''
  f=False
  bufsize=BUFSIZE
  SMTP_SERVER=('127.0.0.1',25)

class smtpc(smtp):
  '''
  SMTP client class.
  '''
  def __init__(self):
    try:
      self.conn=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      socket_settimeout(self.conn,120)
      self.conn.connect(self.SMTP_SERVER)
    except socket.error,(ec,es):
      debug.echo(0,"SMTPC: ERROR: connect: ",es)
      raise SmtpcError,(SmtpcError.ConnectError,"451 SMTP connection refused\r\n")
    self.f=self.conn.makefile('rw',self.bufsize)
  def __del__(self):
    self.close()
  def send(self,data,rc='...'):
    if data!='':
      debug.echo(5,"SMTPC>: ",data.rstrip())
      self.conn.sendall(data+"\r\n")
    ret=''
    while 1:
      r=self.readline()
      ret=ret+r
      if re.search("^[0-9][0-9][0-9] ",r):
        break
    debug.echo(5,"SMTPC<: ",ret.rstrip())
    if not re.search("^"+rc+" ",ret):
      raise SmtpcError,(SmtpcError.WrongReturnCode,ret.rstrip()+"!="+rc)
    return ret
  def readline(self):
    return self.f.readline()
  def close(self):
    try:
      self.f.close()
      self.conn.shutdown(2)
      self.conn.close()
    except:
      pass
  def sendmail(self,sender,recipients,ldata=''):
    ret="451 SMTP connection error\r\n"
    try:
      self.send('','220')
      self.send("HELO "+socket.gethostname(),'250')
      self.send("MAIL FROM:"+sender,'250')
      for rec in recipients:
        self.send("RCPT TO:"+rec,'250')
      self.send("DATA",'354')
      self.conn.sendall(ldata)
      debug.echo(5,"SMTPC>: DATA SENT")
      ret=self.send(".","250")
    except SmtpcError.WrongReturnCode,ret:
      raise SmtpcError,(SmtpcError.SendmailError,ret)
    try:
      self.send("QUIT","221")
      self.close()
    except SmtpcError.WrongReturnCode,ret:
      debug.echo(5,"SMTPC: quit error",ret)
    return ret

#########################################################################
### Scanner templates

class ScannerError(Exception):
      """Any Error Of Any Scanner"""

class scanoper:
  '''
  A scanner used to operate over various scanners.
  '''
  name='op()'
  is_scanner=True
  is_multistream=False
  is_interscanner=True
  ignore_name=True
  filename='stream'
  def __init__(self,op,a,b=None):
      self._oper=(op,a,b)
      if b:
        self.name=a.name+op+b.name
      else:
        self.name=a.name
      self.scanner=a
  def _eval(self,l1,d1,v1,l2,d2,v2):
      op=self._oper[0]
      if op=='+':
        return l1+l2,d1+','+d2,v1+v2
      elif op=='-':
        return l1-l2,d1+','+d2,v1+v2
      elif op=='*':
        return l1*l2,d1+','+d2,v1+v2
      elif op=='/':
        return l1/l2,d1+','+d2,v1+v2
      else:
        raise ScannerError,"Unknown operator: "+self._oper[0]
  def scanstream(self,stream):
      l,d,v={},{},{}
      if self._oper[0] in '+-*/':
        for i in [1,2]:
          self._oper[i].prescan()
          l[i],d[i],v[i]=self._oper[i].scanstream(stream)
          self._oper[i].postscan(l[i],d[i],v[i])
        return self._eval(l[1],d[1],v[1],l[2],d[2],v[2])
      elif self._oper[0]=='|':
        try:
          self._oper[1].prescan()
          l,d,v=self._oper[1].scanstream(stream)
          self._oper[1].postscan(l,d,v)
          return l,d,v
        except:
          self._oper[2].prescan()
          l,d,v=self._oper[2].scanstream(stream)
          self._oper[2].postscan(l,d,v)
          return l,d,v
      elif self._oper[0]=='&':
        self._oper[1].prescan()
        l1,d1,v1=self._oper[1].scanstream(stream)
        self._oper[1].postscan(l1,d1,v1)
        if is_infected(l1):
          self._oper[2].prescan()
          l2,d2,v2=self._oper[2].scanstream(stream)
          self._oper[2].postscan(l2,d2,v2)
          return l1*l2,d1+'*'+d2,v1+v2
        else:
          return 0.0,'',[]
      elif self._oper[0]=='~':
        try:
          self._oper[1].prescan()
          l,d,v=self._oper[1].scanstream(stream)
        except:
          debug.echo(4,"Scanner %s failed, recovering..." % self._oper[1].name)
          if self._oper[1].is_interscanner:
            l,d,v=self._oper[1].get('child_status')
          else:
            return 0.0,'',[]
        self._oper[1].postscan(l,d,v)
        return l,d,v
      else:
        raise ScannerError,"Unknown operator: "+self._oper[0]
  def scanfile(self,files,dirname=''):
      l,d,v={},{},{}
      if self._oper[0] in '+-*/':
        for i in [1,2]:
          self._oper[i].prescan()
          l[i],d[i],v[i]=self._oper[i].scanfile(files,dirname)
          self._oper[i].postscan(l[i],d[i],v[i])
        return self._eval(l[1],d[1],v[1],l[2],d[2],v[2])
      elif self._oper[0]=='|':
        try:
          self._oper[1].prescan()
          l,d,v=self._oper[1].scanfile(files,dirname)
          self._oper[1].postscan(l,d,v)
          return l,d,v
        except:
          self._oper[2].prescan()
          l,d,v=self._oper[2].scanfile(files,dirname)
          self._oper[2].postscan(l,d,v)
          return l,d,v
      elif self._oper[0]=='&':
        self._oper[1].prescan()
        l1,d1,v1=self._oper[1].scanfile(files,dirname)
        self._oper[1].postscan(l1,d1,v1)
        if is_infected(l1):
          self._oper[2].prescan()
          l2,d2,v2=self._oper[2].scanfile(files,dirname)
          self._oper[2].postscan(l2,d2,v2)
          return l1*l2,d1+'*'+d2,v1+v2
        else:
          return 0.0,'',[]
      elif self._oper[0]=='~':
        try:
          self._oper[1].prescan()
          l,d,v=self._oper[1].scanfile(files,dirname)
        except:
          debug.echo(4,"Scanner %s failed, recovering..." % self._oper[1].name)
          if self._oper[1].is_interscanner:
            l,d,v=self._oper[1].get('child_status')
          else:
            return 0.0,'',[]
        self._oper[1].postscan(l,d,v)
        return l,d,v
      else:
        raise ScannerError,"Unknown operator: "+self._oper[0]
  def prescan(self):
      pass
  def postscan(self,level,vir,ret):
      pass
  def destroy(self):
      self._oper[1].destroy()
      if self._oper[2]:
        self._oper[2].destroy()
  def reinit(self):
      self._oper[1].reinit()
      if self._oper[2]:
        self._oper[2].reinit()
  def get(self,var):
      try:
        return eval("self._oper[1]."+var)
      except AttributeError:
        try:
          return eval("self._oper[2]."+var)
        except AttributeError:
          return "UNKNOWN"
  def __add__(self,second):
      return scanoper('+',self,second)
  def __sub__(self,second):
      return scanoper('-',self,second)
  def __mul__(self,second):
      return scanoper('*',self,second)
  def __div__(self,second):
      return scanoper('/',self,second)
  def __or__(self,second):
      return scanoper('|',self,second)
  def __and__(self,second):
      return scanoper('&',self,second)
  def __invert__(self):
      return scanoper('~',self)

class ascanner(scanoper):
  '''
  Default scanner user for building all other realscanners.
  '''
  name='AScanner()'
  is_spamscan=False
  is_interscanner=False
  ignore_name=False
  scanner=0
  def __init__(self):
      pass
  def destroy(self):
      pass
  def prescan(self):
      '''This function is called before running a scanner'''
      debug.echo(4,"Running: ",self.name)
  def postscan(self,level,vir,ret):
      '''This function is called after running a scanner'''
      self.child_status=(level,vir,ret)
      if is_infected(level):
        if not self.ignore_name:
          globals.found_by=self
          debug.echo(5,"Found %s by %s, level %f" % (vir,self.name,level))
  def scanstream(self,stream):
      raise ScannerError,'Not implemented'
  def scanfile(self,files,dirname=''):
      raise ScannerError,'Not implemented'
  def reinit(self):
      pass
  def get(self,var):
      try:
        return eval("self."+var)
      except AttributeError:
        return "UNKNOWN"

class interscanner(ascanner):
  '''
  Default scanner used for building all other interscanners.
  '''
  name='AInterScanner()'
  is_interscanner=True
  scanners=[]
  def rename(self,scanners):
      n=[]
      for s in scanners:
        n.append(s.name)
      self.name=self.name.replace("()","("+string.join(n,', ')+")")
      if self.name=="":
        self.name=string.join(n,',')
  def destroy(self):
      self.scanner.destroy()
  def prescan(self):
      '''This function is called before running a scanner'''
      debug.echo(5,"Running: ",self.name)
  def postscan(self,level,vir,ret):
      '''This function is called after running a scanner'''
      self.child_status=(level,vir,ret)
  def reinit(self):
      self.scanner.reinit()
  def get(self,var):
      try:
        r=eval("self."+var)
        if not r:
          r=self.scanner.get(var)
        return r
      except AttributeError:
        return self.scanner.get(var)

class globals_class:
  ACTION=S_REJECT
  QFNAME='' # quarantine filename (generated)
  USER=''
  GROUP=''
  CHROOT=''
  id=None # sagator's message ID
  scan_only=False # only scan or also send reports?
  daemon=True # daemonize or not?
  service_pids=[] # service PIDs
  pid_file=None # PID filename
  pidf=None # PID file (as python file object)
  def __init__(self):
      self.reset()
  def reset(self):
      self.found_by=ascanner
      self.QFNAME=''
  def action(self,level,detected=''):
      if is_infected(level):
        return self.ACTION
      else:
        return S_OK
globals=globals_class()
