4 commits - cyruslib.py pykolab/cli pykolab/imap

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Wed Aug 8 16:05:23 CEST 2012


 cyruslib.py                              |   69 ++++++++++++++++++++++++-----
 pykolab/cli/cmd_list_mailbox_acls.py     |    8 ++-
 pykolab/cli/cmd_list_mailbox_metadata.py |   66 ++++++++++++++++++++++++++++
 pykolab/cli/cmd_set_mailbox_metadata.py  |   72 +++++++++++++++++++++++++++++++
 pykolab/imap/__init__.py                 |   50 +++++++++++++++++++++
 pykolab/imap/cyrus.py                    |    5 +-
 6 files changed, 253 insertions(+), 17 deletions(-)

New commits:
commit 7aafe929d98318c7a9bd4a5b6bc04e3c299588bc
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Aug 8 15:05:01 2012 +0100

    Ship commands to set and list mailbox metadata

diff --git a/pykolab/cli/cmd_list_mailbox_metadata.py b/pykolab/cli/cmd_list_mailbox_metadata.py
new file mode 100644
index 0000000..87fe1b0
--- /dev/null
+++ b/pykolab/cli/cmd_list_mailbox_metadata.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com)
+#
+# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 or, at your option, any later version
+#
+# 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 Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+import commands
+
+import pykolab
+
+from pykolab.imap import IMAP
+from pykolab.translate import _
+from pykolab import utils
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+    commands.register('list_mailbox_metadata', execute, description=description())
+
+def description():
+    return """Obtain a list of metadata entries on a folder."""
+
+def execute(*args, **kw):
+    try:
+        folder = conf.cli_args.pop(0)
+    except IndexError, errmsg:
+        folder = utils.ask_question(_("Folder name"))
+
+    if len(folder.split('@')) > 1:
+        domain = folder.split('@')[1]
+    else:
+        domain = conf.get('kolab', 'primary_domain')
+
+    imap = IMAP()
+    imap.connect(domain=domain)
+
+    if not imap.has_folder(folder):
+        print >> sys.stderr, _("No such folder %r") % (folder)
+
+    else:
+        metadata = []
+        folders = imap.lm(folder)
+        for folder in folders:
+            print "Folder", folder
+
+            metadata = imap.get_metadata(folder)
+
+            for annotation in metadata[folder].keys():
+                print "  %-49s %s" % (
+                        annotation,
+                        metadata[folder][annotation]
+                    )
diff --git a/pykolab/cli/cmd_set_mailbox_metadata.py b/pykolab/cli/cmd_set_mailbox_metadata.py
new file mode 100644
index 0000000..9aa9b4e
--- /dev/null
+++ b/pykolab/cli/cmd_set_mailbox_metadata.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+# Copyright 2010-2012 Kolab Systems AG (http://www.kolabsys.com)
+#
+# Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen a kolabsys.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 or, at your option, any later version
+#
+# 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 Library General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+import sys
+
+import commands
+
+import pykolab
+
+from pykolab.imap import IMAP
+from pykolab.translate import _
+from pykolab import utils
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+    commands.register('set_mailbox_metadata', execute, description=description())
+
+def description():
+    return """Set an metadata entry on a folder."""
+
+def execute(*args, **kw):
+    try:
+        folder = conf.cli_args.pop(0)
+        try:
+            metadata_path = conf.cli_args.pop(0)
+            try:
+                metadata_value = conf.cli_args.pop(0)
+            except IndexError, errmsg:
+                metadata_value = utils.ask_question(_("Metadata value"))
+
+        except IndexError, errmsg:
+            metadata_path = utils.ask_question(_("Metadata path"))
+            metadata_value = utils.ask_question(_("Metadata value"))
+
+    except IndexError, errmsg:
+        folder = utils.ask_question(_("Folder name"))
+        metadata_path = utils.ask_question(_("Metadata path"))
+        metadata_value = utils.ask_question(_("Metadata value"))
+
+    if len(folder.split('@')) > 1:
+        domain = folder.split('@')[1]
+    else:
+        domain = conf.get('kolab', 'primary_domain')
+
+    imap = IMAP()
+    imap.connect(domain=domain)
+
+    if not imap.has_folder(folder):
+        print >> sys.stderr, _("No such folder %r") % (folder)
+
+    else:
+        folders = imap.lm(folder)
+        for folder in folders:
+            imap.set_metadata(folder, metadata_path, metadata_value)


commit c8a39374cb97cb8ff70ff85b8613e74d77067f40
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Aug 8 15:04:38 2012 +0100

    Add functions that support some of the new command line client commands

diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index 65c232c..aee9148 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -212,6 +212,13 @@ class IMAP(object):
         else:
             raise AttributeError, _("%r has no attribute %s") % (self,name)
 
+    def get_metadata(self, folder):
+        """
+            Obtain all metadata entries on a folder
+        """
+
+        return self.imap.getannotation(folder, '*')
+
     def namespaces(self):
         """
             Obtain the namespaces.
@@ -255,6 +262,49 @@ class IMAP(object):
 
         self.imap.sam(folder, identifier, acl)
 
+    def set_metadata(self, folder, metadata_path, metadata_value, shared=True):
+        """
+            Set a metadata entry on a folder
+        """
+
+        if metadata_path.startswith('/shared/'):
+            shared = True
+
+        if metadata_path.startswith('/shared/'):
+            metadata_path = metadata_path.replace('/shared/', '/')
+        elif metadata_path.startswith('/private/'):
+            shared = False
+            metadata_path = metadata_path.replace('/private/', '/')
+
+        backend = conf.get('kolab', 'imap_backend')
+
+        if not self.domain == None:
+            if conf.has_section(self.domain) and conf.has_option(self.domain, 'imap_backend'):
+                backend = conf.get(self.domain, 'imap_backend')
+
+        if not shared:
+            log.warning(_("Private annotations need to be set using the appropriate user account."))
+
+        admin_login = conf.get(backend, 'admin_login')
+
+        admin_acl = None
+
+        acls = self.list_acls(folder)
+        if acls.has_key(admin_login):
+            admin_acl = acls[admin_login]
+
+        if admin_acl == None:
+            self.set_acl(folder, admin_login, 'lrsipwa')
+        elif not 'w' in admin_acl:
+            self.set_acl(folder, admin_login, '%sw' % (admin_acl))
+
+        self.imap._setannotation(folder, metadata_path, metadata_value, shared)
+
+        if admin_acl == None:
+            self.set_acl(folder, admin_login, '')
+        elif not 'w' in admin_acl:
+            self.set_acl(folder, admin_login, admin_acl)
+
     def shared_folder_create(self, folder_path, server=None):
         """
             Create a shared folder.
diff --git a/pykolab/imap/cyrus.py b/pykolab/imap/cyrus.py
index af3e5b8..9b291bf 100644
--- a/pykolab/imap/cyrus.py
+++ b/pykolab/imap/cyrus.py
@@ -81,6 +81,7 @@ class Cyrus(cyruslib.CYRUS):
 
         if conf.debuglevel > 8:
             self.VERBOSE = True
+            self.m.debug = 5
 
         # Initialize our variables
         self.separator = self.SEP
@@ -217,7 +218,7 @@ class Cyrus(cyruslib.CYRUS):
     def _getannotation(self, *args, **kw):
         return self.getannotation(*args, **kw)
 
-    def _setannotation(self, mailfolder, annotation, value):
+    def _setannotation(self, mailfolder, annotation, value, shared=False):
         """
             Login to the actual backend server, then set annotation.
         """
@@ -228,7 +229,7 @@ class Cyrus(cyruslib.CYRUS):
         #if annotation.startswith('/private'):
 
         try:
-            self.setannotation(mailfolder, annotation, value)
+            self.setannotation(mailfolder, annotation, value, shared)
         except cyruslib.CYRUSError, e:
             log.error(_("Could not set annotation %r on mail folder %r: %r") % (annotation,mailfolder,e))
 


commit fd3e861d85da2093be6d579e253e4290bf8ce932
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Aug 8 15:04:02 2012 +0100

    Print mailbox ACLs listed in a somewhat parseable format

diff --git a/pykolab/cli/cmd_list_mailbox_acls.py b/pykolab/cli/cmd_list_mailbox_acls.py
index 10a3321..7f4a9fc 100644
--- a/pykolab/cli/cmd_list_mailbox_acls.py
+++ b/pykolab/cli/cmd_list_mailbox_acls.py
@@ -55,7 +55,9 @@ def execute(*args, **kw):
         acls = []
         folders = imap.lm(folder)
         for folder in folders:
-            acls.append((folder, imap.list_acls(folder)))
+            print "Folder", folder
+            acls = imap.list_acls(folder)
+
+            for acl in acls.keys():
+                print "  %-13s %s" %(acls[acl], acl)
 
-        for folder, acl in acls:
-            print folder, acl


commit dab08039fcd528f60b4703ec13b79cf3f3224314
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Wed Aug 8 15:03:26 2012 +0100

    Make cyruslib support recognizing the difference between shared and private annotations

diff --git a/cyruslib.py b/cyruslib.py
index f141059..fe06b24 100644
--- a/cyruslib.py
+++ b/cyruslib.py
@@ -137,16 +137,26 @@ class IMAP4(imaplib.IMAP4):
             return False, dat[0]
         return ok(res), dat[0]
 
-    def getannotation(self, mailbox, pattern='*'):
-        typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.shared'))
+    def getannotation(self, mailbox, pattern='*', shared=None):
+        if shared == None:
+            typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('*'))
+        elif shared:
+            typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.shared'))
+        else:
+            typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.priv'))
+
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
-    def setannotation(self, mailbox, desc, value):
+    def setannotation(self, mailbox, desc, value, shared=False):
         if value:
             value = quote(value)
         else:
             value = "NIL"
-        typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) )
+        if shared:
+            typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) )
+        else:
+            typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) )
+
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
     def setquota(self, mailbox, limit):
@@ -208,16 +218,26 @@ class IMAP4_SSL(imaplib.IMAP4_SSL):
             return False, dat[0]
         return ok(res), dat[0]
 
-    def getannotation(self, mailbox, pattern='*'):
-        typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.shared'))
+    def getannotation(self, mailbox, pattern='*', shared=None):
+        if shared == None:
+            typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('*'))
+        elif shared:
+            typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.shared'))
+        else:
+            typ, dat = self._simple_command('GETANNOTATION', mailbox, quote(pattern), quote('value.priv'))
+
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
-    def setannotation(self, mailbox, desc, value):
+    def setannotation(self, mailbox, desc, value, shared=False):
         if value:
             value = quote(value)
         else:
             value = "NIL"
-        typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) )
+        if shared:
+            typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.shared'), value) )
+        else:
+            typ, dat = self._simple_command('SETANNOTATION', mailbox, quote(desc), "(%s %s)" % (quote('value.priv'), value) )
+
         return self._untagged_response(typ, dat, 'ANNOTATION')
 
     def setquota(self, mailbox, limit):
@@ -627,24 +647,49 @@ class CYRUS:
             return {}
         ann = {}
         for annotation in data:
+            self.__verbose( '[GETANNOTATION] RAW %r (length %d)' % (annotation,len(annotation)))
             annotation = annotation.split('"')
-            if len(annotation) != 9:
+            self.__verbose( '[GETANNOTATION] SPLIT %r (length %d)' % (annotation,len(annotation)))
+            if not len(annotation) in [ 9, 17, 21, 37 ]:
                 self.__verbose( '[GETANNOTATION] Invalid annotation entry' )
                 continue
             mbx = self.encode(annotation[1])
-            key = annotation[3]
+            _key = annotation[3]
+
+            if annotation[5] == "value.shared":
+                key = "/shared%s" % (_key)
+            elif annotation[5] == "value.priv":
+                key = "/private%s" % (_key)
+
             value = annotation[7]
+
             self.__verbose( '[GETANNOTATION %s] %s: %s' % (mbx, key, value) )
             if not ann.has_key(mbx):
                 ann[mbx] = {}
             if not ann[mbx].has_key(key):
                 ann[mbx][key] = value
+
+            if len(annotation) > 21:
+                # There's another one hidden in here.
+                if annotation[21] == "value.shared":
+                    key = "/shared%s" % (_key)
+                elif annotation[21] == "value.priv":
+                    key = "/private%s" % (_key)
+
+                value = annotation[23]
+
+                self.__verbose( '[GETANNOTATION %s] %s: %s' % (mbx, key, value) )
+                if not ann.has_key(mbx):
+                    ann[mbx] = {}
+                if not ann[mbx].has_key(key):
+                    ann[mbx][key] = value
+
         return ann
 
-    def setannotation(self, mailbox, annotation, value):
+    def setannotation(self, mailbox, annotation, value, shared=False):
         """Set Annotation"""
         self.__prepare('SETANNOTATION')
-        res, msg = self.__docommand("setannotation", self.decode(mailbox), annotation, value)
+        res, msg = self.__docommand("setannotation", self.decode(mailbox), annotation, value, shared)
         self.__verbose( '[SETANNOTATION %s] %s: %s' % (mailbox, res, msg[0]) )
 
     def __reconstruct(self, mailbox):





More information about the commits mailing list