From 5cacda68ac76f1c706223c61ddfd024c95002ac5 Mon Sep 17 00:00:00 2001 From: Sebastian Spaeth Date: Wed, 24 Sep 2014 10:24:49 +0200 Subject: [PATCH 1/4] Add the OpenSSL exception The GNU GPL and the OpenSSL license are incompatible. Some distributions take a hardline stance and do not consider OpenSSL to be a systems library (which would permit the usage/distribution of OpenSSL) when distributing apps such as OfflineImap from the same repository. In order to solve these distributions dilemma, we add the OpenSSL exception to our GNU GPL v2+ license. This allows for unambiguous use/distribution of our GNU GPL'ed application with a python linking to openssl. Consent of all contributors has been requested via email by Sebastian@SSpaeth.de. With very few exceptions of minor contributions (which might or might not by copyright-worthy) all past contributors have consented to adding the OpenSSL exception. None of the replying authors has disagreed with adding the exception. The corresponding issues at question: https://github.com/OfflineIMAP/offlineimap/issues/104 Debian bug #747033 We are still missing consent from: 1 Asheesh Laroia 2 Bart Kerkvliet 4 Daniel Burrows 5 David Favro 1 David Logie 1 Eric Dorland 1 Ethan Schoonover 49 Eygene Ryabinkin 1 Loui Chang 1 Luca Capello 1 Michael Witten 2 Mike Dawson 1 Peter Colberg 1 Scott Henson 1 Tom Lawton 1 W. Trevor King 2 X-Ryl669 1 buergi 2 dtk 5 mj Signed-off-by: Sebastian Spaeth --- COPYING | 14 ++++++++++++++ README.md | 7 ++++--- offlineimap/__init__.py | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/COPYING b/COPYING index 5f3f605..6526be4 100644 --- a/COPYING +++ b/COPYING @@ -348,3 +348,17 @@ proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. + +---------------------------------------------------------------- +In addition, as a special exception, the copyright holders give +permission to link the code of portions of this program with the OpenSSL +library under certain conditions as described in each individual source +file, and distribute linked combinations including the two. + +You must obey the GNU General Public License in all respects for all of +the code used other than OpenSSL. If you modify file(s) with this +exception, you may extend this exception to your version of the file(s), +but you are not obligated to do so. If you do not wish to do so, delete +this exception statement from your version. If you delete this exception +statement from all source files in the program, then also delete it +here. diff --git a/README.md b/README.md index 81f78de..7748f62 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,10 @@ documentation. OfflineIMAP does not require additional python dependencies beyond python >=2.6 (although python-sqlite is strongly recommended). -OfflineIMAP is a Free Software project licensed under the GNU General Public -License version 2 (or later). You can download it for free, and you can modify -it. In fact, you are encouraged to contribute to OfflineIMAP. +OfflineIMAP is a Free Software project licensed under the GNU General +Public License version 2 (or later) with a special exception that allows +the OpenSSL library to be used. You can download it for free, and you +can modify it. In fact, you are encouraged to contribute to OfflineIMAP. Documentation ------------- diff --git a/offlineimap/__init__.py b/offlineimap/__init__.py index 1df5d2c..9c62089 100644 --- a/offlineimap/__init__.py +++ b/offlineimap/__init__.py @@ -8,7 +8,7 @@ __copyright__ = "Copyright 2002-2013 John Goerzen & contributors" __author__ = "John Goerzen" __author_email__= "john@complete.org" __description__ = "Disconnected Universal IMAP Mail Synchronization/Reader Support" -__license__ = "Licensed under the GNU GPL v2+ (v2 or any later version)" +__license__ = "Licensed under the GNU GPL v2 or any later version (with an openssl exception)" __bigcopyright__ = """%(__productname__)s %(__bigversion__)s %(__license__)s""" % locals() __homepage__ = "http://offlineimap.org" From 31d06d1ccb5ab8a0113e537151d2f18aa2ff5263 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 5 Oct 2014 11:33:22 +0400 Subject: [PATCH 2/4] Properly capitalize OpenSSL And I am also not against this change in licensing. Signed-off-by: Eygene Ryabinkin --- offlineimap/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/offlineimap/__init__.py b/offlineimap/__init__.py index 9c62089..6a4e0e2 100644 --- a/offlineimap/__init__.py +++ b/offlineimap/__init__.py @@ -8,7 +8,7 @@ __copyright__ = "Copyright 2002-2013 John Goerzen & contributors" __author__ = "John Goerzen" __author_email__= "john@complete.org" __description__ = "Disconnected Universal IMAP Mail Synchronization/Reader Support" -__license__ = "Licensed under the GNU GPL v2 or any later version (with an openssl exception)" +__license__ = "Licensed under the GNU GPL v2 or any later version (with an OpenSSL exception)" __bigcopyright__ = """%(__productname__)s %(__bigversion__)s %(__license__)s""" % locals() __homepage__ = "http://offlineimap.org" From 0aa44c5df41e761367e055f39811713e89ea8c5d Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Wed, 1 Oct 2014 08:14:43 +0400 Subject: [PATCH 3/4] Fix warning processing for MachineUI Old code were trying to pass the message to be output as-is, but it should really be passed as the 'mesg' key in order to be properly processed by MAchineLogFormatter.format(). GitHub pull: https://github.com/OfflineIMAP/offlineimap/pull/64 GitHub pull: https://github.com/OfflineIMAP/offlineimap/pull/118 Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 3 + offlineimap/ui/Machine.py | 138 ++++++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 56 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index 13eba38..47e28ae 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,9 @@ ChangeLog OfflineIMAP v6.5.6.1 (YYYY-MM-DD) ================================= +* Fix warning-level message processing by MachineUI + (GitHub pull #64, GitHub pull #118). + * Support default CA bundle locations for a couple of known Unix systems (Michael Vogt, GutHub pull #19) diff --git a/offlineimap/ui/Machine.py b/offlineimap/ui/Machine.py index cee2849..01abd6f 100644 --- a/offlineimap/ui/Machine.py +++ b/offlineimap/ui/Machine.py @@ -28,82 +28,107 @@ protocol = '7.0.0' class MachineLogFormatter(logging.Formatter): """urlencodes any outputted line, to avoid multi-line output""" - def format(self, record): - # urlencode the "mesg" attribute and append to regular line... - line = super(MachineLogFormatter, self).format(record) - return line + urlencode([('', record.mesg)])[1:] + def format(s, record): + # Mapping of log levels to historic tag names + severity_map = { + 'info': 'msg', + 'warning': 'warn', + } + line = super(MachineLogFormatter, s).format(record) + severity = record.levelname.lower() + if severity in severity_map: + severity = severity_map[severity] + if hasattr(record, "machineui"): + command = record.machineui["command"] + whoami = record.machineui["id"] + else: + command = "" + whoami = currentThread().getName() + + prefix = "%s:%s" % (command, urlencode([('', whoami)])[1:]) + return "%s:%s:%s" % (severity, prefix, urlencode([('', line)])[1:]) + class MachineUI(UIBase): - def __init__(self, config, loglevel = logging.INFO): - super(MachineUI, self).__init__(config, loglevel) - self._log_con_handler.createLock() + def __init__(s, config, loglevel = logging.INFO): + super(MachineUI, s).__init__(config, loglevel) + s._log_con_handler.createLock() """lock needed to block on password input""" # Set up the formatter that urlencodes the strings... - self._log_con_handler.setFormatter(MachineLogFormatter()) + s._log_con_handler.setFormatter(MachineLogFormatter()) - def _printData(self, command, msg): - self.logger.info("%s:%s:%s" % ( - 'msg', command, currentThread().getName()), extra={'mesg': msg}) + # Arguments: + # - handler: must be method from s.logger that reflects + # the severity of the passed message + # - command: command that produced this message + # - msg: the message itself + def _printData(s, handler, command, msg): + handler(msg, + extra = { + 'machineui': { + 'command': command, + 'id': currentThread().getName(), + } + }) def _msg(s, msg): - s._printData('_display', msg) + s._printData(s.logger.info, '_display', msg) - def warn(self, msg, minor = 0): + def warn(s, msg, minor = 0): # TODO, remove and cleanup the unused minor stuff - self.logger.warning("%s:%s:%s:%s" % ( - 'warn', '', currentThread().getName(), msg)) + s._printData(s.logger.warning, '', msg) - def registerthread(self, account): - super(MachineUI, self).registerthread(account) - self._printData('registerthread', account) + def registerthread(s, account): + super(MachineUI, s).registerthread(account) + s._printData(s.logger.info, 'registerthread', account) def unregisterthread(s, thread): UIBase.unregisterthread(s, thread) - s._printData('unregisterthread', thread.getName()) + s._printData(s.logger.info, 'unregisterthread', thread.getName()) def debugging(s, debugtype): - s._printData('debugging', debugtype) + s._printData(s.logger.debug, 'debugging', debugtype) def acct(s, accountname): - s._printData('acct', accountname) + s._printData(s.logger.info, 'acct', accountname) def acctdone(s, accountname): - s._printData('acctdone', accountname) + s._printData(s.logger.info, 'acctdone', accountname) def validityproblem(s, folder): - s._printData('validityproblem', "%s\n%s\n%s\n%s" % \ + s._printData(s.logger.warning, 'validityproblem', "%s\n%s\n%s\n%s" % \ (folder.getname(), folder.getrepository().getname(), folder.get_saveduidvalidity(), folder.get_uidvalidity())) def connecting(s, hostname, port): - s._printData('connecting', "%s\n%s" % (hostname, str(port))) + s._printData(s.logger.info, 'connecting', "%s\n%s" % (hostname, str(port))) def syncfolders(s, srcrepos, destrepos): - s._printData('syncfolders', "%s\n%s" % (s.getnicename(srcrepos), + s._printData(s.logger.info, 'syncfolders', "%s\n%s" % (s.getnicename(srcrepos), s.getnicename(destrepos))) def syncingfolder(s, srcrepos, srcfolder, destrepos, destfolder): - s._printData('syncingfolder', "%s\n%s\n%s\n%s\n" % \ + s._printData(s.logger.info, 'syncingfolder', "%s\n%s\n%s\n%s\n" % \ (s.getnicename(srcrepos), srcfolder.getname(), s.getnicename(destrepos), destfolder.getname())) def loadmessagelist(s, repos, folder): - s._printData('loadmessagelist', "%s\n%s" % (s.getnicename(repos), + s._printData(s.logger.info, 'loadmessagelist', "%s\n%s" % (s.getnicename(repos), folder.getvisiblename())) def messagelistloaded(s, repos, folder, count): - s._printData('messagelistloaded', "%s\n%s\n%d" % \ + s._printData(s.logger.info, 'messagelistloaded', "%s\n%s\n%d" % \ (s.getnicename(repos), folder.getname(), count)) def syncingmessages(s, sr, sf, dr, df): - s._printData('syncingmessages', "%s\n%s\n%s\n%s\n" % \ + s._printData(s.logger.info, 'syncingmessages', "%s\n%s\n%s\n%s\n" % \ (s.getnicename(sr), sf.getname(), s.getnicename(dr), df.getname())) - def copyingmessage(self, uid, num, num_to_copy, srcfolder, destfolder): - self._printData('copyingmessage', "%d\n%s\n%s\n%s[%s]" % \ - (uid, self.getnicename(srcfolder), srcfolder.getname(), - self.getnicename(destfolder), destfolder)) + def copyingmessage(s, uid, num, num_to_copy, srcfolder, destfolder): + s._printData(s.logger.info, 'copyingmessage', "%d\n%s\n%s\n%s[%s]" % \ + (uid, s.getnicename(srcfolder), srcfolder.getname(), + s.getnicename(destfolder), destfolder)) def folderlist(s, list): return ("\f".join(["%s\t%s" % (s.getnicename(x), x.getname()) for x in list])) @@ -113,57 +138,58 @@ class MachineUI(UIBase): def deletingmessages(s, uidlist, destlist): ds = s.folderlist(destlist) - s._printData('deletingmessages', "%s\n%s" % (s.uidlist(uidlist), ds)) + s._printData(s.logger.info, 'deletingmessages', "%s\n%s" % (s.uidlist(uidlist), ds)) def addingflags(s, uidlist, flags, dest): - s._printData("addingflags", "%s\n%s\n%s" % (s.uidlist(uidlist), + s._printData(s.logger.info, "addingflags", "%s\n%s\n%s" % (s.uidlist(uidlist), "\f".join(flags), dest)) def deletingflags(s, uidlist, flags, dest): - s._printData('deletingflags', "%s\n%s\n%s" % (s.uidlist(uidlist), + s._printData(s.logger.info, 'deletingflags', "%s\n%s\n%s" % (s.uidlist(uidlist), "\f".join(flags), dest)) - def threadException(self, thread): - self._printData('threadException', "%s\n%s" % \ - (thread.getName(), self.getThreadExceptionString(thread))) - self.delThreadDebugLog(thread) - self.terminate(100) + def threadException(s, thread): + s._printData(s.logger.warning, 'threadException', "%s\n%s" % \ + (thread.getName(), s.getThreadExceptionString(thread))) + s.delThreadDebugLog(thread) + s.terminate(100) def terminate(s, exitstatus = 0, errortitle = '', errormsg = ''): - s._printData('terminate', "%d\n%s\n%s" % (exitstatus, errortitle, errormsg)) + s._printData(s.logger.info, 'terminate', "%d\n%s\n%s" % (exitstatus, errortitle, errormsg)) sys.exit(exitstatus) def mainException(s): - s._printData('mainException', s.getMainExceptionString()) + s._printData(s.logger.warning, 'mainException', s.getMainExceptionString()) def threadExited(s, thread): - s._printData('threadExited', thread.getName()) + s._printData(s.logger.info, 'threadExited', thread.getName()) UIBase.threadExited(s, thread) def sleeping(s, sleepsecs, remainingsecs): - s._printData('sleeping', "%d\n%d" % (sleepsecs, remainingsecs)) + s._printData(s.logger.info, 'sleeping', "%d\n%d" % (sleepsecs, remainingsecs)) if sleepsecs > 0: time.sleep(sleepsecs) return 0 - def getpass(self, accountname, config, errmsg = None): + def getpass(s, accountname, config, errmsg = None): if errmsg: - self._printData('getpasserror', "%s\n%s" % (accountname, errmsg), - False) + s._printData(s.logger.warning, + 'getpasserror', "%s\n%s" % (accountname, errmsg), + False) - self._log_con_handler.acquire() # lock the console output + s._log_con_handler.acquire() # lock the console output try: - self._printData('getpass', accountname, False) + s._printData(s.logger.info, 'getpass', accountname, False) return (sys.stdin.readline()[:-1]) finally: - self._log_con_handler.release() + s._log_con_handler.release() - def init_banner(self): - self._printData('protocol', protocol) - self._printData('initbanner', offlineimap.banner) + def init_banner(s): + s._printData(s.logger.info, 'protocol', protocol) + s._printData(s.logger.info, 'initbanner', offlineimap.banner) - def callhook(self, msg): - self._printData('callhook', msg) + def callhook(s, msg): + s._printData(s.logger.info, 'callhook', msg) From a76bb0d2a3cb584ebd200abf18f4c425f5bce5d1 Mon Sep 17 00:00:00 2001 From: Eygene Ryabinkin Date: Sun, 5 Oct 2014 11:42:07 +0400 Subject: [PATCH 4/4] Announce addition of OpenSSL license exception in ChangeLog Someone is probably picky about licensing, so allow them to discover the recent change (provided that they read change logs). Signed-off-by: Eygene Ryabinkin --- Changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.rst b/Changelog.rst index 47e28ae..ea7b082 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,12 @@ ChangeLog OfflineIMAP v6.5.6.1 (YYYY-MM-DD) ================================= +* Added OpenSSL exception clause to our main GPL to allow + people to link with OpenSSL in run-time. It is needed + at least for Debian, see + https://lists.debian.org/debian-legal/2002/10/msg00113.html + for details. + * Fix warning-level message processing by MachineUI (GitHub pull #64, GitHub pull #118).