~bzr-pqm/bzr/bzr.dev

2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
1
# Copyright (C) 2007 Canonical Ltd
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
17
import errno
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
18
import os
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
19
import subprocess
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
20
import sys
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
21
import tempfile
22
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.
23
import bzrlib
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
24
from bzrlib import (
25
    email_message,
26
    errors,
27
    msgeditor,
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
28
    osutils,
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
29
    urlutils,
30
    )
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
31
32
33
class MailClient(object):
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
34
    """A mail client that can send messages with attachements."""
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
35
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
36
    def __init__(self, config):
37
        self.config = config
38
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
39
    def compose(self, prompt, to, subject, attachment, mime_subtype,
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
40
                extension, basename=None):
2681.1.36 by Aaron Bentley
Update docs
41
        """Compose (and possibly send) an email message
42
43
        Must be implemented by subclasses.
44
45
        :param prompt: A message to tell the user what to do.  Supported by
46
            the Editor client, but ignored by others
47
        :param to: The address to send the message to
48
        :param subject: The contents of the subject line
49
        :param attachment: An email attachment, as a bytestring
50
        :param mime_subtype: The attachment is assumed to be a subtype of
51
            Text.  This allows the precise subtype to be specified, e.g.
52
            "plain", "x-patch", etc.
53
        :param extension: The file extension associated with the attachment
54
            type, e.g. ".patch"
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
55
        :param basename: The name to use for the attachment, e.g.
56
            "send-nick-3252"
2681.1.36 by Aaron Bentley
Update docs
57
        """
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
58
        raise NotImplementedError
59
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
60
    def compose_merge_request(self, to, subject, directive, basename=None):
2681.1.36 by Aaron Bentley
Update docs
61
        """Compose (and possibly send) a merge request
62
63
        :param to: The address to send the request to
64
        :param subject: The subject line to use for the request
65
        :param directive: A merge directive representing the merge request, as
66
            a bytestring.
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
67
        :param basename: The name to use for the attachment, e.g.
68
            "send-nick-3252"
2681.1.36 by Aaron Bentley
Update docs
69
        """
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
70
        prompt = self._get_merge_prompt("Please describe these changes:", to,
71
                                        subject, directive)
72
        self.compose(prompt, to, subject, directive,
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
73
            'x-patch', '.patch', basename)
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
74
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
75
    def _get_merge_prompt(self, prompt, to, subject, attachment):
2681.1.36 by Aaron Bentley
Update docs
76
        """Generate a prompt string.  Overridden by Editor.
77
78
        :param prompt: A string suggesting what user should do
79
        :param to: The address the mail will be sent to
80
        :param subject: The subject line of the mail
81
        :param attachment: The attachment that will be used
82
        """
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
83
        return ''
84
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
85
86
class Editor(MailClient):
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
87
    """DIY mail client that uses commit message editor"""
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
88
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
89
    def _get_merge_prompt(self, prompt, to, subject, attachment):
2681.1.37 by Aaron Bentley
Update docstrings and string formatting
90
        """See MailClient._get_merge_prompt"""
91
        return (u"%s\n\n"
92
                u"To: %s\n"
93
                u"Subject: %s\n\n"
94
                u"%s" % (prompt, to, subject,
95
                         attachment.decode('utf-8', 'replace')))
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
96
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
97
    def compose(self, prompt, to, subject, attachment, mime_subtype,
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
98
                extension, basename=None):
2681.1.37 by Aaron Bentley
Update docstrings and string formatting
99
        """See MailClient.compose"""
3042.1.1 by Lukáš Lalinský
Make mail-to address in ``bzr send`` optional for interactive mail clients.
100
        if not to:
101
            raise errors.NoMailAddressSpecified()
2681.1.21 by Aaron Bentley
Refactor prompt generation to make it testable, test it with unicode
102
        body = msgeditor.edit_commit_message(prompt)
2681.1.9 by Aaron Bentley
Add support for mail-from-editor
103
        if body == '':
104
            raise errors.NoMessageSupplied()
105
        email_message.EmailMessage.send(self.config,
106
                                        self.config.username(),
107
                                        to,
108
                                        subject,
109
                                        body,
110
                                        attachment,
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
111
                                        attachment_mime_subtype=mime_subtype)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
112
113
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
114
class ExternalMailClient(MailClient):
115
    """An external mail client."""
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
116
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
117
    def _get_client_commands(self):
2681.1.36 by Aaron Bentley
Update docs
118
        """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
119
        if sys.platform == 'win32':
2681.1.29 by Aaron Bentley
Make conditional import explicit
120
            import win32utils
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
121
            return [win32utils.get_app_path(i) for i in self._client_commands]
122
        else:
123
            return self._client_commands
124
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
125
    def compose(self, prompt, to, subject, attachment, mime_subtype,
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
126
                extension, basename=None):
2681.1.36 by Aaron Bentley
Update docs
127
        """See MailClient.compose.
128
129
        Writes the attachment to a temporary file, invokes _compose.
130
        """
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
131
        if basename is None:
132
            basename = 'attachment'
133
        pathname = tempfile.mkdtemp(prefix='bzr-mail-')
134
        attach_path = osutils.pathjoin(pathname, basename + extension)
135
        outfile = open(attach_path, 'wb')
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
136
        try:
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
137
            outfile.write(attachment)
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
138
        finally:
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
139
            outfile.close()
140
        self._compose(prompt, to, subject, attach_path, mime_subtype,
141
                      extension)
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
142
143
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
144
                extension):
2681.1.36 by Aaron Bentley
Update docs
145
        """Invoke a mail client as a commandline process.
146
147
        Overridden by MAPIClient.
148
        :param to: The address to send the mail to
149
        :param subject: The subject line for the mail
150
        :param pathname: The path to the attachment
151
        :param mime_subtype: The attachment is assumed to have a major type of
152
            "text", but the precise subtype can be specified here
153
        :param extension: A file extension (including period) associated with
154
            the attachment type.
155
        """
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
156
        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.
157
            cmdline = [self._encode_path(name, 'executable')]
158
            cmdline.extend(self._get_compose_commandline(to, subject,
159
                                                         attach_path))
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
160
            try:
161
                subprocess.call(cmdline)
162
            except OSError, e:
163
                if e.errno != errno.ENOENT:
164
                    raise
165
            else:
166
                break
167
        else:
168
            raise errors.MailClientNotFound(self._client_commands)
169
170
    def _get_compose_commandline(self, to, subject, attach_path):
2681.1.36 by Aaron Bentley
Update docs
171
        """Determine the commandline to use for composing a message
172
173
        Implemented by various subclasses
174
        :param to: The address to send the mail to
175
        :param subject: The subject line for the mail
176
        :param attach_path: The path to the attachment
177
        """
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
178
        raise NotImplementedError
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
179
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.
180
    def _encode_safe(self, u):
181
        """Encode possible unicode string argument to 8-bit string
182
        in user_encoding. Unencodable characters will be replaced
183
        with '?'.
184
185
        :param  u:  possible unicode string.
186
        :return:    encoded string if u is unicode, u itself otherwise.
187
        """
188
        if isinstance(u, unicode):
189
            return u.encode(bzrlib.user_encoding, 'replace')
190
        return u
191
192
    def _encode_path(self, path, kind):
193
        """Encode unicode path in user encoding.
194
195
        :param  path:   possible unicode path.
196
        :param  kind:   path kind ('executable' or 'attachment').
197
        :return:        encoded path if path is unicode,
198
                        path itself otherwise.
199
        :raise:         UnableEncodePath.
200
        """
201
        if isinstance(path, unicode):
202
            try:
203
                return path.encode(bzrlib.user_encoding)
204
            except UnicodeEncodeError:
205
                raise errors.UnableEncodePath(path, kind)
206
        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).
207
2681.1.18 by Aaron Bentley
Refactor to increase code sharing, allow multiple command names for tbird
208
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
209
class Evolution(ExternalMailClient):
210
    """Evolution mail client."""
211
212
    _client_commands = ['evolution']
213
214
    def _get_compose_commandline(self, to, subject, attach_path):
2681.1.36 by Aaron Bentley
Update docs
215
        """See ExternalMailClient._get_compose_commandline"""
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
216
        message_options = {}
217
        if subject is not None:
218
            message_options['subject'] = subject
219
        if attach_path is not None:
220
            message_options['attach'] = attach_path
221
        options_list = ['%s=%s' % (k, urlutils.escape(v)) for (k, v) in
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.
222
                        sorted(message_options.iteritems())]
223
        return ['mailto:%s?%s' % (self._encode_safe(to or ''),
224
            '&'.join(options_list))]
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
225
226
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
227
class Mutt(ExternalMailClient):
228
    """Mutt mail client."""
229
230
    _client_commands = ['mutt']
231
232
    def _get_compose_commandline(self, to, subject, attach_path):
233
        """See ExternalMailClient._get_compose_commandline"""
234
        message_options = []
235
        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.
236
            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
237
        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.
238
            message_options.extend(['-a',
239
                self._encode_path(attach_path, 'attachment')])
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
240
        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.
241
            message_options.append(self._encode_safe(to))
2790.2.1 by Keir Mierle
Add Mutt as a supported client email program. Also rearranges various listings
242
        return message_options
243
244
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
245
class Thunderbird(ExternalMailClient):
2681.1.11 by Aaron Bentley
Add docstrings, add compose_merge_request
246
    """Mozilla Thunderbird (or Icedove)
247
248
    Note that Thunderbird 1.5 is buggy and does not support setting
249
    "to" simultaneously with including a attachment.
250
251
    There is a workaround if no attachment is present, but we always need to
252
    send attachments.
253
    """
254
2681.1.37 by Aaron Bentley
Update docstrings and string formatting
255
    _client_commands = ['thunderbird', 'mozilla-thunderbird', 'icedove',
256
        '/Applications/Mozilla/Thunderbird.app/Contents/MacOS/thunderbird-bin']
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
257
258
    def _get_compose_commandline(self, to, subject, attach_path):
2681.1.36 by Aaron Bentley
Update docs
259
        """See ExternalMailClient._get_compose_commandline"""
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
260
        message_options = {}
261
        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.
262
            message_options['to'] = self._encode_safe(to)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
263
        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.
264
            message_options['subject'] = self._encode_safe(subject)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
265
        if attach_path is not None:
3234.2.2 by Alexander Belchenko
[merge] URL is always ascii.
266
            message_options['attachment'] = urlutils.local_path_to_url(
267
                attach_path)
2681.1.8 by Aaron Bentley
Add Thunderbird support to bzr send
268
        options_list = ["%s='%s'" % (k, v) for k, v in
269
                        sorted(message_options.iteritems())]
270
        return ['-compose', ','.join(options_list)]
2681.1.23 by Aaron Bentley
Add support for xdg-email
271
272
2681.5.3 by ghigo
Add KMail mail client
273
class KMail(ExternalMailClient):
2681.5.1 by ghigo
Add KMail support to bzr send
274
    """KDE mail client."""
275
276
    _client_commands = ['kmail']
277
278
    def _get_compose_commandline(self, to, subject, attach_path):
2681.1.36 by Aaron Bentley
Update docs
279
        """See ExternalMailClient._get_compose_commandline"""
2681.5.1 by ghigo
Add KMail support to bzr send
280
        message_options = []
281
        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.
282
            message_options.extend(['-s', self._encode_safe(subject)])
2681.5.1 by ghigo
Add KMail support to bzr send
283
        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.
284
            message_options.extend(['--attach',
285
                self._encode_path(attach_path, 'attachment')])
2681.5.1 by ghigo
Add KMail support to bzr send
286
        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.
287
            message_options.extend([self._encode_safe(to)])
2681.5.1 by ghigo
Add KMail support to bzr send
288
        return message_options
289
290
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
291
class XDGEmail(ExternalMailClient):
2681.1.23 by Aaron Bentley
Add support for xdg-email
292
    """xdg-email attempts to invoke the user's preferred mail client"""
293
294
    _client_commands = ['xdg-email']
295
296
    def _get_compose_commandline(self, to, subject, attach_path):
2681.1.36 by Aaron Bentley
Update docs
297
        """See ExternalMailClient._get_compose_commandline"""
3042.1.1 by Lukáš Lalinský
Make mail-to address in ``bzr send`` optional for interactive mail clients.
298
        if not to:
299
            raise errors.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.
300
        commandline = [self._encode_safe(to)]
2681.1.23 by Aaron Bentley
Add support for xdg-email
301
        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.
302
            commandline.extend(['--subject', self._encode_safe(subject)])
2681.1.23 by Aaron Bentley
Add support for xdg-email
303
        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.
304
            commandline.extend(['--attach',
305
                self._encode_path(attach_path, 'attachment')])
2681.1.23 by Aaron Bentley
Add support for xdg-email
306
        return commandline
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
307
308
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
309
class EmacsMail(ExternalMailClient):
310
    """Call emacsclient to have a mail buffer.
311
312
    This only work for emacs >= 22.1 due to recent -e/--eval support.
313
314
    The good news is that this implementation will work with all mail
315
    agents registered against ``mail-user-agent``. So there is no need
316
    to instantiate ExternalMailClient for each and every GNU Emacs
317
    MUA.
318
319
    Users just have to ensure that ``mail-user-agent`` is set according
320
    to their tastes.
3322.1.1 by Ian Clatworthy
Add mail-mode GNU Emacs mail package as a mail client option (Xavier Maillard)
321
    """
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
322
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
323
    _client_commands = ['emacsclient']
324
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
325
    def _prepare_send_function(self):
326
        """Write our wrapper function into a temporary file.
327
328
        This temporary file will be loaded at runtime in
329
        _get_compose_commandline function.
330
331
        FIXME: this function does not remove the file. That's a wanted
332
        behaviour since _get_compose_commandline won't run the send
333
        mail function directly but return the eligible command line.
334
        Removing our temporary file here would prevent our sendmail
335
        function to work.
336
337
        A possible workaround could be to load the function here with
338
        emacsclient --eval '(load temp)' but this is not robust since
339
        emacs could have been stopped between here and the call to
340
        mail client.
341
        """
342
343
        _defun = r"""(defun bzr-add-mime-att (file)
344
  "Attach FILe to a mail buffer as a MIME attachment."
345
  (let ((agent mail-user-agent))
346
    (mail-text)
347
    (newline)
348
    (if (and file (file-exists-p file))
349
        (cond
350
         ((eq agent 'sendmail-user-agent)
351
          (etach-attach file))
352
         ((or (eq agent 'message-user-agent)(eq agent 'gnus-user-agent))
353
          (mml-attach-file file "text/x-patch" "BZR merge" "attachment"))
354
         (t
355
          (message "Unhandled MUA")))
356
      (error "File %s does not exist." file))))
357
"""
358
359
        fd, temp_file = tempfile.mkstemp(prefix="emacs-bzr-send-",
360
                                         suffix=".el")
361
        try:
362
            os.write(fd, _defun)
363
        finally:
364
            os.close(fd) # Just close the handle but do not remove the file.
365
        return temp_file
366
3322.1.1 by Ian Clatworthy
Add mail-mode GNU Emacs mail package as a mail client option (Xavier Maillard)
367
    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.
368
        commandline = ["--eval"]
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
369
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
370
        _to = "nil"
371
        _subject = "nil"
372
373
        if to is not None:
374
            _to = ("\"%s\"" % self._encode_safe(to))
375
        if subject is not None:
376
            _subject = ("\"%s\"" % self._encode_safe(subject))
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
377
378
        # Funcall the default mail composition function
379
        # This will work with any mail mode including default mail-mode
380
        # User must tweak mail-user-agent variable to tell what function
381
        # will be called inside compose-mail.
382
        mail_cmd = "(compose-mail %s %s)" % (_to, _subject)
383
        commandline.append(mail_cmd)
384
385
        # 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.
386
        if attach_path is not None:
3324.4.1 by Xavier Maillard
Replace mail-mode call with compose-mail from GNU Emacs.
387
            # Do not create a file if there is no attachment
388
            lmmform = '(load "%s")' % self._prepare_send_function()
389
            mmform  = '(bzr-add-mime-att "%s")' % \
390
                self._encode_path(attach_path, 'attachment')
391
            commandline.append(lmmform)
392
            commandline.append(mmform)
393
3302.6.1 by Xavier Maillard
Add mail-mode GNU Emacs mail package as a mail_client option.
394
        return commandline
395
396
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
397
class MAPIClient(ExternalMailClient):
398
    """Default Windows mail client launched using MAPI."""
399
400
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
401
                 extension):
2681.1.36 by Aaron Bentley
Update docs
402
        """See ExternalMailClient._compose.
403
404
        This implementation uses MAPI via the simplemapi ctypes wrapper
405
        """
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
406
        from bzrlib.util import simplemapi
407
        try:
408
            simplemapi.SendMail(to or '', subject or '', '', attach_path)
2681.3.6 by Lukáš Lalinsky
New version of simplemapi.py with MIT license.
409
        except simplemapi.MAPIError, e:
410
            if e.code != simplemapi.MAPI_USER_ABORT:
411
                raise errors.MailClientNotFound(['MAPI supported mail client'
412
                                                 ' (error %d)' % (e.code,)])
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
413
414
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
415
class DefaultMail(MailClient):
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
416
    """Default mail handling.  Tries XDGEmail (or MAPIClient on Windows),
417
    falls back to Editor"""
418
419
    def _mail_client(self):
2681.1.36 by Aaron Bentley
Update docs
420
        """Determine the preferred mail client for this platform"""
2681.3.4 by Lukáš Lalinsky
- Rename 'windows' to 'mapi'
421
        if osutils.supports_mapi():
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
422
            return MAPIClient(self.config)
423
        else:
424
            return XDGEmail(self.config)
2681.1.25 by Aaron Bentley
Cleanup
425
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
426
    def compose(self, prompt, to, subject, attachment, mime_subtype,
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
427
                extension, basename=None):
2681.1.36 by Aaron Bentley
Update docs
428
        """See MailClient.compose"""
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
429
        try:
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
430
            return self._mail_client().compose(prompt, to, subject,
431
                                               attachment, mimie_subtype,
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
432
                                               extension, basename)
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
433
        except errors.MailClientNotFound:
434
            return Editor(self.config).compose(prompt, to, subject,
435
                          attachment, mimie_subtype, extension)
436
3270.3.1 by James Westby
Fix the other implementation of compose_merge_request to accept basename.
437
    def compose_merge_request(self, to, subject, directive, basename=None):
2681.1.36 by Aaron Bentley
Update docs
438
        """See MailClient.compose_merge_request"""
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
439
        try:
2681.3.1 by Lukáš Lalinsky
Support for sending bundles using MAPI on Windows.
440
            return self._mail_client().compose_merge_request(to, subject,
3270.3.1 by James Westby
Fix the other implementation of compose_merge_request to accept basename.
441
                    directive, basename=basename)
2681.1.24 by Aaron Bentley
Handle default mail client by trying xdg-email, falling back to editor
442
        except errors.MailClientNotFound:
443
            return Editor(self.config).compose_merge_request(to, subject,
3270.3.1 by James Westby
Fix the other implementation of compose_merge_request to accept basename.
444
                          directive, basename=basename)