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