#! /usr/local/bin/python2.3 """Simple version repository for SpamBayes core, and our main apps. Also has the ability to load this version information from a remote location (in that case, we actually load a "ConfigParser" version of the file to avoid importing code we can't trust.) This allows any app to check if there is a later version available. The makefile process for the website will execute this as a script, which will generate the "ConfigParser" version for the web. """ # See bug 806238: urllib2 fails in Outlook new-version chk. # A reason for why the spambayes.org URL fails is given in a comment there. #LATEST_VERSION_HOME="http://www.spambayes.org/download/Version.cfg" # The SF URL instead works for Tim and xenogeist. LATEST_VERSION_HOME="http://spambayes.sourceforge.net/download/Version.cfg" # This module is part of the spambayes project, which is Copyright 2002-4 # The Python Software Foundation and is covered by the Python Software # Foundation license. versions = { # Non app specific - changed when "spambayes\*" changes significantly "Version": 0.3, "Description": "SpamBayes Engine", "Date": "January 2004", "Full Description": "%(Description)s Version %(Version)s (%(Date)s)", # Sub-dict for application specific version strings. "Apps": { "sb_filter" : { "Version": 0.3, "Description": "SpamBayes Command Line Filter", "Date": "April 2004", "Full Description": "%(Description)s Version %(Version)s (%(Date)s)", }, "Outlook" : { # Note these version numbers currently don't appear in the # "description" strings below - they just need to increment # so automated version checking works. "Version": 1.04, "BinaryVersion": 1.04, "Description": "SpamBayes Outlook Addin", "Date": "March 2005", "Full Description": "%(Description)s Version 1.0.4 (%(Date)s)", "Full Description Binary": "%(Description)s Binary Version 1.0.4 (%(Date)s)", # Note this means we can change the download page later, and old # versions will still go to the new page. # We may also like to have a "Release Notes Page" item later? "Download Page": "http://spambayes.sourceforge.net/windows.html" }, "POP3 Proxy" : { # Note these version numbers also currently don't appear in the # "description" strings below - see above "Version": 1.04, "BinaryVersion": 1.04, "Description": "SpamBayes POP3 Proxy", "Date": "March 2005", "Full Description": """%(Description)s Version 1.0.4 (%(Date)s)""", "Full Description Binary": """%(Description)s Binary Version 1.0.4 (%(Date)s)""", # Note this means we can change the download page later, and old # versions will still go to the new page. # We may also like to have a "Release Notes Page" item later? "Download Page": "http://spambayes.sourceforge.net/windows.html" }, "Lotus Notes Filter" : { "Version": 0.02, "Description": "SpamBayes Lotus Notes Filter", "Date": "February 2004", "Full Description": "%(Description)s Version %(Version)s (%(Date)s)", }, "IMAP Filter" : { "Version": 0.6, "Description": "SpamBayes IMAP Filter", "Date": "January 2005", "Full Description": """%(Description)s Version %(Version)s (%(Date)s)""", }, "IMAP Server" : { "Version": 0.02, "Description": "SpamBayes IMAP Server", "Date": "January 2004", "Full Description": """%(Description)s Version %(Version)s (%(Date)s)""", }, }, } def get_version_string(app = None, description_key = "Full Description", version_dict = None): """Get a pretty version string, generally just to log or show in a UI""" if version_dict is None: version_dict = versions if app is None: dict = version_dict else: dict = version_dict["Apps"][app] return dict[description_key] % dict def get_version_number(app = None, version_key = "Version", version_dict = None): """Get a version number, as a float. This would primarily be used so some app or extension can determine if we are later than a specific version of either the engine or a specific app. Maybe YAGNI. """ if version_dict is None: version_dict = versions if app is None: dict = version_dict else: dict = version_dict["Apps"][app] return dict[version_key] # Utilities to check the "latest" version of an app. # Assumes that a 'config' version of this file exists at the given URL # No exceptions are caught try: import ConfigParser class MySafeConfigParser(ConfigParser.SafeConfigParser): def optionxform(self, optionstr): return optionstr # no lower! except AttributeError: # No SafeConfigParser! MySafeConfigParser = None def fetch_latest_dict(url=LATEST_VERSION_HOME): if MySafeConfigParser is None: raise RuntimeError, \ "Sorry, but only Python 2.3 can trust remote config files" import urllib2 from spambayes.Options import options server = options["globals", "proxy_server"] if server != "": if ':' in server: server, port = server.split(':', 1) port = int(port) else: port = 8080 if options["globals", "proxy_username"]: user_pass_string = "%s:%s" % \ (options["globals", "proxy_username"], options["globals", "proxy_password"]) else: user_pass_string = "" proxy_support = urllib2.ProxyHandler({"http" : "http://%s@%s:%d" % \ (user_pass_string, server, port)}) opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler) urllib2.install_opener(opener) stream = urllib2.urlopen(url) cfg = MySafeConfigParser() cfg.readfp(stream) ret_dict = {} apps_dict = ret_dict["Apps"] = {} for sect in cfg.sections(): if sect=="SpamBayes": target_dict = ret_dict else: target_dict = apps_dict.setdefault(sect, {}) for opt in cfg.options(sect): val = cfg.get(sect, opt) # some butchering try: val = float(val) except ValueError: pass target_dict[opt] = val return ret_dict # Utilities for generating a 'config' version of this file. # The output of this should exist at the URL above. def _make_cfg_section(stream, key, this_dict): stream.write("[%s]\n" % key) for name, val in this_dict.items(): if type(val)==type(''): val_str = repr(val)[1:-1] elif type(val)==type(0.0): val_str = str(val) elif type(val)==type({}): val_str = None # sub-dict else: print "Skipping unknown value type: %r" % val val_str = None if val_str is not None: stream.write("%s:%s\n" % (name, val_str)) stream.write("\n") def make_cfg(stream): stream.write("# This file is generated from spambayes/Version.py" \ " - do not edit\n") _make_cfg_section(stream, "SpamBayes", versions) for appname in versions["Apps"]: _make_cfg_section(stream, appname, versions["Apps"][appname]) def main(args): import sys if '-g' in args: make_cfg(sys.stdout) sys.exit(0) print "SpamBayes engine version:", get_version_string() # Enumerate applications print print "Application versions:" for app in versions["Apps"]: print "\n%s: %s" % (app, get_version_string(app)) print print "Fetching the lastest version information..." try: latest_dict = fetch_latest_dict() except: print "FAILED to fetch the latest version" import traceback traceback.print_exc() sys.exit(1) print print "SpamBayes engine version:", get_version_string(version_dict=latest_dict) # Enumerate applications print print "Application versions:" for app in latest_dict["Apps"]: print "\n%s: %s" % (app, get_version_string(app, version_dict=latest_dict)) if __name__=='__main__': import sys main(sys.argv)