Branch 'pykolab-0.5' - 7 commits - cyruslib.py pykolab/cli pykolab/imap pykolab.spec.in
Jeroen van Meeuwen
vanmeeuwen at kolabsys.com
Thu Aug 9 14:27:07 CEST 2012
cyruslib.py | 69 ++++++++++++++++++++++++-----
pykolab.spec.in | 7 +--
pykolab/cli/cmd_list_mailbox_acls.py | 8 ++-
pykolab/cli/cmd_list_mailbox_metadata.py | 66 ++++++++++++++++++++++++++++
pykolab/cli/cmd_rename_mailbox.py | 70 ++++++++++++++++++++++++++++++
pykolab/cli/cmd_set_mailbox_metadata.py | 72 +++++++++++++++++++++++++++++++
pykolab/imap/__init__.py | 52 +++++++++++++++++++++-
pykolab/imap/cyrus.py | 5 +-
8 files changed, 327 insertions(+), 22 deletions(-)
New commits:
commit 0df2808a48ddaaa3f7c3150e036d102303c5ec12
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Wed Aug 8 16:56:13 2012 +0200
Add a command to rename a (user's) mailbox
diff --git a/pykolab/cli/cmd_rename_mailbox.py b/pykolab/cli/cmd_rename_mailbox.py
new file mode 100644
index 0000000..9917f6b
--- /dev/null
+++ b/pykolab/cli/cmd_rename_mailbox.py
@@ -0,0 +1,70 @@
+# -*- 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 _
+
+log = pykolab.getLogger('pykolab.cli')
+conf = pykolab.getConf()
+
+def __init__():
+ commands.register('rename_mailbox', execute, description=description(), aliases=['rm'])
+
+def description():
+ return """Rename a mailbox or sub-folder."""
+
+def execute(*args, **kw):
+ """
+ Rename mailbox
+ """
+
+ try:
+ source_folder = conf.cli_args.pop(0)
+ try:
+ target_folder = conf.cli_args.pop(0)
+ except IndexError, errmsg:
+ print >> sys.stderr, _("No target mailbox name specified")
+ except IndexError, errmsg:
+ print >> sys.stderr, _("No source mailbox name specified")
+ sys.exit(1)
+
+ if len(source_folder.split('@')) > 1:
+ domain = source_folder.split('@')[1]
+ else:
+ domain = conf.get('kolab', 'primary_domain')
+
+ imap = IMAP()
+ imap.connect(domain=domain)
+
+ if not imap.has_folder(source_folder):
+ print >> sys.stderr, _("Source folder %r does not exist") % (source_folder)
+ sys.exit(1)
+
+ if imap.has_folder(target_folder):
+ print >> sys.stderr, _("Target folder %r already exists") % (target_folder)
+ sys.exit(1)
+
+ imap.user_mailbox_rename(source_folder.replace('user/',''), target_folder.replace('user/',''))
+
commit b11254c47b8e34841096aa29bdaddee77032802a
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Wed Aug 8 15:55:55 2012 +0100
Fix typo
diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index aee9148..f0cb9be 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -533,7 +533,7 @@ class IMAP(object):
try:
self.imap.rename(old_name,new_name)
except:
- log.error(_("Could not rename INBOX folder %s to %s") % (oldname,new_name))
+ log.error(_("Could not rename INBOX folder %s to %s") % (old_name,new_name))
else:
log.warning(_("Moving INBOX folder %s won't succeed as target folder %s already exists") % (old_name,new_name))
commit 417ff49dc51d627a09a8c84c39d270deee3d168b
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 a445352de6186de3e2f2b73adc1dfdc0a73d48ed
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 c3b6340193458d39fd83b67d8c3cf9ad0869cb44
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 6edd913b83e57e2df5b8403c3fb9757d237003fe
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):
commit c290f4d355ef9ab4a3011eb846affd60f1457049
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Tue Aug 7 12:39:45 2012 +0100
Remove duplicate build requirements
diff --git a/pykolab.spec.in b/pykolab.spec.in
index 00261f8..90a8176 100644
--- a/pykolab.spec.in
+++ b/pykolab.spec.in
@@ -26,7 +26,10 @@ Source0: http://files.kolab.org/releases/%{name}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildArch: noarch
BuildRequires: gcc
+BuildRequires: gettext
BuildRequires: glib2-devel
+BuildRequires: intltool
+BuildRequires: python
BuildRequires: python-icalendar
BuildRequires: python-kolabformat
BuildRequires: python-ldap
@@ -72,7 +75,6 @@ Kolab Format XML bindings wrapper for %{name}
%package -n kolab-cli
Summary: Kolab CLI components
Group: Applications/System
-BuildRequires: intltool, gettext, python
Requires: %{name} = %{version}-%{release}
Requires: python-augeas
Requires: python-cheetah
@@ -86,7 +88,6 @@ Kolab CLI utilities
%package -n kolab-saslauthd
Summary: Kolab SASL Authentication Daemon
Group: Applications/System
-BuildRequires: intltool, gettext, python
Requires: %{name} = %{version}-%{release}
Requires: cyrus-sasl
Requires: cyrus-sasl-plain
@@ -100,7 +101,6 @@ Kolab SASL Authentication Daemon for multi-domain, multi-authn database deployme
%package -n kolab-server
Summary: Kolab Server implemented in Python
Group: Applications/System
-BuildRequires: intltool, gettext, python
Requires: %{name} = %{version}-%{release}
%description -n kolab-server
@@ -112,7 +112,6 @@ Kolab Server implemented in Python
%package -n postfix-kolab
Summary: Kolab SMTP Access Policy for Postfix
Group: Applications/System
-BuildRequires: intltool, gettext, python
Requires: postfix
Requires: %{name} = %{version}-%{release}
Requires: python-sqlalchemy
More information about the commits
mailing list