3 # Copyright (C) 2008-2012 W. Trevor King
4 # Copyright (C) 2012-2013 Wade Berrier
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 """LDAP address searches for Mutt.
21 Add :file:`mutt-ldap.py` to your ``PATH`` and add the following line
22 to your :file:`.muttrc`::
24 set query_command = "mutt-ldap.py '%s'"
26 Search for addresses with `^t`, optionally after typing part of the
27 name. Configure your connection by creating :file:`~/.mutt-ldap.rc`
28 contaning something like::
31 server = myserver.example.net
32 basedn = ou=people,dc=example,dc=net
34 See the `CONFIG` options for other available settings.
46 CONFIG = ConfigParser.SafeConfigParser()
47 CONFIG.add_section('connection')
48 CONFIG.set('connection', 'server', 'domaincontroller.yourdomain.com')
49 CONFIG.set('connection', 'port', '389') # set to 636 for default over SSL
50 CONFIG.set('connection', 'ssl', 'no')
51 CONFIG.set('connection', 'starttls', 'no')
52 CONFIG.set('connection', 'basedn', 'ou=x co.,dc=example,dc=net')
53 CONFIG.add_section('auth')
54 CONFIG.set('auth', 'user', '')
55 CONFIG.set('auth', 'password', '')
56 CONFIG.set('auth', 'gssapi', 'no')
57 CONFIG.add_section('query')
58 CONFIG.set('query', 'filter', '') # only match entries according to this filter
59 CONFIG.set('query', 'search_fields', 'cn displayName uid mail') # fields to wildcard search
60 CONFIG.add_section('results')
61 CONFIG.set('results', 'optional_column', '') # mutt can display one optional column
62 CONFIG.read(os.path.expanduser('~/.mutt-ldap.rc'))
66 if CONFIG.getboolean('connection', 'ssl'):
68 url = '{0}://{1}:{2}'.format(
70 CONFIG.get('connection', 'server'),
71 CONFIG.get('connection', 'port'))
72 connection = ldap.initialize(url)
73 if CONFIG.getboolean('connection', 'starttls') and protocol == 'ldap':
74 connection.start_tls_s()
75 if CONFIG.getboolean('auth', 'gssapi'):
76 sasl = ldap.sasl.gssapi()
77 connection.sasl_interactive_bind_s('', sasl)
80 CONFIG.get('auth', 'user'),
81 CONFIG.get('auth', 'password'),
85 def search(query, connection=None):
86 local_connection = False
89 local_connection = True
90 connection = connect()
94 filterstr = u'(|{0})'.format(
95 u' '.join([u'({0}=*{1}{2})'.format(field, query, post)
96 for field in CONFIG.get('query', 'search_fields').split()]))
97 query_filter = CONFIG.get('query', 'filter')
99 filterstr = u'(&({0}){1})'.format(query_filter, filterstr)
100 r = connection.search_s(
101 CONFIG.get('connection', 'basedn'),
103 filterstr.encode('utf-8'))
105 if local_connection and connection:
109 def format_columns(address, data):
111 yield data.get('displayName', data['cn'])[-1]
112 optional_column = CONFIG.get('results', 'optional_column')
113 if optional_column in data:
114 yield data[optional_column][-1]
116 def format_entry(entry):
119 for m in data['mail']:
120 # http://www.mutt.org/doc/manual/manual-4.html#ss4.5
121 # Describes the format mutt expects: address\tname
122 yield "\t".join(format_columns(m, data))
125 if __name__ == '__main__':
128 query = unicode(' '.join(sys.argv[1:]), 'utf-8')
129 entries = search(query)
130 addresses = list(itertools.chain(
131 *[format_entry(e) for e in sorted(entries)]))
132 print('{0} addresses found:'.format(len(addresses)))
133 print('\n'.join(addresses))