summaryrefslogtreecommitdiff
path: root/alot
diff options
context:
space:
mode:
authorPatrick Totzke <patricktotzke@gmail.com>2015-05-01 15:11:15 +0100
committerPatrick Totzke <patricktotzke@gmail.com>2015-05-01 15:11:15 +0100
commit3988f5f74b154d47755da206bb0659b957e495fa (patch)
tree37ebe6f0001c4bc64a7cfdf7b314f65ab611bf93 /alot
parent7b1aee9f1625fafa6cf899239a1207a51b825682 (diff)
parentdbfaa680ce89ae18642e6ce3c76b39157db3a246 (diff)
Merge branch '0.3.6-feature-abooks'
Diffstat (limited to 'alot')
-rw-r--r--alot/account.py2
-rw-r--r--alot/addressbook/__init__.py38
-rw-r--r--alot/addressbook/abook.py31
-rw-r--r--alot/addressbook/external.py73
-rw-r--r--alot/addressbooks.py115
-rw-r--r--alot/completion.py2
-rw-r--r--alot/defaults/alot.rc.spec7
-rw-r--r--alot/settings/manager.py9
8 files changed, 157 insertions, 120 deletions
diff --git a/alot/account.py b/alot/account.py
index 0c4abfbe..4a62f0b4 100644
--- a/alot/account.py
+++ b/alot/account.py
@@ -44,7 +44,7 @@ class Account(object):
signature_as_attachment = None
"""attach signature file instead of appending its content to body text"""
abook = None
- """addressbook (:class:`addressbooks.AddressBook`)
+ """addressbook (:class:`addressbook.AddressBook`)
managing this accounts contacts"""
def __init__(self, address=None, aliases=None, realname=None,
diff --git a/alot/addressbook/__init__.py b/alot/addressbook/__init__.py
new file mode 100644
index 00000000..f649b523
--- /dev/null
+++ b/alot/addressbook/__init__.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2011-2015 Patrick Totzke <patricktotzke@gmail.com>
+# This file is released under the GNU GPL, version 3 or a later revision.
+# For further details see the COPYING file
+import re
+
+
+class AddressbookError(Exception):
+ pass
+
+
+class AddressBook(object):
+ """can look up email addresses and realnames for contacts.
+
+ .. note::
+
+ This is an abstract class that leaves :meth:`get_contacts`
+ unspecified. See :class:`AbookAddressBook` and
+ :class:`MatchSdtoutAddressbook` for implementations.
+ """
+ def __init__(self, ignorecase=True):
+ self.reflags = re.IGNORECASE if ignorecase else 0
+
+ def get_contacts(self):
+ """list all contacts tuples in this abook as (name, email) tuples"""
+ return []
+
+ def lookup(self, query=''):
+ """looks up all contacts where name or address match query"""
+ res = []
+ query = '.*%s.*' % query
+ for name, email in self.get_contacts():
+ try:
+ if re.match(query, name, self.reflags) or \
+ re.match(query, email, self.reflags):
+ res.append((name, email))
+ except:
+ pass
+ return res
diff --git a/alot/addressbook/abook.py b/alot/addressbook/abook.py
new file mode 100644
index 00000000..c71f485c
--- /dev/null
+++ b/alot/addressbook/abook.py
@@ -0,0 +1,31 @@
+# Copyright (C) 2011-2015 Patrick Totzke <patricktotzke@gmail.com>
+# This file is released under the GNU GPL, version 3 or a later revision.
+# For further details see the COPYING file
+
+import os
+from . import AddressBook
+from ..settings.utils import read_config
+
+
+class AbookAddressBook(AddressBook):
+ """:class:`AddressBook` that parses abook's config/database files"""
+ def __init__(self, path='~/.abook/addressbook', **kwargs):
+ """
+ :param path: path to theme file
+ :type path: str
+ """
+ AddressBook.__init__(self, **kwargs)
+ DEFAULTSPATH = os.path.join(os.path.dirname(__file__), 'defaults')
+ self._spec = os.path.join(DEFAULTSPATH, 'abook_contacts.spec')
+ path = os.path.expanduser(path)
+ self._config = read_config(path, self._spec)
+ del(self._config['format'])
+
+ def get_contacts(self):
+ c = self._config
+ res = []
+ for id in c.sections:
+ for email in c[id]['email']:
+ if email:
+ res.append((c[id]['name'], email))
+ return res
diff --git a/alot/addressbook/external.py b/alot/addressbook/external.py
new file mode 100644
index 00000000..5c88b291
--- /dev/null
+++ b/alot/addressbook/external.py
@@ -0,0 +1,73 @@
+# Copyright (C) 2011-2015 Patrick Totzke <patricktotzke@gmail.com>
+# This file is released under the GNU GPL, version 3 or a later revision.
+# For further details see the COPYING file
+import re
+
+from ..helper import call_cmd
+from ..helper import split_commandstring
+from . import AddressBook, AddressbookError
+
+
+class ExternalAddressbook(AddressBook):
+ """:class:`AddressBook` that parses a shell command's output"""
+
+ def __init__(self, commandline, regex, reflags=0,
+ external_filtering=True,
+ **kwargs):
+ """
+ :param commandline: commandline
+ :type commandline: str
+ :param regex: regular expression used to match contacts in `commands`
+ output to stdout. Must define subparts named "email" and
+ "name".
+ :type regex: str
+ :param reflags: flags to use with regular expression.
+ Use the constants defined in :mod:`re` here
+ (`re.IGNORECASE` etc.)
+ :type reflags: str
+ :param external_filtering: if True the command is fired
+ with the given search string as parameter
+ and the result is not filtered further.
+ If set to False, the command is fired without
+ additional parameters and the result list is filtered
+ according to the search string.
+ :type external_filtering: bool
+ """
+ AddressBook.__init__(self, **kwargs)
+ self.commandline = commandline
+ self.regex = regex
+ self.reflags = reflags
+ self.external_filtering = external_filtering
+
+ def get_contacts(self):
+ return self._call_and_parse(self.commandline)
+
+ def lookup(self, prefix):
+ if self.external_filtering:
+ return self._call_and_parse(self.commandline + " " + prefix)
+ else:
+ return AddressBook.lookup(self, prefix)
+
+ def _call_and_parse(self, commandline):
+ cmdlist = split_commandstring(commandline)
+ resultstring, errmsg, retval = call_cmd(cmdlist)
+ if retval != 0:
+ msg = 'abook command "%s" returned with ' % commandline
+ msg += 'return code %d' % retval
+ if errmsg:
+ msg += ':\n%s' % errmsg
+ raise AddressbookError(msg)
+
+ if not resultstring:
+ return []
+ lines = resultstring.splitlines()
+ res = []
+ for l in lines:
+ m = re.match(self.regex, l, self.reflags)
+ if m:
+ info = m.groupdict()
+ if 'email' and 'name' in info:
+ email = info['email'].strip()
+ name = info['name']
+ res.append((name, email))
+ return res
diff --git a/alot/addressbooks.py b/alot/addressbooks.py
deleted file mode 100644
index 890eb0e3..00000000
--- a/alot/addressbooks.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright (C) 2011-2012 Patrick Totzke <patricktotzke@gmail.com>
-# This file is released under the GNU GPL, version 3 or a later revision.
-# For further details see the COPYING file
-import re
-import os
-
-from alot.settings.utils import read_config
-from helper import call_cmd
-from alot.helper import split_commandstring
-
-
-class AddressbookError(Exception):
- pass
-
-
-class AddressBook(object):
- """can look up email addresses and realnames for contacts.
-
- .. note::
-
- This is an abstract class that leaves :meth:`get_contacts`
- unspecified. See :class:`AbookAddressBook` and
- :class:`MatchSdtoutAddressbook` for implementations.
- """
- def __init__(self, ignorecase=True):
- self.reflags = re.IGNORECASE if ignorecase else 0
-
- def get_contacts(self):
- """list all contacts tuples in this abook as (name, email) tuples"""
- return []
-
- def lookup(self, query=''):
- """looks up all contacts where name or address match query"""
- res = []
- query = '.*%s.*' % query
- for name, email in self.get_contacts():
- try:
- if re.match(query, name, self.reflags) or \
- re.match(query, email, self.reflags):
- res.append((name, email))
- except:
- pass
- return res
-
-
-class AbookAddressBook(AddressBook):
- """:class:`AddressBook` that parses abook's config/database files"""
- def __init__(self, path='~/.abook/addressbook', **kwargs):
- """
- :param path: path to theme file
- :type path: str
- """
- AddressBook.__init__(self, **kwargs)
- DEFAULTSPATH = os.path.join(os.path.dirname(__file__), 'defaults')
- self._spec = os.path.join(DEFAULTSPATH, 'abook_contacts.spec')
- path = os.path.expanduser(path)
- self._config = read_config(path, self._spec)
- del(self._config['format'])
-
- def get_contacts(self):
- c = self._config
- res = []
- for id in c.sections:
- for email in c[id]['email']:
- if email:
- res.append((c[id]['name'], email))
- return res
-
-
-class MatchSdtoutAddressbook(AddressBook):
- """:class:`AddressBook` that parses a shell command's output for lookups"""
-
- def __init__(self, command, match=None, **kwargs):
- """
- :param command: lookup command
- :type command: str
- :param match: regular expression used to match contacts in `commands`
- output to stdout. Must define subparts named "email" and
- "name". Defaults to
- :regexp:`^(?P<email>[^@]+@[^\t]+)\t+(?P<name>[^\t]+)`.
- :type match: str
- """
- AddressBook.__init__(self, **kwargs)
- self.command = command
- if not match:
- self.match = '^(?P<email>[^@]+@[^\t]+)\t+(?P<name>[^\t]+)'
- else:
- self.match = match
-
- def get_contacts(self):
- return self.lookup('\'\'')
-
- def lookup(self, prefix):
- cmdlist = split_commandstring(self.command)
- resultstring, errmsg, retval = call_cmd(cmdlist + [prefix])
- if retval != 0:
- msg = 'abook command "%s" returned with ' % self.command
- msg += 'return code %d' % retval
- if errmsg:
- msg += ':\n%s' % errmsg
- raise AddressbookError(msg)
-
- if not resultstring:
- return []
- lines = resultstring.splitlines()
- res = []
- for l in lines:
- m = re.match(self.match, l, self.reflags)
- if m:
- info = m.groupdict()
- if 'email' and 'name' in info:
- email = info['email'].strip()
- name = info['name']
- res.append((name, email))
- return res
diff --git a/alot/completion.py b/alot/completion.py
index 75d50e0c..300592b3 100644
--- a/alot/completion.py
+++ b/alot/completion.py
@@ -13,7 +13,7 @@ from alot.buffers import EnvelopeBuffer
from alot.settings import settings
from alot.utils.booleanaction import BooleanAction
from alot.helper import split_commandline
-from alot.addressbooks import AddressbookError
+from alot.addressbook import AddressbookError
from errors import CompletionError
diff --git a/alot/defaults/alot.rc.spec b/alot/defaults/alot.rc.spec
index df81c432..e0525279 100644
--- a/alot/defaults/alot.rc.spec
+++ b/alot/defaults/alot.rc.spec
@@ -301,3 +301,10 @@ prefer_plaintext = boolean(default=False)
# contacts file used for type 'abook' address book
abook_contacts_file = string(default='~/.abook/addressbook')
+
+ # (shellcommand addressbooks)
+ # let the external command do the filtering when looking up addresses.
+ # If set to True, the command is fired with the given search string
+ # as parameter. Otherwise, the command is fired without additional parameters
+ # and the result list is filtered according to the search string.
+ shellcommand_external_filtering = boolean(default=True)
diff --git a/alot/settings/manager.py b/alot/settings/manager.py
index 66c8e355..9dfe52b0 100644
--- a/alot/settings/manager.py
+++ b/alot/settings/manager.py
@@ -9,7 +9,8 @@ import logging
from configobj import ConfigObj, Section
from alot.account import SendmailAccount
-from alot.addressbooks import MatchSdtoutAddressbook, AbookAddressBook
+from alot.addressbook.abook import AbookAddressBook
+from alot.addressbook.external import ExternalAddressbook
from alot.helper import pretty_datetime, string_decode
from errors import ConfigError
@@ -127,8 +128,10 @@ class SettingsManager(object):
cmd = abook['command']
regexp = abook['regexp']
if cmd is not None and regexp is not None:
- args['abook'] = MatchSdtoutAddressbook(cmd,
- match=regexp)
+ ef = abook['shellcommand_external_filtering']
+ args['abook'] = ExternalAddressbook(cmd,
+ regexp,
+ external_filtering=ef)
else:
msg = 'underspecified abook of type \'shellcommand\':'
msg += '\ncommand: %s\nregexp:%s' % (cmd, regexp)