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