'''
libclamav, version 1.0.1
 
(c) 2003-2006 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.
'''

# return codes
CL_CLEAN=0
CL_VIRUS=1

# error codes
CL_EMAXREC=10
CL_EMAXSIZE=11
CL_EMAXFILES=12
CL_ERAR=100
CL_EZIP=101
CL_EMALFZIP=102
CL_EGZIP=103
CL_EBZIP=104
CL_EOLE2=105
CL_EMSCOMP=106
CL_EMSCAB=107
CL_EACCES=200
CL_ENULLARG=300
CL_ETMPFILE=-1
CL_EFSYNC=-2
CL_EMEM=-3
CL_EOPEN=-4
CL_EMALFDB=-5
CL_EPATSHORT=-6
CL_ETMPDIR=-7
CL_ECVD=-8
CL_ECVDEXTR=-9
CL_EMD5=-10
CL_EDSIG=-11
CL_EIO=-12
CL_EFORMAT=-13

# options
CL_SCAN_RAW=0
CL_SCAN_ARCHIVE=1
CL_SCAN_MAIL=2
CL_SCAN_DISABLERAR=4
CL_SCAN_OLE2=8
CL_SCAN_BLOCKENCRYPTED=16
CL_SCAN_HTML=32
CL_SCAN_PE=64
CL_SCAN_BLOCKBROKEN=128
CL_SCAN_MAILURL=256
CL_SCAN_BLOCKMAX=512

CL_SCAN_DEFAULT=CL_SCAN_ARCHIVE|CL_SCAN_MAIL\
  |CL_SCAN_OLE2|CL_SCAN_HTML|CL_SCAN_PE\
  |CL_SCAN_DISABLERAR

# backward compatibility
CL_RAW=CL_SCAN_RAW
CL_ARCHIVE=CL_SCAN_ARCHIVE
CL_MAIL=CL_SCAN_MAIL
CL_DISABLERAR=CL_SCAN_DISABLERAR
CL_OLE2=CL_SCAN_OLE2
CL_ENCRYPTED=CL_SCAN_BLOCKENCRYPTED
CL_DEFAULT=CL_SCAN_DEFAULT

cdef extern from "const_types.h":
     ctypedef char constchar

cdef extern from "clamav.h":
     struct cl_limits:
            int maxreclevel
            int maxmailrec
            int maxfiles
            int maxratio
            short archivememlim
            long int maxfilesize
     struct cl_stat:
            char *dir
            int no
            char *stattab
     struct cl_node
     int cl_loaddb(char *filename, cl_node **root, int *virnum)
     int cl_loaddbdir(char *dirname, cl_node **root, int *virnum)
     char *cl_retdbdir()
     int cl_buildtrie(cl_node *root)
     void cl_freetrie(cl_node *root)
     int cl_scanbuff(char *buffer, unsigned int length,
                     constchar **virname, cl_node *root)
     int cl_scandesc(int desc, constchar **virname, unsigned long int *scanned,
                     cl_node *root, cl_limits *limits, int options)
     int cl_scanfile(char *filename, constchar **virname, unsigned long int *scanned,
                     cl_node *root, cl_limits *limits, int options)
     int cl_statinidir(char *dirname, cl_stat *dbstat)
     int cl_statchkdir(cl_stat *dbstat)
     int cl_statfree(cl_stat *dbstat)
     char *cl_strerror(int clerror)
     char *cl_retver()

cdef extern from "string.h":
     cdef void *memset(void *s, int c, int n)

class ClamAVError(Exception):
      '''ClamAVError'''
      pass

cdef class clamav:
  cdef cl_node *root
  cdef readonly int virnum
  cdef char *virname
  cdef readonly status
  cdef unsigned long int size
  cdef cl_limits limits
  cdef cl_stat dbstat
  cdef object dirname
  def __init__(self,dirname=None):
      cdef int ret
      self.virnum=0
      self.root=NULL
      self.statinidir(dirname)
      self.loaddbdir(dirname)
      memset(&self.limits, 0, sizeof(self.limits)) # zero limits
      self.limits.maxreclevel=8
      self.limits.maxmailrec=64
      self.limits.maxfilesize=10485760 # 10 MB
      self.limits.maxfiles=1024
      self.limits.maxratio=250
      self.limits.archivememlim=1
  def __del__(self):
      self.statfree()
      self.freetrie()
  def retver(self):
      return cl_retver()
  def freetrie(self):
      cl_freetrie(self.root)
      self.root=NULL
      self.virnum=0
  def loaddb(self,dirname):
      self.status=cl_loaddb(dirname, &self.root, &self.virnum)
      self.ckeckstatus()
      self.status=cl_buildtrie(self.root)
      self.checkstatus()
  def loaddbdir(self,dirname=None):
      if dirname:
        self.status=cl_loaddbdir(dirname, &self.root, &self.virnum)
      else:
        self.status=cl_loaddbdir(cl_retdbdir(), &self.root, &self.virnum)
      self.checkstatus()
      if self.virnum==0:
        raise ClamAVError,'No signatures loaded!'
      self.status=cl_buildtrie(self.root)
      self.checkstatus()
  def checkstatus(self,ret=None):
      if self.status in [CL_CLEAN,CL_VIRUS]:
        return ret
      else:
        raise ClamAVError,self.strerror()
  def scanbuff(self,buff):
      self.virname=''
      self.status=cl_scanbuff(buff,len(buff),<constchar**>&self.virname,
                              self.root)
      return self.checkstatus(self.virname)
  def scandesc(self,desc,options=CL_SCAN_DEFAULT):
      self.size,self.virname=0,''
      self.status=cl_scandesc(desc,<constchar**>&self.virname,&self.size,
                              self.root,&self.limits,options)
      return self.checkstatus(self.virname)
  def scanfile(self,filename,options=CL_SCAN_DEFAULT):
      self.size,self.virname=0,''
      self.status=cl_scanfile(filename,<constchar**>&self.virname,&self.size,
                              self.root,&self.limits,options)
      return self.checkstatus(self.virname)
  def statinidir(self,dirname=None):
      self.dirname=dirname
      memset(&self.dbstat, 0, sizeof(self.dbstat))
      if dirname:
        self.status=cl_statinidir(dirname, &self.dbstat)
      else:
        self.status=cl_statinidir(cl_retdbdir(), &self.dbstat)
      self.checkstatus()
  def statchkdir(self):
      self.status=cl_statchkdir(&self.dbstat)
      return self.checkstatus(self.status)
  def statfree(self):
      self.status=cl_statfree(&self.dbstat)
      self.checkstatus()
  def strerror(self,clerror=None):
      if clerror==None:
        return cl_strerror(self.status)
      else:
        return cl_strerror(clerror)
  def reload(self,dirname=None):
      if dirname:
        self.dirname=dirname
      if self.statchkdir():
        self.statfree()
        self.freetrie()
        self.statinidir(self.dirname)
        self.loaddbdir(self.dirname)
        return 1
      else:
        return 0
  def setlimits(self,args={}):
      for key,value in args.items():
        if key=='maxreclevel':
          self.limits.maxreclevel=value
        elif key=='maxmailrec':
          self.limits.maxmailrec=value
        elif key=='maxfiles':
          self.limits.maxfiles=value
        elif key=='maxratio':
          self.limits.maxratio=value
        elif key=='archivememlim':
          self.limits.archivememlim=value
        elif key=='maxfilesize':
          self.limits.maxfilesize=value
