/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5177.1.1 by Vincent Ladeuil
Manually assign docstrings to command objects, so that they work with python -OO
1
# Copyright (C) 2007-2010 Canonical Ltd
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
16
6379.6.3 by Jelmer Vernooij
Use absolute_import.
17
from __future__ import absolute_import
18
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
19
import errno
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
20
import os
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
21
import subprocess
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
22
import sys
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
23
import tempfile
24
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
25
import breezy
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
26
from . import (
6449.5.1 by Jelmer Vernooij
Migrate mail_client to config stacks.
27
    config as _mod_config,
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
28
    email_message,
29
    errors,
30
    msgeditor,
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
31
    osutils,
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
32
    urlutils,
6973.5.10 by Jelmer Vernooij
Random bunch of python3 bee-improvements.
33
    registry,
34
    )
35
from .sixish import (
7045.2.12 by Jelmer Vernooij
Fix some mail client tests.
36
    PY3,
6973.5.10 by Jelmer Vernooij
Random bunch of python3 bee-improvements.
37
    text_type,
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
38
    )
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
39
3638.2.1 by Neil Martinsen-Burrell
Use a registry for mail clients.
40
mail_client_registry = registry.Registry()
41
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
42
6734.1.1 by Jelmer Vernooij
Fix more imports.
43
class MailClientNotFound(errors.BzrError):
44
45
    _fmt = "Unable to find mail client with the following names:"\
46
        " %(mail_command_list_string)s"
47
48
    def __init__(self, mail_command_list):
49
        mail_command_list_string = ', '.join(mail_command_list)
50
        errors.BzrError.__init__(
51
            self, mail_command_list=mail_command_list,
52
            mail_command_list_string=mail_command_list_string)
53
54
55
class NoMessageSupplied(errors.BzrError):
56
57
    _fmt = "No message supplied."
58
59
60
class NoMailAddressSpecified(errors.BzrError):
61
62
    _fmt = "No mail-to address (--mail-to) or output (-o) specified."
63
64
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
65
class MailClient(object):
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
66
    """A mail client that can send messages with attachements."""
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
67
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
68
    def __init__(self, config):
69
        self.config = config
70
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
71
    def compose(self, prompt, to, subject, attachment, mime_subtype,
4098.5.2 by Aaron Bentley
Push body support through bzr send.
72
                extension, basename=None, body=None):
2681.1.36 by Aaron Bentley
Update docs
73
        """Compose (and possibly send) an email message
74
75
        Must be implemented by subclasses.
76
77
        :param prompt: A message to tell the user what to do.  Supported by
78
            the Editor client, but ignored by others
79
        :param to: The address to send the message to
80
        :param subject: The contents of the subject line
81
        :param attachment: An email attachment, as a bytestring
82
        :param mime_subtype: The attachment is assumed to be a subtype of
83
            Text.  This allows the precise subtype to be specified, e.g.
84
            "plain", "x-patch", etc.
85
        :param extension: The file extension associated with the attachment
86
            type, e.g. ".patch"
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
87
        :param basename: The name to use for the attachment, e.g.
88
            "send-nick-3252"
2681.1.36 by Aaron Bentley
Update docs
89
        """
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
90
        raise NotImplementedError
91
4098.5.2 by Aaron Bentley
Push body support through bzr send.
92
    def compose_merge_request(self, to, subject, directive, basename=None,
93
                              body=None):
2681.1.36 by Aaron Bentley
Update docs
94
        """Compose (and possibly send) a merge request
95
96
        :param to: The address to send the request to
97
        :param subject: The subject line to use for the request
98
        :param directive: A merge directive representing the merge request, as
99
            a bytestring.
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
100
        :param basename: The name to use for the attachment, e.g.
101
            "send-nick-3252"
2681.1.36 by Aaron Bentley
Update docs
102
        """
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
103
        prompt = self._get_merge_prompt("Please describe these changes:", to,
104
                                        subject, directive)
105
        self.compose(prompt, to, subject, directive,
7143.15.2 by Jelmer Vernooij
Run autopep8.
106
                     'x-patch', '.patch', basename, body)
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
107
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
108
    def _get_merge_prompt(self, prompt, to, subject, attachment):
2681.1.36 by Aaron Bentley
Update docs
109
        """Generate a prompt string.  Overridden by Editor.
110
111
        :param prompt: A string suggesting what user should do
112
        :param to: The address the mail will be sent to
113
        :param subject: The subject line of the mail
114
        :param attachment: The attachment that will be used
115
        """
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
116
        return ''
117
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
118
119
class Editor(MailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
120
    __doc__ = """DIY mail client that uses commit message editor"""
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
121
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
122
    supports_body = True
123
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
124
    def _get_merge_prompt(self, prompt, to, subject, attachment):
2681.1.37 by Aaron Bentley
Update docstrings and string formatting
125
        """See MailClient._get_merge_prompt"""
126
        return (u"%s\n\n"
127
                u"To: %s\n"
128
                u"Subject: %s\n\n"
129
                u"%s" % (prompt, to, subject,
130
                         attachment.decode('utf-8', 'replace')))
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
131
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
132
    def compose(self, prompt, to, subject, attachment, mime_subtype,
4098.5.2 by Aaron Bentley
Push body support through bzr send.
133
                extension, basename=None, body=None):
2681.1.37 by Aaron Bentley
Update docstrings and string formatting
134
        """See MailClient.compose"""
3042.1.1 by Lukáš Lalinský
Make mail-to address in ``bzr send`` optional for interactive mail clients.
135
        if not to:
6734.1.1 by Jelmer Vernooij
Fix more imports.
136
            raise NoMailAddressSpecified()
4098.5.2 by Aaron Bentley
Push body support through bzr send.
137
        body = msgeditor.edit_commit_message(prompt, start_message=body)
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
138
        if body == '':
6734.1.1 by Jelmer Vernooij
Fix more imports.
139
            raise NoMessageSupplied()
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
140
        email_message.EmailMessage.send(self.config,
6449.5.1 by Jelmer Vernooij
Migrate mail_client to config stacks.
141
                                        self.config.get('email'),
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
142
                                        to,
143
                                        subject,
144
                                        body,
145
                                        attachment,
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
146
                                        attachment_mime_subtype=mime_subtype)
7143.15.2 by Jelmer Vernooij
Run autopep8.
147
148
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
149
mail_client_registry.register('editor', Editor,
150
                              help=Editor.__doc__)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
151
152
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
153
class BodyExternalMailClient(MailClient):
154
155
    supports_body = True
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
156
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
157
    def _get_client_commands(self):
2681.1.36 by Aaron Bentley
Update docs
158
        """Provide a list of commands that may invoke the mail client"""
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
159
        if sys.platform == 'win32':
2681.1.29 by Aaron Bentley
Make conditional import explicit
160
            import win32utils
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
161
            return [win32utils.get_app_path(i) for i in self._client_commands]
162
        else:
163
            return self._client_commands
164
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
165
    def compose(self, prompt, to, subject, attachment, mime_subtype,
4098.5.2 by Aaron Bentley
Push body support through bzr send.
166
                extension, basename=None, body=None):
2681.1.36 by Aaron Bentley
Update docs
167
        """See MailClient.compose.
168
169
        Writes the attachment to a temporary file, invokes _compose.
170
        """
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
171
        if basename is None:
172
            basename = 'attachment'
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
173
        pathname = osutils.mkdtemp(prefix='bzr-mail-')
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
174
        attach_path = osutils.pathjoin(pathname, basename + extension)
175
        outfile = open(attach_path, 'wb')
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
176
        try:
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
177
            outfile.write(attachment)
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
178
        finally:
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
179
            outfile.close()
4282.2.4 by Neil Martinsen-Burrell
a better fix that doesn't break backwards compatibility
180
        if body is not None:
181
            kwargs = {'body': body}
182
        else:
183
            kwargs = {}
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
184
        self._compose(prompt, to, subject, attach_path, mime_subtype,
4282.2.4 by Neil Martinsen-Burrell
a better fix that doesn't break backwards compatibility
185
                      extension, **kwargs)
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
186
187
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
188
                 extension, body=None, from_=None):
2681.1.36 by Aaron Bentley
Update docs
189
        """Invoke a mail client as a commandline process.
190
191
        Overridden by MAPIClient.
192
        :param to: The address to send the mail to
193
        :param subject: The subject line for the mail
194
        :param pathname: The path to the attachment
195
        :param mime_subtype: The attachment is assumed to have a major type of
196
            "text", but the precise subtype can be specified here
197
        :param extension: A file extension (including period) associated with
198
            the attachment type.
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
199
        :param body: Optional body text.
200
        :param from_: Optional From: header.
2681.1.36 by Aaron Bentley
Update docs
201
        """
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
202
        for name in self._get_client_commands():
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
203
            cmdline = [self._encode_path(name, 'executable')]
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
204
            if body is not None:
205
                kwargs = {'body': body}
206
            else:
207
                kwargs = {}
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
208
            if from_ is not None:
209
                kwargs['from_'] = from_
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
210
            cmdline.extend(self._get_compose_commandline(to, subject,
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
211
                                                         attach_path,
212
                                                         **kwargs))
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
213
            try:
214
                subprocess.call(cmdline)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
215
            except OSError as e:
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
216
                if e.errno != errno.ENOENT:
217
                    raise
218
            else:
219
                break
220
        else:
6734.1.1 by Jelmer Vernooij
Fix more imports.
221
            raise MailClientNotFound(self._client_commands)
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
222
4098.5.2 by Aaron Bentley
Push body support through bzr send.
223
    def _get_compose_commandline(self, to, subject, attach_path, body):
2681.1.36 by Aaron Bentley
Update docs
224
        """Determine the commandline to use for composing a message
225
226
        Implemented by various subclasses
227
        :param to: The address to send the mail to
228
        :param subject: The subject line for the mail
229
        :param attach_path: The path to the attachment
230
        """
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
231
        raise NotImplementedError
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
232
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
233
    def _encode_safe(self, u):
234
        """Encode possible unicode string argument to 8-bit string
235
        in user_encoding. Unencodable characters will be replaced
236
        with '?'.
237
238
        :param  u:  possible unicode string.
239
        :return:    encoded string if u is unicode, u itself otherwise.
240
        """
7045.2.12 by Jelmer Vernooij
Fix some mail client tests.
241
        if not PY3 and isinstance(u, text_type):
3224.5.8 by Andrew Bennetts
Fix failing tests.
242
            return u.encode(osutils.get_user_encoding(), 'replace')
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
243
        return u
244
245
    def _encode_path(self, path, kind):
246
        """Encode unicode path in user encoding.
247
248
        :param  path:   possible unicode path.
249
        :param  kind:   path kind ('executable' or 'attachment').
250
        :return:        encoded path if path is unicode,
251
                        path itself otherwise.
252
        :raise:         UnableEncodePath.
253
        """
7045.2.12 by Jelmer Vernooij
Fix some mail client tests.
254
        if not PY3 and isinstance(path, text_type):
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
255
            try:
3224.5.8 by Andrew Bennetts
Fix failing tests.
256
                return path.encode(osutils.get_user_encoding())
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
257
            except UnicodeEncodeError:
258
                raise errors.UnableEncodePath(path, kind)
259
        return path
3234.2.3 by Alexander Belchenko
mail_client.py: provide new private method ExternalMailClient._get_compose_8bit_commandline to make bug #139318 testable (as Aaron requested).
260
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
261
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
262
class ExternalMailClient(BodyExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
263
    __doc__ = """An external mail client."""
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
264
265
    supports_body = False
266
267
268
class Evolution(BodyExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
269
    __doc__ = """Evolution mail client."""
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
270
271
    _client_commands = ['evolution']
272
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
273
    def _get_compose_commandline(self, to, subject, attach_path, body=None):
2681.1.36 by Aaron Bentley
Update docs
274
        """See ExternalMailClient._get_compose_commandline"""
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
275
        message_options = {}
276
        if subject is not None:
277
            message_options['subject'] = subject
278
        if attach_path is not None:
279
            message_options['attach'] = attach_path
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
280
        if body is not None:
281
            message_options['body'] = body
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
282
        options_list = ['%s=%s' % (k, urlutils.escape(v)) for (k, v) in
6656.1.1 by Martin
Apply 2to3 dict fixer and clean up resulting mess using view helpers
283
                        sorted(message_options.items())]
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
284
        return ['mailto:%s?%s' % (self._encode_safe(to or ''),
7143.15.2 by Jelmer Vernooij
Run autopep8.
285
                                  '&'.join(options_list))]
286
287
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
288
mail_client_registry.register('evolution', Evolution,
289
                              help=Evolution.__doc__)
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
290
291
4416.1.1 by Edwin Grubbs
Added ability to pass the body into mutt.
292
class Mutt(BodyExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
293
    __doc__ = """Mutt mail client."""
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
294
295
    _client_commands = ['mutt']
296
4416.1.1 by Edwin Grubbs
Added ability to pass the body into mutt.
297
    def _get_compose_commandline(self, to, subject, attach_path, body=None):
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
298
        """See ExternalMailClient._get_compose_commandline"""
299
        message_options = []
300
        if subject is not None:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
301
            message_options.extend(
302
                ['-s', self._encode_safe(subject)])
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
303
        if attach_path is not None:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
304
            message_options.extend(
305
                ['-a', self._encode_path(attach_path, 'attachment')])
4416.1.1 by Edwin Grubbs
Added ability to pass the body into mutt.
306
        if body is not None:
4416.1.3 by Edwin Grubbs
Stored temp file in self to allow using NamedTemporaryFile.
307
            # Store the temp file object in self, so that it does not get
308
            # garbage collected and delete the file before mutt can read it.
309
            self._temp_file = tempfile.NamedTemporaryFile(
7045.2.12 by Jelmer Vernooij
Fix some mail client tests.
310
                prefix="mutt-body-", suffix=".txt", mode="w+")
4416.1.3 by Edwin Grubbs
Stored temp file in self to allow using NamedTemporaryFile.
311
            self._temp_file.write(body)
312
            self._temp_file.flush()
313
            message_options.extend(['-i', self._temp_file.name])
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
314
        if to is not None:
4292.1.1 by Jelmer Vernooij
Mutt requires -- before the recipient address if -a is being used.
315
            message_options.extend(['--', self._encode_safe(to)])
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
316
        return message_options
7143.15.2 by Jelmer Vernooij
Run autopep8.
317
318
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
319
mail_client_registry.register('mutt', Mutt,
320
                              help=Mutt.__doc__)
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
321
322
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
323
class Thunderbird(BodyExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
324
    __doc__ = """Mozilla Thunderbird (or Icedove)
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
325
326
    Note that Thunderbird 1.5 is buggy and does not support setting
327
    "to" simultaneously with including a attachment.
328
329
    There is a workaround if no attachment is present, but we always need to
330
    send attachments.
331
    """
332
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
333
    _client_commands = [
334
        'thunderbird', 'mozilla-thunderbird', 'icedove',
335
        '/Applications/Mozilla/Thunderbird.app/Contents/MacOS/thunderbird-bin',
336
        '/Applications/Thunderbird.app/Contents/MacOS/thunderbird-bin']
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
337
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
338
    def _get_compose_commandline(self, to, subject, attach_path, body=None):
2681.1.36 by Aaron Bentley
Update docs
339
        """See ExternalMailClient._get_compose_commandline"""
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
340
        message_options = {}
341
        if to is not None:
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
342
            message_options['to'] = self._encode_safe(to)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
343
        if subject is not None:
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
344
            message_options['subject'] = self._encode_safe(subject)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
345
        if attach_path is not None:
3234.2.2 by Alexander Belchenko
[merge] URL is always ascii.
346
            message_options['attachment'] = urlutils.local_path_to_url(
347
                attach_path)
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
348
        if body is not None:
7143.15.2 by Jelmer Vernooij
Run autopep8.
349
            options_list = ['body=%s' %
350
                            urlutils.quote(self._encode_safe(body))]
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
351
        else:
352
            options_list = []
353
        options_list.extend(["%s='%s'" % (k, v) for k, v in
7143.15.2 by Jelmer Vernooij
Run autopep8.
354
                             sorted(message_options.items())])
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
355
        return ['-compose', ','.join(options_list)]
7143.15.2 by Jelmer Vernooij
Run autopep8.
356
357
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
358
mail_client_registry.register('thunderbird', Thunderbird,
359
                              help=Thunderbird.__doc__)
2681.1.23 by Aaron Bentley
Add support for xdg-email
360
361
2681.5.3 by ghigo
Add KMail mail client
362
class KMail(ExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
363
    __doc__ = """KDE mail client."""
2681.5.1 by ghigo
Add KMail support to bzr send
364
365
    _client_commands = ['kmail']
366
367
    def _get_compose_commandline(self, to, subject, attach_path):
2681.1.36 by Aaron Bentley
Update docs
368
        """See ExternalMailClient._get_compose_commandline"""
2681.5.1 by ghigo
Add KMail support to bzr send
369
        message_options = []
370
        if subject is not None:
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
371
            message_options.extend(['-s', self._encode_safe(subject)])
2681.5.1 by ghigo
Add KMail support to bzr send
372
        if attach_path is not None:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
373
            message_options.extend(
374
                ['--attach', self._encode_path(attach_path, 'attachment')])
2681.5.1 by ghigo
Add KMail support to bzr send
375
        if to is not None:
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
376
            message_options.extend([self._encode_safe(to)])
2681.5.1 by ghigo
Add KMail support to bzr send
377
        return message_options
7143.15.2 by Jelmer Vernooij
Run autopep8.
378
379
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
380
mail_client_registry.register('kmail', KMail,
381
                              help=KMail.__doc__)
2681.5.1 by ghigo
Add KMail support to bzr send
382
383
3921.2.1 by Gavin Panella
Support Claws.
384
class Claws(ExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
385
    __doc__ = """Claws mail client."""
3921.2.1 by Gavin Panella
Support Claws.
386
4401.2.1 by Barry Warsaw
Make Claws mail client work, with bodies and setting the From field.
387
    supports_body = True
388
3921.2.1 by Gavin Panella
Support Claws.
389
    _client_commands = ['claws-mail']
390
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
391
    def _get_compose_commandline(self, to, subject, attach_path, body=None,
392
                                 from_=None):
3921.2.1 by Gavin Panella
Support Claws.
393
        """See ExternalMailClient._get_compose_commandline"""
4401.2.1 by Barry Warsaw
Make Claws mail client work, with bodies and setting the From field.
394
        compose_url = []
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
395
        if from_ is not None:
6379.4.2 by Jelmer Vernooij
Add urlutils.quote / urlutils.unquote.
396
            compose_url.append('from=' + urlutils.quote(from_))
3921.2.1 by Gavin Panella
Support Claws.
397
        if subject is not None:
6379.4.2 by Jelmer Vernooij
Add urlutils.quote / urlutils.unquote.
398
            # Don't use urlutils.quote_plus because Claws doesn't seem
3921.2.4 by Gavin Panella
Use the --attach option, and don't specify a From: header.
399
            # to recognise spaces encoded as "+".
400
            compose_url.append(
6379.4.2 by Jelmer Vernooij
Add urlutils.quote / urlutils.unquote.
401
                'subject=' + urlutils.quote(self._encode_safe(subject)))
4401.2.1 by Barry Warsaw
Make Claws mail client work, with bodies and setting the From field.
402
        if body is not None:
403
            compose_url.append(
6379.4.2 by Jelmer Vernooij
Add urlutils.quote / urlutils.unquote.
404
                'body=' + urlutils.quote(self._encode_safe(body)))
4401.2.1 by Barry Warsaw
Make Claws mail client work, with bodies and setting the From field.
405
        # to must be supplied for the claws-mail --compose syntax to work.
406
        if to is None:
6734.1.1 by Jelmer Vernooij
Fix more imports.
407
            raise NoMailAddressSpecified()
4401.2.1 by Barry Warsaw
Make Claws mail client work, with bodies and setting the From field.
408
        compose_url = 'mailto:%s?%s' % (
409
            self._encode_safe(to), '&'.join(compose_url))
3921.2.4 by Gavin Panella
Use the --attach option, and don't specify a From: header.
410
        # Collect command-line options.
4401.2.1 by Barry Warsaw
Make Claws mail client work, with bodies and setting the From field.
411
        message_options = ['--compose', compose_url]
3921.2.1 by Gavin Panella
Support Claws.
412
        if attach_path is not None:
3921.2.4 by Gavin Panella
Use the --attach option, and don't specify a From: header.
413
            message_options.extend(
414
                ['--attach', self._encode_path(attach_path, 'attachment')])
415
        return message_options
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
416
417
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
418
                 extension, body=None, from_=None):
419
        """See ExternalMailClient._compose"""
420
        if from_ is None:
6449.5.1 by Jelmer Vernooij
Migrate mail_client to config stacks.
421
            from_ = self.config.get('email')
4401.2.2 by Barry Warsaw
Much simpler approach to support From: in Claws, after discussion with
422
        super(Claws, self)._compose(prompt, to, subject, attach_path,
423
                                    mime_subtype, extension, body, from_)
424
425
3921.2.1 by Gavin Panella
Support Claws.
426
mail_client_registry.register('claws', Claws,
427
                              help=Claws.__doc__)
428
429
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
430
class XDGEmail(BodyExternalMailClient):
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
431
    __doc__ = """xdg-email attempts to invoke the preferred mail client"""
2681.1.23 by Aaron Bentley
Add support for xdg-email
432
433
    _client_commands = ['xdg-email']
434
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
435
    def _get_compose_commandline(self, to, subject, attach_path, body=None):
2681.1.36 by Aaron Bentley
Update docs
436
        """See ExternalMailClient._get_compose_commandline"""
3042.1.1 by Lukáš Lalinský
Make mail-to address in ``bzr send`` optional for interactive mail clients.
437
        if not to:
6734.1.1 by Jelmer Vernooij
Fix more imports.
438
            raise NoMailAddressSpecified()
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
439
        commandline = [self._encode_safe(to)]
2681.1.23 by Aaron Bentley
Add support for xdg-email
440
        if subject is not None:
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
441
            commandline.extend(['--subject', self._encode_safe(subject)])
2681.1.23 by Aaron Bentley
Add support for xdg-email
442
        if attach_path is not None:
3234.2.6 by Alexander Belchenko
because every mail client has different rules to compose command line we should encode arguments to 8 bit string only when needed.
443
            commandline.extend(['--attach',
7143.15.2 by Jelmer Vernooij
Run autopep8.
444
                                self._encode_path(attach_path, 'attachment')])
4098.5.1 by Aaron Bentley
Allow specifying body for t-bird, evo and xdg
445
        if body is not None:
446
            commandline.extend(['--body', self._encode_safe(body)])
2681.1.23 by Aaron Bentley
Add support for xdg-email
447
        return commandline
7143.15.2 by Jelmer Vernooij
Run autopep8.
448
449
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
450
mail_client_registry.register('xdg-email', XDGEmail,
451
                              help=XDGEmail.__doc__)
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
452
453
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
454
class EmacsMail(ExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
455
    __doc__ = """Call emacsclient to have a mail buffer.
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
456
457
    This only work for emacs >= 22.1 due to recent -e/--eval support.
458
459
    The good news is that this implementation will work with all mail
460
    agents registered against ``mail-user-agent``. So there is no need
461
    to instantiate ExternalMailClient for each and every GNU Emacs
462
    MUA.
463
464
    Users just have to ensure that ``mail-user-agent`` is set according
465
    to their tastes.
3322.1.1 by Ian Clatworthy
Add mail-mode GNU Emacs mail package as a mail client option (Xavier Maillard)
466
    """
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
467
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
468
    _client_commands = ['emacsclient']
469
4659.2.1 by Vincent Ladeuil
Cleanup emacs-bzr-send-XXXXXX.el leaks in /tmp during selftest.
470
    def __init__(self, config):
471
        super(EmacsMail, self).__init__(config)
472
        self.elisp_tmp_file = None
473
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
474
    def _prepare_send_function(self):
475
        """Write our wrapper function into a temporary file.
476
477
        This temporary file will be loaded at runtime in
478
        _get_compose_commandline function.
479
3506.1.6 by Christophe Troestler
EmacsMail: _prepare_send_function: corrected doc and proper handling of
480
        This function does not remove the file.  That's a wanted
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
481
        behaviour since _get_compose_commandline won't run the send
482
        mail function directly but return the eligible command line.
483
        Removing our temporary file here would prevent our sendmail
3506.1.6 by Christophe Troestler
EmacsMail: _prepare_send_function: corrected doc and proper handling of
484
        function to work.  (The file is deleted by some elisp code
485
        after being read by Emacs.)
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
486
        """
487
7045.2.12 by Jelmer Vernooij
Fix some mail client tests.
488
        _defun = br"""(defun bzr-add-mime-att (file)
3506.1.1 by Christophe Troestler
Handled the MUA "mew" in the class EmacsMail.
489
  "Attach FILE to a mail buffer as a MIME attachment."
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
490
  (let ((agent mail-user-agent))
491
    (if (and file (file-exists-p file))
492
        (cond
493
         ((eq agent 'sendmail-user-agent)
3506.1.1 by Christophe Troestler
Handled the MUA "mew" in the class EmacsMail.
494
          (progn
3506.1.10 by Christophe Troestler
Removed TABS in mail_client.py and added a NEWS entry.
495
            (mail-text)
496
            (newline)
497
            (if (functionp 'etach-attach)
498
              (etach-attach file)
499
              (mail-attach-file file))))
4056.3.1 by epg at pretzelnet
Support MH-E in EmacsMail, using mml.
500
         ((or (eq agent 'message-user-agent)
501
              (eq agent 'gnus-user-agent)
502
              (eq agent 'mh-e-user-agent))
3506.1.1 by Christophe Troestler
Handled the MUA "mew" in the class EmacsMail.
503
          (progn
3506.1.10 by Christophe Troestler
Removed TABS in mail_client.py and added a NEWS entry.
504
            (mml-attach-file file "text/x-patch" "BZR merge" "inline")))
505
         ((eq agent 'mew-user-agent)
506
          (progn
507
            (mew-draft-prepare-attachments)
508
            (mew-attach-link file (file-name-nondirectory file))
509
            (let* ((nums (mew-syntax-nums))
510
                   (syntax (mew-syntax-get-entry mew-encode-syntax nums)))
511
              (mew-syntax-set-cd syntax "BZR merge")
512
              (mew-encode-syntax-print mew-encode-syntax))
513
            (mew-header-goto-body)))
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
514
         (t
3506.1.8 by Christophe Troestler
When the Emacs MUA is not supported, the error message encourage to report it.
515
          (message "Unhandled MUA, report it on bazaar@lists.canonical.com")))
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
516
      (error "File %s does not exist." file))))
517
"""
518
519
        fd, temp_file = tempfile.mkstemp(prefix="emacs-bzr-send-",
520
                                         suffix=".el")
521
        try:
522
            os.write(fd, _defun)
523
        finally:
7143.15.2 by Jelmer Vernooij
Run autopep8.
524
            os.close(fd)  # Just close the handle but do not remove the file.
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
525
        return temp_file
526
3322.1.1 by Ian Clatworthy
Add mail-mode GNU Emacs mail package as a mail client option (Xavier Maillard)
527
    def _get_compose_commandline(self, to, subject, attach_path):
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
528
        commandline = ["--eval"]
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
529
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
530
        _to = "nil"
531
        _subject = "nil"
532
533
        if to is not None:
3506.1.3 by Christophe Troestler
Better escaping of To and Subject in the class EmacsMail.
534
            _to = ("\"%s\"" % self._encode_safe(to).replace('"', '\\"'))
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
535
        if subject is not None:
3506.1.3 by Christophe Troestler
Better escaping of To and Subject in the class EmacsMail.
536
            _subject = ("\"%s\"" %
537
                        self._encode_safe(subject).replace('"', '\\"'))
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
538
539
        # Funcall the default mail composition function
540
        # This will work with any mail mode including default mail-mode
541
        # User must tweak mail-user-agent variable to tell what function
542
        # will be called inside compose-mail.
543
        mail_cmd = "(compose-mail %s %s)" % (_to, _subject)
544
        commandline.append(mail_cmd)
545
546
        # Try to attach a MIME attachment using our wrapper function
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
547
        if attach_path is not None:
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
548
            # Do not create a file if there is no attachment
3506.1.4 by Christophe Troestler
Remove the temporary elisp file created for attachments by EmacsMail.
549
            elisp = self._prepare_send_function()
4659.2.1 by Vincent Ladeuil
Cleanup emacs-bzr-send-XXXXXX.el leaks in /tmp during selftest.
550
            self.elisp_tmp_file = elisp
3506.1.4 by Christophe Troestler
Remove the temporary elisp file created for attachments by EmacsMail.
551
            lmmform = '(load "%s")' % elisp
7143.15.2 by Jelmer Vernooij
Run autopep8.
552
            mmform = '(bzr-add-mime-att "%s")' % \
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
553
                self._encode_path(attach_path, 'attachment')
3506.1.4 by Christophe Troestler
Remove the temporary elisp file created for attachments by EmacsMail.
554
            rmform = '(delete-file "%s")' % elisp
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
555
            commandline.append(lmmform)
556
            commandline.append(mmform)
3506.1.4 by Christophe Troestler
Remove the temporary elisp file created for attachments by EmacsMail.
557
            commandline.append(rmform)
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
558
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
559
        return commandline
7143.15.2 by Jelmer Vernooij
Run autopep8.
560
561
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
562
mail_client_registry.register('emacsclient', EmacsMail,
563
                              help=EmacsMail.__doc__)
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
564
565
4098.5.4 by Aaron Bentley
Cleaner support for mail clients lacking body support.
566
class MAPIClient(BodyExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
567
    __doc__ = """Default Windows mail client launched using MAPI."""
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
568
569
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
4282.2.4 by Neil Martinsen-Burrell
a better fix that doesn't break backwards compatibility
570
                 extension, body=None):
2681.1.36 by Aaron Bentley
Update docs
571
        """See ExternalMailClient._compose.
572
573
        This implementation uses MAPI via the simplemapi ctypes wrapper
574
        """
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
575
        from .util import simplemapi
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
576
        try:
4098.5.3 by Aaron Bentley
Add Windows MAPI support for send --body
577
            simplemapi.SendMail(to or '', subject or '', body or '',
578
                                attach_path)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
579
        except simplemapi.MAPIError as e:
2681.3.6 by Lukáš Lalinsky
New version of simplemapi.py with MIT license.
580
            if e.code != simplemapi.MAPI_USER_ABORT:
6734.1.1 by Jelmer Vernooij
Fix more imports.
581
                raise MailClientNotFound(['MAPI supported mail client'
7143.15.2 by Jelmer Vernooij
Run autopep8.
582
                                          ' (error %d)' % (e.code,)])
583
584
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
585
mail_client_registry.register('mapi', MAPIClient,
586
                              help=MAPIClient.__doc__)
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
587
588
4715.3.1 by Brian de Alwis
Introduce new mailer to support MacOS X's Mail.app
589
class MailApp(BodyExternalMailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
590
    __doc__ = """Use MacOS X's Mail.app for sending email messages.
4715.3.1 by Brian de Alwis
Introduce new mailer to support MacOS X's Mail.app
591
592
    Although it would be nice to use appscript, it's not installed
593
    with the shipped Python installations.  We instead build an
594
    AppleScript and invoke the script using osascript(1).  We don't
595
    use the _encode_safe() routines as it's not clear what encoding
596
    osascript expects the script to be in.
597
    """
598
599
    _client_commands = ['osascript']
600
601
    def _get_compose_commandline(self, to, subject, attach_path, body=None,
7143.15.2 by Jelmer Vernooij
Run autopep8.
602
                                 from_=None):
603
        """See ExternalMailClient._get_compose_commandline"""
604
605
        fd, self.temp_file = tempfile.mkstemp(prefix="bzr-send-",
606
                                              suffix=".scpt")
607
        try:
608
            os.write(fd, 'tell application "Mail"\n')
609
            os.write(fd, 'set newMessage to make new outgoing message\n')
610
            os.write(fd, 'tell newMessage\n')
611
            if to is not None:
612
                os.write(fd, 'make new to recipient with properties'
613
                         ' {address:"%s"}\n' % to)
614
            if from_ is not None:
615
                # though from_ doesn't actually seem to be used
616
                os.write(fd, 'set sender to "%s"\n'
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
617
                         % from_.replace('"', '\\"'))
7143.15.2 by Jelmer Vernooij
Run autopep8.
618
            if subject is not None:
619
                os.write(fd, 'set subject to "%s"\n'
620
                         % subject.replace('"', '\\"'))
621
            if body is not None:
622
                # FIXME: would be nice to prepend the body to the
623
                # existing content (e.g., preserve signature), but
624
                # can't seem to figure out the right applescript
625
                # incantation.
626
                os.write(fd, 'set content to "%s\\n\n"\n' %
627
                         body.replace('"', '\\"').replace('\n', '\\n'))
628
629
            if attach_path is not None:
630
                # FIXME: would be nice to first append a newline to
631
                # ensure the attachment is on a new paragraph, but
632
                # can't seem to figure out the right applescript
633
                # incantation.
634
                os.write(fd, 'tell content to make new attachment'
635
                         ' with properties {file name:"%s"}'
636
                         ' at after the last paragraph\n'
637
                         % self._encode_path(attach_path, 'attachment'))
638
            os.write(fd, 'set visible to true\n')
639
            os.write(fd, 'end tell\n')
640
            os.write(fd, 'end tell\n')
641
        finally:
642
            os.close(fd)  # Just close the handle but do not remove the file.
643
        return [self.temp_file]
644
645
4715.3.1 by Brian de Alwis
Introduce new mailer to support MacOS X's Mail.app
646
mail_client_registry.register('mail.app', MailApp,
647
                              help=MailApp.__doc__)
648
649
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
650
class DefaultMail(MailClient):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
651
    __doc__ = """Default mail handling.  Tries XDGEmail (or MAPIClient on Windows),
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
652
    falls back to Editor"""
653
4273.2.2 by Aaron Bentley
Indicate that DefaultMail supports message bodies.
654
    supports_body = True
655
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
656
    def _mail_client(self):
2681.1.36 by Aaron Bentley
Update docs
657
        """Determine the preferred mail client for this platform"""
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
658
        if osutils.supports_mapi():
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
659
            return MAPIClient(self.config)
660
        else:
661
            return XDGEmail(self.config)
2681.1.25 by Aaron Bentley
Cleanup
662
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
663
    def compose(self, prompt, to, subject, attachment, mime_subtype,
4098.5.2 by Aaron Bentley
Push body support through bzr send.
664
                extension, basename=None, body=None):
2681.1.36 by Aaron Bentley
Update docs
665
        """See MailClient.compose"""
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
666
        try:
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
667
            return self._mail_client().compose(prompt, to, subject,
6449.5.1 by Jelmer Vernooij
Migrate mail_client to config stacks.
668
                                               attachment, mime_subtype,
4098.5.2 by Aaron Bentley
Push body support through bzr send.
669
                                               extension, basename, body)
6734.1.1 by Jelmer Vernooij
Fix more imports.
670
        except MailClientNotFound:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
671
            return Editor(self.config).compose(
672
                prompt, to, subject, attachment, mime_subtype, extension, body)
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
673
4098.5.2 by Aaron Bentley
Push body support through bzr send.
674
    def compose_merge_request(self, to, subject, directive, basename=None,
675
                              body=None):
2681.1.36 by Aaron Bentley
Update docs
676
        """See MailClient.compose_merge_request"""
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
677
        try:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
678
            return self._mail_client().compose_merge_request(
679
                to, subject, directive, basename=basename, body=body)
6734.1.1 by Jelmer Vernooij
Fix more imports.
680
        except MailClientNotFound:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
681
            return Editor(self.config).compose_merge_request(
682
                to, subject, directive, basename=basename, body=body)
7143.15.2 by Jelmer Vernooij
Run autopep8.
683
684
6973.10.9 by Jelmer Vernooij
Merge trunk.
685
mail_client_registry.register(u'default', DefaultMail,
3638.2.5 by Neil Martinsen-Burrell
use docstrings as help messages for registered mail clients
686
                              help=DefaultMail.__doc__)
6973.11.8 by Jelmer Vernooij
Merge python3-i.
687
mail_client_registry.default_key = u'default'
4715.3.1 by Brian de Alwis
Introduce new mailer to support MacOS X's Mail.app
688
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
689
opt_mail_client = _mod_config.RegistryOption(
690
    'mail_client', mail_client_registry, help='E-mail client to use.',
691
    invalid='error')