[Python-modules-commits] [pyclamd] 01/04: Imported Upstream version 0.4.0

Scott Kitterman kitterman at moszumanska.debian.org
Mon Jan 8 22:07:00 UTC 2018


This is an automated email from the git hooks/post-receive script.

kitterman pushed a commit to branch debian/master
in repository pyclamd.

commit bfbfc30cd9f98ae2d981fa86eb5635c0dda5840f
Author: Scott Kitterman <scott at kitterman.com>
Date:   Mon Jan 8 16:56:58 2018 -0500

    Imported Upstream version 0.4.0
---
 PKG-INFO                  |   4 +-
 README.txt                |   5 +-
 pyClamd.egg-info/PKG-INFO |   4 +-
 pyclamd/pyclamd.py        | 177 ++++++++++++++++++++++++++++++++--------------
 pyclamd/test_pyclamd.py   |  30 +++++---
 setup.cfg                 |   3 +-
 setup.py                  |   2 +-
 7 files changed, 157 insertions(+), 68 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 3bc6132..6c1337e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: pyClamd
-Version: 0.3.17
+Version: 0.4.0
 Summary: pyClamd is a python interface to Clamd (Clamav daemon).
 Home-page: http://xael.org/pages/pyclamd-en.html
 Author: Alexandre Norman
-Author-email: norman()xael.org
+Author-email: norman at xael.org
 License: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
 Download-URL: http://xael.org/norman/python/pyclamd/
 Description: pyClamd is a python interface to Clamd (Clamav daemon). By using pyClamd, you can add virus detection capabilities to your python software in an efficient and easy way. Instead of pyClamav which uses libclamav, pyClamd may be used by a closed source product.
diff --git a/README.txt b/README.txt
index e04f08f..ab1163f 100644
--- a/README.txt
+++ b/README.txt
@@ -78,4 +78,7 @@ END
 None
 >>> # scan a stream
 >>> print "{0}".format(cd.scan_stream(cd.EICAR()))
-{u'stream': ('FOUND', 'Eicar-Test-Signature')}
\ No newline at end of file
+{u'stream': ('FOUND', 'Eicar-Test-Signature')}
+>>> # or via file-like object
+>>> print "{0}".format(cd.scan_stream(open('/tmp/EICAR', 'rb')))
+{u'stream': ('FOUND', 'Eicar-Test-Signature')}
diff --git a/pyClamd.egg-info/PKG-INFO b/pyClamd.egg-info/PKG-INFO
index 3bc6132..6c1337e 100644
--- a/pyClamd.egg-info/PKG-INFO
+++ b/pyClamd.egg-info/PKG-INFO
@@ -1,10 +1,10 @@
 Metadata-Version: 1.1
 Name: pyClamd
-Version: 0.3.17
+Version: 0.4.0
 Summary: pyClamd is a python interface to Clamd (Clamav daemon).
 Home-page: http://xael.org/pages/pyclamd-en.html
 Author: Alexandre Norman
-Author-email: norman()xael.org
+Author-email: norman at xael.org
 License: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
 Download-URL: http://xael.org/norman/python/pyclamd/
 Description: pyClamd is a python interface to Clamd (Clamav daemon). By using pyClamd, you can add virus detection capabilities to your python software in an efficient and easy way. Instead of pyClamav which uses libclamav, pyClamd may be used by a closed source product.
diff --git a/pyclamd/pyclamd.py b/pyclamd/pyclamd.py
index 9fea4e8..c2a68d2 100644
--- a/pyclamd/pyclamd.py
+++ b/pyclamd/pyclamd.py
@@ -3,13 +3,13 @@
 #------------------------------------------------------------------------------
 # LICENSE:
 # This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free 
+# the terms of the GNU Lesser General Public License as published by the Free
 # Software  Foundation; either version 3 of the License, or (at your option) any
 # later version. See http://www.gnu.org/licenses/lgpl-3.0.txt.
 #
 # This program is distributed in the hope that it will be useful, but WITHOUT
 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 # details.
 #
 # You should have received a copy of the GNU Lesser General Public License along
@@ -19,9 +19,9 @@
 # CHANGELOG:
 # 2006-07-15 v0.1.1 AN: - released version
 # 2007-10-09 v0.2.0 PL: - fixed error with deprecated string exceptions
-#			- added optional timeout to sockets to avoid blocking 
+#			- added optional timeout to sockets to avoid blocking
 #			  operations
-# 2010-07-11 v0.2.1 AN: - change all raise exception (was deprecated), license 
+# 2010-07-11 v0.2.1 AN: - change all raise exception (was deprecated), license
 #						  change to LGPL
 # 2010-07-12 v0.2.2 TK: - PEP8 compliance
 #						  isolating send and receive functions
@@ -53,6 +53,9 @@
 # 2015-03-14 v0.3.14 AN : - Bug correction for clamd.conf default path
 # 2015-06-04 v0.3.15 AN : - optimization in scan_stream
 # 2015-10-21 v0.3.16 JMS : - avoid EICAR detection in py3 pyc file
+# 2016-08-07 v0.3.17 AN: - typo change (Connexion to Connection)
+# 2017-08-27 v0.4.0 RC: - modified scan_stream() to add support for passing file-like objects
+#                   BM: - add allmatchscan with file and directory support
 #------------------------------------------------------------------------------
 # TODO:
 # - improve tests for Win32 platform (avoid to write EICAR file to disk, or
@@ -71,10 +74,12 @@ pyclamd.py
 
 Author : Alexandre Norman - norman()xael.org
 Contributors :
+ - BM : Brandon Murphy - bitbucket () zoomequipd.com
  - JB :  Joe Brandt  - brandt.joe () gmail.com
  - JMS: Jack Saunders - jack () oldstlabs.com
  - JS : Joni Salonen - joni.salonen () qindel.com
  - PL :  Philippe Lagadec - philippe.lagadec()laposte.net
+ - RC : Robert Coup
  - SK : Scott Kitterman - debian () kitterman.com
  - TK :  Thomas Kastner - tk()underground8.com
  - TKL : Thomas Kluyver - thomas () kluyver.me.uk
@@ -129,7 +134,7 @@ True
 >>> os.remove('/tmp/EICAR-éèô请收藏我们的网址')
 """
 
-__version__ = "0.3.17"
+__version__ = "0.4.0"
 
 
 # $Source$
@@ -169,7 +174,7 @@ class _ClamdGeneric(object):
     """
     Abstract class for clamd
     """
-    
+
     def EICAR(self):
         """
         returns Eicar test string
@@ -184,7 +189,7 @@ class _ClamdGeneric(object):
 
         EICAR = base64.b64decode(eicar_b64.encode('ascii'))
         return EICAR
-        
+
 
     def ping(self):
         """
@@ -213,7 +218,7 @@ class _ClamdGeneric(object):
         return
 
 
-    
+
     def version(self):
         """
         Get Clamscan version
@@ -253,7 +258,7 @@ class _ClamdGeneric(object):
 
         return result
 
-    
+
     def reload(self):
         """
         Force Clamd to reload signature database
@@ -270,14 +275,14 @@ class _ClamdGeneric(object):
             self._send_command('RELOAD')
             result = self._recv_response()
             self._close_socket()
-            
+
         except socket.error:
             raise ConnectionError('Could probably not reload signature database')
 
         return result
 
 
-    
+
     def shutdown(self):
         """
         Force Clamd to shutdown and exit
@@ -296,7 +301,7 @@ class _ClamdGeneric(object):
             raise ConnectionError('Could probably not shutdown clamd')
 
 
-    
+
     def scan_file(self, file):
         """
         Scan a file or directory given by filename and stop on first virus or error found.
@@ -335,7 +340,7 @@ class _ClamdGeneric(object):
                 if status == 'ERROR':
                     dr[filename] = ('ERROR', '{0}'.format(reason))
                     return dr
-                    
+
                 elif status == 'FOUND':
                     dr[filename] = ('FOUND', '{0}'.format(reason))
 
@@ -344,7 +349,7 @@ class _ClamdGeneric(object):
             return None
         return dr
 
-    
+
 
 
 
@@ -385,7 +390,7 @@ class _ClamdGeneric(object):
 
                     if status == 'ERROR':
                         dr[filename] = ('ERROR', '{0}'.format(reason))
-                    
+
                     elif status == 'FOUND':
                         dr[filename] = ('FOUND', '{0}'.format(reason))
 
@@ -396,7 +401,65 @@ class _ClamdGeneric(object):
 
 
 
+    def allmatchscan(self, file):
+        """
+        Scan a file or directory given by filename and after finding a virus within a file, continues scanning for additional viruses.
+        Scan with archive support enabled.
+
+        file (string) : filename or directoy (MUST BE ABSOLUTE PATH !)
+
+        return either :
+          - (dict): {filename1: [(FOUND', 'virusname1'), (FOUND', 'virusname2')], filename2:  [(FOUND', 'virusname1'), (FOUND', 'virusname3')]}
+          - None: if no virus found
+
+        May raise :
+          - ConnectionError: in case of communication problem
+          - socket.timeout: if timeout has expired
+        """
+        assert isstr(file), 'Wrong type for [file], should be a string [was {0}]'.format(type(file))
+
+        dr={}
+
+        if os.path.isdir(file):
+            for path, subdirs, files in os.walk(file):
+                for name in files:
+                     single_file_result = self.allmatchscan(os.path.join(path,name))
+                     if single_file_result:
+                         dr.update(single_file_result)
 
+        else:
+            try:
+                self._init_socket()
+                self._send_command('ALLMATCHSCAN {0}'.format(file))
+            except socket.error:
+                raise ConnectionError('Unable to scan {0}'.format(file))
+
+            result='...'
+
+            while result:
+                try:
+                    result = self._recv_response()
+                except socket.error:
+                    raise ConnectionError('Unable to scan {0}'.format(file))
+
+                if len(result) > 0:
+                    for resline in result.splitlines():
+                        filename, reason, status = self._parse_response(resline)
+
+                        if status == 'ERROR':
+                            if filename not in dr:
+                                dr[filename] = []
+                            dr[filename].append(('ERROR', '{0}'.format(reason)))
+
+                        elif status == 'FOUND':
+                            if filename not in dr:
+                                dr[filename] = []
+                            dr[filename].append(('FOUND', '{0}'.format(reason)))
+            self._close_socket()
+
+        if not dr:
+            return None
+        return dr
 
 
 
@@ -434,10 +497,10 @@ class _ClamdGeneric(object):
             if len(result) > 0:
                 for resline in result.splitlines():
                     filename, reason, status = self._parse_response(resline)
-                    
+
                     if status == 'ERROR':
                         dr[filename] = ('ERROR', '{0}'.format(reason))
-                    
+
                     elif status == 'FOUND':
                         dr[filename] = ('FOUND', '{0}'.format(reason))
 
@@ -448,14 +511,14 @@ class _ClamdGeneric(object):
 
 
 
-    def scan_stream(self, buffer_to_test):
+    def scan_stream(self, stream, chunk_size=4096):
         """
         Scan a buffer
 
         on Python2.X :
-          - buffer_to_test (string): buffer to scan
+          - input (string): buffer to scan
         on Python3.X :
-          - buffer_to_test (bytes or bytearray): buffer to scan
+          - input (bytes or bytearray): buffer to scan
 
         return either:
           - (dict): {filename1: "virusname"}
@@ -467,10 +530,12 @@ class _ClamdGeneric(object):
         """
         if sys.version_info[0] <= 2:
             # Python2
-            assert isstr(buffer_to_test), 'Wrong type fom [buffer_to_test], should be str [was {0}]'.format(type(buffer_to_test))
+            assert hasattr(stream, "read") or isinstance(stream, str), 'Wrong type for [stream], should be str/file-like [was {0}]'.format(type(stream))
         else:
             # Python3
-            assert isinstance(buffer_to_test, bytes) or isinstance(buffer_to_test, bytearray), 'Wrong type fom [buffer_to_test], should be bytes or bytearray [was {0}]'.format(type(buffer_to_test))
+            assert hasattr(stream, "read") or isinstance(stream, (bytes, bytearray)), 'Wrong type for [stream], should be bytes/bytearray/file-like [was {0}]'.format(type(stream))
+
+        is_file_like = hasattr(stream, 'read')
 
         try:
             self._init_socket()
@@ -479,21 +544,33 @@ class _ClamdGeneric(object):
         except socket.error:
             raise ConnectionError('Unable to scan stream')
 
-        # MUST be < StreamMaxLength in /etc/clamav/clamd.conf
-        # or /etc/clamd.conf
-        max_chunk_size = 1024
+        if is_file_like:
+            while True:
+                chunk = stream.read(chunk_size)
+                if not chunk:
+                    break
+                size = struct.pack('!L', len(chunk))
+                try:
+                    self.clamd_socket.send(size)
+                    self.clamd_socket.send(chunk)
+                except socket.error:
+                    raise
 
-        for n in range(1 + int(len(buffer_to_test)/max_chunk_size)):
-            chunk = buffer_to_test[n*max_chunk_size:(n+1)*max_chunk_size]
-            size = struct.pack('!L', len(chunk))
-            try:
-                self.clamd_socket.send(size)
-                self.clamd_socket.send(chunk)
-            except socket.error:
-                break
-        else:
             # Terminating stream
             self.clamd_socket.send(struct.pack('!L', 0))
+        else:
+            # bytearray
+            for n in range(1 + int(len(stream)/chunk_size)):
+                chunk = stream[n*chunk_size:(n+1)*chunk_size]
+                size = struct.pack('!L', len(chunk))
+                try:
+                    self.clamd_socket.send(size)
+                    self.clamd_socket.send(chunk)
+                except socket.error:
+                    raise
+            else:
+                # Terminating stream
+                self.clamd_socket.send(struct.pack('!L', 0))
 
         result='...'
         dr = {}
@@ -522,10 +599,6 @@ class _ClamdGeneric(object):
         return dr
 
 
-
-
-
-    
     def _send_command(self, cmd):
         """
         `man clamd` recommends to prefix commands with z, but we will use \n
@@ -543,7 +616,7 @@ class _ClamdGeneric(object):
         receive response from clamd and strip all whitespace characters
         """
         # If we connect too quickly
-        # sometimes we get a connexion error
+        # sometimes we get a connection error
         # so we retry
         failed_count = 5
         while True:
@@ -585,7 +658,7 @@ class _ClamdGeneric(object):
         """
         self.clamd_socket.close()
         return
-    
+
 
     def _parse_response(self, msg):
         """
@@ -598,7 +671,7 @@ class _ClamdGeneric(object):
             result = left
         else:
             result = ": ".join(left)
-            
+
         if result != 'OK':
             parts = result.split()
             reason = ' '.join(parts[:-1])
@@ -622,7 +695,7 @@ class ClamdUnixSocket(_ClamdGeneric):
     def __init__(self, filename=None, timeout=None):
         """
         Unix Socket Class initialisation
-        
+
         filename (string) : unix socket filename or None to get the socket from /etc/clamav/clamd.conf or /etc/clamd.conf
         timeout (float or None) : socket timeout
         """
@@ -634,7 +707,7 @@ class ClamdUnixSocket(_ClamdGeneric):
                     break
             else:
                 raise ConnectionError('Could not find clamd unix socket from /etc/clamav/clamd.conf or /etc/clamd.conf')
-            
+
             with open(clamdpath, 'r') as conffile:
                 for line in conffile.readlines():
                     try:
@@ -643,15 +716,15 @@ class ClamdUnixSocket(_ClamdGeneric):
                             break
                     except IndexError:
                         pass
-                            
+
                 else:
                     raise ConnectionError('Could not find clamd unix socket from /etc/clamav/clamd.conf or /etc/clamd.conf')
-        
+
         assert isstr(filename), 'Wrong type for [file], should be a string [was {0}]'.format(type(file))
         assert isinstance(timeout, (float, int)) or timeout is None, 'Wrong type for [timeout], should be either None or a float [was {0}]'.format(type(timeout))
 
         _ClamdGeneric.__init__(self)
-        
+
         self.unix_socket = filename
         self.timeout = timeout
 
@@ -675,7 +748,7 @@ class ClamdUnixSocket(_ClamdGeneric):
         except socket.error:
             raise ConnectionError('Could not reach clamd using unix socket ({0})'.format((self.unix_socket)))
         return
-    
+
 
 ############################################################################
 
@@ -691,13 +764,13 @@ class ClamdNetworkSocket(_ClamdGeneric):
         port (int) : TCP port
         timeout (float or None) : socket timeout
         """
-            
+
         assert isinstance(host, str), 'Wrong type for [host], should be a string [was {0}]'.format(type(host))
         assert isinstance(port, int), 'Wrong type for [port], should be an int [was {0}]'.format(type(port))
         assert isinstance(timeout, (float, int)) or timeout is None, 'Wrong type for [timeout], should be either None or a float [was {0}]'.format(type(timeout))
-        
+
         _ClamdGeneric.__init__(self)
-        
+
         self.host = host
         self.port = port
         self.timeout = timeout
@@ -723,7 +796,7 @@ class ClamdNetworkSocket(_ClamdGeneric):
 
         return
 
-    
+
 
 ############################################################################
 
@@ -800,7 +873,7 @@ def _non_regression_test():
 	import doctest
 	doctest.testmod()
 	return
-	
+
 
 ############################################################################
 
diff --git a/pyclamd/test_pyclamd.py b/pyclamd/test_pyclamd.py
index 9c7435a..b6eb39c 100644
--- a/pyclamd/test_pyclamd.py
+++ b/pyclamd/test_pyclamd.py
@@ -21,7 +21,7 @@ Author :
 
 * Alexandre Norman - norman at xael.org
 
- 
+
 Licence : GPL v3 or any later version
 
 
@@ -60,7 +60,7 @@ class Pdb(Plugin):
     enabled_for_errors = False
     enabled_for_failures = False
     score = 5 # run last, among builtins
-    
+
     def options(self, parser, env):
         """Register commandline options.
         """
@@ -167,7 +167,7 @@ def test_no_eicar():
 
 def test_stream():
     """
-    Tests eicar infected stream    
+    Tests eicar infected stream
     """
     assert_equals(cd.scan_stream(cd.EICAR())['stream'], ('FOUND', 'Eicar-Test-Signature'))
 
@@ -186,10 +186,10 @@ def test_multiscan_file():
     directory = cd.multiscan_file('/tmp/')
     assert_equals(directory['/tmp/EICAR'], ('FOUND', 'Eicar-Test-Signature'))
 
-    
+
 def test_unicode_scanning():
     """
-    Tests encoding with non latin characters 
+    Tests encoding with non latin characters
     (Chinese ideograms taken from random site, don't know what it mean, sorry)
     """
     void = open('/tmp/EICAR-éèô请收藏我们的网址','wb').write(cd.EICAR())
@@ -201,7 +201,7 @@ def test_unicode_scanning():
 
 def test_scan_stream_unicode_test_eicar_in_pdf():
     """
-    Tests stream scan with eicar in pdf file. Not detected by design of ClamAv. 
+    Tests stream scan with eicar in pdf file. Not detected by design of ClamAv.
     """
     file_data = open('./probleme_data.pdf', 'rb').read()
     v = cd.scan_stream(file_data)
@@ -210,7 +210,7 @@ def test_scan_stream_unicode_test_eicar_in_pdf():
 
 def test_scan_stream_unicode_test_clean():
     """
-    Tests stream scan with clean pdf file 
+    Tests stream scan with clean pdf file
     """
     file_data = open('./probleme_data_clean.pdf', 'rb').read()
     v = cd.scan_stream(file_data)
@@ -218,9 +218,23 @@ def test_scan_stream_unicode_test_clean():
     return
 
 
+def test_scan_stream_filelike_eicar():
+    void = open('/tmp/EICAR','wb').write(cd.EICAR())
+    f = open('/tmp/EICAR', 'rb')
+    v = cd.scan_stream(f)
+    assert_equals(v, {'stream': ('FOUND', 'Eicar-Test-Signature')})
+
+
+def test_scan_stream_filelike_clean():
+    void = open('/tmp/NO_EICAR','w').write('no virus in this file')
+    f = open('/tmp/NO_EICAR', 'rb')
+    v = cd.scan_stream(f)
+    assert_equals(v, None)
+
+
 def test_scan_file_unicode_test_eicar_in_pdf():
     """
-    Tests stream scan with clean pdf file 
+    Tests stream scan with clean pdf file
     """
     v = cd.scan_file('/home/xael/ESPACE_KM/python/pyclamd/probleme_data.pdf')
     #.assertEqual(v, {u'stream': ('FOUND', 'Eicar-Test-Signature')})
diff --git a/setup.cfg b/setup.cfg
index 72f9d44..8bfd5a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,4 @@
 [egg_info]
-tag_svn_revision = 0
-tag_date = 0
 tag_build = 
+tag_date = 0
 
diff --git a/setup.py b/setup.py
index 5abde91..29d6ecf 100644
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@ setup (name = 'pyClamd',
 
        license ='License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)',
        author = 'Alexandre Norman',
-       author_email = 'norman()xael.org',
+       author_email = 'norman at xael.org',
        keywords='python, clamav, antivirus, scanner, virus, libclamav',
        url = 'http://xael.org/pages/pyclamd-en.html',
        bugtrack_url = 'https://bitbucket.org/xael/pyclamd',

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pyclamd.git



More information about the Python-modules-commits mailing list