~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/mail_client.py

  • Committer: Ian Clatworthy
  • Date: 2009-09-09 15:30:59 UTC
  • mto: (4634.37.2 prepare-2.0)
  • mto: This revision was merged to the branch mainline in revision 4689.
  • Revision ID: ian.clatworthy@canonical.com-20090909153059-sb038agvd38ci2q8
more link fixes in the User Guide

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import errno
20
18
import os
21
19
import subprocess
22
20
import sys
23
21
import tempfile
 
22
import urllib
24
23
 
25
24
import bzrlib
26
25
from bzrlib import (
27
 
    config as _mod_config,
28
26
    email_message,
29
27
    errors,
30
28
    msgeditor,
91
89
 
92
90
 
93
91
class Editor(MailClient):
94
 
    __doc__ = """DIY mail client that uses commit message editor"""
 
92
    """DIY mail client that uses commit message editor"""
95
93
 
96
94
    supports_body = True
97
95
 
112
110
        if body == '':
113
111
            raise errors.NoMessageSupplied()
114
112
        email_message.EmailMessage.send(self.config,
115
 
                                        self.config.get('email'),
 
113
                                        self.config.username(),
116
114
                                        to,
117
115
                                        subject,
118
116
                                        body,
232
230
 
233
231
 
234
232
class ExternalMailClient(BodyExternalMailClient):
235
 
    __doc__ = """An external mail client."""
 
233
    """An external mail client."""
236
234
 
237
235
    supports_body = False
238
236
 
239
237
 
240
238
class Evolution(BodyExternalMailClient):
241
 
    __doc__ = """Evolution mail client."""
 
239
    """Evolution mail client."""
242
240
 
243
241
    _client_commands = ['evolution']
244
242
 
260
258
 
261
259
 
262
260
class Mutt(BodyExternalMailClient):
263
 
    __doc__ = """Mutt mail client."""
 
261
    """Mutt mail client."""
264
262
 
265
263
    _client_commands = ['mutt']
266
264
 
288
286
 
289
287
 
290
288
class Thunderbird(BodyExternalMailClient):
291
 
    __doc__ = """Mozilla Thunderbird (or Icedove)
 
289
    """Mozilla Thunderbird (or Icedove)
292
290
 
293
291
    Note that Thunderbird 1.5 is buggy and does not support setting
294
292
    "to" simultaneously with including a attachment.
312
310
            message_options['attachment'] = urlutils.local_path_to_url(
313
311
                attach_path)
314
312
        if body is not None:
315
 
            options_list = ['body=%s' % urlutils.quote(self._encode_safe(body))]
 
313
            options_list = ['body=%s' % urllib.quote(self._encode_safe(body))]
316
314
        else:
317
315
            options_list = []
318
316
        options_list.extend(["%s='%s'" % (k, v) for k, v in
323
321
 
324
322
 
325
323
class KMail(ExternalMailClient):
326
 
    __doc__ = """KDE mail client."""
 
324
    """KDE mail client."""
327
325
 
328
326
    _client_commands = ['kmail']
329
327
 
343
341
 
344
342
 
345
343
class Claws(ExternalMailClient):
346
 
    __doc__ = """Claws mail client."""
 
344
    """Claws mail client."""
347
345
 
348
346
    supports_body = True
349
347
 
354
352
        """See ExternalMailClient._get_compose_commandline"""
355
353
        compose_url = []
356
354
        if from_ is not None:
357
 
            compose_url.append('from=' + urlutils.quote(from_))
 
355
            compose_url.append('from=' + urllib.quote(from_))
358
356
        if subject is not None:
359
 
            # Don't use urlutils.quote_plus because Claws doesn't seem
 
357
            # Don't use urllib.quote_plus because Claws doesn't seem
360
358
            # to recognise spaces encoded as "+".
361
359
            compose_url.append(
362
 
                'subject=' + urlutils.quote(self._encode_safe(subject)))
 
360
                'subject=' + urllib.quote(self._encode_safe(subject)))
363
361
        if body is not None:
364
362
            compose_url.append(
365
 
                'body=' + urlutils.quote(self._encode_safe(body)))
 
363
                'body=' + urllib.quote(self._encode_safe(body)))
366
364
        # to must be supplied for the claws-mail --compose syntax to work.
367
365
        if to is None:
368
366
            raise errors.NoMailAddressSpecified()
379
377
                 extension, body=None, from_=None):
380
378
        """See ExternalMailClient._compose"""
381
379
        if from_ is None:
382
 
            from_ = self.config.get('email')
 
380
            from_ = self.config.get_user_option('email')
383
381
        super(Claws, self)._compose(prompt, to, subject, attach_path,
384
382
                                    mime_subtype, extension, body, from_)
385
383
 
389
387
 
390
388
 
391
389
class XDGEmail(BodyExternalMailClient):
392
 
    __doc__ = """xdg-email attempts to invoke the user's preferred mail client"""
 
390
    """xdg-email attempts to invoke the user's preferred mail client"""
393
391
 
394
392
    _client_commands = ['xdg-email']
395
393
 
411
409
 
412
410
 
413
411
class EmacsMail(ExternalMailClient):
414
 
    __doc__ = """Call emacsclient to have a mail buffer.
 
412
    """Call emacsclient to have a mail buffer.
415
413
 
416
414
    This only work for emacs >= 22.1 due to recent -e/--eval support.
417
415
 
426
424
 
427
425
    _client_commands = ['emacsclient']
428
426
 
429
 
    def __init__(self, config):
430
 
        super(EmacsMail, self).__init__(config)
431
 
        self.elisp_tmp_file = None
432
 
 
433
427
    def _prepare_send_function(self):
434
428
        """Write our wrapper function into a temporary file.
435
429
 
506
500
        if attach_path is not None:
507
501
            # Do not create a file if there is no attachment
508
502
            elisp = self._prepare_send_function()
509
 
            self.elisp_tmp_file = elisp
510
503
            lmmform = '(load "%s")' % elisp
511
504
            mmform  = '(bzr-add-mime-att "%s")' % \
512
505
                self._encode_path(attach_path, 'attachment')
521
514
 
522
515
 
523
516
class MAPIClient(BodyExternalMailClient):
524
 
    __doc__ = """Default Windows mail client launched using MAPI."""
 
517
    """Default Windows mail client launched using MAPI."""
525
518
 
526
519
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
527
520
                 extension, body=None):
541
534
                              help=MAPIClient.__doc__)
542
535
 
543
536
 
544
 
class MailApp(BodyExternalMailClient):
545
 
    __doc__ = """Use MacOS X's Mail.app for sending email messages.
546
 
 
547
 
    Although it would be nice to use appscript, it's not installed
548
 
    with the shipped Python installations.  We instead build an
549
 
    AppleScript and invoke the script using osascript(1).  We don't
550
 
    use the _encode_safe() routines as it's not clear what encoding
551
 
    osascript expects the script to be in.
552
 
    """
553
 
 
554
 
    _client_commands = ['osascript']
555
 
 
556
 
    def _get_compose_commandline(self, to, subject, attach_path, body=None,
557
 
                                from_=None):
558
 
       """See ExternalMailClient._get_compose_commandline"""
559
 
 
560
 
       fd, self.temp_file = tempfile.mkstemp(prefix="bzr-send-",
561
 
                                         suffix=".scpt")
562
 
       try:
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')
566
 
           if to is not None:
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('"', '\\"'))
576
 
           if body is not None:
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
580
 
               # incantation.
581
 
               os.write(fd, 'set content to "%s\\n\n"\n' %
582
 
                   body.replace('"', '\\"').replace('\n', '\\n'))
583
 
 
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
588
 
               # incantation.
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')
596
 
       finally:
597
 
           os.close(fd) # Just close the handle but do not remove the file.
598
 
       return [self.temp_file]
599
 
mail_client_registry.register('mail.app', MailApp,
600
 
                              help=MailApp.__doc__)
601
 
 
602
 
 
603
537
class DefaultMail(MailClient):
604
 
    __doc__ = """Default mail handling.  Tries XDGEmail (or MAPIClient on Windows),
 
538
    """Default mail handling.  Tries XDGEmail (or MAPIClient on Windows),
605
539
    falls back to Editor"""
606
540
 
607
541
    supports_body = True
618
552
        """See MailClient.compose"""
619
553
        try:
620
554
            return self._mail_client().compose(prompt, to, subject,
621
 
                                               attachment, mime_subtype,
 
555
                                               attachment, mimie_subtype,
622
556
                                               extension, basename, body)
623
557
        except errors.MailClientNotFound:
624
558
            return Editor(self.config).compose(prompt, to, subject,
625
 
                          attachment, mime_subtype, extension, body)
 
559
                          attachment, mimie_subtype, extension, body)
626
560
 
627
561
    def compose_merge_request(self, to, subject, directive, basename=None,
628
562
                              body=None):
636
570
mail_client_registry.register('default', DefaultMail,
637
571
                              help=DefaultMail.__doc__)
638
572
mail_client_registry.default_key = 'default'
639
 
 
640
 
opt_mail_client = _mod_config.RegistryOption('mail_client',
641
 
        mail_client_registry, help='E-mail client to use.', invalid='error')