canonical-email 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Centro
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.rdoc ADDED
@@ -0,0 +1,108 @@
1
+ = Canonical
2
+
3
+ Canonical is an ActionMailer extension that acts just like the Postfix
4
+ utility of the same name. Essentially, Canonical lets you substitute the To,
5
+ CC, and/or BCC email destinations with one of many replacements via a rule.
6
+
7
+ You may want this behavior for many reasons; especially if you're actually
8
+ using Postfix's canonical functionality. Let's say you don't want to setup
9
+ your integration or staging environment to send email in test mode, but you
10
+ also do not want to accidentally send email to real users. You may add a
11
+ canonical rule like the following to send any email from the system to any
12
+ address to the QA email distribution list:
13
+
14
+ TMail::Mail.add_canonical_override(/.*/, 'qa@foo.bar')
15
+
16
+ Assuming you are working at Foo Bar, that rule will match any destination
17
+ address that has any character in it - meaning, it matches everything - and
18
+ will subsitute the matched email with qa@foo.bar. This is the same as saying
19
+ the following in Postfix's canonical file:
20
+
21
+ /.*/, qa@foo.bar
22
+
23
+ What you should notice is that canonical expects to see a regular expression
24
+ as the first argument. Passing in anything that does not respond to =~ will
25
+ result in an error.
26
+
27
+ == First matched, first replaced (FMFR)
28
+
29
+ Now, let's say you didn't want to just replace everything. Let's you wanted
30
+ to send everything for your boss to your team, everything to marketing to
31
+ another email address, and catch everything else. The rules would then be
32
+ defined as:
33
+
34
+ TMail::Mail.add_canonical_override(/ceo@foo\.bar/, 'developers@foo.bar')
35
+ TMail::Mail.add_canonical_override(/marketing@/, 'technology@foo.bar')
36
+ TMail::Mail.add_canonical_override(/.*/, 'qa@foo.bar')
37
+
38
+ What you should notice here is the ordering. Canonical works with multiple
39
+ rules, but will process a substition according to the rule that matches first.
40
+ Thus, whichever rule you define first will be checked first. If the rule matches
41
+ the email address being checked, the substitution is made and matching stops.
42
+ Hence the FMFR; first matched, first replaced.
43
+
44
+ The best way to take advantage of rules is to put the most specific rule at the
45
+ top of the list and the most generic rule at the bottom. If we had reversed the
46
+ order of the above rules, everything would go to qa@foo.bar.
47
+
48
+ == Passing Through
49
+
50
+ Perhaps you have a catch-all rule like the ones used above (e.g. /.*/), but you
51
+ want certain email addresses to pass-through. Sort of like an opt-in approach,
52
+ which might be considered safer in a closed environment. You could match on the
53
+ specific address and then substitute it with the same thing, like so:
54
+
55
+ TMail::Mail.add_canonical_override(/ceo@foo\.bar/, 'ceo@foo.bar')
56
+
57
+ But that's kind of silly; and what if you wanted to match on everything for your
58
+ company? This is where Canonical deviates slightly from Postfix's version. This
59
+ Canonical will allow you to provide a passthrough for a pattern. For instance:
60
+
61
+ TMail::Mail.add_canonical_passthrough(/@foo\.bar/)
62
+
63
+ Will explicitly allow any email address with "@foo.bar" in it. Even if there is
64
+ a catch-all rule; so long as this rule comes before any other rules that would
65
+ match.
66
+
67
+ Handy? Yes!
68
+
69
+ == How does it work
70
+
71
+ Essentially, Canonical works by hijacking the TMail::Mail#destinations which is
72
+ used at the moment ActionMailer is about to write the RCPT TOs to the SMTP
73
+ server chosen. Canonical only modifies destinations, it does not modify anything
74
+ about the body of the email. Thus, the To, Cc, and Bcc will all look the same
75
+ as an unmodified email would to the recipient.
76
+
77
+ Canonical also disables the :sendmail option since it only tries to work with
78
+ SMTP. At some point in the future, this may change. If you need sendmail support
79
+ then fix the code and send a patch :)
80
+
81
+ == How should I use it?
82
+
83
+ The most obvious way to use canonical is to put the rules in your Rails
84
+ environment files. We suggest an opt-in approach to rule setting such that you
85
+ catch-all in config/environment.rb and specifically allow emails in
86
+ config/environments/production.rb.
87
+
88
+ For example:
89
+
90
+ # config/environment.rb
91
+ ...
92
+ TMail::Mail.add_canonical_override(/.*/, 'testing@foo.bar')
93
+
94
+ # config/environments/production.rb
95
+ ...
96
+ TMail::Mail.add_canonical_passthrough(/.*/)
97
+
98
+ That's the most basic way to localize email in every environment but production.
99
+
100
+ = License
101
+
102
+ Copyright (c) 2008 {Centro}[www.centro.net], released under the MIT license.
103
+
104
+ Authored by:
105
+
106
+ {Eric Schwartz}[eric.schwartz@centro.net]
107
+ {Brett Neumeier}[brett.neumeier@centro.net]
108
+ {Justin Knowlden}[justin.knowlden@centro.net]
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "canonical-email"
8
+ gem.summary = %Q{Safely and easily rewrite your email destination headers in Ruby}
9
+ gem.description = %Q{canonical-email lets you substitute the To, CC, and/or BCC email destinations with one of many replacements via a simple regular expression.}
10
+ gem.homepage = "http://github.com/centro/canonical-email"
11
+ gem.authors = ['emschwar', 'jaknowlden']
12
+ gem.add_development_dependency "actionmailer", ">=2.1.0"
13
+ gem.add_development_dependency "actionpack", ">=2.1.0"
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ desc 'Default: run unit tests.'
21
+ task :default => :test
22
+
23
+ desc 'Test the canonical plugin.'
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.pattern = 'test/**/*_test.rb'
27
+ t.verbose = true
28
+ end
29
+
30
+ require 'rake/rdoctask'
31
+ desc 'Generate documentation for the canonical plugin.'
32
+ Rake::RDocTask.new(:rdoc) do |rdoc|
33
+ rdoc.rdoc_dir = 'rdoc'
34
+ rdoc.title = 'Canonical'
35
+ rdoc.options << '--line-numbers' << '--inline-source'
36
+ rdoc.rdoc_files.include('README')
37
+ rdoc.rdoc_files.include('lib/**/*.rb')
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'action_mailer'
2
+ require 'canonical'
3
+ require 'disallow_sendmail_method'
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,138 @@
1
+ require 'disallow_sendmail_method'
2
+
3
+ module Centro #:nodoc:
4
+ =begin rdoc
5
+ Canonical is an ActionMailer extension that acts just like the Postfix
6
+ utility of the same name. Essentially, Canonical lets you substitute the To,
7
+ CC, and/or BCC email destinations with one of many replacements via a rule.
8
+
9
+ You may want this behavior for many reasons; especially if you're actually
10
+ using Postfix's canonical functionality. Let's say you don't want to setup
11
+ your integration or staging environment to send email in test mode, but you
12
+ also do not want to accidentally send email to real users. You may add a
13
+ canonical rule like the following to send any email from the system to any
14
+ address to the QA email distribution list:
15
+
16
+ TMail::Mail.add_canonical_override(/.*/, 'qa@foo.bar')
17
+
18
+ Assuming you are working at Foo Bar, that rule will match any destination
19
+ address that has any character in it - meaning, it matches everything - and
20
+ will subsitute the matched email with qa@foo.bar. This is the same as saying
21
+ the following in Postfix's canonical file:
22
+
23
+ /.*/, qa@foo.bar
24
+
25
+ What you should notice is that canonical expects to see a regular expression
26
+ as the first argument. Passing in anything that does not respond to =~ will
27
+ result in an error.
28
+
29
+ = First matched, first replaced (FMFR)
30
+
31
+ Now, let's say you didn't want to just replace everything. Let's you wanted
32
+ to send everything for your boss to your team, everything to marketing to
33
+ another email address, and catch everything else. The rules would then be
34
+ defined as:
35
+
36
+ TMail::Mail.add_canonical_override(/ceo@foo\.bar/, 'developers@foo.bar')
37
+ TMail::Mail.add_canonical_override(/marketing@/, 'technology@foo.bar')
38
+ TMail::Mail.add_canonical_override(/.*/, 'qa@foo.bar')
39
+
40
+ What you should notice here is the ordering. Canonical works with multiple
41
+ rules, but will process a substition according to the rule that matches first.
42
+ Thus, whichever rule you define first will be checked first. If the rule matches
43
+ the email address being checked, the substitution is made and matching stops.
44
+ Hence the FMFR; first matched, first replaced.
45
+
46
+ The best way to take advantage of rules is to put the most specific rule at the
47
+ top of the list and the most generic rule at the bottom. If we had reversed the
48
+ order of the above rules, everything would go to qa@foo.bar.
49
+
50
+ = Passing Through
51
+
52
+ Perhaps you have a catch-all rule like the ones used above (e.g. /.*/), but you
53
+ want certain email addresses to pass-through. Sort of like an opt-in approach,
54
+ which might be considered safer in a closed environment. You could match on the
55
+ specific address and then substitute it with the same thing, like so:
56
+
57
+ TMail::Mail.add_canonical_override(/ceo@foo\.bar/, 'ceo@foo.bar')
58
+
59
+ But that's kind of silly; and what if you wanted to match on everything for your
60
+ company? This is where Canonical deviates slightly from Postfix's version. This
61
+ Canonical will allow you to provide a passthrough for a pattern. For instance:
62
+
63
+ TMail::Mail.add_canonical_passthrough(/@foo\.bar/)
64
+
65
+ Will explicitly allow any email address with "@foo.bar" in it. Even if there is
66
+ a catch-all rule; so long as this rule comes before any other rules that would
67
+ match.
68
+
69
+ Handy? Yes!
70
+
71
+ = How does it work
72
+
73
+ Essentially, Canonical works by hijacking the TMail::Mail#destinations which is
74
+ used at the moment ActionMailer is about to write the RCPT TOs to the SMTP
75
+ server chosen. Canonical only modifies destinations, it does not modify anything
76
+ about the body of the email. Thus, the To, Cc, and Bcc will all look the same
77
+ as an unmodified email would to the recipient.
78
+
79
+ Canonical also disables the :sendmail option since it only tries to work with
80
+ SMTP. At some point in the future, this may change. If you need sendmail support
81
+ then fix the code and send a patch :)
82
+ =end
83
+ module Canonical
84
+
85
+ def self.included(klass)
86
+ klass.extend(ClassMethods)
87
+ klass.send(:include, InstanceMethods)
88
+ end
89
+
90
+ module ClassMethods
91
+ def clear_canonical
92
+ self.overrides = nil
93
+ end
94
+
95
+ def add_canonical_passthrough(pattern)
96
+ overrides << [ pattern, :passthrough ]
97
+ end
98
+
99
+ def add_canonical_override(pattern, override)
100
+ overrides << [ pattern, override ]
101
+ end
102
+
103
+ def canonical_destination(email)
104
+ overrides.each do |pattern, replace|
105
+ return (:passthrough == replace ? email : replace) if email =~ pattern
106
+ end
107
+ email
108
+ end
109
+
110
+ private
111
+
112
+ def overrides
113
+ @@smtp_overridden ||= []
114
+ end
115
+
116
+ def overrides=(overrides)
117
+ @@smtp_overridden = overrides
118
+ end
119
+
120
+ end # ClassMethods
121
+
122
+ module InstanceMethods
123
+ def canonical_destinations
124
+ self.destinations_without_canonical.collect do |email|
125
+ self.class.canonical_destination(email)
126
+ end.uniq
127
+ end
128
+
129
+ def self.included(base)
130
+ base.send(:alias_method, :destinations_without_canonical, :destinations)
131
+ base.send(:alias_method, :destinations, :canonical_destinations)
132
+ end
133
+
134
+ end # InstanceMethods
135
+ end # Canonical
136
+ end # Centro
137
+
138
+ TMail::Mail.send(:include, Centro::Canonical)
@@ -0,0 +1,7 @@
1
+ class ActionMailer::Base
2
+ def perform_delivery_sendmail(ignored)
3
+ raise SecurityError,
4
+ "[Canonical] Sendmail method is explicitly disallowed so that we don't " +
5
+ "accidentally configure ourselves into a security hole"
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :canonical do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class CanonicalTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @mail = TMail::Mail.new
7
+ end
8
+
9
+ def teardown
10
+ TMail::Mail.clear_canonical
11
+ end
12
+
13
+ def test_add_override_rewrites_matching_destinations
14
+ TMail::Mail.add_canonical_override(/me\.please$/, "foo@bar.com")
15
+ assert_equal "foo@bar.com",
16
+ TMail::Mail.canonical_destination("override@me.please")
17
+ end
18
+
19
+ def test_add_override_ignores_non_matching_destinations
20
+ TMail::Mail.add_canonical_override(/me\.please$/, "foo@bar.com")
21
+ assert_equal "same@centro.net",
22
+ TMail::Mail.canonical_destination("same@centro.net")
23
+ end
24
+
25
+ def test_add_override_does_not_change_destination_with_passthrough
26
+ TMail::Mail.add_canonical_override(/@centro.net$/, :passthrough)
27
+ assert_equal "passthrough@centro.net",
28
+ TMail::Mail.canonical_destination("passthrough@centro.net")
29
+ end
30
+
31
+ def test_add_passthrough_does_not_change_destination
32
+ TMail::Mail.add_canonical_passthrough(/@centro.net$/)
33
+ assert_equal "passthrough@centro.net",
34
+ TMail::Mail.canonical_destination("passthrough@centro.net")
35
+ end
36
+
37
+ def test_overrides_are_applied_in_the_order_they_were_added
38
+ TMail::Mail.add_canonical_passthrough(/@centro.net$/)
39
+ TMail::Mail.add_canonical_override(/.*/, "redirect@centro.net")
40
+
41
+ assert_equal "same@centro.net",
42
+ TMail::Mail.canonical_destination("same@centro.net")
43
+ assert_equal "redirect@centro.net",
44
+ TMail::Mail.canonical_destination("some@random.addr.com")
45
+ end
46
+
47
+ def test_overrides_rewrite_tmail_destinations
48
+ TMail::Mail.add_canonical_override(/.*/, "redirect@centro.net")
49
+
50
+ @mail.to = "foo@bar.com"
51
+ assert_equal ["redirect@centro.net"], @mail.destinations
52
+ end
53
+
54
+ def test_overrides_should_list_unique_replacements
55
+ TMail::Mail.add_canonical_override(/.*/, "redirect@centro.net")
56
+
57
+ @mail.to = ["foo@bar.com", "bar@foo.com"]
58
+ assert_equal ["redirect@centro.net"], @mail.destinations
59
+ end
60
+
61
+ end
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class DisallowSendmailMethodTest < Test::Unit::TestCase
4
+
5
+ class MyMailer < ActionMailer::Base
6
+ def self.template_root
7
+ File.dirname(__FILE__)
8
+ end
9
+
10
+ def test_mail
11
+ part :content_type => 'text/plain', :body => "a test"
12
+ end
13
+ end
14
+
15
+ def test_sendmail_method_raises_security_error
16
+ ActionMailer::Base.delivery_method = :sendmail
17
+ ActionMailer::Base.template_root = "."
18
+ assert_raises (SecurityError) { MyMailer.deliver_test_mail }
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'action_mailer'
4
+ require 'action_controller'
5
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'canonical-email')
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: canonical-email
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - emschwar
13
+ - jaknowlden
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-03-29 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: actionmailer
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 1
31
+ - 0
32
+ version: 2.1.0
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: actionpack
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 2
44
+ - 1
45
+ - 0
46
+ version: 2.1.0
47
+ type: :development
48
+ version_requirements: *id002
49
+ description: canonical-email lets you substitute the To, CC, and/or BCC email destinations with one of many replacements via a simple regular expression.
50
+ email:
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - README.rdoc
57
+ files:
58
+ - MIT-LICENSE
59
+ - README.rdoc
60
+ - Rakefile
61
+ - VERSION
62
+ - init.rb
63
+ - install.rb
64
+ - lib/canonical-email.rb
65
+ - lib/disallow_sendmail_method.rb
66
+ - tasks/canonical.rake
67
+ - test/canonical_test.rb
68
+ - test/disallow_sendmail_method_test.rb
69
+ - test/test_helper.rb
70
+ - uninstall.rb
71
+ has_rdoc: true
72
+ homepage: http://github.com/centro/canonical-email
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --charset=UTF-8
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.3.6
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Safely and easily rewrite your email destination headers in Ruby
101
+ test_files:
102
+ - test/canonical_test.rb
103
+ - test/disallow_sendmail_method_test.rb
104
+ - test/test_helper.rb