Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: boto/ses/connection.py

Issue 8386013: Merging in latest boto. (Closed) Base URL: svn://svn.chromium.org/boto
Patch Set: Redoing vendor drop by deleting and then merging. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « boto/ses/__init__.py ('k') | boto/sns/__init__.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2010 Mitch Garnaat http://garnaat.org/ 1 # Copyright (c) 2010 Mitch Garnaat http://garnaat.org/
2 # Copyright (c) 2011 Harry Marr http://hmarr.com/ 2 # Copyright (c) 2011 Harry Marr http://hmarr.com/
3 # 3 #
4 # Permission is hereby granted, free of charge, to any person obtaining a 4 # Permission is hereby granted, free of charge, to any person obtaining a
5 # copy of this software and associated documentation files (the 5 # copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including 6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish, dis- 7 # without limitation the rights to use, copy, modify, merge, publish, dis-
8 # tribute, sublicense, and/or sell copies of the Software, and to permit 8 # tribute, sublicense, and/or sell copies of the Software, and to permit
9 # persons to whom the Software is furnished to do so, subject to the fol- 9 # persons to whom the Software is furnished to do so, subject to the fol-
10 # lowing conditions: 10 # lowing conditions:
11 # 11 #
12 # The above copyright notice and this permission notice shall be included 12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software. 13 # in all copies or substantial portions of the Software.
14 # 14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 # IN THE SOFTWARE. 21 # IN THE SOFTWARE.
22 22
23 from boto.connection import AWSAuthConnection 23 from boto.connection import AWSAuthConnection
24 from boto.exception import BotoServerError 24 from boto.exception import BotoServerError
25 from boto.regioninfo import RegionInfo
25 import boto 26 import boto
26 import boto.jsonresponse 27 import boto.jsonresponse
27 28
28 import urllib 29 import urllib
29 import base64 30 import base64
30 31
31 32
32 class SESConnection(AWSAuthConnection): 33 class SESConnection(AWSAuthConnection):
33 34
34 ResponseError = BotoServerError 35 ResponseError = BotoServerError
35 DefaultHost = 'email.us-east-1.amazonaws.com' 36 DefaultRegionName = 'us-east-1'
37 DefaultRegionEndpoint = 'email.us-east-1.amazonaws.com'
36 APIVersion = '2010-12-01' 38 APIVersion = '2010-12-01'
37 39
38 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, 40 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
39 port=None, proxy=None, proxy_port=None, 41 is_secure=True, port=None, proxy=None, proxy_port=None,
40 host=DefaultHost, debug=0): 42 proxy_user=None, proxy_pass=None, debug=0,
41 AWSAuthConnection.__init__(self, host, aws_access_key_id, 43 https_connection_factory=None, region=None, path='/'):
42 aws_secret_access_key, True, port, proxy, 44 if not region:
43 proxy_port, debug=debug) 45 region = RegionInfo(self, self.DefaultRegionName,
46 self.DefaultRegionEndpoint)
47 self.region = region
48 AWSAuthConnection.__init__(self, self.region.endpoint,
49 aws_access_key_id, aws_secret_access_key,
50 is_secure, port, proxy, proxy_port,
51 proxy_user, proxy_pass, debug,
52 https_connection_factory, path)
44 53
45 def _required_auth_capability(self): 54 def _required_auth_capability(self):
46 return ['ses'] 55 return ['ses']
47 56
48 def _build_list_params(self, params, items, label): 57 def _build_list_params(self, params, items, label):
49 """Add an AWS API-compatible parameter list to a dictionary. 58 """Add an AWS API-compatible parameter list to a dictionary.
50 59
51 :type params: dict 60 :type params: dict
52 :param params: The parameter dictionary 61 :param params: The parameter dictionary
53 62
54 :type items: list 63 :type items: list
55 :param items: Items to be included in the list 64 :param items: Items to be included in the list
56 65
57 :type label: string 66 :type label: string
58 :param label: The parameter list's name 67 :param label: The parameter list's name
59 """ 68 """
60 if isinstance(items, str): 69 if isinstance(items, basestring):
61 items = [items] 70 items = [items]
62 for i in range(1, len(items) + 1): 71 for i in range(1, len(items) + 1):
63 params['%s.%d' % (label, i)] = items[i - 1] 72 params['%s.%d' % (label, i)] = items[i - 1]
64 73
65 74
66 def _make_request(self, action, params=None): 75 def _make_request(self, action, params=None):
67 """Make a call to the SES API. 76 """Make a call to the SES API.
68 77
69 :type action: string 78 :type action: string
70 :param action: The API method to use (e.g. SendRawEmail) 79 :param action: The API method to use (e.g. SendRawEmail)
71 80
72 :type params: dict 81 :type params: dict
73 :param params: Parameters that will be sent as POST data with the API 82 :param params: Parameters that will be sent as POST data with the API
74 call. 83 call.
75 """ 84 """
76 headers = {'Content-Type': 'application/x-www-form-urlencoded'} 85 ct = 'application/x-www-form-urlencoded; charset=UTF-8'
86 headers = {'Content-Type': ct}
77 params = params or {} 87 params = params or {}
78 params['Action'] = action 88 params['Action'] = action
89
90 for k, v in params.items():
91 if isinstance(v, unicode): # UTF-8 encode only if it's Unicode
92 params[k] = v.encode('utf-8')
93
79 response = super(SESConnection, self).make_request( 94 response = super(SESConnection, self).make_request(
80 'POST', 95 'POST',
81 '/', 96 '/',
82 headers=headers, 97 headers=headers,
83 data=urllib.urlencode(params) 98 data=urllib.urlencode(params)
84 ) 99 )
85 body = response.read() 100 body = response.read()
86 if response.status == 200: 101 if response.status == 200:
87 list_markers = ('VerifiedEmailAddresses', 'SendDataPoints') 102 list_markers = ('VerifiedEmailAddresses', 'SendDataPoints')
88 e = boto.jsonresponse.Element(list_marker=list_markers) 103 e = boto.jsonresponse.Element(list_marker=list_markers)
89 h = boto.jsonresponse.XmlHandler(e, None) 104 h = boto.jsonresponse.XmlHandler(e, None)
90 h.parse(body) 105 h.parse(body)
91 return e 106 return e
92 else: 107 else:
93 boto.log.error('%s %s' % (response.status, response.reason)) 108 boto.log.error('%s %s' % (response.status, response.reason))
94 boto.log.error('%s' % body) 109 boto.log.error('%s' % body)
95 raise self.ResponseError(response.status, response.reason, body) 110 raise self.ResponseError(response.status, response.reason, body)
96 111
97 112
98 def send_email(self, source, subject, body, to_addresses, cc_addresses=None, 113 def send_email(self, source, subject, body, to_addresses, cc_addresses=None,
99 bcc_addresses=None, format='text'): 114 bcc_addresses=None, format='text', reply_addresses=None,
115 return_path=None, text_body=None, html_body=None):
100 """Composes an email message based on input data, and then immediately 116 """Composes an email message based on input data, and then immediately
101 queues the message for sending. 117 queues the message for sending.
102 118
103 :type source: string 119 :type source: string
104 :param source: The sender's email address. 120 :param source: The sender's email address.
105 121
106 :type subject: string 122 :type subject: string
107 :param subject: The subject of the message: A short summary of the 123 :param subject: The subject of the message: A short summary of the
108 content, which will appear in the recipient's inbox. 124 content, which will appear in the recipient's inbox.
109 125
110 :type body: string 126 :type body: string
111 :param body: The message body. 127 :param body: The message body.
112 128
113 :type to_addresses: list of strings or string 129 :type to_addresses: list of strings or string
114 :param to_addresses: The To: field(s) of the message. 130 :param to_addresses: The To: field(s) of the message.
115 131
116 :type cc_addresses: list of strings or string 132 :type cc_addresses: list of strings or string
117 :param cc_addresses: The CC: field(s) of the message. 133 :param cc_addresses: The CC: field(s) of the message.
118 134
119 :type bcc_addresses: list of strings or string 135 :type bcc_addresses: list of strings or string
120 :param bcc_addresses: The BCC: field(s) of the message. 136 :param bcc_addresses: The BCC: field(s) of the message.
121 137
122 :type format: string 138 :type format: string
123 :param format: The format of the message's body, must be either "text" 139 :param format: The format of the message's body, must be either "text"
124 or "html". 140 or "html".
125 141
142 :type reply_addresses: list of strings or string
143 :param reply_addresses: The reply-to email address(es) for the
144 message. If the recipient replies to the
145 message, each reply-to address will
146 receive the reply.
147
148 :type return_path: string
149 :param return_path: The email address to which bounce notifications are
150 to be forwarded. If the message cannot be delivered
151 to the recipient, then an error message will be
152 returned from the recipient's ISP; this message will
153 then be forwarded to the email address specified by
154 the ReturnPath parameter.
155
156 :type text_body: string
157 :param text_body: The text body to send with this email.
158
159 :type html_body: string
160 :param html_body: The html body to send with this email.
161
126 """ 162 """
163 format = format.lower().strip()
164 if body is not None:
165 if format == "text":
166 if text_body is not None:
167 raise Warning("You've passed in both a body and a text_body; please choose one or the other.")
168 text_body = body
169 else:
170 if html_body is not None:
171 raise Warning("You've passed in both a body and an html_body ; please choose one or the other.")
172 html_body = body
173
127 params = { 174 params = {
128 'Source': source, 175 'Source': source,
129 'Message.Subject.Data': subject, 176 'Message.Subject.Data': subject,
130 } 177 }
131 178
132 format = format.lower().strip() 179 if return_path:
133 if format == 'html': 180 params['ReturnPath'] = return_path
134 params['Message.Body.Html.Data'] = body 181
135 elif format == 'text': 182 if html_body is not None:
136 params['Message.Body.Text.Data'] = body 183 params['Message.Body.Html.Data'] = html_body
137 else: 184 if text_body is not None:
185 params['Message.Body.Text.Data'] = text_body
186
187 if(format not in ("text","html")):
138 raise ValueError("'format' argument must be 'text' or 'html'") 188 raise ValueError("'format' argument must be 'text' or 'html'")
139 189
190 if(not (html_body or text_body)):
191 raise ValueError("No text or html body found for mail")
192
140 self._build_list_params(params, to_addresses, 193 self._build_list_params(params, to_addresses,
141 'Destination.ToAddresses.member') 194 'Destination.ToAddresses.member')
142 if cc_addresses: 195 if cc_addresses:
143 self._build_list_params(params, cc_addresses, 196 self._build_list_params(params, cc_addresses,
144 'Destination.CcAddresses.member') 197 'Destination.CcAddresses.member')
145 198
146 if bcc_addresses: 199 if bcc_addresses:
147 self._build_list_params(params, bcc_addresses, 200 self._build_list_params(params, bcc_addresses,
148 'Destination.BccAddresses.member') 201 'Destination.BccAddresses.member')
149 202
203 if reply_addresses:
204 self._build_list_params(params, reply_addresses,
205 'ReplyToAddresses.member')
206
150 return self._make_request('SendEmail', params) 207 return self._make_request('SendEmail', params)
151 208
152 def send_raw_email(self, source, raw_message, destinations=None): 209 def send_raw_email(self, raw_message, source=None, destinations=None):
153 """Sends an email message, with header and content specified by the 210 """Sends an email message, with header and content specified by the
154 client. The SendRawEmail action is useful for sending multipart MIME 211 client. The SendRawEmail action is useful for sending multipart MIME
155 emails, with attachments or inline content. The raw text of the message 212 emails, with attachments or inline content. The raw text of the message
156 must comply with Internet email standards; otherwise, the message 213 must comply with Internet email standards; otherwise, the message
157 cannot be sent. 214 cannot be sent.
158 215
159 :type source: string 216 :type source: string
160 :param source: The sender's email address. 217 :param source: The sender's email address. Amazon's docs say:
218
219 If you specify the Source parameter, then bounce notifications and
220 complaints will be sent to this email address. This takes precedence
221 over any Return-Path header that you might include in the raw text of
222 the message.
161 223
162 :type raw_message: string 224 :type raw_message: string
163 :param raw_message: The raw text of the message. The client is 225 :param raw_message: The raw text of the message. The client is
164 responsible for ensuring the following: 226 responsible for ensuring the following:
165 227
166 - Message must contain a header and a body, separated by a blank line. 228 - Message must contain a header and a body, separated by a blank line.
167 - All required header fields must be present. 229 - All required header fields must be present.
168 - Each part of a multipart MIME message must be formatted properly. 230 - Each part of a multipart MIME message must be formatted properly.
169 - MIME content types must be among those supported by Amazon SES. 231 - MIME content types must be among those supported by Amazon SES.
170 Refer to the Amazon SES Developer Guide for more details. 232 Refer to the Amazon SES Developer Guide for more details.
171 - Content must be base64-encoded, if MIME requires it. 233 - Content must be base64-encoded, if MIME requires it.
172 234
173 :type destinations: list of strings or string 235 :type destinations: list of strings or string
174 :param destinations: A list of destinations for the message. 236 :param destinations: A list of destinations for the message.
175 237
176 """ 238 """
177 params = { 239 params = {
178 'Source': source,
179 'RawMessage.Data': base64.b64encode(raw_message), 240 'RawMessage.Data': base64.b64encode(raw_message),
180 } 241 }
242
243 if source:
244 params['Source'] = source
181 245
182 self._build_list_params(params, destinations, 246 if destinations:
183 'Destinations.member') 247 self._build_list_params(params, destinations,
248 'Destinations.member')
184 249
185 return self._make_request('SendRawEmail', params) 250 return self._make_request('SendRawEmail', params)
186 251
187 def list_verified_email_addresses(self): 252 def list_verified_email_addresses(self):
188 """Fetch a list of the email addresses that have been verified. 253 """Fetch a list of the email addresses that have been verified.
189 254
190 :rtype: dict 255 :rtype: dict
191 :returns: A ListVerifiedEmailAddressesResponse structure. Note that 256 :returns: A ListVerifiedEmailAddressesResponse structure. Note that
192 keys must be unicode strings. 257 keys must be unicode strings.
193 """ 258 """
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 :type email_adddress: string 303 :type email_adddress: string
239 :param email_address: The email address to be verified. 304 :param email_address: The email address to be verified.
240 305
241 :rtype: dict 306 :rtype: dict
242 :returns: A VerifyEmailAddressResponse structure. Note that keys must 307 :returns: A VerifyEmailAddressResponse structure. Note that keys must
243 be unicode strings. 308 be unicode strings.
244 """ 309 """
245 return self._make_request('VerifyEmailAddress', { 310 return self._make_request('VerifyEmailAddress', {
246 'EmailAddress': email_address, 311 'EmailAddress': email_address,
247 }) 312 })
248
OLDNEW
« no previous file with comments | « boto/ses/__init__.py ('k') | boto/sns/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698