axtro-aws-ses 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * Use a better XML parser (and be consistent)
2
+ * Rename Base to something else (probably Mailer): Nothing else actually inherits from Base, so that is a very poor naming convention. I intend to change it in the future
3
+ * Integer responses should probably be cast to ints instead of left as strings
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.4
@@ -0,0 +1,102 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{aws-ses}
8
+ s.version = "0.4.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Drew Blas", "Marcel Molina Jr."]
12
+ s.date = %q{2011-06-23}
13
+ s.description = %q{Client library for Amazon's Simple Email Service's REST API}
14
+ s.email = %q{drew.blas@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.erb",
18
+ "README.rdoc",
19
+ "TODO"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ "CHANGELOG",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE",
27
+ "README.erb",
28
+ "README.rdoc",
29
+ "Rakefile",
30
+ "TODO",
31
+ "VERSION",
32
+ "aws-ses.gemspec",
33
+ "lib/aws/actionmailer/ses_extension.rb",
34
+ "lib/aws/ses.rb",
35
+ "lib/aws/ses/addresses.rb",
36
+ "lib/aws/ses/base.rb",
37
+ "lib/aws/ses/extensions.rb",
38
+ "lib/aws/ses/info.rb",
39
+ "lib/aws/ses/response.rb",
40
+ "lib/aws/ses/send_email.rb",
41
+ "lib/aws/ses/version.rb",
42
+ "test/address_test.rb",
43
+ "test/base_test.rb",
44
+ "test/extensions_test.rb",
45
+ "test/fixtures.rb",
46
+ "test/helper.rb",
47
+ "test/info_test.rb",
48
+ "test/mocks/fake_response.rb",
49
+ "test/response_test.rb",
50
+ "test/send_email_test.rb"
51
+ ]
52
+ s.homepage = %q{http://github.com/drewblas/aws-ses}
53
+ s.licenses = ["MIT"]
54
+ s.require_paths = ["lib"]
55
+ s.rubygems_version = %q{1.5.2}
56
+ s.summary = %q{Client library for Amazon's Simple Email Service's REST API}
57
+ s.test_files = [
58
+ "test/address_test.rb",
59
+ "test/base_test.rb",
60
+ "test/extensions_test.rb",
61
+ "test/fixtures.rb",
62
+ "test/helper.rb",
63
+ "test/info_test.rb",
64
+ "test/mocks/fake_response.rb",
65
+ "test/response_test.rb",
66
+ "test/send_email_test.rb"
67
+ ]
68
+
69
+ if s.respond_to? :specification_version then
70
+ s.specification_version = 3
71
+
72
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
73
+ s.add_runtime_dependency(%q<xml-simple>, [">= 0"])
74
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
75
+ s.add_runtime_dependency(%q<mime-types>, [">= 0"])
76
+ s.add_development_dependency(%q<shoulda-context>, [">= 0"])
77
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
78
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
79
+ s.add_development_dependency(%q<rcov>, [">= 0"])
80
+ s.add_development_dependency(%q<flexmock>, ["~> 0.8.11"])
81
+ else
82
+ s.add_dependency(%q<xml-simple>, [">= 0"])
83
+ s.add_dependency(%q<builder>, [">= 0"])
84
+ s.add_dependency(%q<mime-types>, [">= 0"])
85
+ s.add_dependency(%q<shoulda-context>, [">= 0"])
86
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
87
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
88
+ s.add_dependency(%q<rcov>, [">= 0"])
89
+ s.add_dependency(%q<flexmock>, ["~> 0.8.11"])
90
+ end
91
+ else
92
+ s.add_dependency(%q<xml-simple>, [">= 0"])
93
+ s.add_dependency(%q<builder>, [">= 0"])
94
+ s.add_dependency(%q<mime-types>, [">= 0"])
95
+ s.add_dependency(%q<shoulda-context>, [">= 0"])
96
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
97
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
98
+ s.add_dependency(%q<rcov>, [">= 0"])
99
+ s.add_dependency(%q<flexmock>, ["~> 0.8.11"])
100
+ end
101
+ end
102
+
@@ -0,0 +1,104 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{axtro-aws-ses}
8
+ s.version = "0.4.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Drew Blas", "Marcel Molina Jr."]
12
+ s.date = %q{2011-10-31}
13
+ s.description = %q{Client library for Amazon's Simple Email Service's REST API}
14
+ s.email = %q{drew.blas@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.erb",
18
+ "README.rdoc",
19
+ "TODO"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ "CHANGELOG",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE",
27
+ "README.erb",
28
+ "README.rdoc",
29
+ "Rakefile",
30
+ "TODO",
31
+ "VERSION",
32
+ "aws-ses.gemspec",
33
+ "axtro-aws-ses.gemspec",
34
+ "lib/aws/actionmailer/ses_extension.rb",
35
+ "lib/aws/ses.rb",
36
+ "lib/aws/ses/addresses.rb",
37
+ "lib/aws/ses/base.rb",
38
+ "lib/aws/ses/expirable_memoize.rb",
39
+ "lib/aws/ses/info.rb",
40
+ "lib/aws/ses/response.rb",
41
+ "lib/aws/ses/send_email.rb",
42
+ "lib/aws/ses/version.rb",
43
+ "test/address_test.rb",
44
+ "test/base_test.rb",
45
+ "test/expirable_memoize_test.rb",
46
+ "test/fixtures.rb",
47
+ "test/helper.rb",
48
+ "test/info_test.rb",
49
+ "test/mocks/fake_response.rb",
50
+ "test/response_test.rb",
51
+ "test/send_email_test.rb"
52
+ ]
53
+ s.homepage = %q{http://github.com/axtro/aws-ses}
54
+ s.licenses = ["MIT"]
55
+ s.require_paths = ["lib"]
56
+ s.rubygems_version = %q{1.3.7}
57
+ s.summary = %q{Client library for Amazon's Simple Email Service's REST API}
58
+ s.test_files = [
59
+ "test/address_test.rb",
60
+ "test/base_test.rb",
61
+ "test/expirable_memoize_test.rb",
62
+ "test/fixtures.rb",
63
+ "test/helper.rb",
64
+ "test/info_test.rb",
65
+ "test/mocks/fake_response.rb",
66
+ "test/response_test.rb",
67
+ "test/send_email_test.rb"
68
+ ]
69
+
70
+ if s.respond_to? :specification_version then
71
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
72
+ s.specification_version = 3
73
+
74
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
75
+ s.add_runtime_dependency(%q<xml-simple>, [">= 0"])
76
+ s.add_runtime_dependency(%q<builder>, [">= 0"])
77
+ s.add_runtime_dependency(%q<mime-types>, [">= 0"])
78
+ s.add_development_dependency(%q<shoulda-context>, [">= 0"])
79
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
80
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
81
+ s.add_development_dependency(%q<rcov>, [">= 0"])
82
+ s.add_development_dependency(%q<flexmock>, ["~> 0.8.11"])
83
+ else
84
+ s.add_dependency(%q<xml-simple>, [">= 0"])
85
+ s.add_dependency(%q<builder>, [">= 0"])
86
+ s.add_dependency(%q<mime-types>, [">= 0"])
87
+ s.add_dependency(%q<shoulda-context>, [">= 0"])
88
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
89
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
90
+ s.add_dependency(%q<rcov>, [">= 0"])
91
+ s.add_dependency(%q<flexmock>, ["~> 0.8.11"])
92
+ end
93
+ else
94
+ s.add_dependency(%q<xml-simple>, [">= 0"])
95
+ s.add_dependency(%q<builder>, [">= 0"])
96
+ s.add_dependency(%q<mime-types>, [">= 0"])
97
+ s.add_dependency(%q<shoulda-context>, [">= 0"])
98
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
99
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
100
+ s.add_dependency(%q<rcov>, [">= 0"])
101
+ s.add_dependency(%q<flexmock>, ["~> 0.8.11"])
102
+ end
103
+ end
104
+
@@ -0,0 +1,19 @@
1
+ # A quick little extension to use this lib with with rails 2.3.X
2
+ # To use it, in your environment.rb or some_environment.rb you simply set
3
+ #
4
+ # config.after_initialize do
5
+ # ActionMailer::Base.delivery_method = :amazon_ses
6
+ # ActionMailer::Base.custom_amazon_ses_mailer = AWS::SES::Base.new(:secret_access_key => S3_CONFIG[:secret_access_key], :access_key_id => S3_CONFIG[:access_key_id])
7
+ # end
8
+
9
+ module ActionMailer
10
+ class Base
11
+ cattr_accessor :custom_amazon_ses_mailer
12
+
13
+ def perform_delivery_amazon_ses(mail)
14
+ raise 'AWS::SES::Base has not been intitialized.' unless @@custom_amazon_ses_mailer
15
+ @@custom_amazon_ses_mailer.deliver!(mail)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ %w[ base64 cgi openssl digest/sha1 net/https net/http rexml/document time ostruct].each { |f| require f }
2
+
3
+ begin
4
+ require 'URI' unless defined? URI
5
+ rescue Exception => e
6
+ # nothing
7
+ end
8
+
9
+ begin
10
+ require 'xmlsimple' unless defined? XmlSimple
11
+ rescue Exception => e
12
+ require 'xml-simple' unless defined? XmlSimple
13
+ end
14
+
15
+ $:.unshift(File.dirname(__FILE__))
16
+
17
+ require 'ses/expirable_memoize'
18
+
19
+ require 'ses/response'
20
+ require 'ses/send_email'
21
+ require 'ses/info'
22
+ require 'ses/base'
23
+ require 'ses/version'
24
+ require 'ses/addresses'
25
+
26
+ if defined?(Rails)
27
+ major, minor = Rails.version.split('.')
28
+ require 'actionmailer/ses_extension' if major == '2' && minor == '3'
29
+ end
@@ -0,0 +1,75 @@
1
+ module AWS
2
+ module SES
3
+ # AWS::SES::Addresses provides for:
4
+ # * Listing verified e-mail addresses
5
+ # * Adding new e-mail addresses to verify
6
+ # * Deleting verified e-mail addresses
7
+ #
8
+ # You can access these methods as follows:
9
+ #
10
+ # ses = AWS::SES::Base.new( ... connection info ... )
11
+ #
12
+ # # Get a list of verified addresses
13
+ # ses.addresses.list.result
14
+ #
15
+ # # Add a new e-mail address to verify
16
+ # ses.addresses.verify('jon@example.com')
17
+ #
18
+ # # Delete an e-mail address
19
+ # ses.addresses.delete('jon@example.com')
20
+ class Addresses < Base
21
+ def initialize(ses)
22
+ @ses = ses
23
+ end
24
+
25
+ # List all verified e-mail addresses
26
+ #
27
+ # Usage:
28
+ # ses.addresses.list.result
29
+ # =>
30
+ # ['email1@example.com', email2@example.com']
31
+ def list
32
+ @ses.request('ListVerifiedEmailAddresses')
33
+ end
34
+
35
+ def verify(email)
36
+ @ses.request('VerifyEmailAddress',
37
+ 'EmailAddress' => email
38
+ )
39
+ end
40
+
41
+ def delete(email)
42
+ @ses.request('DeleteVerifiedEmailAddress',
43
+ 'EmailAddress' => email
44
+ )
45
+ end
46
+ end
47
+
48
+ class ListVerifiedEmailAddressesResponse < AWS::SES::Response
49
+ def result
50
+ if members = parsed['ListVerifiedEmailAddressesResult']['VerifiedEmailAddresses']
51
+ [members['member']].flatten
52
+ else
53
+ []
54
+ end
55
+ end
56
+ memoized :result
57
+ end
58
+
59
+ class VerifyEmailAddressResponse < AWS::SES::Response
60
+ end
61
+
62
+ class DeleteVerifiedEmailAddressResponse < AWS::SES::Response
63
+ def result
64
+ success?
65
+ end
66
+ end
67
+
68
+ class Base
69
+ def addresses
70
+ @addresses ||= Addresses.new(self)
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,176 @@
1
+ module AWS #:nodoc:
2
+ # AWS::SES is a Ruby library for Amazon's Simple Email Service's REST API (http://aws.amazon.com/ses).
3
+ #
4
+ # == Getting started
5
+ #
6
+ # To get started you need to require 'aws/ses':
7
+ #
8
+ # % irb -rubygems
9
+ # irb(main):001:0> require 'aws/ses'
10
+ # # => true
11
+ #
12
+ # Before you can do anything, you must establish a connection using Base.new. A basic connection would look something like this:
13
+ #
14
+ # ses = AWS::SES::Base.new(
15
+ # :access_key_id => 'abc',
16
+ # :secret_access_key => '123'
17
+ # )
18
+ #
19
+ # The minimum connection options that you must specify are your access key id and your secret access key.
20
+ module SES
21
+
22
+ API_VERSION = '2010-12-01'
23
+
24
+ DEFAULT_HOST = 'email.us-east-1.amazonaws.com'
25
+
26
+ USER_AGENT = 'github-aws-ses-ruby-gem'
27
+
28
+ # Encodes the given string with the secret_access_key by taking the
29
+ # hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
30
+ # url encode the result of that to protect the string if it's going to
31
+ # be used as a query string parameter.
32
+ #
33
+ # @param [String] secret_access_key the user's secret access key for signing.
34
+ # @param [String] str the string to be hashed and encoded.
35
+ # @param [Boolean] urlencode whether or not to url encode the result., true or false
36
+ # @return [String] the signed and encoded string.
37
+ def SES.encode(secret_access_key, str, urlencode=true)
38
+ digest = OpenSSL::Digest::Digest.new('sha256')
39
+ b64_hmac =
40
+ Base64.encode64(
41
+ OpenSSL::HMAC.digest(digest, secret_access_key, str)).gsub("\n","")
42
+
43
+ if urlencode
44
+ return CGI::escape(b64_hmac)
45
+ else
46
+ return b64_hmac
47
+ end
48
+ end
49
+
50
+ # Generates the HTTP Header String that Amazon looks for
51
+ #
52
+ # @param [String] key the AWS Access Key ID
53
+ # @param [String] alg the algorithm used for the signature
54
+ # @param [String] sig the signature itself
55
+ def SES.authorization_header(key, alg, sig)
56
+ "AWS3-HTTPS AWSAccessKeyId=#{key}, Algorithm=#{alg}, Signature=#{sig}"
57
+ end
58
+
59
+ # AWS::SES::Base is the abstract super class of all classes who make requests against SES
60
+ class Base
61
+ include SendEmail
62
+ include Info
63
+
64
+ attr_reader :use_ssl, :server, :proxy_server, :port
65
+
66
+ # @option options [String] :access_key_id ("") The user's AWS Access Key ID
67
+ # @option options [String] :secret_access_key ("") The user's AWS Secret Access Key
68
+ # @option options [Boolean] :use_ssl (true) Connect using SSL?
69
+ # @option options [String] :server ("email.us-east-1.amazonaws.com") The server API endpoint host
70
+ # @option options [String] :proxy_server (nil) An HTTP proxy server FQDN
71
+ # @option options [String] :user_agent ("github-aws-ses-ruby-gem") The HTTP User-Agent header value
72
+ # @return [Object] the object.
73
+ def initialize( options = {} )
74
+
75
+ options = { :access_key_id => "",
76
+ :secret_access_key => "",
77
+ :use_ssl => true,
78
+ :server => DEFAULT_HOST,
79
+ :path => "/",
80
+ :user_agent => USER_AGENT,
81
+ :proxy_server => nil
82
+ }.merge(options)
83
+
84
+ @server = options[:server]
85
+ @proxy_server = options[:proxy_server]
86
+ @use_ssl = options[:use_ssl]
87
+ @path = options[:path]
88
+ @user_agent = options[:user_agent]
89
+
90
+ raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
91
+ raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
92
+ raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
93
+ raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
94
+ raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?
95
+
96
+ if options[:port]
97
+ # user-specified port
98
+ @port = options[:port]
99
+ elsif @use_ssl
100
+ # https
101
+ @port = 443
102
+ else
103
+ # http
104
+ @port = 80
105
+ end
106
+
107
+ @access_key_id = options[:access_key_id]
108
+ @secret_access_key = options[:secret_access_key]
109
+
110
+ # Use proxy server if defined
111
+ # Based on patch by Mathias Dalheimer. 20070217
112
+ proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
113
+ @http = Net::HTTP::Proxy( proxy.host,
114
+ proxy.port,
115
+ proxy.user,
116
+ proxy.password).new(options[:server], @port)
117
+
118
+ @http.use_ssl = @use_ssl
119
+
120
+ # Don't verify the SSL certificates. Avoids SSL Cert warning in log on every GET.
121
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
122
+
123
+ end
124
+
125
+ attr_accessor :settings
126
+
127
+ def connection
128
+ @http
129
+ end
130
+
131
+ # Make the connection to AWS passing in our request.
132
+ # allow us to have a one line call in each method which will do all of the work
133
+ # in making the actual request to AWS.
134
+ def request(action, params = {})
135
+ # Use a copy so that we don't modify the caller's Hash, remove any keys that have nil or empty values
136
+ params = params.reject { |key, value| value.nil? or value.empty?}
137
+
138
+ timestamp = Time.now.getutc
139
+
140
+ params.merge!( {"Action" => action,
141
+ "SignatureVersion" => "2",
142
+ "SignatureMethod" => 'HmacSHA256',
143
+ "AWSAccessKeyId" => @access_key_id,
144
+ "Version" => API_VERSION,
145
+ "Timestamp" => timestamp.iso8601 } )
146
+
147
+ query = params.sort.collect do |param|
148
+ CGI::escape(param[0]) + "=" + CGI::escape(param[1])
149
+ end.join("&")
150
+
151
+ req = {}
152
+
153
+ req['X-Amzn-Authorization'] = get_aws_auth_param(timestamp.httpdate, @secret_access_key)
154
+ req['Date'] = timestamp.httpdate
155
+ req['User-Agent'] = @user_agent
156
+
157
+ response = connection.post(@path, query, req)
158
+
159
+ response_class = AWS::SES.const_get( "#{action}Response" )
160
+ result = response_class.new(action, response)
161
+
162
+ if result.error?
163
+ raise ResponseError.new(result)
164
+ end
165
+
166
+ result
167
+ end
168
+
169
+ # Set the Authorization header using AWS signed header authentication
170
+ def get_aws_auth_param(timestamp, secret_access_key)
171
+ encoded_canonical = SES.encode(secret_access_key, timestamp, false)
172
+ SES.authorization_header(@access_key_id, 'HmacSHA256', encoded_canonical)
173
+ end
174
+ end # class Base
175
+ end # Module SES
176
+ end # Module AWS