multi_mail 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ .yardoc
4
+ Gemfile.lock
5
+ doc/*
6
+ pkg/*
7
+ api_keys.yml
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in scraperwiki-api.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Open North Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # MultiMail: easily switch between email APIs
2
+
3
+ [![Dependency Status](https://gemnasium.com/opennorth/multi_mail.png)](https://gemnasium.com/opennorth/multi_mail)
4
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/opennorth/multi_mail)
5
+
6
+ Many providers – including [Cloudmailin](http://www.cloudmailin.com/), [Mailgun](http://www.mailgun.com/), [Mandrill](http://mandrill.com/), [Postmark](http://postmarkapp.com/) and [SendGrid](http://sendgrid.com/) – offer APIs to send, receive, parse and forward email. MultiMail lets you easily switch between these APIs.
7
+
8
+ ## Usage
9
+
10
+ ```ruby
11
+ require 'multi_mail'
12
+
13
+ service = MultiMail::Receiver.new({
14
+ :provider => 'mailgun',
15
+ :mailgun_api_key => 'key-xxxxxxxxxxxxxxxxxxxxxxx-x-xxxxxx',
16
+ })
17
+
18
+ message = service.process data # raw POST data or params hash
19
+ ```
20
+
21
+ `message` is an array of [Mail::Message](https://github.com/mikel/mail) instances.
22
+
23
+ ## Supported APIs
24
+
25
+ Incoming email:
26
+
27
+ * [Mailgun](http://www.mailgun.com/)
28
+ * [Mandrill](http://mandrill.com/)
29
+
30
+ [Attachment parsing](https://github.com/mikel/mail#attaching-and-detaching-files) on incoming email is not implemented yet. No outgoing email services are implemented yet.
31
+
32
+ ## Bugs? Questions?
33
+
34
+ This gem's main repository is on GitHub: [http://github.com/opennorth/multi_mail](http://github.com/opennorth/multi_mail), where your contributions, forks, bug reports, feature requests, and feedback are greatly welcomed.
35
+
36
+ ## Copyright
37
+
38
+ This gem re-uses code from [fog](https://github.com/fog/fog), released under the MIT license.
39
+
40
+ Copyright (c) 2012 Open North Inc., released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,100 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ begin
10
+ require 'yard'
11
+ YARD::Rake::YardocTask.new
12
+ rescue LoadError
13
+ task :yard do
14
+ abort 'YARD is not available. In order to run yard, you must: gem install yard'
15
+ end
16
+ end
17
+
18
+ require 'yaml'
19
+
20
+ def credentials
21
+ @credentials ||= YAML.load_file File.expand_path(File.join(File.dirname(__FILE__), 'api_keys.yml'))
22
+ end
23
+
24
+ namespace :mailgun do
25
+ require 'json'
26
+ require 'rest-client'
27
+
28
+ desc 'Create a Mailgun catch-all route forwarding to a postbin'
29
+ task :postbin do
30
+
31
+ bin_name = ENV['BIN_NAME'] || JSON.load(RestClient.post('http://requestb.in/api/v1/bins', {}))['name']
32
+ bin_url = "http://requestb.in/#{bin_name}"
33
+
34
+ base_url = "https://api:#{credentials[:mailgun_api_key]}@api.mailgun.net/v2"
35
+ action = %(forward("#{bin_url}"))
36
+
37
+ domain = ENV['DOMAIN'] || "#{SecureRandom.base64(4).tr('+/=lIO0', 'pqrsxyz')}.mailgun.com"
38
+
39
+ if JSON.load(RestClient.get("#{base_url}/domains"))['items'].empty?
40
+ puts "Creating the #{domain} domain..."
41
+ RestClient.post("#{base_url}/domains", :name => domain)
42
+ end
43
+
44
+ route = JSON.load(RestClient.get("#{base_url}/routes"))['items'].find do |route|
45
+ route['expression'] == 'catch_all()'
46
+ end
47
+
48
+ if route
49
+ unless route['action'] == action
50
+ puts "Updating the catch_all() route..."
51
+ JSON.load(RestClient.put("#{base_url}/routes/#{route['id']}", :action => action))
52
+ end
53
+ else
54
+ puts "Creating a catch_all() route..."
55
+ JSON.load(RestClient.post("#{base_url}/routes", :expression => 'catch_all()', :action => action))
56
+ end
57
+
58
+ puts "#{bin_url}?inspect"
59
+ end
60
+ end
61
+
62
+ namespace :mandrill do
63
+ require 'mandrill'
64
+
65
+ def mandrill_api
66
+ @mandrill_api ||= Mandrill::API.new credentials[:mandrill_api_key]
67
+ end
68
+
69
+ def mandrill_domains
70
+ @mandrill_domains ||= mandrill_api.inbound.domains.each_with_object({}) do |domain,domains|
71
+ domains[domain['domain']] = domain
72
+ end
73
+ end
74
+
75
+ def mandrill_domain
76
+ @mandrill_domain ||= ENV['DOMAIN'] || mandrill_domains.keys.first
77
+ end
78
+
79
+ desc 'Create a Mandrill catch-all route forwarding to a postbin'
80
+ task :validate do
81
+ if mandrill_domains.empty?
82
+ abort 'Add an inbound domain'
83
+ elsif mandrill_domains.size > 1 && ENV['DOMAIN'].nil?
84
+ abort "ENV['DOMAIN'] must be one of #{mandrill_domains.keys.join ', '}"
85
+ end
86
+
87
+ if ENV['DOMAIN'] && !mandrill_domains.keys.include?(ENV['DOMAIN'])
88
+ abort "#{ENV['DOMAIN']} must be one of #{mandrill_domains.keys.join ', '}"
89
+ end
90
+
91
+ unless mandrill_domains[mandrill_domain]['valid_mx']
92
+ puts "The MX for #{mandrill_domain} is not valid"
93
+ end
94
+
95
+ routes = mandrill_api.inbound.routes mandrill_domain
96
+ if routes.empty? || routes.none?{|route| route['pattern'] == '*'}
97
+ puts "Add a catchall (*) route for #{mandrill_domain}"
98
+ end
99
+ end
100
+ end
data/USAGE ADDED
@@ -0,0 +1 @@
1
+ See README.md for full usage details.
@@ -0,0 +1,84 @@
1
+ module MultiMail
2
+ module Receiver
3
+ class Mailgun < MultiMail::Service
4
+ include MultiMail::Receiver::Base
5
+
6
+ requires :mailgun_api_key
7
+
8
+ # @param [Hash] options required and optional arguments
9
+ # @option opts [String] :mailgun_api_key a Mailgun API key
10
+ def initialize(options = {})
11
+ super
12
+ @mailgun_api_key = options[:mailgun_api_key]
13
+ end
14
+
15
+ # @param [Hash] params the content of Mailgun's webhook
16
+ # @return [Boolean] whether the request originates from Mailgun
17
+ # @raises [KeyError] if the request is missing parameters
18
+ # @see http://documentation.mailgun.net/user_manual.html#securing-webhooks
19
+ def valid?(params)
20
+ params.fetch('signature') == OpenSSL::HMAC.hexdigest(
21
+ OpenSSL::Digest::Digest.new('sha256'), @mailgun_api_key,
22
+ '%s%s' % [params.fetch('timestamp'), params.fetch('token')])
23
+ end
24
+
25
+ # @param [Hash] params the content of Mailgun's webhook
26
+ # @return [Array<Mail::Message>] messages
27
+ # @note Mailgun sends the message headers both individually and in the
28
+ # `message-headers` parameter. Only `message-headers` is documented.
29
+ # @todo parse attachments properly
30
+ def transform(params)
31
+ headers = Multimap.new
32
+ JSON.parse(params['message-headers']).each do |key,value|
33
+ headers[key] = value
34
+ end
35
+
36
+ message = Mail.new do
37
+ headers headers
38
+
39
+ # The following are redundant with `message-headers`:
40
+ #
41
+ # from params['from']
42
+ # sender params['sender']
43
+ # to params['recipient']
44
+ # subject params['subject']
45
+
46
+ text_part do
47
+ body params['body-plain']
48
+ end
49
+
50
+ html_part do
51
+ content_type 'text/html; charset=UTF-8'
52
+ body params['body-html']
53
+ end
54
+ end
55
+
56
+ # Extra Mailgun parameters.
57
+ [ 'stripped-text',
58
+ 'stripped-signature',
59
+ 'stripped-html',
60
+ 'attachment-count',
61
+ 'attachment-x',
62
+ 'content-id-map',
63
+ ].each do |key|
64
+ if !params[key].nil? && !params[key].empty?
65
+ message[key] = params[key]
66
+ end
67
+ end
68
+
69
+ [message]
70
+ end
71
+
72
+ # @param [Mail::Message] message a message
73
+ # @return [Boolean] whether the message is spam
74
+ # @see http://documentation.mailgun.net/user_manual.html#spam-filter
75
+ # @note You must enable spam filtering for each domain in Mailgun's [Control
76
+ # Panel](https://mailgun.net/cp/domains).
77
+ # @note We may also inspect `X-Mailgun-SScore` and `X-Mailgun-Spf`, whose
78
+ # possible values are "Pass", "Neutral", "Fail" and "SoftFail".
79
+ def spam?(message)
80
+ message['X-Mailgun-Sflag'].value == 'Yes'
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,71 @@
1
+ module MultiMail
2
+ module Receiver
3
+ class Mandrill < MultiMail::Service
4
+ include MultiMail::Receiver::Base
5
+
6
+ requires :mandrill_api_key
7
+
8
+ # @param [Hash] options required and optional arguments
9
+ # @option opts [String] :mandrill_api_key a Mandrill API key
10
+ def initialize(options = {})
11
+ super
12
+ @mandrill_api_key = options[:mandrill_api_key]
13
+ end
14
+
15
+ # @param [Hash] params the content of Mandrill's webhook
16
+ # @return [Boolean] whether the request originates from Mandrill
17
+ def valid?(params)
18
+ JSON.parse(params['mandrill_events']).all? do |event|
19
+ event.fetch('event') == 'inbound'
20
+ end
21
+ end
22
+
23
+ # @param [Mail::Message] message a message
24
+ # @return [Boolean] whether the message is spam
25
+ def spam?(message)
26
+ false
27
+ end
28
+
29
+ # @param [Hash] params the content of Mandrill's webhook
30
+ # @return [Array<Mail::Message>] messages
31
+ # @todo parse attachments properly
32
+ def transform(params)
33
+ JSON.parse(params['mandrill_events']).map do |event|
34
+ message = Mail.new do
35
+ headers event['msg']['headers'].reject{|k,_| k=='Received'} # @todo
36
+
37
+ # The following are redundant with `message-headers`:
38
+ #
39
+ # address = Mail::Address.new event['msg']['from_email']
40
+ # address.display_name = event['msg']['from_name']
41
+ #
42
+ # from address.format
43
+ # to event['msg']['to'].flatten.compact
44
+ # subject event['msg']['subject']
45
+
46
+ text_part do
47
+ body event['msg']['text']
48
+ end
49
+
50
+ html_part do
51
+ content_type 'text/html; charset=UTF-8' # unsure about charset
52
+ body event['msg']['html']
53
+ end
54
+ end
55
+
56
+ # Extra Mandrill parameters. Discard `raw_msg`.
57
+ [ 'email',
58
+ 'tags',
59
+ 'sender',
60
+ ].each do |key|
61
+ if !event['msg'][key].nil? && !event['msg'][key].empty?
62
+ message[key] = event['msg'][key]
63
+ end
64
+ end
65
+
66
+ message
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,65 @@
1
+ module MultiMail
2
+ module Receiver
3
+ # Abstract class for incoming email services.
4
+ #
5
+ # The `transform` instance method must be implemented in sub-classes. The
6
+ # `valid?` and `spam?` instance methods may be implemented in sub-classes.
7
+ module Base
8
+ def self.included(subclass)
9
+ subclass.class_eval do
10
+ extend MultiMail::Receiver::Base::ClassMethods
11
+ end
12
+ end
13
+
14
+ # @param [String,Hash] raw raw POST data or a params hash
15
+ # @return [Array<Mail::Message>] messages
16
+ # @raises [ForgedRequest] if the request is not authentic
17
+ def process(raw)
18
+ params = self.class.parse raw
19
+ if valid? params
20
+ transform params
21
+ else
22
+ raise ForgedRequest
23
+ end
24
+ end
25
+
26
+ # @param [Hash] params the content of the provider's webhook
27
+ # @return [Boolean] whether the request is authentic
28
+ def valid?(params)
29
+ true
30
+ end
31
+
32
+ # @param [Hash] params the content of the provider's webhook
33
+ # @return [Array<Mail::Message>] messages
34
+ def transform(params)
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # @param [Mail::Message] message a message
39
+ # @return [Boolean] whether the message is spam
40
+ def spam?(message)
41
+ false
42
+ end
43
+
44
+ module ClassMethods
45
+ # @param [String,Hash] raw raw POST data or a params hash
46
+ def parse(raw)
47
+ case raw
48
+ when String
49
+ params = CGI.parse raw
50
+ params.each do |key,value|
51
+ if Array === value && value.size == 1
52
+ params[key] = value.first
53
+ end
54
+ end
55
+ params
56
+ when Hash
57
+ raw
58
+ else
59
+ raise ArgumentError, "Can't handle #{raw.class.name} input"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,43 @@
1
+ module MultiMail
2
+ # @see http://rdoc.info/gems/fog/Fog/Storage
3
+ module Receiver
4
+ autoload :Base, 'multi_mail/receiver/base'
5
+
6
+ # @example
7
+ # require 'multi_mail'
8
+ # service = MultiMail::Receiver.new({
9
+ # :provider => 'mailgun',
10
+ # :mailgun_api_key => 'key-xxxxxxxxxxxxxxxxxxxxxxx-x-xxxxxx',
11
+ # })
12
+ #
13
+ # @param [Hash] attributes required arguments
14
+ # @option opts [String,Symbol] :provider a provider
15
+ # @raises [ArgumentError] if the provider does not exist
16
+ # @see Fog::Storage::new
17
+ def self.new(attributes)
18
+ attributes = attributes.dup # prevent delete from having side effects
19
+ case provider = attributes.delete(:provider).to_s.downcase.to_sym
20
+ when :cloudmailin
21
+ require 'multi_mail/cloudmailin/receiver'
22
+ MultiMail::Receiver::Cloudmailin.new(attributes)
23
+ when :mailgun
24
+ require 'multi_mail/mailgun/receiver'
25
+ MultiMail::Receiver::Mailgun.new(attributes)
26
+ when :mandrill
27
+ require 'multi_mail/mandrill/receiver'
28
+ MultiMail::Receiver::Mandrill.new(attributes)
29
+ when :postmark
30
+ require 'multi_mail/postmark/receiver'
31
+ MultiMail::Receiver::Postmark.new(attributes)
32
+ when :sendgrid
33
+ require 'multi_mail/sendgrid/receiver'
34
+ MultiMail::Receiver::SendGrid.new(attributes)
35
+ when :mock
36
+ # for testing
37
+ MultiMail::Receiver::Mock.new(attributes)
38
+ else
39
+ raise ArgumentError.new("#{provider} is not a recognized provider")
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ module MultiMail
2
+ # @see http://rdoc.info/gems/fog/Fog/Storage
3
+ module Sender
4
+ autoload :Base, 'multi_mail/sender/base'
5
+
6
+ # @example
7
+ # require 'multi_mail'
8
+ # service = MultiMail::Sender.new({
9
+ # :provider => 'mailgun',
10
+ # :mailgun_api_key => 'key-xxxxxxxxxxxxxxxxxxxxxxx-x-xxxxxx',
11
+ # })
12
+ #
13
+ # @param [Hash] attributes required arguments
14
+ # @option opts [String,Symbol] :provider a provider
15
+ # @raises [ArgumentError] if the provider does not exist
16
+ # @see Fog::Storage::new
17
+ def self.new(attributes)
18
+ attributes = attributes.dup # prevent delete from having side effects
19
+ case provider = attributes.delete(:provider).to_s.downcase.to_sym
20
+ when :mailgun
21
+ require 'multi_mail/mailgun/sender'
22
+ MultiMail::Sender::Mailgun.new(attributes)
23
+ when :mandrill
24
+ require 'multi_mail/mandrill/sender'
25
+ MultiMail::Sender::Mandrill.new(attributes)
26
+ when :postmark
27
+ require 'multi_mail/postmark/sender'
28
+ MultiMail::Sender::Postmark.new(attributes)
29
+ when :sendgrid
30
+ require 'multi_mail/sendgrid/sender'
31
+ MultiMail::Sender::SendGrid.new(attributes)
32
+ when :mock
33
+ # for testing
34
+ MultiMail::Sender::Mock.new(attributes)
35
+ else
36
+ raise ArgumentError.new("#{provider} is not a recognized provider")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,63 @@
1
+ module MultiMail
2
+ # @see http://rdoc.info/gems/fog/Fog/Service
3
+ class Service
4
+ # @param [Hash] options optional arguments
5
+ def initialize(options = {})
6
+ self.class.validate_options(options)
7
+ end
8
+
9
+ class << self
10
+ # Appends the given arguments to the list of required arguments.
11
+ #
12
+ # @param args one or more required arguments
13
+ # @see Fog::Service::requires
14
+ def requires(*args)
15
+ requirements.concat(args)
16
+ end
17
+
18
+ # @return [Array] a list of required arguments
19
+ # @see Fog::Service::requirements
20
+ def requirements
21
+ @requirements ||= []
22
+ end
23
+
24
+ # Appends the given arguments to the list of optional arguments.
25
+ #
26
+ # @param args one or more optional arguments
27
+ # @see Fog::Service::recognizes
28
+ def recognizes(*args)
29
+ recognized.concat(args)
30
+ end
31
+
32
+ # @return [Array] a list of optional arguments
33
+ # @see Fog::Service::recognized
34
+ def recognized
35
+ @recognized ||= []
36
+ end
37
+
38
+ # @param [Hash] options arguments
39
+ # @raises [ArgumentError] if can't find required arguments or can't
40
+ # recognize optional arguments
41
+ # @see Fog::Service::validate_options
42
+ def validate_options(options)
43
+ keys = []
44
+ for key, value in options
45
+ unless value.nil?
46
+ keys << key
47
+ end
48
+ end
49
+ missing = requirements - keys
50
+ unless missing.empty?
51
+ raise ArgumentError, "Missing required arguments: #{missing.join(', ')}"
52
+ end
53
+
54
+ unless recognizes.empty?
55
+ unrecognized = options.keys - requirements - recognized
56
+ unless unrecognized.empty?
57
+ raise ArgumentError, "Unrecognized arguments: #{unrecognized.join(', ')}"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module MultiMail
2
+ VERSION = "0.0.1"
3
+ end
data/lib/multi_mail.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'cgi'
2
+ require 'json'
3
+ require 'openssl'
4
+
5
+ require 'mail'
6
+ require 'multimap'
7
+
8
+ module MultiMail
9
+ # @see http://rdoc.info/gems/fog/Fog/Errors
10
+ class Error < StandardError; end
11
+ class ForgedRequest < MultiMail::Error; end
12
+
13
+ autoload :Service, 'multi_mail/service'
14
+ autoload :Receiver, 'multi_mail/receiver'
15
+ autoload :Sender, 'multi_mail/sender'
16
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "multi_mail/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "multi_mail"
7
+ s.version = MultiMail::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Open North"]
10
+ s.email = ["info@opennorth.ca"]
11
+ s.homepage = "http://github.com/opennorth/multi_mail"
12
+ s.summary = %q{Easily switch between email APIs}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_runtime_dependency 'mail', '~> 2.4.4' # Rails 3.2.9
20
+ s.add_runtime_dependency 'multimap', '~> 1.1.2'
21
+ s.add_development_dependency 'rspec', '~> 2.10'
22
+ s.add_development_dependency 'rest-client', '~> 1.6.7'
23
+ s.add_development_dependency 'mandrill-api', '~> 1.0.12'
24
+ s.add_development_dependency 'rake'
25
+ end
@@ -0,0 +1,8 @@
1
+ HTTP/1.1 200 OK
2
+ User-Agent: Python-urllib/2.7
3
+ Host: requestb.in
4
+ Content-Type: application/x-www-form-urlencoded
5
+ Content-Length: 5027
6
+ Connection: keep-alive
7
+
8
+ recipient=foo%2Bbar%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=Test&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29&Received=by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D&X-Received=by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=text%2Fplain&Content-Transfer-Encoding=7bit&Subject=Test&Date=Sun%2C+23+Dec+2012+17%3A34%3A26+-0500&Message-Id=%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E&To=foo%2Bbar%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc&X-Mailgun-Sflag=No&X-Mailgun-Sscore=-1.9&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22text%2Fplain%22%5D%2C+%5B%22Content-Transfer-Encoding%22%2C+%227bit%22%5D%2C+%5B%22Subject%22%2C+%22Test%22%5D%2C+%5B%22Date%22%2C+%22Sun%2C+23+Dec+2012+17%3A34%3A26+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22foo%2Bbar%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22No%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22-1.9%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D&timestamp=1356302072&token=3ye06wh177qi0rogrkrw25fj8an42ncxpyw72h7d4gj946hpb2&signature=xxx
@@ -0,0 +1,8 @@
1
+ HTTP/1.1 200 OK
2
+ User-Agent: Python-urllib/2.7
3
+ Host: requestb.in
4
+ Content-Type: application/x-www-form-urlencoded
5
+ Content-Length: 5027
6
+ Connection: keep-alive
7
+
8
+ recipient=foo%2Bbar%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=Test&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29&Received=by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D&X-Received=by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=text%2Fplain&Content-Transfer-Encoding=7bit&Subject=Test&Date=Sun%2C+23+Dec+2012+17%3A34%3A26+-0500&Message-Id=%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E&To=foo%2Bbar%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc&X-Mailgun-Sflag=No&X-Mailgun-Sscore=-1.9&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22text%2Fplain%22%5D%2C+%5B%22Content-Transfer-Encoding%22%2C+%227bit%22%5D%2C+%5B%22Subject%22%2C+%22Test%22%5D%2C+%5B%22Date%22%2C+%22Sun%2C+23+Dec+2012+17%3A34%3A26+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22foo%2Bbar%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22No%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22-1.9%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D&timestamp=1356302072&token=3ye06wh177qi0rogrkrw25fj8an42ncxpyw72h7d4gj946hpb2
@@ -0,0 +1,8 @@
1
+ HTTP/1.1 200 OK
2
+ User-Agent: Python-urllib/2.7
3
+ Host: requestb.in
4
+ Content-Type: application/x-www-form-urlencoded
5
+ Content-Length: 5402
6
+ Connection: keep-alive
7
+
8
+ recipient=bar%2Bfoo%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8777239695241%3B+Sun%2C+23+Dec+2012+22%3A48%3A57+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ie0-f171.google.com+%28mail-ie0-f171.google.com+%5B209.85.223.171%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d78a58.7f0a74469eb0-in3%3B+Sun%2C+23+Dec+2012+22%3A48%3A56+-0000+%28UTC%29&Received=by+mail-ie0-f171.google.com+with+SMTP+id+17so8387368iea.16+for+%3Cbar%2Bfoo%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3Did84uDPa9SMZAVd%2Bg2pYg3Hl9aPfnapoGlOygxrIuV4%3D%3B+b%3DW5NwO9jAkVJVUPLJtrQMhre2Ry9XT8kuN8dNyin46tqI%2Bs3yvH8gc52K7mmXabo32i+du9Drxck9YIRyfHh%2Fza%2FeJS%2FkhHqo6L5QUiR7pG8DQI1Y7proRdDJqIa0aR92Tqj61ND+a5lAAi5r3bACZ81BBeAQvEqspKSuXSHyBUFdpDUP2cu50M1X3d7oxRdbV%2FC%2B0Z3hvh84+OoJ30XgT%2FO9zcn4XZ5B%2FcdQ%2BAsoCmBWotAwd3XT3yFcFE4uVNuuJkoRsRXZYn%2FeqnJvq+SlNW%2BHbW0QElLamOIfMxWh25mAMMSIsH5jfyG0ITnMfsXOENCoZEXsw3uvXss6StpL8Q+Suyw%3D%3D&X-Received=by+10.50.158.170+with+SMTP+id+wv10mr14044075igb.75.1356302936324%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+xn10sm21091101igb.4.2012.12.23.14.48.53+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A48%3A54+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=text%2Fplain%3B+charset%3D%22us-ascii%22&Content-Transfer-Encoding=7bit&Subject=&Date=Sun%2C+23+Dec+2012+17%3A48%3A51+-0500&Message-Id=%3CA95D0B9A-7F74-40A6-8CDE-70E287117BB5%40opennorth.ca%3E&To=bar%2Bfoo%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQmOpQVgvzMb%2FDJNG97pU0B1TdWlpBAwUrLSpAxDtQMx8mpQpxFwbALmQmN7O2ggEeyGdC66&X-Mailgun-Sflag=Yes&X-Mailgun-Sscore=998.1&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8777239695241%3B+Sun%2C+23+Dec+2012+22%3A48%3A57+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ie0-f171.google.com+%28mail-ie0-f171.google.com+%5B209.85.223.171%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d78a58.7f0a74469eb0-in3%3B+Sun%2C+23+Dec+2012+22%3A48%3A56+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ie0-f171.google.com+with+SMTP+id+17so8387368iea.16+for+%3Cbar%2Bfoo%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3Did84uDPa9SMZAVd%2Bg2pYg3Hl9aPfnapoGlOygxrIuV4%3D%3B+b%3DW5NwO9jAkVJVUPLJtrQMhre2Ry9XT8kuN8dNyin46tqI%2Bs3yvH8gc52K7mmXabo32i+du9Drxck9YIRyfHh%2Fza%2FeJS%2FkhHqo6L5QUiR7pG8DQI1Y7proRdDJqIa0aR92Tqj61ND+a5lAAi5r3bACZ81BBeAQvEqspKSuXSHyBUFdpDUP2cu50M1X3d7oxRdbV%2FC%2B0Z3hvh84+OoJ30XgT%2FO9zcn4XZ5B%2FcdQ%2BAsoCmBWotAwd3XT3yFcFE4uVNuuJkoRsRXZYn%2FeqnJvq+SlNW%2BHbW0QElLamOIfMxWh25mAMMSIsH5jfyG0ITnMfsXOENCoZEXsw3uvXss6StpL8Q+Suyw%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.158.170+with+SMTP+id+wv10mr14044075igb.75.1356302936324%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+xn10sm21091101igb.4.2012.12.23.14.48.53+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A48%3A54+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22text%2Fplain%3B+charset%3D%5C%22us-ascii%5C%22%22%5D%2C+%5B%22Content-Transfer-Encoding%22%2C+%227bit%22%5D%2C+%5B%22Subject%22%2C+%22%22%5D%2C+%5B%22Date%22%2C+%22Sun%2C+23+Dec+2012+17%3A48%3A51+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3CA95D0B9A-7F74-40A6-8CDE-70E287117BB5%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22bar%2Bfoo%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQmOpQVgvzMb%2FDJNG97pU0B1TdWlpBAwUrLSpAxDtQMx8mpQpxFwbALmQmN7O2ggEeyGdC66%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22Yes%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22998.1%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D&timestamp=1356302937&token=358k6n1x3wgth57c4c05qwhw61mr5ip9zws0mzk6du4d582g50&signature=58032e7c84f180ca3627226f443d7237e789ff30da23898af1aa8cb589e81632&body-plain=XJS%2AC4JDBQADN1.NSBN3%2A2IDNEN%2AGTUBE-STANDARD-ANTI-UBE-TEST-EMAIL%2AC.34X%0D%0A%0D%0A&stripped-html=%3Cp%3EXJS%2AC4JDBQADN1.NSBN3%2A2IDNEN%2AGTUBE-STANDARD-ANTI-UBE-TEST-EMAIL%2AC.34X%3C%2Fp%3E&stripped-text=XJS%2AC4JDBQADN1.NSBN3%2A2IDNEN%2AGTUBE-STANDARD-ANTI-UBE-TEST-EMAIL%2AC.34X&stripped-signature=
@@ -0,0 +1,8 @@
1
+ HTTP/1.1 200 OK
2
+ User-Agent: Python-urllib/2.7
3
+ Host: requestb.in
4
+ Content-Type: application/x-www-form-urlencoded
5
+ Content-Length: 6108
6
+ Connection: keep-alive
7
+
8
+ recipient=foo%2Bbar%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=Test&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8757958553753%3B+Mon%2C+24+Dec+2012+05%3A31%3A14+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d7e8a1.59309b0-in1%3B+Mon%2C+24+Dec+2012+05%3A31%3A13+-0000+%28UTC%29&Received=by+mail-ia0-f172.google.com+with+SMTP+id+z13so5721887iaz.3+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Asubject%3Adate%3Amessage-id%3Ato+%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D6upuK3YAruhelVsnSVxaSLj4p3tANY1XJwtf3oISt3o%3D%3B+b%3DewYc7cjB7NYk%2Bj3pL3xXRm60pVzerzE6P3mX%2FLWa3CmRWbHZlQSs95483ShvqSkRhz+IXVr6AFDA%2BxHu7CcaR5Ge1jn9EKmoa6nTd7DMjHBX4bdnyQQcjdQJAwQeArXcmihNBPU+7pDyPkqMCzqEL2H1CExVODhOkgZYURXwpYDBsQdy7XEgdTRueJIKtgrTyrTuxz%2FupjIC+RaSk24GKJHMvGvLso%2F%2BNPhIbbih1cIXnx7vG2F8NM%2BFrmtHrMlRxs%2B3BLKza4zfHRTIg+mtUjPlJmCa%2BfdnYYUd0yhtgIsx2iODI8Rw%2FqdH76ZKWxC6h%2BGShu%2Br36uX8sI2mIexBY+qrPg%3D%3D&X-Received=by+10.50.33.212+with+SMTP+id+t20mr14433982igi.108.1356327073551%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+c3sm22042054igj.1.2012.12.23.21.31.10+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+21%3A31%3A12+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=multipart%2Falternative%3B+boundary%3D%22Apple-Mail%3D_062FA552-79D4-4FFA-95A1-647F73FD11A1%22&Subject=Test&Date=Mon%2C+24+Dec+2012+00%3A31%3A08+-0500&Message-Id=%3C79F1A964-ABCE-4131-82F8-0E5073AE1A65%40opennorth.ca%3E&To=foo%2Bbar%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQm%2FYGSXiuENvTKhfHsnlafwStjOWi4IXZEsxEQWK1k8rjhvh%2BDPhjxtztKZ2agv3AQp5aYn&X-Mailgun-Sflag=No&X-Mailgun-Sscore=-1.9&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8757958553753%3B+Mon%2C+24+Dec+2012+05%3A31%3A14+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d7e8a1.59309b0-in1%3B+Mon%2C+24+Dec+2012+05%3A31%3A13+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ia0-f172.google.com+with+SMTP+id+z13so5721887iaz.3+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Asubject%3Adate%3Amessage-id%3Ato+%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D6upuK3YAruhelVsnSVxaSLj4p3tANY1XJwtf3oISt3o%3D%3B+b%3DewYc7cjB7NYk%2Bj3pL3xXRm60pVzerzE6P3mX%2FLWa3CmRWbHZlQSs95483ShvqSkRhz+IXVr6AFDA%2BxHu7CcaR5Ge1jn9EKmoa6nTd7DMjHBX4bdnyQQcjdQJAwQeArXcmihNBPU+7pDyPkqMCzqEL2H1CExVODhOkgZYURXwpYDBsQdy7XEgdTRueJIKtgrTyrTuxz%2FupjIC+RaSk24GKJHMvGvLso%2F%2BNPhIbbih1cIXnx7vG2F8NM%2BFrmtHrMlRxs%2B3BLKza4zfHRTIg+mtUjPlJmCa%2BfdnYYUd0yhtgIsx2iODI8Rw%2FqdH76ZKWxC6h%2BGShu%2Br36uX8sI2mIexBY+qrPg%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.33.212+with+SMTP+id+t20mr14433982igi.108.1356327073551%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+c3sm22042054igj.1.2012.12.23.21.31.10+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+21%3A31%3A12+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22multipart%2Falternative%3B+boundary%3D%5C%22Apple-Mail%3D_062FA552-79D4-4FFA-95A1-647F73FD11A1%5C%22%22%5D%2C+%5B%22Subject%22%2C+%22Test%22%5D%2C+%5B%22Date%22%2C+%22Mon%2C+24+Dec+2012+00%3A31%3A08+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3C79F1A964-ABCE-4131-82F8-0E5073AE1A65%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22foo%2Bbar%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQm%2FYGSXiuENvTKhfHsnlafwStjOWi4IXZEsxEQWK1k8rjhvh%2BDPhjxtztKZ2agv3AQp5aYn%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22No%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22-1.9%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D&timestamp=1356327074&token=1u0u68zfxyfyur1cglwzwumutta-fxqfirgbfptaxf3xaq5o41&signature=00b70fb2f2a7008628e0d15bca063f0046bfaa24798fc79b89b48ce765138410&body-plain=bold+text%0D%0A%0D%0A%3E+multiline%0D%0A%3E+quoted%0D%0A%3E+text%0D%0A%0D%0A%0D%0A--%0D%0ASignature+block&body-html=%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody+style%3D%22word-wrap%3A+break-word%3B+-webkit-nbsp-mode%3A+space%3B+-webkit-line-break%3A+after-white-space%3B+%22%3E%3Cb%3Ebold+text%3C%2Fb%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E%3Cblockquote+type%3D%22cite%22%3Emultiline%3C%2Fblockquote%3E%3Cblockquote+type%3D%22cite%22%3Equoted%3C%2Fblockquote%3E%3Cblockquote+type%3D%22cite%22%3Etext%3C%2Fblockquote%3E%3C%2Fdiv%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E--%3C%2Fdiv%3E%3Cdiv%3ESignature+block%3C%2Fdiv%3E%3C%2Fbody%3E%3C%2Fhtml%3E&stripped-html=%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody+style%3D%22word-wrap%3A+break-word%3B+-webkit-nbsp-mode%3A+space%3B+-webkit-line-break%3A+after-white-space%3B+%22%3E%3Cb%3Ebold+text%3C%2Fb%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E--%3C%2Fdiv%3E%3Cdiv%3ESignature+block%3C%2Fdiv%3E%3C%2Fbody%3E%3C%2Fhtml%3E&stripped-text=bold+text&stripped-signature=--%0D%0ASignature+block
@@ -0,0 +1,75 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'multi_mail/mailgun/receiver'
3
+
4
+ describe MultiMail::Receiver::Mailgun do
5
+ describe '#initialize' do
6
+ it 'should raise an error if :mailgun_api_key is missing' do
7
+ expect{ MultiMail::Receiver.new :provider => :mailgun }.to raise_error(ArgumentError)
8
+ expect{ MultiMail::Receiver.new :provider => :mailgun, :mailgun_api_key => nil }.to raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ context 'after initialization' do
13
+ def params(fixture)
14
+ MultiMail::Receiver::Mailgun.parse(response('mailgun', fixture))
15
+ end
16
+
17
+ before :all do
18
+ @service = MultiMail::Receiver.new({
19
+ :provider => :mailgun,
20
+ :mailgun_api_key => credentials[:mailgun_api_key],
21
+ })
22
+ end
23
+
24
+ describe '#valid?' do
25
+ it 'should return true if the response is valid' do
26
+ @service.valid?(params('valid')).should == true
27
+ end
28
+
29
+ it 'should return false if the response is invalid' do
30
+ @service.valid?(params('invalid')).should == false
31
+ end
32
+
33
+ it 'should raise an error if parameters are missing' do
34
+ expect{ @service.valid?(params('missing')) }.to raise_error(KeyError)
35
+ end
36
+ end
37
+
38
+ describe '#transform' do
39
+ it 'should return a mail message' do
40
+ message = @service.transform(params('valid'))[0]
41
+
42
+ # Headers
43
+ message.date.should == DateTime.parse('Mon, 24 Dec 2012 00:31:08 -0500')
44
+ message.from.should == ['james@opennorth.ca']
45
+ message.to.should == ['foo+bar@multimail.mailgun.org']
46
+ message.subject.should == 'Test'
47
+
48
+ # Body
49
+ message.multipart?.should == true
50
+ message.parts.size.should == 2
51
+ message.parts[0].content_type.should == 'text/plain'
52
+ message.parts[0].body.should == "bold text\n\n> multiline\n> quoted\n> text\n\n\n--\nSignature block"
53
+ message.parts[1].content_type.should == 'text/html; charset=UTF-8'
54
+ message.parts[1].body.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div><blockquote type="cite">multiline</blockquote><blockquote type="cite">quoted</blockquote><blockquote type="cite">text</blockquote></div><div><br></div><div>--</div><div>Signature block</div></body></html>)
55
+
56
+ # Extra Mailgun parameters
57
+ message['stripped-text'].value.should == 'bold text'
58
+ message['stripped-signature'].value.should == "--\r\nSignature block"
59
+ message['stripped-html'].value.should == '<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div><br></div><div>--</div><div>Signature block</div></body></html>'
60
+ end
61
+ end
62
+
63
+ describe '#spam?' do
64
+ it 'should return true if the response is spam' do
65
+ message = @service.transform(params('spam'))[0]
66
+ @service.spam?(message).should == true
67
+ end
68
+
69
+ it 'should return false if the response is ham' do
70
+ message = @service.transform(params('valid'))[0]
71
+ @service.spam?(message).should == false
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'multi_mail/mandrill/receiver'
3
+
4
+ describe MultiMail::Receiver::Mandrill do
5
+ describe '#initialize' do
6
+ it 'should raise an error if :mandrill_api_key is missing' do
7
+ expect{ MultiMail::Receiver.new :provider => :mandrill }.to raise_error(ArgumentError)
8
+ expect{ MultiMail::Receiver.new :provider => :mandrill, :mandrill_api_key => nil }.to raise_error(ArgumentError)
9
+ end
10
+ end
11
+
12
+ context 'after initialization' do
13
+ def params(fixture)
14
+ MultiMail::Receiver::Mandrill.parse(response('mandrill', fixture))
15
+ end
16
+
17
+ before :all do
18
+ @service = MultiMail::Receiver.new({
19
+ :provider => :mandrill,
20
+ :mandrill_api_key => credentials[:mandrill_api_key],
21
+ })
22
+ end
23
+
24
+ describe '#valid?' do
25
+ it 'should return true if the response is valid' do
26
+ @service.valid?(params('valid')).should == true
27
+ end
28
+
29
+ it 'should return false if the response is invalid' do
30
+ @service.valid?(params('invalid')).should == false
31
+ end
32
+
33
+ it 'should raise an error if parameters are missing' do
34
+ expect{ @service.valid?(params('missing')) }.to raise_error(KeyError)
35
+ end
36
+ end
37
+
38
+ describe '#transform' do
39
+ it 'should return a mail message' do
40
+ message = @service.transform(params('valid'))[0]
41
+
42
+ # Headers
43
+ message.date.should == DateTime.parse('Thu, 27 Dec 2012 15:25:37 -0500')
44
+ message.from.should == ['james@opennorth.ca']
45
+ message.to.should == ['foo+bar@govkit.org']
46
+ message.subject.should == 'Test'
47
+
48
+ # Body
49
+ message.multipart?.should == true
50
+ message.parts.size.should == 2
51
+ message.parts[0].content_type.should == 'text/plain'
52
+ message.parts[0].body.should == "bold text\n\n\n> multiline\n> quoted\n> text\n\n--\nSignature block\n"
53
+ message.parts[1].content_type.should == 'text/html; charset=UTF-8'
54
+ p message.parts[1].body.decoded
55
+ message.parts[1].body.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><div><b>bold text</b></div><div><br></div><div><blockquote type="cite"></blockquote></div><div><blockquote type="cite">multiline</blockquote></div><blockquote type="cite"><div>quoted</div><div>text</div></blockquote><br><div>--</div><div>Signature block</div></body></html>\n)
56
+
57
+ # Extra Mandrill parameters
58
+ message['email'].value.should == 'foo+bar@govkit.org'
59
+ message['tags'].should be_nil
60
+ message['sender'].should be_nil
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe MultiMail do
4
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe MultiMail::Receiver::Base do
4
+ let :klass do
5
+ Class.new(MultiMail::Service) do
6
+ include MultiMail::Receiver::Base
7
+
8
+ def valid?(params)
9
+ params['foo'] == 1
10
+ end
11
+
12
+ def transform(params)
13
+ [Mail.new]
14
+ end
15
+ end
16
+ end
17
+
18
+ describe '#process' do
19
+ before :all do
20
+ @service = klass.new
21
+ end
22
+
23
+ it 'should parse the request' do
24
+ klass.should_receive(:parse).with('foo' => 1).once.and_return('foo' => 1)
25
+ @service.process('foo' => 1)
26
+ end
27
+
28
+ it 'should transform the request if the request is valid' do
29
+ @service.should_receive(:transform).with('foo' => 1).once
30
+ @service.process('foo' => 1)
31
+ end
32
+
33
+ it 'raise an error if the request is invalid' do
34
+ expect{ @service.process('foo' => 0) }.to raise_error(MultiMail::ForgedRequest)
35
+ end
36
+ end
37
+
38
+ describe '#parse' do
39
+ it 'should parse raw POST data' do
40
+ klass.parse('foo=1&bar=1&bar=1').should == {'foo' => '1', 'bar' => ['1', '1']}
41
+ end
42
+
43
+ it 'should pass-through a hash' do
44
+ klass.parse('foo' => 1).should == {'foo' => 1}
45
+ end
46
+
47
+ it 'should raise an error if the argument is invalid' do
48
+ expect{ klass.parse(1) }.to raise_error(ArgumentError, "Can't handle Fixnum input")
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe MultiMail::Receiver do
4
+ describe '#new' do
5
+ it 'should not raise an error if the provider is recognized' do
6
+ expect{ MultiMail::Receiver.new :provider => :mailgun, :mailgun_api_key => 1 }.to_not raise_error
7
+ end
8
+
9
+ it 'should raise an error if the provider is not recognized' do
10
+ expect{ MultiMail::Receiver.new :provider => :foo }.to raise_error(ArgumentError)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe MultiMail::Sender do
4
+ describe '#new' do
5
+ it 'should not raise an error if the provider is recognized' do
6
+ expect{ MultiMail::Sender.new :provider => :mailgun, :mailgun_api_key => 1 }.to_not raise_error
7
+ end
8
+
9
+ it 'should raise an error if the provider is not recognized' do
10
+ expect{ MultiMail::Sender.new :provider => :foo }.to raise_error(ArgumentError)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe MultiMail::Service do
4
+ let :klass do
5
+ Class.new(MultiMail::Service) do
6
+ requires :required_argument1, :required_argument2
7
+ recognizes :optional_argument1, :optional_argument2
8
+ end
9
+ end
10
+
11
+ describe '#initialize' do
12
+ it 'should validate options' do
13
+ klass.should_receive(:validate_options).with(:required_argument1 => 1, :required_argument2 => 1).once
14
+ klass.new(:required_argument1 => 1, :required_argument2 => 1)
15
+ end
16
+ end
17
+
18
+ describe '#requires' do
19
+ it 'should return required arguments' do
20
+ klass.requirements.should == [:required_argument1, :required_argument2]
21
+ end
22
+ end
23
+
24
+ describe '#recognizes' do
25
+ it 'should return optional arguments' do
26
+ klass.recognized.should == [:optional_argument1, :optional_argument2]
27
+ end
28
+ end
29
+
30
+ describe '#validate_options' do
31
+ it 'should not raise an error if the arguments are valid' do
32
+ expect{
33
+ klass.validate_options({
34
+ :required_argument1 => 1,
35
+ :required_argument2 => 1,
36
+ })
37
+ }.to_not raise_error
38
+ end
39
+
40
+ it 'should raise an error if a required argument is missing' do
41
+ expect{
42
+ klass.validate_options({
43
+ :optional_argument1 => 1,
44
+ :optional_argument2 => 1,
45
+ })
46
+ }.to raise_error(ArgumentError, "Missing required arguments: required_argument1, required_argument2")
47
+ end
48
+
49
+ it 'should raise an error if an argument is not recognized' do
50
+ expect{
51
+ klass.validate_options({
52
+ :required_argument1 => 1,
53
+ :required_argument2 => 1,
54
+ :foo => 1,
55
+ :bar => 1,
56
+ })
57
+ }.to raise_error(ArgumentError, "Unrecognized arguments: foo, bar")
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,27 @@
1
+ require 'net/http'
2
+ require 'yaml'
3
+
4
+ require 'rubygems'
5
+ require 'rspec'
6
+ require File.dirname(__FILE__) + '/../lib/multi_mail'
7
+
8
+ # Requires supporting ruby files with custom matchers and macros, etc,
9
+ # in spec/support/ and its subdirectories.
10
+ Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
11
+
12
+ # @return [Hash] required arguments to initialize services
13
+ # @todo warn if unable to authenticate with details in api_keys.yml
14
+ def credentials
15
+ @credentials ||= YAML.load_file(File.expand_path('../../api_keys.yml', __FILE__))
16
+ end
17
+
18
+ # @param [String] provider a provider
19
+ # @param [String] fixture one of "valid", "invalid" or "spam"
20
+ # @return [String] the provider's baked response
21
+ # @see FakeWeb::Responder#baked_response
22
+ def response(provider, fixture)
23
+ io = File.open(File.expand_path("../fixtures/#{provider}/#{fixture}.txt", __FILE__), 'r')
24
+ socket = Net::BufferedIO.new(io)
25
+ response = Net::HTTPResponse.read_new(socket)
26
+ response.reading_body(socket, true) {}
27
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multi_mail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Open North
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mail
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.4.4
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.4.4
30
+ - !ruby/object:Gem::Dependency
31
+ name: multimap
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.1.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.1.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.10'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.10'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rest-client
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.6.7
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.6.7
78
+ - !ruby/object:Gem::Dependency
79
+ name: mandrill-api
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.0.12
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.0.12
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description:
111
+ email:
112
+ - info@opennorth.ca
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .travis.yml
119
+ - Gemfile
120
+ - LICENSE
121
+ - README.md
122
+ - Rakefile
123
+ - USAGE
124
+ - lib/multi_mail.rb
125
+ - lib/multi_mail/mailgun/receiver.rb
126
+ - lib/multi_mail/mandrill/receiver.rb
127
+ - lib/multi_mail/receiver.rb
128
+ - lib/multi_mail/receiver/base.rb
129
+ - lib/multi_mail/sender.rb
130
+ - lib/multi_mail/service.rb
131
+ - lib/multi_mail/version.rb
132
+ - multi_mail.gemspec
133
+ - spec/fixtures/mailgun/invalid.txt
134
+ - spec/fixtures/mailgun/missing.txt
135
+ - spec/fixtures/mailgun/spam.txt
136
+ - spec/fixtures/mailgun/valid.txt
137
+ - spec/mailgun/receiver_spec.rb
138
+ - spec/mandrill/receiver_spec.rb
139
+ - spec/multi_mail_spec.rb
140
+ - spec/receiver/base_spec.rb
141
+ - spec/receiver_spec.rb
142
+ - spec/sender_spec.rb
143
+ - spec/service_spec.rb
144
+ - spec/spec_helper.rb
145
+ homepage: http://github.com/opennorth/multi_mail
146
+ licenses: []
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubyforge_project:
165
+ rubygems_version: 1.8.24
166
+ signing_key:
167
+ specification_version: 3
168
+ summary: Easily switch between email APIs
169
+ test_files:
170
+ - spec/fixtures/mailgun/invalid.txt
171
+ - spec/fixtures/mailgun/missing.txt
172
+ - spec/fixtures/mailgun/spam.txt
173
+ - spec/fixtures/mailgun/valid.txt
174
+ - spec/mailgun/receiver_spec.rb
175
+ - spec/mandrill/receiver_spec.rb
176
+ - spec/multi_mail_spec.rb
177
+ - spec/receiver/base_spec.rb
178
+ - spec/receiver_spec.rb
179
+ - spec/sender_spec.rb
180
+ - spec/service_spec.rb
181
+ - spec/spec_helper.rb
182
+ has_rdoc: