multi_mail 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: