13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
41
41
self.config = config
43
43
def compose(self, prompt, to, subject, attachment, mime_subtype,
44
extension, basename=None, body=None):
44
extension, basename=None):
45
45
"""Compose (and possibly send) an email message
47
47
Must be implemented by subclasses.
62
62
raise NotImplementedError
64
def compose_merge_request(self, to, subject, directive, basename=None,
64
def compose_merge_request(self, to, subject, directive, basename=None):
66
65
"""Compose (and possibly send) a merge request
68
67
:param to: The address to send the request to
75
74
prompt = self._get_merge_prompt("Please describe these changes:", to,
76
75
subject, directive)
77
76
self.compose(prompt, to, subject, directive,
78
'x-patch', '.patch', basename, body)
77
'x-patch', '.patch', basename)
80
79
def _get_merge_prompt(self, prompt, to, subject, attachment):
81
80
"""Generate a prompt string. Overridden by Editor.
91
90
class Editor(MailClient):
92
91
"""DIY mail client that uses commit message editor"""
96
93
def _get_merge_prompt(self, prompt, to, subject, attachment):
97
94
"""See MailClient._get_merge_prompt"""
102
99
attachment.decode('utf-8', 'replace')))
104
101
def compose(self, prompt, to, subject, attachment, mime_subtype,
105
extension, basename=None, body=None):
102
extension, basename=None):
106
103
"""See MailClient.compose"""
108
105
raise errors.NoMailAddressSpecified()
109
body = msgeditor.edit_commit_message(prompt, start_message=body)
106
body = msgeditor.edit_commit_message(prompt)
111
108
raise errors.NoMessageSupplied()
112
109
email_message.EmailMessage.send(self.config,
133
129
return self._client_commands
135
131
def compose(self, prompt, to, subject, attachment, mime_subtype,
136
extension, basename=None, body=None):
132
extension, basename=None):
137
133
"""See MailClient.compose.
139
135
Writes the attachment to a temporary file, invokes _compose.
147
143
outfile.write(attachment)
151
kwargs = {'body': body}
154
146
self._compose(prompt, to, subject, attach_path, mime_subtype,
157
149
def _compose(self, prompt, to, subject, attach_path, mime_subtype,
158
extension, body=None):
159
151
"""Invoke a mail client as a commandline process.
161
153
Overridden by MAPIClient.
187
174
raise errors.MailClientNotFound(self._client_commands)
189
def _get_compose_commandline(self, to, subject, attach_path, body):
176
def _get_compose_commandline(self, to, subject, attach_path):
190
177
"""Determine the commandline to use for composing a message
192
179
Implemented by various subclasses
228
class ExternalMailClient(BodyExternalMailClient):
229
"""An external mail client."""
231
supports_body = False
234
class Evolution(BodyExternalMailClient):
215
class Evolution(ExternalMailClient):
235
216
"""Evolution mail client."""
237
218
_client_commands = ['evolution']
239
def _get_compose_commandline(self, to, subject, attach_path, body=None):
220
def _get_compose_commandline(self, to, subject, attach_path):
240
221
"""See ExternalMailClient._get_compose_commandline"""
241
222
message_options = {}
242
223
if subject is not None:
243
224
message_options['subject'] = subject
244
225
if attach_path is not None:
245
226
message_options['attach'] = attach_path
247
message_options['body'] = body
248
227
options_list = ['%s=%s' % (k, urlutils.escape(v)) for (k, v) in
249
228
sorted(message_options.iteritems())]
250
229
return ['mailto:%s?%s' % (self._encode_safe(to or ''),
287
266
'/Applications/Mozilla/Thunderbird.app/Contents/MacOS/thunderbird-bin',
288
267
'/Applications/Thunderbird.app/Contents/MacOS/thunderbird-bin']
290
def _get_compose_commandline(self, to, subject, attach_path, body=None):
269
def _get_compose_commandline(self, to, subject, attach_path):
291
270
"""See ExternalMailClient._get_compose_commandline"""
292
271
message_options = {}
293
272
if to is not None:
297
276
if attach_path is not None:
298
277
message_options['attachment'] = urlutils.local_path_to_url(
301
options_list = ['body=%s' % urllib.quote(self._encode_safe(body))]
304
options_list.extend(["%s='%s'" % (k, v) for k, v in
305
sorted(message_options.iteritems())])
279
options_list = ["%s='%s'" % (k, v) for k, v in
280
sorted(message_options.iteritems())]
306
281
return ['-compose', ','.join(options_list)]
307
282
mail_client_registry.register('thunderbird', Thunderbird,
308
283
help=Thunderbird.__doc__)
354
329
help=Claws.__doc__)
357
class XDGEmail(BodyExternalMailClient):
332
class XDGEmail(ExternalMailClient):
358
333
"""xdg-email attempts to invoke the user's preferred mail client"""
360
335
_client_commands = ['xdg-email']
362
def _get_compose_commandline(self, to, subject, attach_path, body=None):
337
def _get_compose_commandline(self, to, subject, attach_path):
363
338
"""See ExternalMailClient._get_compose_commandline"""
365
340
raise errors.NoMailAddressSpecified()
369
344
if attach_path is not None:
370
345
commandline.extend(['--attach',
371
346
self._encode_path(attach_path, 'attachment')])
373
commandline.extend(['--body', self._encode_safe(body)])
374
347
return commandline
375
348
mail_client_registry.register('xdg-email', XDGEmail,
376
349
help=XDGEmail.__doc__)
481
454
help=EmacsMail.__doc__)
484
class MAPIClient(BodyExternalMailClient):
457
class MAPIClient(ExternalMailClient):
485
458
"""Default Windows mail client launched using MAPI."""
487
460
def _compose(self, prompt, to, subject, attach_path, mime_subtype,
489
462
"""See ExternalMailClient._compose.
491
464
This implementation uses MAPI via the simplemapi ctypes wrapper
493
466
from bzrlib.util import simplemapi
495
simplemapi.SendMail(to or '', subject or '', body or '',
468
simplemapi.SendMail(to or '', subject or '', '', attach_path)
497
469
except simplemapi.MAPIError, e:
498
470
if e.code != simplemapi.MAPI_USER_ABORT:
499
471
raise errors.MailClientNotFound(['MAPI supported mail client'
514
486
return XDGEmail(self.config)
516
488
def compose(self, prompt, to, subject, attachment, mime_subtype,
517
extension, basename=None, body=None):
489
extension, basename=None):
518
490
"""See MailClient.compose"""
520
492
return self._mail_client().compose(prompt, to, subject,
521
493
attachment, mimie_subtype,
522
extension, basename, body)
523
495
except errors.MailClientNotFound:
524
496
return Editor(self.config).compose(prompt, to, subject,
525
attachment, mimie_subtype, extension, body)
497
attachment, mimie_subtype, extension)
527
def compose_merge_request(self, to, subject, directive, basename=None,
499
def compose_merge_request(self, to, subject, directive, basename=None):
529
500
"""See MailClient.compose_merge_request"""
531
502
return self._mail_client().compose_merge_request(to, subject,
532
directive, basename=basename, body=body)
503
directive, basename=basename)
533
504
except errors.MailClientNotFound:
534
505
return Editor(self.config).compose_merge_request(to, subject,
535
directive, basename=basename, body=body)
506
directive, basename=basename)
536
507
mail_client_registry.register('default', DefaultMail,
537
508
help=DefaultMail.__doc__)
538
509
mail_client_registry.default_key = 'default'