~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/mail_client.py

  • Committer: Alexander Belchenko
  • Date: 2008-02-06 15:33:12 UTC
  • mto: This revision was merged to the branch mainline in revision 3231.
  • Revision ID: bialix@ukr.net-20080206153312-qycs7u05d7fjtwqq
Ian's review

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
import sys
21
21
import tempfile
22
22
 
23
 
import bzrlib
24
23
from bzrlib import (
25
24
    email_message,
26
25
    errors,
37
36
        self.config = config
38
37
 
39
38
    def compose(self, prompt, to, subject, attachment, mime_subtype,
40
 
                extension, basename=None):
 
39
                extension):
41
40
        """Compose (and possibly send) an email message
42
41
 
43
42
        Must be implemented by subclasses.
52
51
            "plain", "x-patch", etc.
53
52
        :param extension: The file extension associated with the attachment
54
53
            type, e.g. ".patch"
55
 
        :param basename: The name to use for the attachment, e.g.
56
 
            "send-nick-3252"
57
54
        """
58
55
        raise NotImplementedError
59
56
 
60
 
    def compose_merge_request(self, to, subject, directive, basename=None):
 
57
    def compose_merge_request(self, to, subject, directive):
61
58
        """Compose (and possibly send) a merge request
62
59
 
63
60
        :param to: The address to send the request to
64
61
        :param subject: The subject line to use for the request
65
62
        :param directive: A merge directive representing the merge request, as
66
63
            a bytestring.
67
 
        :param basename: The name to use for the attachment, e.g.
68
 
            "send-nick-3252"
69
64
        """
70
65
        prompt = self._get_merge_prompt("Please describe these changes:", to,
71
66
                                        subject, directive)
72
67
        self.compose(prompt, to, subject, directive,
73
 
            'x-patch', '.patch', basename)
 
68
            'x-patch', '.patch')
74
69
 
75
70
    def _get_merge_prompt(self, prompt, to, subject, attachment):
76
71
        """Generate a prompt string.  Overridden by Editor.
95
90
                         attachment.decode('utf-8', 'replace')))
96
91
 
97
92
    def compose(self, prompt, to, subject, attachment, mime_subtype,
98
 
                extension, basename=None):
 
93
                extension):
99
94
        """See MailClient.compose"""
100
95
        if not to:
101
96
            raise errors.NoMailAddressSpecified()
123
118
            return self._client_commands
124
119
 
125
120
    def compose(self, prompt, to, subject, attachment, mime_subtype,
126
 
                extension, basename=None):
 
121
                extension):
127
122
        """See MailClient.compose.
128
123
 
129
124
        Writes the attachment to a temporary file, invokes _compose.
130
125
        """
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')
 
126
        fd, pathname = tempfile.mkstemp(extension, 'bzr-mail-')
136
127
        try:
137
 
            outfile.write(attachment)
 
128
            os.write(fd, attachment)
138
129
        finally:
139
 
            outfile.close()
140
 
        self._compose(prompt, to, subject, attach_path, mime_subtype,
141
 
                      extension)
 
130
            os.close(fd)
 
131
        self._compose(prompt, to, subject, pathname, mime_subtype, extension)
142
132
 
143
133
    def _compose(self, prompt, to, subject, attach_path, mime_subtype,
144
134
                extension):
154
144
            the attachment type.
155
145
        """
156
146
        for name in self._get_client_commands():
157
 
            cmdline = [self._encode_path(name, 'executable')]
 
147
            cmdline = [name]
158
148
            cmdline.extend(self._get_compose_commandline(to, subject,
159
149
                                                         attach_path))
160
150
            try:
177
167
        """
178
168
        raise NotImplementedError
179
169
 
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
207
 
 
208
170
 
209
171
class Evolution(ExternalMailClient):
210
172
    """Evolution mail client."""
219
181
        if attach_path is not None:
220
182
            message_options['attach'] = attach_path
221
183
        options_list = ['%s=%s' % (k, urlutils.escape(v)) for (k, v) in
222
 
                        sorted(message_options.iteritems())]
223
 
        return ['mailto:%s?%s' % (self._encode_safe(to or ''),
224
 
            '&'.join(options_list))]
 
184
                        message_options.iteritems()]
 
185
        return ['mailto:%s?%s' % (to or '', '&'.join(options_list))]
225
186
 
226
187
 
227
188
class Mutt(ExternalMailClient):
233
194
        """See ExternalMailClient._get_compose_commandline"""
234
195
        message_options = []
235
196
        if subject is not None:
236
 
            message_options.extend(['-s', self._encode_safe(subject)])
 
197
            message_options.extend(['-s', subject ])
237
198
        if attach_path is not None:
238
 
            message_options.extend(['-a',
239
 
                self._encode_path(attach_path, 'attachment')])
 
199
            message_options.extend(['-a', attach_path])
240
200
        if to is not None:
241
 
            message_options.append(self._encode_safe(to))
 
201
            message_options.append(to)
242
202
        return message_options
243
203
 
244
204
 
259
219
        """See ExternalMailClient._get_compose_commandline"""
260
220
        message_options = {}
261
221
        if to is not None:
262
 
            message_options['to'] = self._encode_safe(to)
 
222
            message_options['to'] = to
263
223
        if subject is not None:
264
 
            message_options['subject'] = self._encode_safe(subject)
 
224
            message_options['subject'] = subject
265
225
        if attach_path is not None:
266
226
            message_options['attachment'] = urlutils.local_path_to_url(
267
227
                attach_path)
279
239
        """See ExternalMailClient._get_compose_commandline"""
280
240
        message_options = []
281
241
        if subject is not None:
282
 
            message_options.extend(['-s', self._encode_safe(subject)])
 
242
            message_options.extend( ['-s', subject ] )
283
243
        if attach_path is not None:
284
 
            message_options.extend(['--attach',
285
 
                self._encode_path(attach_path, 'attachment')])
 
244
            message_options.extend( ['--attach', attach_path] )
286
245
        if to is not None:
287
 
            message_options.extend([self._encode_safe(to)])
 
246
            message_options.extend( [ to ] )
 
247
 
288
248
        return message_options
289
249
 
290
250
 
297
257
        """See ExternalMailClient._get_compose_commandline"""
298
258
        if not to:
299
259
            raise errors.NoMailAddressSpecified()
300
 
        commandline = [self._encode_safe(to)]
 
260
        commandline = [to]
301
261
        if subject is not None:
302
 
            commandline.extend(['--subject', self._encode_safe(subject)])
 
262
            commandline.extend(['--subject', subject])
303
263
        if attach_path is not None:
304
 
            commandline.extend(['--attach',
305
 
                self._encode_path(attach_path, 'attachment')])
 
264
            commandline.extend(['--attach', attach_path])
306
265
        return commandline
307
266
 
308
267
 
336
295
            return XDGEmail(self.config)
337
296
 
338
297
    def compose(self, prompt, to, subject, attachment, mime_subtype,
339
 
                extension, basename=None):
 
298
                extension):
340
299
        """See MailClient.compose"""
341
300
        try:
342
301
            return self._mail_client().compose(prompt, to, subject,
343
302
                                               attachment, mimie_subtype,
344
 
                                               extension, basename)
 
303
                                               extension)
345
304
        except errors.MailClientNotFound:
346
305
            return Editor(self.config).compose(prompt, to, subject,
347
306
                          attachment, mimie_subtype, extension)