sanitize_email 1.0.0.alpha2 → 1.0.0.rc1

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/CHANGELOG CHANGED
@@ -1,6 +1,18 @@
1
- Version 1.0.0.alpha1
2
- - Implementing initial support for Rails >= 3.0 (new ActionMailer API)
1
+ Version 1.0.0.rc1
2
+ - Added a good_list and a bad_list (whitelist and blacklist)
3
+ - Added Deprecation library
4
+ - Refactored Sanitization module into Hook class
5
+ - Renamed Hook Class to Bleach Class
6
+ - Improve support for non-rails implementations
7
+ - Deprecated local_environments in favor of local_environment_proc
8
+ - Deprecated sanitized_recipients in favor of sanitized_to
9
+ - More specs
10
+
11
+ Version 1.0.0.alpha2
12
+ - Complete refactor! Implementing initial support for Rails >= 3.0 (new ActionMailer API)
3
13
  - Support for Rails <= 2.X remains in version 0.X.X releases.
14
+ - NinthBit namespace is now SanitizeEmail namespace
15
+ - Now has a first class Config class
4
16
 
5
17
  XXXXXXXXXXXXXXXXXXXXXXX Rail 3.0+ Only Form here on up! XXXXXXXXXXXXXXXXXXXXXXX
6
18
 
data/VERSION.yml CHANGED
@@ -2,4 +2,4 @@
2
2
  :major: 1
3
3
  :minor: 0
4
4
  :patch: 0
5
- :build: alpha2
5
+ :build: rc1
@@ -4,18 +4,19 @@ require 'rails'
4
4
  require 'action_mailer'
5
5
 
6
6
  module SanitizeEmail
7
- if defined?(Rails) && ::Rails::VERSION::MAJOR >= 3
8
- require 'sanitize_email/version'
9
- require 'sanitize_email/config'
10
- require 'sanitize_email/sanitizer'
11
- require 'sanitize_email/hook'
7
+ require 'sanitize_email/version'
8
+ require 'sanitize_email/config'
9
+ require 'sanitize_email/bleach'
10
+ require 'sanitize_email/deprecation'
12
11
 
12
+ # Allow non-rails implementations to use this gem
13
+ if @rails = defined?(Rails) && ::Rails::VERSION::MAJOR >= 3
13
14
  if ::Rails::VERSION::MINOR >= 1
14
15
  require 'sanitize_email/engine'
15
16
  elsif ::Rails::VERSION::MINOR == 0
16
17
  require 'sanitize_email/railtie'
17
18
  end
18
- else
19
+ elsif @rails = defined?(Rails)
19
20
  raise "Please use the 0.X.X versions of sanitize_email for Rails 2.X and below."
20
21
  end
21
22
 
@@ -28,4 +29,17 @@ module SanitizeEmail
28
29
  SanitizeEmail[name]
29
30
  end
30
31
 
32
+ def self.sanitized_recipients
33
+ SanitizeEmail[:sanitized_recipients]
34
+ end
35
+
36
+ def self.local_environments
37
+ SanitizeEmail[:local_environments]
38
+ end
39
+
40
+ class << self
41
+ extend SanitizeEmail::Deprecation
42
+ deprecated_alias :sanitized_recipients, :sanitized_to
43
+ deprecated :local_environments, :local_environment_proc
44
+ end
31
45
  end
@@ -0,0 +1,156 @@
1
+ #Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
2
+ #Released under the MIT license
3
+
4
+ module SanitizeEmail
5
+ class Bleach
6
+
7
+ class MissingTo < StandardError; end
8
+ class UnknownOverride < StandardError; end
9
+
10
+ # Can override global configs at the instance level.
11
+ attr_accessor :engage, # Turn sanitization on or off just for this instance
12
+ :sanitized_to, :sanitized_cc, :sanitized_bcc, # Replace non-white-listed addresses with these sanitized addresses.
13
+ :good_list, # White-listed addresses will not be molested as to, cc, or bcc
14
+ :bad_list, # Black-listed addresses will be removed from to, cc and bcc when sanitization is engaged
15
+ :injected # Track whether or not the subject has been injected with usernames
16
+
17
+ def initialize(args = {})
18
+ # Not using extract_options! because no-rails compatibility is a goal
19
+ @sanitized_to = args[:sanitized_to] || SanitizeEmail[:sanitized_to]
20
+ @sanitized_cc = args[:sanitized_cc] || SanitizeEmail[:sanitized_cc]
21
+ @sanitized_bcc = args[:sanitized_bcc] || SanitizeEmail[:sanitized_bcc]
22
+ @good_list = args[:good_list] || SanitizeEmail[:good_list] || []
23
+ @bad_list = args[:bad_list] || SanitizeEmail[:bad_list] || []
24
+ @engage = args[:engage] || SanitizeEmail[:engage]
25
+ @injected = false
26
+ end
27
+
28
+ # If all recipient addresses are white-listed the field is left alone.
29
+ def delivering_email(message)
30
+ if self.sanitize_engaged?
31
+ message.subject = self.subject_override(message.subject, message.to) if SanitizeEmail.use_actual_email_prepended_to_subject
32
+ message.to = self.to_override(message.to)
33
+ message.cc = self.cc_override(message.cc)
34
+ message.bcc = self.bcc_override(message.bcc)
35
+ end
36
+ end
37
+
38
+ # Only relevant
39
+ def consider_local?
40
+ SanitizeEmail.local_environment_proc.call if SanitizeEmail.local_environment_proc.respond_to?(:call)
41
+ end
42
+
43
+ # This method will be called by the Hook to determine if an override should occur
44
+ def sanitize_engaged?
45
+ !SanitizeEmail.force_sanitize.nil? ? SanitizeEmail.force_sanitize : self.consider_local?
46
+ end
47
+
48
+ def subject_override(real_subject, actual_addresses)
49
+ if !actual_addresses.respond_to?(:join)
50
+ real_subject
51
+ else
52
+ "(#{actual_addresses.join(',').gsub(/@/,' at ').gsub(/[<>]/,'~')}) #{real_subject}"
53
+ end
54
+ end
55
+
56
+ def to_override(actual_addresses)
57
+ to = override_email(:to, actual_addresses)
58
+ raise MissingTo, 'after overriding :to there are no addresses to send in To: header.' if to.empty?
59
+ to.join(',')
60
+ end
61
+
62
+ def cc_override(actual_addresses)
63
+ override_email(:cc, actual_addresses).join(',')
64
+ end
65
+
66
+ def bcc_override(actual_addresses)
67
+ override_email(:bcc, actual_addresses).join(',')
68
+ end
69
+
70
+ #######
71
+ protected
72
+ #######
73
+
74
+ def address_list_filter(list_type, address)
75
+ # TODO: How does this handle email addresses with user names like "Foo Example <foo@example.org>"
76
+ has_address = self.send(list_type).include?(address)
77
+ case list_type
78
+ when :good_list then has_address ? address : nil
79
+ when :bad_list then has_address ? nil : address
80
+ end
81
+ end
82
+
83
+ def inject_user_names(real_addresses, sanitized_addresses)
84
+ real_addresses.inject([]) do |result, real_recipient|
85
+ if real_recipient.nil?
86
+ new_recipient = sanitized_addresses
87
+ else
88
+ new_recipient = sanitized_addresses.map{|sanitized| "#{real_recipient.gsub(/@/,' at ').gsub(/[<>]/,'~')} <#{sanitized}>"}
89
+ end
90
+ result << new_recipient
91
+ result
92
+ end.flatten
93
+ end
94
+
95
+ def clean_addresses(addresses, list_type)
96
+ # Normalize addresses just in case it isn't an array yet
97
+ addresses.map { |address|
98
+ # If this address is on the good list then let it pass
99
+ self.address_list_filter(list_type, address)
100
+ }.compact
101
+ end
102
+
103
+ def sanitize_addresses(type)
104
+ case type
105
+ when :to then Array(self.sanitized_to)
106
+ when :cc then Array(self.sanitized_cc)
107
+ when :bcc then Array(self.sanitized_bcc)
108
+ else raise UnknownOverride, "unknown email override"
109
+ end
110
+ end
111
+
112
+ # Allow good listed email addresses, and then remove the bad listed addresses
113
+ def good_listize(real_addresses)
114
+ good_listed = self.clean_addresses(real_addresses, :good_list)
115
+ good_listed = self.clean_addresses(good_listed, :bad_list) unless good_listed.empty?
116
+ good_listed
117
+ end
118
+
119
+ def override_email(type, actual_addresses)
120
+ # Normalized to an arrays
121
+ #puts "override_email 1: #{type} - #{actual_addresses}"
122
+ real_addresses = Array(actual_addresses)
123
+
124
+ #puts "override_email 2: #{type} - #{real_addresses}"
125
+ # If there were no original recipients, then we DO NOT override the nil with the sanitized recipients
126
+ return [] if real_addresses.empty?
127
+
128
+ good_listed = good_listize(real_addresses)
129
+ #puts "override_email 3: #{type} - #{good_listed}"
130
+ # If there are good_list addresses to send to then use them as is, no mods needed
131
+ return good_listed unless good_listed.empty?
132
+
133
+ # If there are no sanitized addresses we can't override!
134
+ sanitized_addresses = sanitize_addresses(type)
135
+ #puts "override_email 3: #{type} - #{sanitized_addresses}"
136
+ return [] if sanitized_addresses.empty?
137
+
138
+ # At this point it is assured that the address list will need to be sanitized
139
+ # One more check to ensure none of the configured sanitized email addresses are on the bad_list
140
+ sanitized_addresses = self.clean_addresses(sanitized_addresses, :bad_list)
141
+ #puts "override_email 4: #{type} - #{sanitized_addresses}"
142
+
143
+ # If we don't want to inject the 'email' in the 'user name' section of the sanitized recipients,
144
+ # then just return the default sanitized recipients
145
+ return sanitized_addresses unless SanitizeEmail.use_actual_email_as_sanitized_user_name
146
+
147
+ with_user_names = self.inject_user_names(real_addresses, sanitized_addresses)
148
+ #puts "override_email 5: #{type} - #{with_user_names}"
149
+ # Otherwise inject the email as the 'user name'
150
+ return with_user_names
151
+ end
152
+
153
+ end # end Class Hook
154
+ end # end Module SanitizeEmail
155
+
156
+
@@ -13,7 +13,13 @@ module SanitizeEmail
13
13
 
14
14
  # The recipient addresses for the messages, either as a string (for a single
15
15
  # address) or an array (for multiple addresses) that go out in 'local' environments
16
- :sanitized_recipients => nil,
16
+ :sanitized_to => nil,
17
+
18
+ # a white list
19
+ :good_list => nil,
20
+
21
+ # a black list
22
+ :bad_list => nil,
17
23
 
18
24
  # Use the 'real' email address as the username for the sanitized email address
19
25
  # e.g. "real@example.com <sanitized@example.com>"
@@ -23,11 +29,20 @@ module SanitizeEmail
23
29
  # e.g. "real@example.com rest of subject"
24
30
  :use_actual_email_prepended_to_subject => false,
25
31
 
26
- :local_environments => %w( development test )
32
+ :local_environment_proc => Proc.new { true }
27
33
  }
28
34
 
29
35
  def self.configure &block
30
36
  yield self.config
37
+
38
+ # Gracefully handle deprecated config values.
39
+ # Actual deprecation warnings are thrown in the top SanitizeEmail module thanks to our use of dynamic methods.
40
+ if config[:local_environments] && defined?(Rails)
41
+ config[:local_environment_proc] = Proc.new { SanitizeEmail.local_environments.include?(Rails.env) }
42
+ end
43
+ if config[:sanitize_recipients]
44
+ config[:sanitize_to] = SanitizeEmail.sanitized_recipients
45
+ end
31
46
  end
32
47
 
33
48
  end
@@ -0,0 +1,36 @@
1
+ # See http://www.seejohncode.com/2012/01/09/deprecating-methods-in-ruby/
2
+
3
+ module SanitizeEmail
4
+ module Deprecation
5
+
6
+ # Define a deprecated alias for a method
7
+ # @param [Symbol] name - name of method to define
8
+ # @param [Symbol] replacement - name of method to (alias)
9
+ def deprecated_alias(name, replacement)
10
+ # Create a wrapped version
11
+ define_method(name) do |*args, &block|
12
+ warn "SanitizeEmail: ##{name} deprecated (please use ##{replacement})"
13
+ send replacement, *args, &block
14
+ end
15
+ end
16
+
17
+ # Deprecate a defined method
18
+ # @param [Symbol] name - name of deprecated method
19
+ # @param [Symbol] replacement - name of the desired replacement
20
+ def deprecated(name, replacement = nil)
21
+ # Replace old method
22
+ old_name = :"#{name}_without_deprecation"
23
+ alias_method old_name, name
24
+ # And replace it with a wrapped version
25
+ define_method(name) do |*args, &block|
26
+ if replacement
27
+ warn "SanitizeEmail: ##{name} deprecated (please use ##{replacement})"
28
+ else
29
+ warn "SanitizeEmail: ##{name} deprecated"
30
+ end
31
+ send old_name, *args, &block
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -5,7 +5,7 @@ module SanitizeEmail
5
5
  class Engine < ::Rails::Engine
6
6
 
7
7
  config.to_prepare do
8
- ActionMailer::Base.register_interceptor(SanitizeEmail::Hook)
8
+ ActionMailer::Base.register_interceptor(SanitizeEmail::Bleach)
9
9
  end
10
10
 
11
11
  end
@@ -5,7 +5,7 @@ module SanitizeEmail
5
5
  class Railtie < ::Rails::Railtie
6
6
 
7
7
  config.before_configuration do
8
- ActionMailer::Base.register_interceptor(SanitizeEmail::Hook)
8
+ ActionMailer::Base.register_interceptor(SanitizeEmail::Bleach)
9
9
  end
10
10
 
11
11
  end
@@ -1,5 +1,5 @@
1
1
  #Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
2
2
  #Released under the MIT license
3
3
  module SanitizeEmail
4
- VERSION = '1.0.0.alpha2'
4
+ VERSION = '1.0.0.rc1'
5
5
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "sanitize_email"
8
- s.version = "1.0.0.alpha2"
8
+ s.version = "1.0.0.rc1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Peter Boling", "John Trupiano", "George Anderson"]
12
- s.date = "2012-07-28"
12
+ s.date = "2012-08-04"
13
13
  s.description = "Tool to aid in development, testing, qa, and production troubleshooting of email issues without worrying that emails will get sent to actual live addresses."
14
14
  s.email = ["peter.boling@gmail.com", "jtrupiano@gmail.com", "george@benevolentcode.com"]
15
15
  s.extra_rdoc_files = [
@@ -26,16 +26,15 @@ Gem::Specification.new do |s|
26
26
  "VERSION.yml",
27
27
  "init.rb",
28
28
  "lib/sanitize_email.rb",
29
+ "lib/sanitize_email/bleach.rb",
29
30
  "lib/sanitize_email/config.rb",
31
+ "lib/sanitize_email/deprecation.rb",
30
32
  "lib/sanitize_email/engine.rb",
31
- "lib/sanitize_email/hook.rb",
32
33
  "lib/sanitize_email/railtie.rb",
33
- "lib/sanitize_email/sanitizer.rb",
34
34
  "lib/sanitize_email/version.rb",
35
35
  "sanitize_email.gemspec",
36
36
  "spec/sanitize_email_spec.rb",
37
- "spec/spec_helper.rb",
38
- "spec/tmp/mail_dump/1343461037_3f3edd7/plain.html"
37
+ "spec/spec_helper.rb"
39
38
  ]
40
39
  s.homepage = "http://github.com/pboling/sanitize_email"
41
40
  s.licenses = ["MIT"]
@@ -1,68 +1,136 @@
1
1
  require 'spec_helper'
2
2
 
3
+ #
4
+ # TODO: Letter Opener should *not* be required, but setting the delivery method to :file was causing connection errors... WTF?
5
+ #
6
+ # Letter Opener won't let us test the to when the to has a 'user name'
7
+ # @email_file.should have_to("~to at example.org~ <to@sanitize_email.org>")
8
+ # TODO: Also not sure how to test the user name part of the To: header via the mail object
9
+ #
10
+ # Letter Opener won't let us test the bcc
11
+ # @email_file.should have_cc("cc@sanitize_email.org")
12
+ # Fortunately we can still test the mail object returned by the deliver call
13
+ #
14
+
3
15
  describe SanitizeEmail do
4
16
 
5
- SanitizeEmail::Config.configure do |config|
6
- config[:sanitized_recipients] = 'to@sanitize_email.org'
7
- config[:sanitized_bcc] = 'bcc@sanitize_email.org'
8
- config[:sanitized_cc] = 'cc@sanitize_email.org'
9
- config[:local_environments] = []
10
- config[:use_actual_email_prepended_to_subject] = true
11
- config[:use_actual_email_as_sanitized_user_name] = true
17
+ after(:each) do
18
+ SanitizeEmail::Config.config = {}
19
+ end
20
+
21
+ def sanitize_spec_dryer(rails_env = 'test')
22
+ Launchy.stub(:open)
23
+ location = File.expand_path('../tmp/mail_dump', __FILE__)
24
+ FileUtils.rm_rf(location)
25
+ Mail.defaults do
26
+ delivery_method LetterOpener::DeliveryMethod, :location => location
27
+ end
28
+ Rails.stub(:env).and_return(rails_env)
29
+ @location = location
30
+ end
31
+
32
+ def configure_sanitize_email(sanitize_hash = {})
33
+ sanitize_hash.reverse_merge!({
34
+ :sanitized_to => 'to@sanitize_email.org',
35
+ :sanitized_cc => 'cc@sanitize_email.org',
36
+ :sanitized_bcc => 'bcc@sanitize_email.org',
37
+ :use_actual_email_prepended_to_subject => true,
38
+ :use_actual_email_as_sanitized_user_name => true
39
+ })
40
+ SanitizeEmail::Config.configure do |config|
41
+ config[:sanitized_to] = sanitize_hash[:sanitized_to]
42
+ config[:sanitized_cc] = sanitize_hash[:sanitized_cc]
43
+ config[:sanitized_bcc] = sanitize_hash[:sanitized_bcc]
44
+ config[:use_actual_email_prepended_to_subject] = sanitize_hash[:use_actual_email_prepended_to_subject]
45
+ config[:use_actual_email_as_sanitized_user_name] = sanitize_hash[:use_actual_email_as_sanitized_user_name]
46
+ # For testing deprecated configuration options:
47
+ config[:local_environments] = sanitize_hash[:local_environments] if sanitize_hash[:local_environments]
48
+ config[:sanitized_to] ||= sanitize_hash[:sanitized_recipients] if sanitize_hash[:sanitized_recipients]
49
+ end
50
+ Mail.register_interceptor(SanitizeEmail::Bleach.new)
12
51
  end
13
52
 
14
- def sanitize_mail_delivery(sanitization_switch = false)
15
- # Ensure that localish? will returns sanitization_switch
53
+ def sanitized_mail_delivery(sanitization_switch = false)
54
+ # Ensure that localish? will return sanitization_switch if true or false, and use proc when nil
16
55
  SanitizeEmail::Config.config[:force_sanitize] = sanitization_switch
17
56
  Launchy.should_receive(:open)
18
- mail = Mail.deliver do
57
+ @mail_message = Mail.deliver do
19
58
  from 'from@example.org'
20
59
  to 'to@example.org'
21
60
  cc 'cc@example.org'
61
+ bcc 'bcc@example.org'
22
62
  reply_to 'reply_to@example.org'
23
63
  subject 'original subject'
24
64
  end
65
+ # All the email gets dumped to file once for each type of recipient (:to, :cc, :bcc)
66
+ # Each file is identical, so we only need to check one of them:
67
+ @email_file = File.read(Dir["#{@location}/*/plain.html"].first)
25
68
  end
26
69
 
27
- before(:each) do
28
- Launchy.stub(:open)
29
- location = File.expand_path('../tmp/mail_dump', __FILE__)
30
- FileUtils.rm_rf(location)
31
- Mail.defaults do
32
- delivery_method LetterOpener::DeliveryMethod, :location => location
70
+ context "localish?" do
71
+ before(:each) do
72
+ sanitize_spec_dryer
73
+ configure_sanitize_email
33
74
  end
34
- Mail.register_interceptor(SanitizeEmail::Hook)
35
- @location = location
36
- end
37
75
 
38
- context "localish? is false" do
39
- it "alters nothing" do
40
- sanitize_mail_delivery(false)
41
- # All the email gets dumped to file once for each type of recipient (:to, :cc, :bcc)
42
- # Each file is identical, so we only need to check one of them:
43
- email = File.read(Dir["#{@location}/*/plain.html"].first)
44
- email.should have_from("from@example.org")
45
- email.should have_to("to@example.org")
46
- # Letter Opener won't let us test the cc
47
- #email.should have_cc("cc@example.org")
48
- # Letter Opener won't let us test the bcc
49
- #email.should have_bcc("cc@example.org")
50
- email.should have_subject("original subject")
76
+ context "false" do
77
+ it "alters nothing" do
78
+ sanitized_mail_delivery(false)
79
+ @email_file.should have_from("from@example.org")
80
+ @email_file.should have_to("to@example.org")
81
+ @mail_message.header.fields[3].value.should_not have_to("to at example.org")
82
+ @mail_message.should have_cc("cc@example.org")
83
+ @mail_message.should have_bcc("bcc@example.org")
84
+ @email_file.should have_subject("original subject")
85
+ end
86
+ end
87
+
88
+ context "true" do
89
+ it "should override" do
90
+ sanitized_mail_delivery(true)
91
+ @email_file.should have_from("from@example.org")
92
+ #puts "@mail_message.header.fields[3]: #{@mail_message.header.fields[3]}"
93
+ #@mail_message.header.fields[3].value.should have_to("to at example.org")
94
+ @mail_message.should have_cc("cc@sanitize_email.org")
95
+ @mail_message.should have_bcc("bcc@sanitize_email.org")
96
+ @email_file.should have_subject("(to at example.org) original subject")
97
+ end
51
98
  end
52
99
  end
53
100
 
54
- context "localish? is true" do
55
- it "should override" do
56
- sanitize_mail_delivery(true)
57
- # All the email gets dumped to file once for each type of recipient (:to, :cc, :bcc)
58
- # Each file is identical, so we only need to check one of them:
59
- email = File.read(Dir["#{@location}/*/plain.html"].first)
60
- email.should have_from("from@example.org")
61
- # Letter Opener won't let us test the to when the to has a 'user name'
62
- #email.should have_to("~to at example.org~ <to@sanitize_email.org>")
63
- # Letter Opener won't let us test the bcc
64
- #email.should have_cc("cc@sanitize_email.org")
65
- email.should have_subject("(to at example.org) original subject")
101
+ context "deprecated config options" do
102
+ context "local_environments" do
103
+ it "should use local_environment_proc for matching environment" do
104
+ sanitize_spec_dryer('test')
105
+ configure_sanitize_email({:local_environments => ['test']})
106
+ SanitizeEmail[:local_environment_proc].call.should == true
107
+ sanitized_mail_delivery(nil)
108
+ @email_file.should have_to("to@sanitize_email.org")
109
+ @email_file.should have_subject("(to at example.org) original subject")
110
+ end
111
+ it "should use local_environment_proc for non-matching environment" do
112
+ sanitize_spec_dryer('production')
113
+ configure_sanitize_email({:local_environments => ['development']}) # Won't match!
114
+ SanitizeEmail[:local_environment_proc].call.should == false
115
+ sanitized_mail_delivery(nil)
116
+ @mail_message.should_not have_subject("to at example.org")
117
+ end
118
+ end
119
+
120
+ context "sanitized_recipients is set" do
121
+ before(:each) do
122
+ sanitize_spec_dryer
123
+ configure_sanitize_email({:sanitized_recipients => 'barney@sanitize_email.org'})
124
+ end
125
+ it "used as sanitized_to" do
126
+ sanitized_mail_delivery(true)
127
+ @email_file.should have_from("from@example.org")
128
+ @mail_message.should have_to("to@sanitize_email.org")
129
+ @email_file.should have_subject("(to at example.org) original subject")
130
+ end
66
131
  end
67
132
  end
133
+
68
134
  end
135
+
136
+ #TODO: test good_list
data/spec/spec_helper.rb CHANGED
@@ -11,30 +11,61 @@ RSpec.configure do |config|
11
11
  #config.filter_run :focus
12
12
  end
13
13
 
14
+ module EmailMatcherHelpers
15
+ class UnexpectedMailType < StandardError; end
16
+
17
+ # Sweet, nourishing recursion
18
+ def email_matching(matcher, part, mail_or_part)
19
+ if mail_or_part.respond_to?(part)
20
+ email_matching(matcher, part, mail_or_part.send(part))
21
+ elsif mail_or_part.respond_to?(:join)
22
+ email_matching(matcher, part, mail_or_part.join(', '))
23
+ elsif mail_or_part.respond_to?(:=~) # Can we match a regex against it?
24
+ mail_or_part =~ Regexp.new(Regexp.escape(matcher))
25
+ else
26
+ raise UnexpectedMailType, "Cannot match #{matcher} for #{part}"
27
+ end
28
+ end
29
+
30
+ end
31
+
14
32
  RSpec::Matchers.define :have_from do |from|
33
+ include EmailMatcherHelpers
15
34
  match do |container|
16
- container =~ Regexp.new(Regexp.escape(from))
35
+ email_matching(from, :from, container)
17
36
  end
18
37
  end
19
38
  RSpec::Matchers.define :have_to do |to|
39
+ include EmailMatcherHelpers
20
40
  match do |container|
21
- container =~ Regexp.new(Regexp.escape(to))
41
+ email_matching(to, :to, container)
22
42
  end
23
43
  end
24
44
  RSpec::Matchers.define :have_cc do |cc|
45
+ include EmailMatcherHelpers
25
46
  match do |container|
26
- container =~ Regexp.new(Regexp.escape(cc))
47
+ email_matching(cc, :cc, container)
27
48
  end
28
49
  end
29
50
  # The ActionMailer :file delivery method never prints bcc recipients...
30
- # so not testable as such, but with letter_opener we can work magic
51
+ # Neither does LetterOpener :(
52
+ # But the mail object itself can be tested!
31
53
  RSpec::Matchers.define :have_bcc do |bcc|
54
+ include EmailMatcherHelpers
32
55
  match do |container|
33
- container =~ Regexp.new(Regexp.escape(bcc))
56
+ email_matching(bcc, :bcc, container)
34
57
  end
35
58
  end
36
59
  RSpec::Matchers.define :have_subject do |subject|
60
+ include EmailMatcherHelpers
37
61
  match do |container|
38
- container =~ Regexp.new(Regexp.escape(subject))
62
+ email_matching(subject, :subject, container)
39
63
  end
40
64
  end
65
+
66
+ #RSpec::Matchers.define :have_to_username do |to|
67
+ # include EmailMatcherHelpers
68
+ # match do |container|
69
+ # email_matching(to, :, container)
70
+ # end
71
+ #end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sanitize_email
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha2
4
+ version: 1.0.0.rc1
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-07-28 00:00:00.000000000 Z
14
+ date: 2012-08-04 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rails
@@ -194,16 +194,15 @@ files:
194
194
  - VERSION.yml
195
195
  - init.rb
196
196
  - lib/sanitize_email.rb
197
+ - lib/sanitize_email/bleach.rb
197
198
  - lib/sanitize_email/config.rb
199
+ - lib/sanitize_email/deprecation.rb
198
200
  - lib/sanitize_email/engine.rb
199
- - lib/sanitize_email/hook.rb
200
201
  - lib/sanitize_email/railtie.rb
201
- - lib/sanitize_email/sanitizer.rb
202
202
  - lib/sanitize_email/version.rb
203
203
  - sanitize_email.gemspec
204
204
  - spec/sanitize_email_spec.rb
205
205
  - spec/spec_helper.rb
206
- - spec/tmp/mail_dump/1343461037_3f3edd7/plain.html
207
206
  homepage: http://github.com/pboling/sanitize_email
208
207
  licenses:
209
208
  - MIT
@@ -219,7 +218,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
219
218
  version: '0'
220
219
  segments:
221
220
  - 0
222
- hash: -3597304773529474340
221
+ hash: -2863798588423167662
223
222
  required_rubygems_version: !ruby/object:Gem::Requirement
224
223
  none: false
225
224
  requirements:
@@ -1,30 +0,0 @@
1
- #Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
-
4
- module SanitizeEmail
5
- class Hook
6
-
7
- include SanitizeEmail::Sanitizer
8
-
9
- def self.delivering_email(message)
10
- if self.localish?
11
- message.subject = self.subject_override(message.subject, message.to)
12
- message.to = self.recipients_override(message.to)
13
- message.cc = self.cc_override(message.cc)
14
- message.bcc = self.bcc_override(message.bcc)
15
- end
16
- end
17
-
18
- def self.consider_local?
19
- SanitizeEmail.local_environments.include?(Rails.env) if defined?(Rails)
20
- end
21
-
22
- # This method will be called by the Hook to determine if an override should occur
23
- def self.localish?
24
- !SanitizeEmail.force_sanitize.nil? ? SanitizeEmail.force_sanitize : self.consider_local?
25
- end
26
-
27
- end # end Class Hook
28
- end # end Module SanitizeEmail
29
-
30
-
@@ -1,73 +0,0 @@
1
- #Copyright (c) 2008-12 Peter H. Boling of 9thBit LLC
2
- #Released under the MIT license
3
- module SanitizeEmail
4
- module Sanitizer
5
- def self.included(base)
6
- base.extend SanitizeEmail::Sanitizer::ClassMethods
7
- end
8
-
9
- module ClassMethods
10
-
11
- def subject_override(real_subject, actual_addresses)
12
- if actual_addresses.nil? || !SanitizeEmail.use_actual_email_prepended_to_subject
13
- real_subject
14
- else
15
- "(#{actual_addresses.join(',').gsub(/@/,' at ').gsub(/[<>]/,'~')}) #{real_subject}"
16
- end
17
- end
18
-
19
- def recipients_override(actual_addresses)
20
- override_email(:recipients, actual_addresses)
21
- end
22
-
23
- def cc_override(actual_addresses)
24
- override_email(:cc, actual_addresses)
25
- end
26
-
27
- def bcc_override(actual_addresses)
28
- override_email(:bcc, actual_addresses)
29
- end
30
-
31
- #######
32
- private
33
- #######
34
-
35
- def override_email(type, actual_addresses)
36
- real_addresses, sanitized_addresses = case type
37
- when :recipients
38
- [actual_addresses, SanitizeEmail.sanitized_recipients]
39
- when :cc
40
- [actual_addresses, SanitizeEmail.sanitized_cc]
41
- when :bcc
42
- [actual_addresses, SanitizeEmail.sanitized_bcc]
43
- else raise "sanitize_email error: unknown email override"
44
- end
45
- # Normalize to an array
46
- real_addresses = [real_addresses] unless real_addresses.is_a?(Array)
47
- # Normalize to an array
48
- sanitized_addresses = [sanitized_addresses] unless sanitized_addresses.is_a?(Array)
49
-
50
- # If there were no original recipients, then we DO NOT override the nil with the sanitized recipients
51
- return nil if real_addresses.blank?
52
- # If there are no sanitized addresses we can't override!
53
- return nil if sanitized_addresses.blank?
54
- # If we don't want to inject the actual email in the 'user name' section of the sanitized recipients,
55
- # then just return the default sanitized recipients
56
- return sanitized_addresses unless SanitizeEmail.use_actual_email_as_sanitized_user_name
57
-
58
- out = real_addresses.inject([]) do |result, real_recipient|
59
- if real_recipient.nil?
60
- new_recipient = sanitized_addresses
61
- else
62
- new_recipient = sanitized_addresses.map{|sanitized| "#{real_recipient.gsub(/@/,' at ').gsub(/[<>]/,'~')} <#{sanitized}>"}
63
- end
64
- result << new_recipient
65
- result
66
- end.flatten
67
- return out
68
- end
69
-
70
- end
71
-
72
- end
73
- end
@@ -1,76 +0,0 @@
1
- <style type="text/css">
2
- #message_headers {
3
- position: absolute;
4
- top: 0px;
5
- left: 0;
6
- width: 100%;
7
- height: 85px;
8
- padding: 10px 0 0 0;
9
- margin: 0;
10
- background: #fff;
11
- font-size: 12px;
12
- font-family: "Lucida Grande";
13
- border-bottom: 1px solid #dedede;
14
- overflow: hidden;
15
- }
16
-
17
- #message_headers dl {
18
- margin: 0;
19
- padding: 0;
20
- }
21
-
22
- #message_headers dt {
23
- width: 60px;
24
- padding: 1px;
25
- float: left;
26
- text-align: right;
27
- font-weight: bold;
28
- color: #7f7f7f;
29
- }
30
-
31
- #message_headers dd {
32
- margin-left: 70px;
33
- padding: 1px;
34
- }
35
-
36
- #message_headers p.alternate {
37
- position: absolute;
38
- top: 0;
39
- right: 15px;
40
- }
41
-
42
- #message_headers p.alternate a {
43
- color: #09c;
44
- }
45
-
46
- pre#message_body {
47
- padding: 10px;
48
- word-wrap: break-word;
49
- }
50
-
51
- body {
52
- margin-top: 96px;
53
- }
54
- </style>
55
-
56
- <div id="message_headers">
57
- <dl>
58
- <dt>From:</dt>
59
- <dd>from@example.org</dd>
60
-
61
- <dt>Subject:</dt>
62
- <dd><strong>(to at example.org) original subject</strong></dd>
63
-
64
- <dt>Date:</dt>
65
- <dd>Jul 28, 2012 03:37:17 AM EDT</dd>
66
-
67
- <dt>To:</dt>
68
- <dd>to@sanitize_email.org</dd>
69
- </dl>
70
-
71
-
72
- </div>
73
-
74
-
75
- <pre id="message_body"></pre>
76
-