SOURCES: python-elspy-cvs20050901.patch (NEW) - cvs snap

arekm arekm at pld-linux.org
Thu Sep 1 17:56:21 CEST 2005


Author: arekm                        Date: Thu Sep  1 15:56:21 2005 GMT
Module: SOURCES                       Tag: HEAD
---- Log message:
- cvs snap

---- Files affected:
SOURCES:
   python-elspy-cvs20050901.patch (NONE -> 1.1)  (NEW)

---- Diffs:

================================================================
Index: SOURCES/python-elspy-cvs20050901.patch
diff -u /dev/null SOURCES/python-elspy-cvs20050901.patch:1.1
--- /dev/null	Thu Sep  1 17:56:21 2005
+++ SOURCES/python-elspy-cvs20050901.patch	Thu Sep  1 17:56:16 2005
@@ -0,0 +1,1431 @@
+diff -urN elspy-0.1.1.org/CHANGES.txt elspy-0.1.1/CHANGES.txt
+--- elspy-0.1.1.org/CHANGES.txt	2002-10-23 00:52:18.000000000 +0200
++++ elspy-0.1.1/CHANGES.txt	2003-04-09 00:54:20.000000000 +0200
+@@ -15,3 +15,11 @@
+   * Change to accept messages by default if there's any Python
+     problem, not just an import problem.  Made this behaviour a bit more
+     customizable with some #define's at the top of elspy.c
++
++
++0.1.2? 0.2? (?? ??? 2002)
++-------------------------
++
++  * Added elspy.subject_charset module, for rejecting mail with a
++    subject encoded using one of various banned charsets (which are
++    configurable).
+diff -urN elspy-0.1.1.org/elspy.c elspy-0.1.1/elspy.c
+--- elspy-0.1.1.org/elspy.c	2002-10-23 00:43:58.000000000 +0200
++++ elspy-0.1.1/elspy.c	2003-08-24 19:08:32.000000000 +0200
+@@ -58,7 +58,21 @@
+ #define INTERNAL_ERROR_RETURN LOCAL_SCAN_ACCEPT
+ #define ERROR_RETURN LOCAL_SCAN_ACCEPT
+ 
+-static char * __revision__ = "$Id$";
++static char * __revision__ = "$Id$";
++
++
++#ifdef DLOPEN_LOCAL_SCAN
++/* Return the version of the local_scan ABI, if being compiled as a .so */
++int local_scan_version_major(void)
++{
++  return LOCAL_SCAN_ABI_VERSION_MAJOR;
++}
++
++int local_scan_version_minor(void)
++{
++  return LOCAL_SCAN_ABI_VERSION_MINOR;
++}
++#endif
+ 
+ 
+ /* ----------------------------------------------------------------------
+@@ -247,6 +261,7 @@
+    mod = Py_InitModule("_elspy", elspy_methods);
+ 
+    /* Add necessary constants to module's dictionary. */
++   /* XXX should use PyModule_AddIntConstant() here! */
+    mod_dict = PyModule_GetDict(mod);
+    PyDict_SetItemString(mod_dict, "LOCAL_SCAN_ACCEPT",
+                         PyInt_FromLong((long) LOCAL_SCAN_ACCEPT));
+@@ -352,15 +367,12 @@
+    mod_dict = PyModule_GetDict(module);
+    func = PyDict_GetItemString(mod_dict, func_name);
+ 
+-   if (func) {
+-      if (! PyCallable_Check(func)) {
+-         log_error("error: %s object in %s module not callable",
+-                   func_name, mod_name);
+-         return NULL;
+-      }
+-      else
+-         return func;
++   if (func != NULL && !PyCallable_Check(func)) {
++      log_error("error: %s object in %s module not callable",
++		func_name, mod_name);
++      return NULL;
+    }
++   return func;
+ }
+ 
+ 
+@@ -509,6 +521,48 @@
+ }
+ 
+ 
++#if DEBUG_LEVEL > 3
++void dump_sys_module ()
++{
++   PyObject * sys_mod; 
++   PyObject * sys_executable; 
++   PyObject * sys_version; 
++   PyObject * sys_path; 
++   PyObject * cur_dir; 
++ 
++   sys_mod = PyImport_ImportModule("sys"); 
++   if (sys_mod == NULL)  
++      return; 
++   log_debug(3, "sys module: %s", PyString_AsString(PyObject_Repr(sys_mod))); 
++ 
++   /* show sys.executable (just curious) */ 
++   sys_executable = PyObject_GetAttrString(sys_mod, "executable"); 
++   if (sys_executable != NULL) 
++      log_debug(3, "sys.executable: >%s<", PyString_AsString(sys_executable)); 
++ 
++   /* show sys.version */ 
++   sys_version = PyObject_GetAttrString(sys_mod, "version"); 
++   if (sys_version != NULL) 
++      log_debug(3, "sys.version: >%s<", PyString_AsString(sys_version)); 
++ 
++   /* and sys.path */ 
++   sys_path = PyObject_GetAttrString(sys_mod, "path"); 
++   if (sys_path != NULL) { 
++      int n, i; 
++      n = PySequence_Size(sys_path); 
++      log_debug(3, "sys.path has %d elements", n); 
++ 
++      for (i = 0; i < n; i++) { 
++         cur_dir = PySequence_GetItem(sys_path, i); 
++         log_debug(3, "sys.path[%d] = %s", i,
++                   PyString_AsString(PyObject_Repr(cur_dir)));
++      } 
++   } 
++}
++#else
++#define dump_sys_module()
++#endif
++
+ static int py_initialized = 0;
+ 
+ int
+@@ -535,6 +589,7 @@
+    }
+ 
+    /* Import the elspy package. */
++   dump_sys_module();
+    log_debug(1, "attempting to import module 'elspy'");
+    elspy_mod = PyImport_ImportModule("elspy");
+    if (elspy_mod == NULL) {
+@@ -662,12 +717,3 @@
+    return result;
+ 
+ }
+-
+-#ifdef DLOPEN_LOCAL_SCAN
+-/* Return the verion of the local_scan ABI, if being compiled as a .so */
+-int
+-local_scan_version(void)
+-{
+-    return LOCAL_SCAN_ABI_VERSION ;
+-}
+-#endif
+diff -urN elspy-0.1.1.org/lib/bogus_date.py elspy-0.1.1/lib/bogus_date.py
+--- elspy-0.1.1.org/lib/bogus_date.py	2002-10-22 00:20:14.000000000 +0200
++++ elspy-0.1.1/lib/bogus_date.py	2003-04-08 01:12:04.000000000 +0200
+@@ -2,7 +2,7 @@
+ #
+ # This code is based on a strict interpretation of RFC 2822 section 3.3;
+ # the idea is to reject *any* sort of bogosity in the "Date:" header:
+-#   
++#
+ #   * invalid syntax, eg. any of the following would be rejected:
+ #       "Mon 1 Jul 2002 10:11:41 -0400"  (missing comma)
+ #       "Mon,  1 Jul 2002 10:11:41"      (missing timezone)
+@@ -22,7 +22,7 @@
+ #
+ #   * too far ahead of the current date; configurable by editing
+ #     MESSAGE_TOO_NEW below
+-# 
++#
+ 
+ # Implementation notes.
+ #
+@@ -45,7 +45,7 @@
+ #
+ # The elspy home page is at http://elspy.sourceforge.net/ .
+ 
+-__revision__ = "$Id$"
++__revision__ = "$Id$"
+ 
+ import re
+ from time import time, localtime, gmtime, mktime, asctime
+@@ -211,7 +211,7 @@
+     # remote timezone out of the "Date" header, it's easy to adjust
+     # this bogus time value to get the correct time that the "Date"
+     # header represents, as seconds since epoch UTC.
+-    # 
++    #
+     # Given that, it's a simple subtraction to compute the difference
+     # between the "Date" header and the current time, and reject the
+     # message if the "Date" header is wildly off.  The definition of
+diff -urN elspy-0.1.1.org/lib/defanged_virus.py elspy-0.1.1/lib/defanged_virus.py
+--- elspy-0.1.1.org/lib/defanged_virus.py	1970-01-01 01:00:00.000000000 +0100
++++ elspy-0.1.1/lib/defanged_virus.py	2005-05-19 01:51:03.000000000 +0200
+@@ -0,0 +1,172 @@
++# elspy library -- detection of defanged viruses
++#
++# This code is intended to detect and reject defanged viruses,
++# as well as related junk like "caught a virus for you" or
++# "you sent us a virus" messages.  Virus detection software that
++# sends *more* mail just perpetuates the problem; the authors
++# of such software are only one step up from virus authors and
++# will be up against the wall (right after spammers) when the
++# revolution comes.
++#
++# Naturally, this is a hodge-podge of heuristic hacks.  I very
++# much doubt there's any nice way to do it.
++
++# Copyright (c) 2003 Gregory P. Ward.  All rights reserved.
++# See README.txt for licensing information.
++#
++# The elspy home page is at http://elspy.sourceforge.net/ .
++
++__revision__ = "$Id$"
++
++
++import re
++from elspy.util import RejectVirusRelated, RejectVirus, parse_message
++
++
++INTERSCAN_SUBJECT = "InterScan NT Alert"
++INTERSCAN_BODY = "Receiver, InterScan has detected virus(es) in the e-mail attachment."
++INTERSCAN_FILENAME = "InterScan_SafeStamp.txt"
++
++# Rejection messages (consistency is good!)
++REJECT_DEFANGED = "defanged viruses not wanted here"
++REJECT_ALERT = "virus alerts not wanted here"
++REJECT_NOTIFICATION = "misguided virus notifications not wanted here"
++
++
++def local_scan (info, headers, fd, msg=None):
++    if msg is None:
++        msg = parse_message(headers, fd, onerror="raise")
++
++    html_body = plain_body = None
++    attach_names = []
++    if msg.is_multipart():
++        for part in msg.walk():
++            name = (part.get_param('name') or
++                    part.get_param('filename',
++                                   header='content-disposition'))
++            if name:
++                attach_names.append(name)
++
++            part_type = part.get_type()
++            if html_body is None and part_type == "text/html":
++                html_body = part.get_payload().strip()
++            elif plain_body is None and part_type == "text/plain":
++                plain_body = part.get_payload().strip()
++
++        body = plain_body or html_body or ""
++    else:
++        plain_body = body = msg.get_payload().strip()
++
++    subject = msg.get('subject', '')
++
++    # Number One offender: InterScan NT Alert, which doubles your
++    # pleasure by first sending you a "Virus detected" notice,
++    # and *then* sending the defanged virus itself.  Arrghh!
++    # First, reject the "virus detected" message based on the
++    # subject and first line of the body.
++    if (subject == INTERSCAN_SUBJECT and
++        plain_body and
++        plain_body.startswith(INTERSCAN_BODY)):
++        raise RejectVirusRelated(REJECT_ALERT)
++
++    # Now the second half: viruses defanged by InterScan.  
++    if INTERSCAN_FILENAME in attach_names:
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    # InterScan also has the wonderful feature of notifying
++    # user at example.com that he sent a virus when, of course, the virus
++    # just forged that sender address.  Sigh.
++    if (subject.startswith("Failed to clean virus file") and
++        plain_body and
++        plain_body.startswith("The file you have sent was infected with a virus")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject == "Virus Alert !" and
++        body.find("McAfee.com Has recieved an infected message from you") != -1):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject.startswith("Warning: A possible virus has been detected in one of your messages.") and
++        body.startswith("A virus or an infected file has been detected in a message:")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject.startswith("MailMarshal has detected a Virus") and
++        body.startswith("MailMarshal (an automated content monitoring gateway) has stopped")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject.startswith("Your mail server sent us a virus") and
++        body.find("Declude")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject.startswith("RAV AntiVirus") and
++        re.search(r"This e-mail is generated by the \S+ mail server to warn you that the e-mail", body)):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject.startswith("Your mail server sent us a virus!") and
++        body.startswith("The anti-virus servers of IDnet detected")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if (subject == "Virus intercepted" and
++        body.startswith("A message you sent to")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    # Oh great, they come in Italian too.
++    if (subject == "ATTENZIONE: Virus trovato" and
++        body.startswith("I software anti-virus presenti sul nostro server")):
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if subject.find("WARNING: YOU MAY HAVE A VIRUS") != -1:
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if subject.find("Inflex scan report") != -1:
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    if subject.find("This is an alert from eSafe") != -1:
++        raise RejectVirusRelated(REJECT_NOTIFICATION)
++
++    
++
++    # Something called MailScanner adds a header stating that it found a
++    # virus, and then just passes the whole thing on intact.  Duh.
++    mailscanner = msg.get('x-mailscanner', '')
++    if (mailscanner.lower().find("found to be infected") != -1):
++        raise RejectVirusRelated("viruses not wanted here")
++
++    if (subject.startswith("Virus Detected by Network Associates") and
++        body.startswith("Network Associates WebShield") and
++        body.find("detected virus") != -1):
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    if ("DELETED0.TXT" in attach_names or
++        "Norton AntiVirus Deleted1.txt" in attach_names):
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    if msg['X-SpamHunter'] == "Found to be infected":
++        raise RejectVirusRelated("viruses not wanted here")
++
++    if body.strip().startswith("RAV AntiVirus has deleted this file"):
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    if ("WARNING.TXT" in attach_names and
++        body.startswith("WARNING: This e-mail has been altered by MIMEDefang.")):
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    if body.find("NOTICE: One or more files attached to this message "
++                 "were found to contain a malicious virus") != -1:
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    if body.find("VIRUS INFECTION ALERT") != -1:
++        raise RejectVirusRelated(REJECT_DEFANGED)
++
++    # Detect W32/Palyh and W32.Sobig.F at mm viruses.  This isn't really a
++    # "defanged" virus (AFAIK), but it doesn't bear the usual signature,
++    # ie. a Windows executable attachment, and it's easiest to catch
++    # using the machinery in this module.
++    if (plain_body == "All information is in the attached file." or
++        plain_body == "Please see the attached file." or
++        plain_body == "See the attached file for details"):
++        payload = msg.get_payload()
++        # Make sure message has the same MIME structure as W32/Palyh
++        if (msg.get_type() == "multipart/mixed" and
++            len(payload) == 1 and
++            payload[0].get_type() == "text/plain"):
++            raise RejectVirus("W32/Palyh or W32.Sobig.F at mm virus detected")
+diff -urN elspy-0.1.1.org/lib/execontent_simple.py elspy-0.1.1/lib/execontent_simple.py
+--- elspy-0.1.1.org/lib/execontent_simple.py	2002-10-22 00:20:14.000000000 +0200
++++ elspy-0.1.1/lib/execontent_simple.py	2004-07-02 15:03:43.000000000 +0200
+@@ -30,10 +30,10 @@
+ #
+ # The elspy home page is at http://elspy.sourceforge.net/ .
+ 
+-__revision__ = "$Id$"
++__revision__ = "$Id$"
+ 
+ import os, re
+-from elspy import RejectMessage, get_fdpos, set_fdpos
++from elspy import RejectVirus, get_fdpos, set_fdpos
+ 
+ 
+ BAD_EXTENSIONS = ("vbs", "vbe", "wsf", "wsh", "js", "jse",
+@@ -72,26 +72,72 @@
+                        flags|re.MULTILINE)
+ 
+ 
++def check_sober_h(info, headers, fd):
++    # Boy, these anti-immigration Germans are really starting to bug me...
++    banned_prefixes = ["Bankrott des Gesundheitswesens durch Auslaender!",
++                       "Wer an ein Tabu ruehrt, muss und darf vernichtet werden",
++                       "EU Beitritt der Tuerkei ?",
++                       "Bin ich zu weltfremd? Ich glaube wohl kaum",
++                       "Die Deform der sozialen Ordnung",
++                       "Moschee-Bau in Deutschland",
++                       "Augen auf! (So sieht es aus!)",
++                       "Paradies Bundesrepublik - Rente fuer die Welt -",
++                       "Libanesen in Berlin",
++                       "Garather klagen ueber eskalierende Gewalt im Stadtteil!",
++                       "Auslaender erschleichen sich zunehmend Sozialleistungen",
++                       "Auslaenderkriminalitaet steigt weiter!",
++                       "Das kann unmoeglich sein -Leserbrief-",
++                       "Nein zum Zuwanderungsgesetz !",
++                       "Skandalurteil in Darmstadt",
++                       "Auf Kosten der deutschen Beitragszahler und Rentner!",
++                       "Wir haben die Auslaender doch geholt?!",
++                       "TUERKEN-TERROR AM HIMMELFAHRTSTAG",
++                       "MULTI-KULTI-BANDE TYRANNISIERTE MITSCHUELER",
++                       "ASYLANTEN BEGRABSCHTEN DEUTSCHES MAEDCHEN",
++                       "Was Deutschland braucht, sind deutsche Kinder!",
++                       "Diplomatische Zensur",
++                       "EU gibt Erwerbslosen volle Freizuegigkeit",
++                       "Richter unterstuetzt kriminelle Auslaenderin",
++                       "Auslaenderanteile in Schweizer Gefaengnissen",
++                       "Augen auf! (So sieht es aus!)",
++                       "Neue Voelkerwanderung droht!",
++                       "Mehr fuer Auslaender als fuer Deutsche tun!",
++		       "Skandal in Berlin",
++		       "Auslaendergewalt: Herr Rau, wo waren Sie?",
++		       "Marokkanischer Wiederholungstaeter vergewaltigte 17-jaehriges Maedel",
++		       "DEUTSCHES MAEDCHEN FAST VERGEWALTIGT",
++		       "So sieht die Wahrheit aus!",
++		       "Geschrieben von Margrit",
++                       ]
++    subject = headers.get("Subject", "").strip()
++    for prefix in banned_prefixes:
++        if subject.startswith(prefix):
++	    raise RejectVirus("looks like Sober.h virus")
++
++
+ def check_filename (match, what):
+     if match is None:
+         return
+     fn = match.group('fn1') or match.group('fn2')
+     ext = match.group('ext1') or match.group('ext2')
+     if ext.lower() in BAD_EXTENSIONS:
+-        raise RejectMessage(
++        raise RejectVirus(
+             "message rejected -- looks like a virus\n"
+             "(name of %s (%r) indicates executable content)"
+             % (what, fn))
+ 
+ def local_scan (info, headers, fd):
+ 
++    # Easy check for Sober.h virus (German far-right propaganda virus)
++    check_sober_h(info, headers, fd)
++
+     # First check the message's "Content-type" header, to handle the
+     # case of a message whose entire body is a Windows executable.
+     ctype = headers.get("Content-type")
+     if ctype:
+         match = msgname_re.search(ctype)
+         check_filename(match, "message body")
+-    
++
+     # Read in the first chunk (32k by default) of the message body, and
+     # scan it for evil attachments or uuencoded files.
+     fpos = get_fdpos(fd)
+@@ -130,5 +176,5 @@
+     assert fn == fn2[1:-1], "%r != %r" % (fn, fn2[1:-1])
+     assert ext == "scr" and ext.lower() in BAD_EXTENSIONS
+ 
+-    
+-    
++
++
+diff -urN elspy-0.1.1.org/lib/execontent_smart.py elspy-0.1.1/lib/execontent_smart.py
+--- elspy-0.1.1.org/lib/execontent_smart.py	1970-01-01 01:00:00.000000000 +0100
++++ elspy-0.1.1/lib/execontent_smart.py	2004-07-28 02:56:09.000000000 +0200
+@@ -0,0 +1,106 @@
++# elspy library -- smart detection of evil attachments
++#
++# This is based on Neil Schemenauer's qmail-queue wrapper to reject
++# messages with executable attachments:
++#   http://arctrix.com/~nas/misc/qmail-filter-exe.py
++#
++# It parses the message's MIME structure, looks at the name of
++# each attachment, and rejects the whole message if there are
++# any attachments with suspicious-looking names.
++
++# Copyright (c) 2003 Gregory P. Ward.  All rights reserved.
++# See README.txt for licensing information.
++#
++# The elspy home page is at http://elspy.sourceforge.net/ .
++
++__revision__ = "$Id$"
++
++import re
++from cStringIO import StringIO
++import struct
++from zipfile import ZipFile, BadZipfile
++from elspy.util import parse_message, RejectVirus
++from elspy.execontent_simple import BAD_EXTENSIONS
++
++# %s expands to eg. "vbs|vbe|wsf|..." (depending on BAD_EXTENSIONS).
++# Regex is compiled lazily so outside code can tweak BAD_EXTENSIONS.
++fname_pat = r'\.(%s)\b'
++fname_re = None
++
++def is_executable (data):
++    """
++    Return true if 'data' (which should be the initial contents of
++    a file) signifies a Windows executable.
++    """
++    if not data:
++        return 0
++    return len(data) > 24 and data[0:2] == "MZ" and data[24] == "@"
++
++def scan_zipfile (attach_name, attachment):
++    try:
++        file = StringIO(attachment.get_payload(decode=1))
++        zipfile = ZipFile(file)
++
++        # Iterate over all files in the ZIP file; if any of them look like a
++        # Windows executable (either by name or contents), reject the whole
++        # message.
++        names = zipfile.namelist()
++        for name in names:
++            # The name gives it away (too easy!)
++            if fname_re.search(name):
++                raise RejectVirus(
++                    "message rejected -- looks like a virus\n"
++                    "(attachment %r contains executable file %r)"
++                    % (attach_name, name))
++
++            # OK, let's look at the content of the file.  If it looks like
++            # a Windows executable, then reject it.
++            contents = zipfile.read(names[0])
++            if is_executable(contents):
++                raise RejectVirus(
++                    "message rejected -- looks like a virus\n"
++                    ("attachment %r contains a Windows executable file"
++                     % attach_name))
++    except (BadZipfile, RuntimeError, struct.error), err:
++        raise RejectVirus("message rejected -- looks like a virus\n"
++                          "(contains an invalid ZIP file attachment)")
++
++
++
++def local_scan (info, headers, fd, msg=None):
++    global fname_pat, fname_re
++    if msg is None:
++        msg = parse_message(headers, fd)
++
++    if fname_re is None:
++        fname_re = re.compile(fname_pat % '|'.join(BAD_EXTENSIONS),
++                              re.IGNORECASE)
++
++    i = 0
++    for part in msg.walk():
++        # Only interested in the leaves of the tree
++	i += 1
++	if part.is_multipart():
++            continue
++
++        attachname = (part.get_param('name') or
++                      part.get_param('filename',
++                                     header='content-disposition'))
++        if attachname and fname_re.search(attachname):
++            # Look paw -- I caught me a virus!
++            raise RejectVirus(
++                "message rejected -- looks like a virus\n"
++                "(attachment name %r indicates executable content)"
++                % attachname)
++
++        # Check for executables embedded in ZIP file.
++        elif attachname and attachname.lower().endswith(".zip"):
++            scan_zipfile(attachname, part)
++
++        elif is_executable(part.get_payload(decode=1)):
++            if attachname:
++                why = "(attachment %r is executable on Windows)" % attachname
++            else:
++                why = "(contains an attachment executable on Windows)"
++            raise RejectVirus("message rejected -- looks like a virus\n" +
++                              why)
+diff -urN elspy-0.1.1.org/lib/__init__.py elspy-0.1.1/lib/__init__.py
+--- elspy-0.1.1.org/lib/__init__.py	2002-10-23 00:55:30.000000000 +0200
++++ elspy-0.1.1/lib/__init__.py	2003-04-09 00:57:57.000000000 +0200
+@@ -9,9 +9,9 @@
+ #
+ # The elspy home page is at http://elspy.sourceforge.net/ .
+ 
+-__revision__ = "$Id$"
++__revision__ = "$Id$"
+ 
+-__version__ = "0.1.1"
++__version__ = "0.2pre"
+ 
+ import sys
+ from traceback import format_exception
+@@ -80,7 +80,4 @@
+ # Other useful stuff lives in util.py -- import it from there
+ # so local_scan() authors don't have to know about
+ # the internal structure of this package.
+-from elspy.util import \
+-     MessageDisposition, AcceptMessage, RejectMessage, TempRejectMessage, \
+-     log_error, log_warning, get_fdpos, set_fdpos, \
+-     HeaderList, EximInfo
++from elspy.util import *
+diff -urN elspy-0.1.1.org/lib/maildir.py elspy-0.1.1/lib/maildir.py
+--- elspy-0.1.1.org/lib/maildir.py	2002-10-22 00:20:14.000000000 +0200
++++ elspy-0.1.1/lib/maildir.py	2003-04-19 03:30:59.000000000 +0200
<<Diff was trimmed, longer than 597 lines>>



More information about the pld-cvs-commit mailing list