Branch 'pykolab-0.5' - 13 commits - configure.ac pykolab/cli pykolab/imap pykolab/imap_utf7.py pykolab/xml wallace/module_resources.py

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Mon Jul 15 18:31:17 CEST 2013


 configure.ac                             |    4 -
 pykolab/cli/cmd_create_mailbox.py        |    4 -
 pykolab/cli/cmd_delete_mailbox.py        |    4 +
 pykolab/cli/cmd_list_mailbox_acls.py     |    2 
 pykolab/cli/cmd_list_mailbox_metadata.py |    2 
 pykolab/cli/cmd_list_mailboxes.py        |    3 -
 pykolab/cli/cmd_list_quota.py            |   12 +++-
 pykolab/cli/cmd_set_mailbox_acl.py       |    2 
 pykolab/cli/cmd_undelete_mailbox.py      |    2 
 pykolab/imap/__init__.py                 |   51 +++++++++++------
 pykolab/imap/cyrus.py                    |   10 +++
 pykolab/imap_utf7.py                     |   91 +++++++++++++++++++++++++++++++
 pykolab/xml/event.py                     |    1 
 wallace/module_resources.py              |    2 
 14 files changed, 158 insertions(+), 32 deletions(-)

New commits:
commit fa65938b3b1e8efac5a01987fce44eae20a1079c
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Tue Jul 9 14:49:04 2013 +0100

    Make sure events hold the required X-Kolab-MIME-Version: 3.0 header

diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index b92339a..80cee77 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -799,6 +799,7 @@ class Event(object):
         msg['To'] = ', '.join([x.__str__() for x in self.get_attendees()])
         msg['Date'] = formatdate(localtime=True)
 
+        msg.add_header('X-Kolab-MIME-Version', '3.0')
         msg.add_header('X-Kolab-Type', 'application/x-vnd.kolab.event')
 
         text = utils.multiline_message("""


commit 84f8fc974e7847ab325fbefa40041d97fe5cc216
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Jul 6 11:06:48 2013 +0100

    Use utf-7 names when deleting folders by wildcard match as well

diff --git a/pykolab/imap/cyrus.py b/pykolab/imap/cyrus.py
index 079c4dc..9a8e97c 100644
--- a/pykolab/imap/cyrus.py
+++ b/pykolab/imap/cyrus.py
@@ -193,6 +193,14 @@ class Cyrus(cyruslib.CYRUS):
 
         return server
 
+    def folder_utf7(self, folder):
+        from pykolab import imap_utf7
+        return imap_utf7.encode(folder)
+
+    def folder_utf8(self, folder):
+        from pykolab import imap_utf7
+        return imap_utf7.decode(folder)
+
     def _setquota(self, mailfolder, quota):
         """
             Login to the actual backend server.
@@ -355,7 +363,7 @@ class Cyrus(cyruslib.CYRUS):
             if not mbox['domain'] == None:
                 verify_folder_search = "%s@%s" % (verify_folder_search, mbox['domain'])
 
-            folders = self.lm(verify_folder_search)
+            folders = self.lm(self.folder_utf7(verify_folder_search))
 
             # NOTE: Case also covered is valid hexadecimal folders; won't be the
             # actual check as intended, but doesn't give you anyone else's data


commit a35735afa63e61fd49d7cc80f0001988414edbaa
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 21:48:22 2013 +0100

    0.5.14

diff --git a/configure.ac b/configure.ac
index 032cd8d..6468fc5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([pykolab], 0.5.13)
+AC_INIT([pykolab], 0.5.14)
 AC_SUBST([RELEASE], 1)
 
 AC_CONFIG_SRCDIR(pykolab/constants.py.in)


commit 3491b74df4ef6010f2f3df223ca2c3d421cb3d51
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 20:22:33 2013 +0100

    Fix the utf7 backend versus preferred utf8 display

diff --git a/pykolab/cli/cmd_delete_mailbox.py b/pykolab/cli/cmd_delete_mailbox.py
index 51cbe69..47352d2 100644
--- a/pykolab/cli/cmd_delete_mailbox.py
+++ b/pykolab/cli/cmd_delete_mailbox.py
@@ -53,5 +53,5 @@ def execute(*args, **kw):
     delete_folders = imap.list_folders(delete_folder)
 
     for delete_folder in delete_folders:
-        imap.delete_mailfolder(imap.folder_utf8(delete_folder))
+        imap.delete_mailfolder(delete_folder)
 
diff --git a/pykolab/cli/cmd_list_mailbox_acls.py b/pykolab/cli/cmd_list_mailbox_acls.py
index 62bac4f..5d7e04e 100644
--- a/pykolab/cli/cmd_list_mailbox_acls.py
+++ b/pykolab/cli/cmd_list_mailbox_acls.py
@@ -55,7 +55,7 @@ def execute(*args, **kw):
 
     else:
         acls = []
-        folders = imap.lm(folder)
+        folders = imap.list_folders(folder)
         for folder in folders:
             print "Folder", folder
             acls = imap.list_acls(folder)
diff --git a/pykolab/cli/cmd_list_mailbox_metadata.py b/pykolab/cli/cmd_list_mailbox_metadata.py
index b430896..17b8c02 100644
--- a/pykolab/cli/cmd_list_mailbox_metadata.py
+++ b/pykolab/cli/cmd_list_mailbox_metadata.py
@@ -81,7 +81,7 @@ def execute(*args, **kw):
 
     else:
         metadata = []
-        folders = imap.lm(folder)
+        folders = imap.list_folders(folder)
         for folder in folders:
             print "Folder", folder
 
diff --git a/pykolab/cli/cmd_list_quota.py b/pykolab/cli/cmd_list_quota.py
index c81725a..1916631 100644
--- a/pykolab/cli/cmd_list_quota.py
+++ b/pykolab/cli/cmd_list_quota.py
@@ -50,7 +50,7 @@ def execute(*args, **kw):
 
     folders = []
 
-    quota_folders = imap.lm(quota_folder)
+    quota_folders = imap.list_folders(quota_folder)
     for quota_folder in quota_folders:
         try:
             (used, quota) = imap.get_quota(quota_folder)
@@ -62,7 +62,10 @@ def execute(*args, **kw):
                     percentage = round(((float)(used)/(float)(quota)) * 100.0, 1)
                     print "%d (Used: %d, Percentage: %d)" % (quota, used, percentage)
             else:
-                print "No quota"
+                if used == None:
+                    print "%d (Used: %d, Percentage: %d)" % (quota, 0, 0)
+                else:
+                    print "No quota"
         except:
             try:
                 (quota_root, used, quota) = imap.get_quota_root(quota_folder)
@@ -74,7 +77,10 @@ def execute(*args, **kw):
                         percentage = round(((float)(used)/(float)(quota)) * 100.0, 1)
                         print "%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, used, percentage)
                 else:
-                    print "No quota"
+                    if used == None and not quota_root == None:
+                        print "%d (Root: %s, Used: %d, Percentage: %d)" % (quota, quota_root, 0, 0)
+                    else:
+                        print "No quota"
             except:
                 print "No quota root"
 
diff --git a/pykolab/cli/cmd_set_mailbox_acl.py b/pykolab/cli/cmd_set_mailbox_acl.py
index 8ba4567..0c76667 100644
--- a/pykolab/cli/cmd_set_mailbox_acl.py
+++ b/pykolab/cli/cmd_set_mailbox_acl.py
@@ -67,6 +67,6 @@ def execute(*args, **kw):
         print >> sys.stderr, _("No such folder %r") % (folder)
 
     else:
-        folders = imap.lm(folder)
+        folders = imap.list_folders(folder)
         for folder in folders:
             imap.set_acl(folder, identifier, acl)
diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index a9ab38d..15eca90 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -225,8 +225,14 @@ class IMAP(object):
         """
             Obtain all metadata entries on a folder
         """
+        metadata = {}
 
-        return self.imap.getannotation(folder, '*')
+        _metadata = self.imap.getannotation(self.folder_utf7(folder), '*')
+
+        for (k,v) in _metadata.items():
+            metadata[self.folder_utf8(k)] = v
+
+        return metadata
 
     def namespaces(self):
         """
@@ -269,7 +275,7 @@ class IMAP(object):
             Set an ACL entry on a folder.
         """
 
-        self.imap.sam(folder, identifier, acl)
+        self.imap.sam(self.folder_utf7(folder), identifier, acl)
 
     def set_metadata(self, folder, metadata_path, metadata_value, shared=True):
         """
@@ -541,7 +547,7 @@ class IMAP(object):
             Check if the environment has a folder named folder.
         """
         folders = self.imap.lm(self.folder_utf7(folder))
-        log.debug(_("Looking for folder '%s', we found folders: %r") % (folder,folders), level=8)
+        log.debug(_("Looking for folder '%s', we found folders: %r") % (folder,[self.folder_utf8(x) for x in folders]), level=8)
         # Greater then one, this folder may have subfolders.
         if len(folders) > 0:
             return True
@@ -811,10 +817,10 @@ class IMAP(object):
         """
             List the ACL entries on a folder
         """
-        return self.imap.lam(folder)
+        return self.imap.lam(self.folder_utf7(folder))
 
     def list_folders(self, pattern):
-        return self.lm(self.folder_utf7(pattern))
+        return [self.folder_utf8(x) for x in self.lm(self.folder_utf7(pattern))]
 
     def list_user_folders(self, primary_domain=None, secondary_domains=[]):
         """


commit 6ffd3d291d73e778338525c48ba2572d5b4f969a
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 19:50:38 2013 +0100

    Make sure the pattern for deleting mailboxes is passed on right between various function calls

diff --git a/pykolab/cli/cmd_delete_mailbox.py b/pykolab/cli/cmd_delete_mailbox.py
index 0978b7e..51cbe69 100644
--- a/pykolab/cli/cmd_delete_mailbox.py
+++ b/pykolab/cli/cmd_delete_mailbox.py
@@ -49,7 +49,9 @@ def execute(*args, **kw):
     imap = IMAP()
 
     imap.connect()
-    delete_folders = imap.lm(delete_folder)
+
+    delete_folders = imap.list_folders(delete_folder)
+
     for delete_folder in delete_folders:
-        imap.delete_mailfolder(delete_folder)
+        imap.delete_mailfolder(imap.folder_utf8(delete_folder))
 
diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index 6933adb..a9ab38d 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -217,6 +217,10 @@ class IMAP(object):
         from pykolab import imap_utf7
         return imap_utf7.encode(folder)
 
+    def folder_utf8(self, folder):
+        from pykolab import imap_utf7
+        return imap_utf7.decode(folder)
+
     def get_metadata(self, folder):
         """
             Obtain all metadata entries on a folder
@@ -792,16 +796,16 @@ class IMAP(object):
 
         log.info(_("Deleting folder %s") % (mailfolder_path))
 
-        self.imap.dm(mailfolder_path)
+        self.imap.dm(self.folder_utf7(mailfolder_path))
 
     def get_quota(self, mailfolder_path):
         try:
-            return self.lq(mailfolder_path)
+            return self.lq(self.folder_utf7(mailfolder_path))
         except:
             return
 
     def get_quota_root(self, mailfolder_path):
-        return self.lqr(mailfolder_path)
+        return self.lqr(self.folder_utf7(mailfolder_path))
 
     def list_acls(self, folder):
         """
@@ -809,6 +813,9 @@ class IMAP(object):
         """
         return self.imap.lam(folder)
 
+    def list_folders(self, pattern):
+        return self.lm(self.folder_utf7(pattern))
+
     def list_user_folders(self, primary_domain=None, secondary_domains=[]):
         """
             List the INBOX folders in the IMAP backend. Returns a list of unique


commit 26a08fdcc8746650e279770ecf52077d96a0821f
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 19:38:56 2013 +0100

    If a string isn't unicode, attempt to make it so

diff --git a/pykolab/imap_utf7.py b/pykolab/imap_utf7.py
index 6a670b5..038623b 100644
--- a/pykolab/imap_utf7.py
+++ b/pykolab/imap_utf7.py
@@ -29,7 +29,10 @@ class FolderNameError(ValueError):
 
 def encode(s):
     if isinstance(s, str) and sum(n for n in (ord(c) for c in s) if n > 127):
-        raise FolderNameError("%r contains characters not valid in a str folder name. "
+        try:
+            s = unicode(s, "UTF-8")
+        except Exception, errmsg:
+            raise FolderNameError("%r contains characters not valid in a str folder name. "
                               "Convert to unicode first?" % s)
 
     r = []
@@ -49,6 +52,7 @@ def encode(s):
             _in.append(c)
     if _in:
         r.extend(['&', modified_base64(''.join(_in)), '-'])
+
     return ''.join(r)
 
 


commit ae26c04fefaf11e16601348099952b90bdf5bfc8
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 19:38:34 2013 +0100

    Decode the mailbox names in kolab list-mailboxes

diff --git a/pykolab/cli/cmd_list_mailboxes.py b/pykolab/cli/cmd_list_mailboxes.py
index 3451e7c..13d5fd7 100644
--- a/pykolab/cli/cmd_list_mailboxes.py
+++ b/pykolab/cli/cmd_list_mailboxes.py
@@ -21,6 +21,7 @@ import commands
 
 import pykolab
 
+from pykolab import imap_utf7
 from pykolab.imap import IMAP
 from pykolab.translate import _
 
@@ -76,4 +77,4 @@ def execute(*args, **kw):
         folders.extend(imap.lm(search))
 
     for folder in folders:
-        print folder
+        print imap_utf7.decode(folder)


commit f654d9d51a245a6fc3f6b0ff5412f442cbe8c99a
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 19:26:32 2013 +0100

    Use imap.create_folder() for kolab cm

diff --git a/pykolab/cli/cmd_create_mailbox.py b/pykolab/cli/cmd_create_mailbox.py
index 5510e56..ff1bc6a 100644
--- a/pykolab/cli/cmd_create_mailbox.py
+++ b/pykolab/cli/cmd_create_mailbox.py
@@ -63,9 +63,7 @@ def execute(*args, **kw):
     imap = IMAP()
     imap.connect()
 
-    admin_login = conf.get('cyrus-imap', 'admin_login')
-
-    imap.cm(mailbox)
+    imap.create_folder(mailbox)
 
     if not conf.metadata == None:
         imap.set_metadata(mailbox, conf.metadata.split('=')[0], conf.metadata.split('=')[1])


commit cac4578a364762b7b3240acc1e04f23c14d02ade
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 18:08:52 2013 +0100

    pykolab 0.5.13

diff --git a/configure.ac b/configure.ac
index f02ee59..032cd8d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([pykolab], 0.5.12)
-AC_SUBST([RELEASE], 2)
+AC_INIT([pykolab], 0.5.13)
+AC_SUBST([RELEASE], 1)
 
 AC_CONFIG_SRCDIR(pykolab/constants.py.in)
 


commit 67813db2f3fce7ae37c763fd1baf64f38a1709c8
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 18:07:48 2013 +0100

    Correct IMAP not being loaded / initialized

diff --git a/pykolab/cli/cmd_undelete_mailbox.py b/pykolab/cli/cmd_undelete_mailbox.py
index 316e164..6cac2b0 100644
--- a/pykolab/cli/cmd_undelete_mailbox.py
+++ b/pykolab/cli/cmd_undelete_mailbox.py
@@ -21,6 +21,7 @@ import commands
 
 import pykolab
 
+from pykolab.imap import IMAP
 from pykolab.translate import _
 
 log = pykolab.getLogger('pykolab.cli')
@@ -40,6 +41,7 @@ def execute(*args, **kw):
     if len(conf.cli_args) > 0:
         target_folder = conf.cli_args.pop(0)
 
+    imap = IMAP()
     imap.connect()
     imap.undelete_mailfolder(undelete_folder, target_folder)
 


commit 64196a670ccd3efbbf968d64a7b6fce00b8c5bdf
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 18:06:16 2013 +0100

    Correct the resulting message in message_from_string not detecting the message is multipart

diff --git a/wallace/module_resources.py b/wallace/module_resources.py
index 5a66dda..3f34105 100644
--- a/wallace/module_resources.py
+++ b/wallace/module_resources.py
@@ -127,7 +127,7 @@ def execute(*args, **kw):
 
     _message = json.load(open(filepath, 'r'))
     log.debug("Loaded message %r" % (_message), level=9)
-    message = message_from_string(_message['data'])
+    message = message_from_string(str(_message['data']))
     recipients = _message['to']
 
     any_itips = False


commit 9c5a9c6d076317b4ab12a8c7da0a44984bf65dd1
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 18:03:08 2013 +0100

    Correct conversion to utf7

diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index 5e72769..6933adb 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -197,6 +197,8 @@ class IMAP(object):
                 log.warning(_("Called imap.disconnect() on a server that we had no connection to."))
 
     def create_folder(self, folder_path, server=None):
+        folder_path = self.folder_utf7(folder_path)
+
         if not server == None:
             if not self._imap.has_key(server):
                 self.connect(server=server)
@@ -213,7 +215,7 @@ class IMAP(object):
 
     def folder_utf7(self, folder):
         from pykolab import imap_utf7
-        return imap_utf7.encode(folder_path)
+        return imap_utf7.encode(folder)
 
     def get_metadata(self, folder):
         """


commit db06665be4e8356208c975b87fb1f6750a1e842f
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Jul 5 17:09:53 2013 +0100

    Make sure folder names are utf-7 encoded
    
    Conflicts:
    	pykolab/imap/__init__.py

diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index c91d84f..5e72769 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -211,6 +211,10 @@ class IMAP(object):
         else:
             raise AttributeError, _("%r has no attribute %s") % (self,name)
 
+    def folder_utf7(self, folder):
+        from pykolab import imap_utf7
+        return imap_utf7.encode(folder_path)
+
     def get_metadata(self, folder):
         """
             Obtain all metadata entries on a folder
@@ -275,10 +279,7 @@ class IMAP(object):
             shared = False
             metadata_path = metadata_path.replace('/private/', '/')
 
-        if not shared:
-            log.warning(_("Private annotations need to be set using the appropriate user account."))
-
-        self.imap._setannotation(folder, metadata_path, metadata_value, shared)
+        self.imap._setannotation(self.folder_utf7(folder), metadata_path, metadata_value, shared)
 
     def shared_folder_create(self, folder_path, server=None):
         """
@@ -402,9 +403,10 @@ class IMAP(object):
             folder_name = additional_folder
 
             try:
-                self.imap.cm(folder_name)
+                self.create_folder(folder_name)
             except:
                 log.warning(_("Mailbox already exists: %s") % (folder_name))
+                continue
 
             if additional_folders[additional_folder].has_key("annotations"):
                 for annotation in additional_folders[additional_folder]["annotations"].keys():
@@ -532,7 +534,7 @@ class IMAP(object):
         """
             Check if the environment has a folder named folder.
         """
-        folders = self.imap.lm(folder)
+        folders = self.imap.lm(self.folder_utf7(folder))
         log.debug(_("Looking for folder '%s', we found folders: %r") % (folder,folders), level=8)
         # Greater then one, this folder may have subfolders.
         if len(folders) > 0:
@@ -559,8 +561,8 @@ class IMAP(object):
                         _("Setting ACL rights %s for subject %s on folder " + \
                             "%s") % (rights,subject,folder), level=8)
 
-                self.imap.sam(
-                        folder,
+                self.set_acl(
+                        self.folder_utf7(folder),
                         "%s" % (subject),
                         "%s" % (rights)
                     )
@@ -570,8 +572,8 @@ class IMAP(object):
                         _("Removing ACL rights %s for subject %s on folder " + \
                             "%s") % (rights,subject,folder), level=8)
 
-                self.imap.sam(
-                        folder,
+                self.set_acl(
+                        self.folder_utf7(folder),
                         "%s" % (subject),
                         ""
                     )
diff --git a/pykolab/imap_utf7.py b/pykolab/imap_utf7.py
new file mode 100644
index 0000000..6a670b5
--- /dev/null
+++ b/pykolab/imap_utf7.py
@@ -0,0 +1,87 @@
+# The contents of this file has been derived code from the Twisted project
+# (http://twistedmatrix.com/). The original author is Jp Calderone.
+
+# Twisted project license follows:
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+class FolderNameError(ValueError):
+    pass
+
+
+def encode(s):
+    if isinstance(s, str) and sum(n for n in (ord(c) for c in s) if n > 127):
+        raise FolderNameError("%r contains characters not valid in a str folder name. "
+                              "Convert to unicode first?" % s)
+
+    r = []
+    _in = []
+    for c in s:
+        if ord(c) in (range(0x20, 0x26) + range(0x27, 0x7f)):
+            if _in:
+                r.extend(['&', modified_base64(''.join(_in)), '-'])
+                del _in[:]
+            r.append(str(c))
+        elif c == '&':
+            if _in:
+                r.extend(['&', modified_base64(''.join(_in)), '-'])
+                del _in[:]
+            r.append('&-')
+        else:
+            _in.append(c)
+    if _in:
+        r.extend(['&', modified_base64(''.join(_in)), '-'])
+    return ''.join(r)
+
+
+def decode(s):
+    r = []
+    decode = []
+    for c in s:
+        if c == '&' and not decode:
+            decode.append('&')
+        elif c == '-' and decode:
+            if len(decode) == 1:
+                r.append('&')
+            else:
+                r.append(modified_unbase64(''.join(decode[1:])))
+            decode = []
+        elif decode:
+            decode.append(c)
+        else:
+            r.append(c)
+    if decode:
+        r.append(modified_unbase64(''.join(decode[1:])))
+    out = ''.join(r)
+
+    if not isinstance(out, unicode):
+        out = unicode(out, 'latin-1')
+    return out
+
+
+def modified_base64(s):
+    s_utf7 = s.encode('utf-7')
+    return s_utf7[1:-1].replace('/', ',')
+
+
+def modified_unbase64(s):
+    s_utf7 = '+' + s.replace(',', '/') + '-'
+    return s_utf7.decode('utf-7')




More information about the commits mailing list