'''
libclamav.so.5 functions
 
(c) 2009 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 ctypes import *
from const import *
from shared import ClamAVError, DBStat

class Limits(Structure):
  def __init__(self, maxscansize=0, maxfilesize=0,
               maxreclevel=0, maxfiles=0, archivememlim=0,
               min_cc_count=0, min_ssn_count=0):
      super(Limits, self).__init__(
          maxscansize=maxscansize,
          maxfilesize=maxfilesize,
          maxreclevel=maxreclevel,
          maxfiles=maxfiles,
          archivememlim=archivememlim,
          min_cc_count=min_cc_count,
          min_ssn_count=min_ssn_count
      )
  _fields_ = [
    ('maxscansize', c_long),
    ('maxfilesize', c_long),
    ('maxreclevel', c_uint),
    ('maxfiles', c_uint),
    ('archivememlim', c_ushort),
    ('min_cc_count', c_uint),
    ('min_ssn_count', c_uint)
  ]

class clamav_so_3_to_5:
  limits_struct = Limits
  def __init__(self, so, db_options=DB_STDOPT, datadir=None):
      self.so = so
      # set types
      self.so.cl_retdbdir.restype = c_char_p
      self.so.cl_free.restype = c_void_p
      self.so.cl_retver.restype = c_char_p
      self.so.cl_strerror.restype = c_char_p
      self.signo = c_uint(0)
      self.engine = c_void_p(None)
      self.dbstat = DBStat(None, 0, None, None)
      self.statinidir(datadir)
      self.load(db_options, datadir)
      self.limits = self.limits_struct()
  def __del__(self):
      self.statfree()
      self.free()
  def retver(self):
      return self.so.cl_retver()
  def free(self):
      if self.engine:
        self.so.cl_free(self.engine)
      self.signo.value = 0
      self.engine.value = None
      self.virnum = 0
  def load(self, db_options=CL_DB_STDOPT, datadir=None):
      self.db_options = db_options
      if not datadir:
        datadir = self.so.cl_retdbdir()
      self.status = self.so.cl_load(datadir, byref(self.engine),
                                    byref(self.signo), db_options)
      self.virnum = self.signo.value
      self.checkstatus()
      if self.signo.value==0:
        raise ClamAVError('No signatures loaded!')
      self.status = self.so.cl_build(self.engine)
      self.checkstatus()
  def checkstatus(self, ret=None):
      if self.status in [CLEAN,VIRUS]:
        return ret
      else:
        raise ClamAVError(self.strerror())
  def scandesc(self, desc, options=SCAN_STDOPT):
      scanned = c_ulong(0)
      virname = c_char_p()
      self.status = self.so.cl_scandesc(
        desc, byref(virname), byref(scanned),
        self.engine, byref(self.limits), options
      )
      self.virname = virname.value or ""
      return self.checkstatus(self.virname)
  def scanfile(self, filename, options=SCAN_STDOPT):
      scanned = c_ulong(0)
      virname = c_char_p()
      self.status = self.so.cl_scanfile(
        filename, byref(virname), byref(scanned),
        self.engine, byref(self.limits), options
      )
      self.virname = virname.value or ""
      return self.checkstatus(self.virname)
  def statinidir(self, datadir=None):
      self.datadir = datadir
      if not datadir:
        datadir = self.so.cl_retdbdir()
      self.status = self.so.cl_statinidir(datadir, byref(self.dbstat))
      self.checkstatus()
  def statchkdir(self):
      self.status = self.so.cl_statchkdir(byref(self.dbstat))
      return self.checkstatus(self.status)
  def statfree(self):
      try:
        self.status = self.so.cl_statfree(byref(self.dbstat))
        self.checkstatus()
      except TypeError:
        pass # ignore 'NoneType' object is not callable
  def strerror(self, clerror=None):
      if clerror==None:
        clerror = self.status
      return self.so.cl_strerror(clerror)
  def reload(self, datadir=None):
      if datadir:
        self.datadir=datadir
      if self.statchkdir():
        self.statfree()
        self.free()
        self.statinidir(self.datadir)
        self.load(self.db_options, self.datadir)
        return 1
      else:
        return 0
  def setlimits(self, args={}):
      for key,value in args.items():
        setattr(self.limits, key, value)

class clamav(clamav_so_3_to_5):
  limits_struct = Limits
