14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
25
27
config as _mod_config,
34
36
mail_client_registry = registry.Registry()
37
class MailClientNotFound(errors.BzrError):
39
_fmt = "Unable to find mail client with the following names:"\
40
" %(mail_command_list_string)s"
42
def __init__(self, mail_command_list):
43
mail_command_list_string = ', '.join(mail_command_list)
44
errors.BzrError.__init__(
45
self, mail_command_list=mail_command_list,
46
mail_command_list_string=mail_command_list_string)
49
class NoMessageSupplied(errors.BzrError):
51
_fmt = "No message supplied."
54
class NoMailAddressSpecified(errors.BzrError):
56
_fmt = "No mail-to address (--mail-to) or output (-o) specified."
59
39
class MailClient(object):
60
40
"""A mail client that can send messages with attachements."""
282
268
"""See ExternalMailClient._get_compose_commandline"""
283
269
message_options = []
284
270
if subject is not None:
285
message_options.extend(
286
['-s', self._encode_safe(subject)])
271
message_options.extend(['-s', self._encode_safe(subject)])
287
272
if attach_path is not None:
288
message_options.extend(
289
['-a', self._encode_path(attach_path, 'attachment')])
273
message_options.extend(['-a',
274
self._encode_path(attach_path, 'attachment')])
290
275
if body is not None:
291
276
# Store the temp file object in self, so that it does not get
292
277
# garbage collected and delete the file before mutt can read it.
293
278
self._temp_file = tempfile.NamedTemporaryFile(
294
prefix="mutt-body-", suffix=".txt", mode="w+")
279
prefix="mutt-body-", suffix=".txt")
295
280
self._temp_file.write(body)
296
281
self._temp_file.flush()
297
282
message_options.extend(['-i', self._temp_file.name])
298
283
if to is not None:
299
284
message_options.extend(['--', self._encode_safe(to)])
300
285
return message_options
303
286
mail_client_registry.register('mutt', Mutt,
304
287
help=Mutt.__doc__)
414
391
class XDGEmail(BodyExternalMailClient):
415
__doc__ = """xdg-email attempts to invoke the preferred mail client"""
392
__doc__ = """xdg-email attempts to invoke the user's preferred mail client"""
417
394
_client_commands = ['xdg-email']
419
396
def _get_compose_commandline(self, to, subject, attach_path, body=None):
420
397
"""See ExternalMailClient._get_compose_commandline"""
422
raise NoMailAddressSpecified()
399
raise errors.NoMailAddressSpecified()
423
400
commandline = [self._encode_safe(to)]
424
401
if subject is not None:
425
402
commandline.extend(['--subject', self._encode_safe(subject)])
426
403
if attach_path is not None:
427
404
commandline.extend(['--attach',
428
self._encode_path(attach_path, 'attachment')])
405
self._encode_path(attach_path, 'attachment')])
429
406
if body is not None:
430
407
commandline.extend(['--body', self._encode_safe(body)])
431
408
return commandline
434
409
mail_client_registry.register('xdg-email', XDGEmail,
435
410
help=XDGEmail.__doc__)
557
530
This implementation uses MAPI via the simplemapi ctypes wrapper
559
from .util import simplemapi
532
from bzrlib.util import simplemapi
561
534
simplemapi.SendMail(to or '', subject or '', body or '',
563
except simplemapi.MAPIError as e:
536
except simplemapi.MAPIError, e:
564
537
if e.code != simplemapi.MAPI_USER_ABORT:
565
raise MailClientNotFound(['MAPI supported mail client'
566
' (error %d)' % (e.code,)])
538
raise errors.MailClientNotFound(['MAPI supported mail client'
539
' (error %d)' % (e.code,)])
569
540
mail_client_registry.register('mapi', MAPIClient,
570
541
help=MAPIClient.__doc__)
583
554
_client_commands = ['osascript']
585
556
def _get_compose_commandline(self, to, subject, attach_path, body=None,
587
"""See ExternalMailClient._get_compose_commandline"""
589
fd, self.temp_file = tempfile.mkstemp(prefix="bzr-send-",
592
os.write(fd, 'tell application "Mail"\n')
593
os.write(fd, 'set newMessage to make new outgoing message\n')
594
os.write(fd, 'tell newMessage\n')
596
os.write(fd, 'make new to recipient with properties'
597
' {address:"%s"}\n' % to)
598
if from_ is not None:
599
# though from_ doesn't actually seem to be used
600
os.write(fd, 'set sender to "%s"\n'
601
% from_.replace('"', '\\"'))
602
if subject is not None:
603
os.write(fd, 'set subject to "%s"\n'
604
% subject.replace('"', '\\"'))
606
# FIXME: would be nice to prepend the body to the
607
# existing content (e.g., preserve signature), but
608
# can't seem to figure out the right applescript
610
os.write(fd, 'set content to "%s\\n\n"\n' %
611
body.replace('"', '\\"').replace('\n', '\\n'))
613
if attach_path is not None:
614
# FIXME: would be nice to first append a newline to
615
# ensure the attachment is on a new paragraph, but
616
# can't seem to figure out the right applescript
618
os.write(fd, 'tell content to make new attachment'
619
' with properties {file name:"%s"}'
620
' at after the last paragraph\n'
621
% self._encode_path(attach_path, 'attachment'))
622
os.write(fd, 'set visible to true\n')
623
os.write(fd, 'end tell\n')
624
os.write(fd, 'end tell\n')
626
os.close(fd) # Just close the handle but do not remove the file.
627
return [self.temp_file]
558
"""See ExternalMailClient._get_compose_commandline"""
560
fd, self.temp_file = tempfile.mkstemp(prefix="bzr-send-",
563
os.write(fd, 'tell application "Mail"\n')
564
os.write(fd, 'set newMessage to make new outgoing message\n')
565
os.write(fd, 'tell newMessage\n')
567
os.write(fd, 'make new to recipient with properties'
568
' {address:"%s"}\n' % to)
569
if from_ is not None:
570
# though from_ doesn't actually seem to be used
571
os.write(fd, 'set sender to "%s"\n'
572
% sender.replace('"', '\\"'))
573
if subject is not None:
574
os.write(fd, 'set subject to "%s"\n'
575
% subject.replace('"', '\\"'))
577
# FIXME: would be nice to prepend the body to the
578
# existing content (e.g., preserve signature), but
579
# can't seem to figure out the right applescript
581
os.write(fd, 'set content to "%s\\n\n"\n' %
582
body.replace('"', '\\"').replace('\n', '\\n'))
584
if attach_path is not None:
585
# FIXME: would be nice to first append a newline to
586
# ensure the attachment is on a new paragraph, but
587
# can't seem to figure out the right applescript
589
os.write(fd, 'tell content to make new attachment'
590
' with properties {file name:"%s"}'
591
' at after the last paragraph\n'
592
% self._encode_path(attach_path, 'attachment'))
593
os.write(fd, 'set visible to true\n')
594
os.write(fd, 'end tell\n')
595
os.write(fd, 'end tell\n')
597
os.close(fd) # Just close the handle but do not remove the file.
598
return [self.temp_file]
630
599
mail_client_registry.register('mail.app', MailApp,
631
600
help=MailApp.__doc__)
651
620
return self._mail_client().compose(prompt, to, subject,
652
621
attachment, mime_subtype,
653
622
extension, basename, body)
654
except MailClientNotFound:
655
return Editor(self.config).compose(
656
prompt, to, subject, attachment, mime_subtype, extension, body)
623
except errors.MailClientNotFound:
624
return Editor(self.config).compose(prompt, to, subject,
625
attachment, mime_subtype, extension, body)
658
627
def compose_merge_request(self, to, subject, directive, basename=None,
660
629
"""See MailClient.compose_merge_request"""
662
return self._mail_client().compose_merge_request(
663
to, subject, directive, basename=basename, body=body)
664
except MailClientNotFound:
665
return Editor(self.config).compose_merge_request(
666
to, subject, directive, basename=basename, body=body)
669
mail_client_registry.register(u'default', DefaultMail,
631
return self._mail_client().compose_merge_request(to, subject,
632
directive, basename=basename, body=body)
633
except errors.MailClientNotFound:
634
return Editor(self.config).compose_merge_request(to, subject,
635
directive, basename=basename, body=body)
636
mail_client_registry.register('default', DefaultMail,
670
637
help=DefaultMail.__doc__)
671
mail_client_registry.default_key = u'default'
638
mail_client_registry.default_key = 'default'
673
opt_mail_client = _mod_config.RegistryOption(
674
'mail_client', mail_client_registry, help='E-mail client to use.',
640
opt_mail_client = _mod_config.RegistryOption('mail_client',
641
mail_client_registry, help='E-mail client to use.', invalid='error')