mirror of
https://github.com/github/rails.git
synced 2026-01-13 16:48:06 -05:00
Compare commits
1 Commits
3-0-github
...
v2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8da518bbf |
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,21 +0,0 @@
|
||||
pkg
|
||||
.bundle
|
||||
debug.log
|
||||
doc/rdoc
|
||||
activemodel/doc
|
||||
activeresource/doc
|
||||
activerecord/doc
|
||||
activerecord/sqlnet.log
|
||||
actionpack/doc
|
||||
actionmailer/doc
|
||||
activesupport/doc
|
||||
activemodel/test/fixtures/fixture_database.sqlite3
|
||||
actionpack/test/tmp
|
||||
activesupport/test/fixtures/isolation_test
|
||||
dist
|
||||
railties/test/500.html
|
||||
railties/test/fixtures/tmp
|
||||
railties/test/initializer/root/log
|
||||
railties/doc
|
||||
railties/guides/output
|
||||
railties/tmp
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
||||
script: 'ci/travis.rb'
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
env:
|
||||
- "GEM=railties"
|
||||
- "GEM=ap,am,amo,ares,as"
|
||||
- "GEM=ar:mysql"
|
||||
- "GEM=ar:mysql2"
|
||||
- "GEM=ar:sqlite3"
|
||||
- "GEM=ar:postgresql"
|
||||
notifications:
|
||||
email: false
|
||||
irc:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
channels:
|
||||
- "irc.freenode.org#rails-contrib"
|
||||
campfire:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
rooms:
|
||||
- secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
|
||||
bundler_args: --path vendor/bundle
|
||||
53
Gemfile
53
Gemfile
@@ -1,53 +0,0 @@
|
||||
source 'http://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem 'mocha', '>= 0.13.0', :require => false
|
||||
|
||||
gem "pry"
|
||||
|
||||
group :doc do
|
||||
gem "rdoc", "~> 3.4"
|
||||
gem "horo", "= 1.0.3"
|
||||
gem "RedCloth", "~> 4.2" if RUBY_VERSION < "1.9.3"
|
||||
end
|
||||
|
||||
# for perf tests
|
||||
gem "faker"
|
||||
gem "rbench"
|
||||
gem "addressable"
|
||||
|
||||
# AS
|
||||
gem "memcache-client", ">= 1.8.5"
|
||||
|
||||
platforms :ruby do
|
||||
gem 'json'
|
||||
gem 'yajl-ruby'
|
||||
gem "nokogiri", ">= 1.4.4"
|
||||
|
||||
# AR
|
||||
gem "sqlite3", "~> 1.3.3"
|
||||
|
||||
group :db do
|
||||
gem "pg", ">= 0.9.0"
|
||||
gem "mysql", ">= 2.8.1"
|
||||
gem "mysql2", :git => "git://github.com/brianmario/mysql2.git", :branch => "0.2.x"
|
||||
end
|
||||
end
|
||||
|
||||
env :AREL do
|
||||
gem "arel", :path => ENV['AREL']
|
||||
end
|
||||
|
||||
# gems that are necessary for ActiveRecord tests with Oracle database
|
||||
if ENV['ORACLE_ENHANCED_PATH'] || ENV['ORACLE_ENHANCED']
|
||||
platforms :ruby do
|
||||
gem 'ruby-oci8', ">= 2.0.4"
|
||||
end
|
||||
if ENV['ORACLE_ENHANCED_PATH']
|
||||
gem 'activerecord-oracle_enhanced-adapter', :path => ENV['ORACLE_ENHANCED_PATH']
|
||||
else
|
||||
gem "activerecord-oracle_enhanced-adapter", :git => "git://github.com/rsim/oracle-enhanced.git"
|
||||
end
|
||||
end
|
||||
124
Gemfile.lock
124
Gemfile.lock
@@ -1,124 +0,0 @@
|
||||
GIT
|
||||
remote: git://github.com/brianmario/mysql2.git
|
||||
revision: 3c7548851f5bf124eb23307286ef95d61172ac4b
|
||||
branch: 0.2.x
|
||||
specs:
|
||||
mysql2 (0.2.22)
|
||||
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
actionmailer (3.0.20)
|
||||
actionpack (= 3.0.20)
|
||||
mail (~> 2.2)
|
||||
actionpack (3.0.20)
|
||||
activemodel (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
builder (~> 3.2.0)
|
||||
erubis (~> 2.7.0)
|
||||
i18n (~> 0.6.0)
|
||||
rack (~> 1.4.1)
|
||||
rack-mount (~> 0.6.14)
|
||||
rack-test (~> 0.6.1)
|
||||
tzinfo (~> 0.3.23)
|
||||
activemodel (3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
builder (~> 3.2.0)
|
||||
i18n (~> 0.6.0)
|
||||
activerecord (3.0.20)
|
||||
activemodel (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
arel (~> 2.0.10)
|
||||
tzinfo (~> 0.3.23)
|
||||
activeresource (3.0.20)
|
||||
activemodel (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
activesupport (3.0.20)
|
||||
rails (3.0.20)
|
||||
actionmailer (= 3.0.20)
|
||||
actionpack (= 3.0.20)
|
||||
activerecord (= 3.0.20)
|
||||
activeresource (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.0.20)
|
||||
railties (3.0.20)
|
||||
actionpack (= 3.0.20)
|
||||
activesupport (= 3.0.20)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (~> 0.18)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
addressable (2.3.6)
|
||||
arel (2.0.10)
|
||||
builder (3.2.2)
|
||||
coderay (1.1.0)
|
||||
erubis (2.7.0)
|
||||
faker (1.3.0)
|
||||
i18n (~> 0.5)
|
||||
horo (1.0.3)
|
||||
rdoc (>= 2.5)
|
||||
i18n (0.6.9)
|
||||
json (1.8.1)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memcache-client (1.8.5)
|
||||
metaclass (0.0.4)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.5.3)
|
||||
mocha (1.0.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mysql (2.9.1)
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
pg (0.17.1)
|
||||
polyglot (0.3.4)
|
||||
pry (0.9.12.6)
|
||||
coderay (~> 1.0)
|
||||
method_source (~> 0.8)
|
||||
slop (~> 3.4)
|
||||
rack (1.4.5)
|
||||
rack-mount (0.6.14)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
rake (10.2.2)
|
||||
rbench (0.2.3)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
slop (3.5.0)
|
||||
sqlite3 (1.3.9)
|
||||
thor (0.19.1)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.39)
|
||||
yajl-ruby (1.2.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
addressable
|
||||
arel
|
||||
faker
|
||||
horo (= 1.0.3)
|
||||
json
|
||||
memcache-client (>= 1.8.5)
|
||||
mocha (>= 0.13.0)
|
||||
mysql (>= 2.8.1)
|
||||
mysql2!
|
||||
nokogiri (>= 1.4.4)
|
||||
pg (>= 0.9.0)
|
||||
pry
|
||||
rails!
|
||||
rake (>= 0.8.7)
|
||||
rbench
|
||||
rdoc (~> 3.4)
|
||||
sqlite3 (~> 1.3.3)
|
||||
yajl-ruby
|
||||
@@ -1 +0,0 @@
|
||||
3.0.20
|
||||
68
README.rdoc
68
README.rdoc
@@ -1,68 +0,0 @@
|
||||
== Welcome to \Rails
|
||||
|
||||
\Rails is a web-application framework that includes everything needed to create
|
||||
database-backed web applications according to the Model-View-Control pattern.
|
||||
|
||||
This pattern splits the view (also called the presentation) into "dumb"
|
||||
templates that are primarily responsible for inserting pre-built data in between
|
||||
HTML tags. The model contains the "smart" domain objects (such as Account,
|
||||
Product, Person, Post) that holds all the business logic and knows how to
|
||||
persist themselves to a database. The controller handles the incoming requests
|
||||
(such as Save New Account, Update Product, Show Post) by manipulating the model
|
||||
and directing data to the view.
|
||||
|
||||
In \Rails, the model is handled by what's called an object-relational mapping
|
||||
layer entitled Active Record. This layer allows you to present the data from
|
||||
database rows as objects and embellish these data objects with business logic
|
||||
methods. You can read more about Active Record in its
|
||||
{README}[link:files/activerecord/README_rdoc.html].
|
||||
|
||||
The controller and view are handled by the Action Pack, which handles both
|
||||
layers by its two parts: Action View and Action Controller. These two layers
|
||||
are bundled in a single package due to their heavy interdependence. This is
|
||||
unlike the relationship between the Active Record and Action Pack that is much
|
||||
more separate. Each of these packages can be used independently outside of
|
||||
\Rails. You can read more about Action Pack in its
|
||||
{README}[link:files/actionpack/README_rdoc.html].
|
||||
|
||||
|
||||
== Getting Started
|
||||
|
||||
1. Install \Rails at the command prompt if you haven't yet:
|
||||
|
||||
gem install rails
|
||||
|
||||
2. At the command prompt, create a new \Rails application:
|
||||
|
||||
rails new myapp
|
||||
|
||||
where "myapp" is the application name.
|
||||
|
||||
3. Change directory to +myapp+ and start the web server:
|
||||
|
||||
cd myapp; rails server
|
||||
|
||||
Run with <tt>--help</tt> for options.
|
||||
|
||||
4. Go to http://localhost:3000/ and you'll see:
|
||||
|
||||
"Welcome aboard: You're riding Ruby on Rails!"
|
||||
|
||||
5. Follow the guidelines to start developing your application. You can find the following resources handy:
|
||||
|
||||
* The README file created within your application.
|
||||
* The {Getting Started with Rails}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {Ruby on Rails Tutorial}[http://railstutorial.org/book].
|
||||
* The {Ruby on Rails guides}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {API documentation}[http://api.rubyonrails.org].
|
||||
|
||||
|
||||
== Contributing
|
||||
|
||||
We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to Rails
|
||||
guide}[http://edgeguides.rubyonrails.org/contributing_to_rails.html] for guidelines about how
|
||||
to proceed. {Join us}[http://contributors.rubyonrails.org]!
|
||||
|
||||
== License
|
||||
|
||||
Ruby on \Rails is released under the MIT license.
|
||||
154
Rakefile
154
Rakefile
@@ -1,161 +1,21 @@
|
||||
gem 'rdoc', '>= 2.5.10'
|
||||
require 'rdoc'
|
||||
|
||||
require 'rake'
|
||||
require 'rdoc/task'
|
||||
|
||||
$:.unshift File.expand_path('..', __FILE__)
|
||||
require "tasks/release"
|
||||
env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
|
||||
|
||||
desc "Build gem files for all projects"
|
||||
task :build => "all:build"
|
||||
PROJECTS = %w(activesupport actionpack actionmailer activeresource activerecord railties)
|
||||
|
||||
desc "Release all gems to gemcutter and create a tag"
|
||||
task :release => "all:release"
|
||||
|
||||
# RDoc skips some files in the Rails tree due to its binary? predicate. This is a quick
|
||||
# hack for edge docs, until we decide which is the correct way to address this issue.
|
||||
# If not fixed in RDoc itself, via an option or something, we should probably move this
|
||||
# to railties and use it also in doc:rails.
|
||||
def hijack_rdoc!
|
||||
require "rdoc/parser"
|
||||
class << RDoc::Parser
|
||||
def binary?(file)
|
||||
s = File.read(file, 1024) or return false
|
||||
|
||||
if s[0, 2] == Marshal.dump('')[0, 2] then
|
||||
true
|
||||
elsif file =~ /erb\.rb$/ then
|
||||
false
|
||||
elsif s.index("\x00") then # ORIGINAL is s.scan(/<%|%>/).length >= 4 || s.index("\x00")
|
||||
true
|
||||
elsif 0.respond_to? :fdiv then
|
||||
s.count("^ -~\t\r\n").fdiv(s.size) > 0.3
|
||||
else # HACK 1.8.6
|
||||
(s.count("^ -~\t\r\n").to_f / s.size) > 0.3
|
||||
end
|
||||
end
|
||||
end
|
||||
Dir["#{File.dirname(__FILE__)}/*/lib/*/version.rb"].each do |version_path|
|
||||
require version_path
|
||||
end
|
||||
|
||||
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
|
||||
|
||||
desc 'Run all tests by default'
|
||||
task :default => %w(test test:isolated)
|
||||
task :default => :test
|
||||
|
||||
%w(test test:isolated package gem).each do |task_name|
|
||||
%w(test rdoc package pgem release).each do |task_name|
|
||||
desc "Run #{task_name} task for all projects"
|
||||
task task_name do
|
||||
errors = []
|
||||
PROJECTS.each do |project|
|
||||
system(%(cd #{project} && #{$0} #{task_name})) || errors << project
|
||||
end
|
||||
fail("Errors in #{errors.join(', ')}") unless errors.empty?
|
||||
end
|
||||
end
|
||||
|
||||
desc "Smoke-test all projects"
|
||||
task :smoke do
|
||||
(PROJECTS - %w(activerecord)).each do |project|
|
||||
system %(cd #{project} && #{$0} test:isolated)
|
||||
end
|
||||
system %(cd activerecord && #{$0} sqlite3:isolated_test)
|
||||
end
|
||||
|
||||
desc "Install gems for all projects."
|
||||
task :install => :gem do
|
||||
version = File.read("RAILS_VERSION").strip
|
||||
(PROJECTS - ["railties"]).each do |project|
|
||||
puts "INSTALLING #{project}"
|
||||
system("gem install #{project}/pkg/#{project}-#{version}.gem --no-ri --no-rdoc")
|
||||
end
|
||||
system("gem install railties/pkg/railties-#{version}.gem --no-ri --no-rdoc")
|
||||
system("gem install pkg/rails-#{version}.gem --no-ri --no-rdoc")
|
||||
end
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
RDoc::Task.new do |rdoc|
|
||||
hijack_rdoc!
|
||||
|
||||
rdoc.rdoc_dir = 'doc/rdoc'
|
||||
rdoc.title = "Ruby on Rails Documentation"
|
||||
|
||||
rdoc.options << '-f' << 'horo'
|
||||
rdoc.options << '-c' << 'utf-8'
|
||||
rdoc.options << '-m' << 'README.rdoc'
|
||||
|
||||
rdoc.rdoc_files.include('README.rdoc')
|
||||
|
||||
rdoc.rdoc_files.include('railties/CHANGELOG')
|
||||
rdoc.rdoc_files.include('railties/MIT-LICENSE')
|
||||
rdoc.rdoc_files.include('railties/README.rdoc')
|
||||
rdoc.rdoc_files.include('railties/lib/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/*')
|
||||
|
||||
rdoc.rdoc_files.include('activerecord/README.rdoc')
|
||||
rdoc.rdoc_files.include('activerecord/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activeresource/README.rdoc')
|
||||
rdoc.rdoc_files.include('activeresource/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activeresource/lib/active_resource.rb')
|
||||
rdoc.rdoc_files.include('activeresource/lib/active_resource/*')
|
||||
|
||||
rdoc.rdoc_files.include('actionpack/README.rdoc')
|
||||
rdoc.rdoc_files.include('actionpack/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionpack/lib/abstract_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('actionmailer/README.rdoc')
|
||||
rdoc.rdoc_files.include('actionmailer/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
|
||||
rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activesupport/README.rdoc')
|
||||
rdoc.rdoc_files.include('activesupport/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activemodel/README.rdoc')
|
||||
rdoc.rdoc_files.include('activemodel/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activemodel/lib/active_model/**/*.rb')
|
||||
end
|
||||
|
||||
# Enhance rdoc task to copy referenced images also
|
||||
task :rdoc do
|
||||
FileUtils.mkdir_p "doc/rdoc/files/examples/"
|
||||
FileUtils.copy "activerecord/examples/associations.png", "doc/rdoc/files/examples/associations.png"
|
||||
end
|
||||
|
||||
desc 'Bump all versions to match version.rb'
|
||||
task :update_versions do
|
||||
require File.dirname(__FILE__) + "/version"
|
||||
|
||||
File.open("RAILS_VERSION", "w") do |f|
|
||||
f.write Rails::VERSION::STRING + "\n"
|
||||
end
|
||||
|
||||
constants = {
|
||||
"activesupport" => "ActiveSupport",
|
||||
"activemodel" => "ActiveModel",
|
||||
"actionpack" => "ActionPack",
|
||||
"actionmailer" => "ActionMailer",
|
||||
"activeresource" => "ActiveResource",
|
||||
"activerecord" => "ActiveRecord",
|
||||
"railties" => "Rails"
|
||||
}
|
||||
|
||||
version_file = File.read("version.rb")
|
||||
|
||||
PROJECTS.each do |project|
|
||||
Dir["#{project}/lib/*/version.rb"].each do |file|
|
||||
File.open(file, "w") do |f|
|
||||
f.write version_file.gsub(/Rails/, constants[project])
|
||||
end
|
||||
system %(cd #{project} && #{env} rake #{task_name})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,163 +1,3 @@
|
||||
## Rails 3.0.20 (unreleased)
|
||||
|
||||
## Rails 3.0.19 (Jan 8, 2013)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.18 (Jan 2, 2013)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.17 (Aug 9, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.16 (Jul 26, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.0.14 (Jun 12, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
* Rails 3.0.13 (May 31, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
*Rails 3.0.10 (August 16, 2011)*
|
||||
|
||||
*No changes.
|
||||
|
||||
|
||||
*Rails 3.0.9 (June 16, 2011)*
|
||||
|
||||
*No changes.
|
||||
|
||||
|
||||
*Rails 3.0.8 (June 7, 2011)*
|
||||
|
||||
* Mail dependency increased to 2.2.19
|
||||
|
||||
|
||||
*Rails 3.0.7 (April 18, 2011)*
|
||||
|
||||
* remove AM delegating register_observer and register_interceptor to Mail [Josh Kalderimis]
|
||||
|
||||
|
||||
*Rails 3.0.6 (April 5, 2011)
|
||||
|
||||
* Don't allow i18n to change the minor version, version now set to ~> 0.5.0 [Santiago Pastorino]
|
||||
|
||||
|
||||
*Rails 3.0.5 (February 26, 2011)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.4 (February 8, 2011)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.3 (November 16, 2010)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.2 (November 15, 2010)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.1 (October 15, 2010)*
|
||||
|
||||
* No Changes.
|
||||
|
||||
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
* subject is automatically looked up on I18n using mailer_name and action_name as scope as in t(".subject") [JK]
|
||||
|
||||
* Changed encoding behaviour of mail, so updated tests in actionmailer and bumped mail version to 2.2.1 [ML]
|
||||
|
||||
* Added ability to pass Proc objects to the defaults hash [ML]
|
||||
|
||||
* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead [ML]
|
||||
|
||||
* Lot of updates to various test cases that now work better with the new Mail and so have different expectations
|
||||
|
||||
* Added interceptors and observers from Mail [ML]
|
||||
|
||||
ActionMailer::Base.register_interceptor calls Mail.register_interceptor
|
||||
ActionMailer::Base.register_observer calls Mail.register_observer
|
||||
|
||||
* Mail::Part now no longer has nil as a default charset, it is always set to something, and defaults to UTF-8
|
||||
|
||||
* Added explict setting of charset in set_fields! method to make sure Mail has the user defined default
|
||||
|
||||
* Removed quoting.rb and refactored for Mail to take responsibility of all quoting and auto encoding requirements for the header.
|
||||
|
||||
* Fixed several tests which had incorrect encoding.
|
||||
|
||||
* Changed all utf-8 to UTF-8 for consistency
|
||||
|
||||
* Whole new API added with tests. See base.rb for full details. Old API is deprecated.
|
||||
|
||||
* The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted
|
||||
|
||||
* Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'}
|
||||
|
||||
* ActionMailer::Base :default_implicit_parts_order now is in the sequence of the order you want, no reversing of ordering takes place. The default order now is text/plain, then text/enriched, then text/html and then any other part that is not one of these three.
|
||||
|
||||
* Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc
|
||||
|
||||
* Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want.
|
||||
* Mail::Message#set_content_type does not exist, it is simply Mail::Message#content_type
|
||||
|
||||
* Every mail message gets a unique message_id unless you specify one, had to change all the tests that check for equality with expected.encoded == actual.encoded to first replace their message_ids with control values
|
||||
|
||||
* Mail now has a proper concept of parts, remove the ActionMailer::Part and ActionMailer::PartContainer classes
|
||||
|
||||
* Calling #encoded on any object returns it as a string ready to go into the output stream of an email, this means it includes the \r\n at the end of the lines and the object is pre-wrapped with \r\n\t if it is a header field. Also, the "encoded" value includes the field name if it is a header field.
|
||||
|
||||
* Attachments are only the actual attachment, with filename etc. A part contains an attachment. The part has the content_type etc. So attachments.last.content_type is invalid. But parts.last.content_type
|
||||
|
||||
* There is no idea of a "sub_head" in Mail. A part is just a Message with some extra functionality, so it just has a "header" like a normal mail message
|
||||
|
||||
|
||||
*2.3.2 [Final] (March 15, 2009)*
|
||||
|
||||
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
|
||||
|
||||
* Fixed RFC-2045 quoted-printable bug #1421 [squadette]
|
||||
|
||||
* Fixed that no body charset would be set when there are attachments present #740 [Paweł Kondzior]
|
||||
|
||||
|
||||
*2.2.1 [RC2] (November 14th, 2008)*
|
||||
|
||||
* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 [Grant Hollingworth]
|
||||
|
||||
|
||||
*2.2.0 [RC1] (October 24th, 2008)*
|
||||
|
||||
* Add layout functionality to mailers [Pratik Naik]
|
||||
|
||||
Mailer layouts behaves just like controller layouts, except layout names need to
|
||||
have '_mailer' postfix for them to be automatically picked up.
|
||||
|
||||
|
||||
*2.1.0 (May 31st, 2008)*
|
||||
|
||||
* Fixed that a return-path header would be ignored #7572 [joost]
|
||||
|
||||
* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
|
||||
|
||||
* Updated TMail to version 1.2.1 [Mikel Lindsaar]
|
||||
|
||||
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
|
||||
|
||||
|
||||
*2.0.2* (December 16th, 2007)
|
||||
|
||||
* Included in Rails 2.0.2
|
||||
@@ -165,7 +5,7 @@
|
||||
|
||||
*2.0.1* (December 7th, 2007)
|
||||
|
||||
* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [Rick Olson]
|
||||
* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [rick]
|
||||
|
||||
* Pass the template_root as an array as ActionView's view_path
|
||||
* Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
|
||||
@@ -174,11 +14,11 @@
|
||||
|
||||
* Update README to use new smtp settings configuration API. Closes #10060 [psq]
|
||||
|
||||
* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [Zach Dennis]
|
||||
* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [zdennis]
|
||||
|
||||
* Update TMail to v1.1.0. Use an updated version of TMail if available. [Mikel Lindsaar]
|
||||
* Update TMail to v1.1.0. Use an updated version of TMail if available. [mikel]
|
||||
|
||||
* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Michael Koziarski]
|
||||
* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Koz]
|
||||
|
||||
* Fix silent failure of rxml templates. #9879 [jstewart]
|
||||
|
||||
@@ -213,7 +53,7 @@
|
||||
|
||||
*1.3.2* (February 5th, 2007)
|
||||
|
||||
* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Michael Koziarski]
|
||||
* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Koz]
|
||||
|
||||
|
||||
*1.3.1* (January 16th, 2007)
|
||||
@@ -233,7 +73,7 @@
|
||||
|
||||
* Tighten rescue clauses. #5985 [james@grayproductions.net]
|
||||
|
||||
* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [David Heinemeier Hansson]
|
||||
* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [DHH]
|
||||
|
||||
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
|
||||
|
||||
@@ -243,7 +83,7 @@
|
||||
|
||||
* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
|
||||
|
||||
* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
|
||||
* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
|
||||
|
||||
* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
|
||||
|
||||
@@ -389,7 +229,7 @@
|
||||
|
||||
* Added that deliver_* will now return the email that was sent
|
||||
|
||||
* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck]
|
||||
* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck]
|
||||
|
||||
* Fixed quoting for all address headers, not just to #955 [Jamis Buck]
|
||||
|
||||
@@ -428,7 +268,7 @@
|
||||
@body = "Nothing to see here."
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
def unencoded_subject(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "testing unencoded subject"
|
||||
@@ -437,7 +277,7 @@
|
||||
@encode_subject = false
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
|
||||
*0.6.1* (January 18th, 2005)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
Copyright (c) 2004-2007 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
145
actionmailer/README
Executable file
145
actionmailer/README
Executable file
@@ -0,0 +1,145 @@
|
||||
= Action Mailer -- Easy email delivery and testing
|
||||
|
||||
Action Mailer is a framework for designing email-service layers. These layers
|
||||
are used to consolidate code for sending out forgotten passwords, welcome
|
||||
wishes on signup, invoices for billing, and any other use case that requires
|
||||
a written notification to either a person or another system.
|
||||
|
||||
Additionally, an Action Mailer class can be used to process incoming email,
|
||||
such as allowing a weblog to accept new posts from an email (which could even
|
||||
have been sent from a phone).
|
||||
|
||||
== Sending emails
|
||||
|
||||
The framework works by setting up all the email details, except the body,
|
||||
in methods on the service layer. Subject, recipients, sender, and timestamp
|
||||
are all set up this way. An example of such a method:
|
||||
|
||||
def signed_up(recipient)
|
||||
recipients recipient
|
||||
subject "[Signed up] Welcome #{recipient}"
|
||||
from "system@loudthinking.com"
|
||||
|
||||
body(:recipient => recipient)
|
||||
end
|
||||
|
||||
The body of the email is created by using an Action View template (regular
|
||||
ERb) that has the content of the body hash parameter available as instance variables.
|
||||
So the corresponding body template for the method above could look like this:
|
||||
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>
|
||||
|
||||
And if the recipient was given as "david@loudthinking.com", the email
|
||||
generated would look like this:
|
||||
|
||||
Date: Sun, 12 Dec 2004 00:00:00 +0100
|
||||
From: system@loudthinking.com
|
||||
To: david@loudthinking.com
|
||||
Subject: [Signed up] Welcome david@loudthinking.com
|
||||
|
||||
Hello there,
|
||||
|
||||
Mr. david@loudthinking.com
|
||||
|
||||
You never actually call the instance methods like signed_up directly. Instead,
|
||||
you call class methods like deliver_* and create_* that are automatically
|
||||
created for each instance method. So if the signed_up method sat on
|
||||
ApplicationMailer, it would look like this:
|
||||
|
||||
ApplicationMailer.create_signed_up("david@loudthinking.com") # => tmail object for testing
|
||||
ApplicationMailer.deliver_signed_up("david@loudthinking.com") # sends the email
|
||||
ApplicationMailer.new.signed_up("david@loudthinking.com") # won't work!
|
||||
|
||||
== Receiving emails
|
||||
|
||||
To receive emails, you need to implement a public instance method called receive that takes a
|
||||
tmail object as its single parameter. The Action Mailer framework has a corresponding class method,
|
||||
which is also called receive, that accepts a raw, unprocessed email as a string, which it then turns
|
||||
into the tmail object and calls the receive instance method.
|
||||
|
||||
Example:
|
||||
|
||||
class Mailman < ActionMailer::Base
|
||||
def receive(email)
|
||||
page = Page.find_by_address(email.to.first)
|
||||
page.emails.create(
|
||||
:subject => email.subject, :body => email.body
|
||||
)
|
||||
|
||||
if email.has_attachments?
|
||||
for attachment in email.attachments
|
||||
page.attachments.create({
|
||||
:file => attachment, :description => email.subject
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
This Mailman can be the target for Postfix. In Rails, you would use the runner like this:
|
||||
|
||||
./script/runner 'Mailman.receive(STDIN.read)'
|
||||
|
||||
== Configuration
|
||||
|
||||
The Base class has the full list of configuration options. Here's an example:
|
||||
|
||||
ActionMailer::Base.smtp_settings = {
|
||||
:address=>'smtp.yourserver.com', # default: localhost
|
||||
:port=>'25', # default: 25
|
||||
:user_name=>'user',
|
||||
:password=>'pass',
|
||||
:authentication=>:plain # :plain, :login or :cram_md5
|
||||
}
|
||||
|
||||
== Dependencies
|
||||
|
||||
Action Mailer requires that the Action Pack is either available to be required immediately
|
||||
or is accessible as a GEM.
|
||||
|
||||
|
||||
== Bundled software
|
||||
|
||||
* tmail 0.10.8 by Minero Aoki released under LGPL
|
||||
Read more on http://i.loveruby.net/en/prog/tmail.html
|
||||
|
||||
* Text::Format 0.63 by Austin Ziegler released under OpenSource
|
||||
Read more on http://www.halostatue.ca/ruby/Text__Format.html
|
||||
|
||||
|
||||
== Download
|
||||
|
||||
The latest version of Action Mailer can be found at
|
||||
|
||||
* http://rubyforge.org/project/showfiles.php?group_id=361
|
||||
|
||||
Documentation can be found at
|
||||
|
||||
* http://actionmailer.rubyonrails.org
|
||||
|
||||
|
||||
== Installation
|
||||
|
||||
You can install Action Mailer with the following command.
|
||||
|
||||
% [sudo] ruby install.rb
|
||||
|
||||
from its distribution directory.
|
||||
|
||||
|
||||
== License
|
||||
|
||||
Action Mailer is released under the MIT license.
|
||||
|
||||
|
||||
== Support
|
||||
|
||||
The Action Mailer homepage is http://www.rubyonrails.org. You can find
|
||||
the Action Mailer RubyForge page at http://rubyforge.org/projects/actionmailer.
|
||||
And as Jim from Rake says:
|
||||
|
||||
Feel free to submit commits or feature requests. If you send a patch,
|
||||
remember to update the corresponding unit tests. If fact, I prefer
|
||||
new feature to be submitted in the form of new unit tests.
|
||||
@@ -1,147 +0,0 @@
|
||||
= Action Mailer -- Easy email delivery and testing
|
||||
|
||||
Action Mailer is a framework for designing email-service layers. These layers
|
||||
are used to consolidate code for sending out forgotten passwords, welcome
|
||||
wishes on signup, invoices for billing, and any other use case that requires
|
||||
a written notification to either a person or another system.
|
||||
|
||||
Action Mailer is in essence a wrapper around Action Controller and the
|
||||
Mail gem. It provides a way to make emails using templates in the same
|
||||
way that Action Controller renders views using templates.
|
||||
|
||||
Additionally, an Action Mailer class can be used to process incoming email,
|
||||
such as allowing a weblog to accept new posts from an email (which could even
|
||||
have been sent from a phone).
|
||||
|
||||
== Sending emails
|
||||
|
||||
The framework works by initializing any instance variables you want to be
|
||||
available in the email template, followed by a call to +mail+ to deliver
|
||||
the email.
|
||||
|
||||
This can be as simple as:
|
||||
|
||||
class Notifier < ActionMailer::Base
|
||||
delivers_from 'system@loudthinking.com'
|
||||
|
||||
def welcome(recipient)
|
||||
@recipient = recipient
|
||||
mail(:to => recipient,
|
||||
:subject => "[Signed up] Welcome #{recipient}")
|
||||
end
|
||||
end
|
||||
|
||||
The body of the email is created by using an Action View template (regular
|
||||
ERb) that has the instance variables that are declared in the mailer action.
|
||||
|
||||
So the corresponding body template for the method above could look like this:
|
||||
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>
|
||||
|
||||
Thank you for signing up!
|
||||
|
||||
And if the recipient was given as "david@loudthinking.com", the email
|
||||
generated would look like this:
|
||||
|
||||
Date: Mon, 25 Jan 2010 22:48:09 +1100
|
||||
From: system@loudthinking.com
|
||||
To: david@loudthinking.com
|
||||
Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail>
|
||||
Subject: [Signed up] Welcome david@loudthinking.com
|
||||
Mime-Version: 1.0
|
||||
Content-Type: text/plain;
|
||||
charset="US-ASCII";
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
Hello there,
|
||||
|
||||
Mr. david@loudthinking.com
|
||||
|
||||
In previous version of rails you would call <tt>create_method_name</tt> and
|
||||
<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface, you
|
||||
simply call the method and optionally call +deliver+ on the return value.
|
||||
|
||||
Calling the method returns a Mail Message object:
|
||||
|
||||
message = Notifier.welcome # => Returns a Mail::Message object
|
||||
message.deliver # => delivers the email
|
||||
|
||||
Or you can just chain the methods together like:
|
||||
|
||||
Notifier.welcome.deliver # Creates the email and sends it immediately
|
||||
|
||||
== Receiving emails
|
||||
|
||||
To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes a
|
||||
tmail object as its single parameter. The Action Mailer framework has a corresponding class method,
|
||||
which is also called <tt>receive</tt>, that accepts a raw, unprocessed email as a string, which it then turns
|
||||
into the tmail object and calls the receive instance method.
|
||||
|
||||
Example:
|
||||
|
||||
class Mailman < ActionMailer::Base
|
||||
def receive(email)
|
||||
page = Page.find_by_address(email.to.first)
|
||||
page.emails.create(
|
||||
:subject => email.subject, :body => email.body
|
||||
)
|
||||
|
||||
if email.has_attachments?
|
||||
for attachment in email.attachments
|
||||
page.attachments.create({
|
||||
:file => attachment, :description => email.subject
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
This Mailman can be the target for Postfix or other MTAs. In Rails, you would use the runner in the
|
||||
trivial case like this:
|
||||
|
||||
rails runner 'Mailman.receive(STDIN.read)'
|
||||
|
||||
However, invoking Rails in the runner for each mail to be received is very resource intensive. A single
|
||||
instance of Rails should be run within a daemon if it is going to be utilized to process more than just
|
||||
a limited number of email.
|
||||
|
||||
== Configuration
|
||||
|
||||
The Base class has the full list of configuration options. Here's an example:
|
||||
|
||||
ActionMailer::Base.smtp_settings = {
|
||||
:address => 'smtp.yourserver.com', # default: localhost
|
||||
:port => '25', # default: 25
|
||||
:user_name => 'user',
|
||||
:password => 'pass',
|
||||
:authentication => :plain # :plain, :login or :cram_md5
|
||||
}
|
||||
|
||||
|
||||
== Download and installation
|
||||
|
||||
The latest version of Action Mailer can be installed with Rubygems:
|
||||
|
||||
% [sudo] gem install actionmailer
|
||||
|
||||
Source code can be downloaded as part of the Rails project on GitHub
|
||||
|
||||
* http://github.com/rails/rails/tree/master/actionmailer/
|
||||
|
||||
|
||||
== License
|
||||
|
||||
Action Mailer is released under the MIT license.
|
||||
|
||||
|
||||
== Support
|
||||
|
||||
API documentation is at
|
||||
|
||||
* http://api.rubyonrails.com
|
||||
|
||||
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
||||
|
||||
* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets
|
||||
98
actionmailer/Rakefile
Normal file → Executable file
98
actionmailer/Rakefile
Normal file → Executable file
@@ -1,7 +1,21 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'rake/packagetask'
|
||||
require 'rubygems/package_task'
|
||||
require 'rake/gempackagetask'
|
||||
require 'rake/contrib/rubyforgepublisher'
|
||||
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
|
||||
|
||||
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
||||
PKG_NAME = 'actionmailer'
|
||||
PKG_VERSION = ActionMailer::VERSION::STRING + PKG_BUILD
|
||||
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
||||
|
||||
RELEASE_NAME = "REL #{PKG_VERSION}"
|
||||
|
||||
RUBY_FORGE_PROJECT = "actionmailer"
|
||||
RUBY_FORGE_USER = "webster132"
|
||||
|
||||
desc "Default Task"
|
||||
task :default => [ :test ]
|
||||
@@ -9,28 +23,74 @@ task :default => [ :test ]
|
||||
# Run the unit tests
|
||||
Rake::TestTask.new { |t|
|
||||
t.libs << "test"
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.warning = true
|
||||
t.pattern = 'test/*_test.rb'
|
||||
t.verbose = true
|
||||
t.warning = false
|
||||
}
|
||||
|
||||
namespace :test do
|
||||
task :isolated do
|
||||
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
|
||||
Dir.glob("test/**/*_test.rb").all? do |file|
|
||||
sh(ruby, '-Ilib:test', file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
|
||||
# Generate the RDoc documentation
|
||||
Rake::RDocTask.new { |rdoc|
|
||||
rdoc.rdoc_dir = 'doc'
|
||||
rdoc.title = "Action Mailer -- Easy email delivery and testing"
|
||||
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
||||
rdoc.options << '--charset' << 'utf-8'
|
||||
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
||||
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
||||
rdoc.rdoc_files.include('lib/action_mailer.rb')
|
||||
rdoc.rdoc_files.include('lib/action_mailer/*.rb')
|
||||
}
|
||||
|
||||
|
||||
# Create compressed packages
|
||||
spec = Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.name = PKG_NAME
|
||||
s.summary = "Service layer for easy email delivery and testing."
|
||||
s.description = %q{Makes it trivial to test and deliver emails sent from a single service layer.}
|
||||
s.version = PKG_VERSION
|
||||
|
||||
s.author = "David Heinemeier Hansson"
|
||||
s.email = "david@loudthinking.com"
|
||||
s.rubyforge_project = "actionmailer"
|
||||
s.homepage = "http://www.rubyonrails.org"
|
||||
|
||||
s.add_dependency('actionpack', '= 2.0.2' + PKG_BUILD)
|
||||
|
||||
s.has_rdoc = true
|
||||
s.requirements << 'none'
|
||||
s.require_path = 'lib'
|
||||
s.autorequire = 'action_mailer'
|
||||
|
||||
s.files = [ "Rakefile", "install.rb", "README", "CHANGELOG", "MIT-LICENSE" ]
|
||||
s.files = s.files + Dir.glob( "lib/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
s.files = s.files + Dir.glob( "test/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
||||
end
|
||||
|
||||
spec = eval(File.read('actionmailer.gemspec'))
|
||||
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
p.need_tar = true
|
||||
p.need_zip = true
|
||||
end
|
||||
|
||||
desc "Release to gemcutter"
|
||||
task :release => :package do
|
||||
require 'rake/gemcutter'
|
||||
Rake::Gemcutter::Tasks.new(spec).define
|
||||
Rake::Task['gem:push'].invoke
|
||||
|
||||
desc "Publish the API documentation"
|
||||
task :pgem => [:package] do
|
||||
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
||||
end
|
||||
|
||||
desc "Publish the API documentation"
|
||||
task :pdoc => [:rdoc] do
|
||||
Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/am", "doc").upload
|
||||
end
|
||||
|
||||
desc "Publish the release files to RubyForge."
|
||||
task :release => [ :package ] do
|
||||
require 'rubyforge'
|
||||
|
||||
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
||||
|
||||
rubyforge = RubyForge.new
|
||||
rubyforge.login
|
||||
rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
|
||||
end
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
version = File.read(File.expand_path("../../RAILS_VERSION", __FILE__)).strip
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.name = 'actionmailer'
|
||||
s.version = version
|
||||
s.summary = 'Email composition, delivery, and receiving framework (part of Rails).'
|
||||
s.description = 'Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.'
|
||||
s.required_ruby_version = '>= 1.8.7'
|
||||
|
||||
s.author = 'David Heinemeier Hansson'
|
||||
s.email = 'david@loudthinking.com'
|
||||
s.homepage = 'http://www.rubyonrails.org'
|
||||
s.rubyforge_project = 'actionmailer'
|
||||
|
||||
s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
|
||||
s.require_path = 'lib'
|
||||
s.requirements << 'none'
|
||||
|
||||
s.add_dependency('actionpack', version)
|
||||
s.add_dependency('mail', '~> 2.2')
|
||||
end
|
||||
30
actionmailer/install.rb
Normal file
30
actionmailer/install.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
require 'rbconfig'
|
||||
require 'find'
|
||||
require 'ftools'
|
||||
|
||||
include Config
|
||||
|
||||
# this was adapted from rdoc's install.rb by way of Log4r
|
||||
|
||||
$sitedir = CONFIG["sitelibdir"]
|
||||
unless $sitedir
|
||||
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
|
||||
$libdir = File.join(CONFIG["libdir"], "ruby", version)
|
||||
$sitedir = $:.find {|x| x =~ /site_ruby/ }
|
||||
if !$sitedir
|
||||
$sitedir = File.join($libdir, "site_ruby")
|
||||
elsif $sitedir !~ Regexp.quote(version)
|
||||
$sitedir = File.join($sitedir, version)
|
||||
end
|
||||
end
|
||||
|
||||
# the actual gruntwork
|
||||
Dir.chdir("lib")
|
||||
|
||||
Find.find("action_mailer", "action_mailer.rb") { |f|
|
||||
if f[-3..-1] == ".rb"
|
||||
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
|
||||
else
|
||||
File::makedirs(File.join($sitedir, *f.split(/\//)))
|
||||
end
|
||||
}
|
||||
58
actionmailer/lib/action_mailer.rb
Normal file → Executable file
58
actionmailer/lib/action_mailer.rb
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
#--
|
||||
# Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
# Copyright (c) 2004-2007 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
@@ -21,32 +21,32 @@
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#++
|
||||
|
||||
actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__)
|
||||
$:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path)
|
||||
|
||||
require 'abstract_controller'
|
||||
require 'action_view'
|
||||
require 'action_mailer/version'
|
||||
|
||||
# Common Active Support usage in Action Mailer
|
||||
require 'active_support/core_ext/class'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/array/uniq_by'
|
||||
require 'active_support/core_ext/module/attr_internal'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/lazy_load_hooks'
|
||||
|
||||
module ActionMailer
|
||||
extend ::ActiveSupport::Autoload
|
||||
|
||||
autoload :AdvAttrAccessor
|
||||
autoload :Collector
|
||||
autoload :Base
|
||||
autoload :DeliveryMethods
|
||||
autoload :DeprecatedApi
|
||||
autoload :MailHelper
|
||||
autoload :OldApi
|
||||
autoload :TestCase
|
||||
autoload :TestHelper
|
||||
unless defined?(ActionController)
|
||||
begin
|
||||
$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib"
|
||||
require 'action_controller'
|
||||
rescue LoadError
|
||||
require 'rubygems'
|
||||
gem 'actionpack', '>= 1.12.5'
|
||||
end
|
||||
end
|
||||
|
||||
require 'action_mailer/vendor'
|
||||
require 'tmail'
|
||||
|
||||
require 'action_mailer/base'
|
||||
require 'action_mailer/helpers'
|
||||
require 'action_mailer/mail_helper'
|
||||
require 'action_mailer/quoting'
|
||||
require 'action_mailer/test_helper'
|
||||
|
||||
require 'net/smtp'
|
||||
|
||||
ActionMailer::Base.class_eval do
|
||||
include ActionMailer::Quoting
|
||||
include ActionMailer::Helpers
|
||||
|
||||
helper MailHelper
|
||||
end
|
||||
|
||||
silence_warnings { TMail::Encoder.const_set("MAX_LINE_LEN", 200) }
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
module ActionMailer
|
||||
module AdvAttrAccessor #:nodoc:
|
||||
def adv_attr_accessor(*names)
|
||||
names.each do |name|
|
||||
ivar = "@#{name}"
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1
|
||||
def #{name}=(value)
|
||||
#{ivar} = value
|
||||
module ClassMethods #:nodoc:
|
||||
def adv_attr_accessor(*names)
|
||||
names.each do |name|
|
||||
ivar = "@#{name}"
|
||||
|
||||
define_method("#{name}=") do |value|
|
||||
instance_variable_set(ivar, value)
|
||||
end
|
||||
|
||||
def #{name}(*args)
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1
|
||||
if args.empty?
|
||||
#{ivar} if instance_variable_names.include?(#{ivar.inspect})
|
||||
define_method(name) do |*parameters|
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
|
||||
if parameters.empty?
|
||||
if instance_variables.include?(ivar)
|
||||
instance_variable_get(ivar)
|
||||
end
|
||||
else
|
||||
#{ivar} = args.first
|
||||
instance_variable_set(ivar, parameters.first)
|
||||
end
|
||||
end
|
||||
ACCESSORS
|
||||
|
||||
self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
||||
require 'abstract_controller/collector'
|
||||
require 'active_support/core_ext/hash/reverse_merge'
|
||||
require 'active_support/core_ext/array/extract_options'
|
||||
|
||||
module ActionMailer #:nodoc:
|
||||
class Collector
|
||||
include AbstractController::Collector
|
||||
attr_reader :responses
|
||||
|
||||
def initialize(context, &block)
|
||||
@context = context
|
||||
@responses = []
|
||||
@default_render = block
|
||||
end
|
||||
|
||||
def any(*args, &block)
|
||||
options = args.extract_options!
|
||||
raise "You have to supply at least one format" if args.empty?
|
||||
args.each { |type| send(type, options.dup, &block) }
|
||||
end
|
||||
alias :all :any
|
||||
|
||||
def custom(mime, options={})
|
||||
options.reverse_merge!(:content_type => mime.to_s)
|
||||
@context.freeze_formats([mime.to_sym])
|
||||
options[:body] = block_given? ? yield : @default_render.call
|
||||
@responses << options
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,86 +0,0 @@
|
||||
require 'tmpdir'
|
||||
|
||||
module ActionMailer
|
||||
# This modules handles everything related to the delivery, from registering new
|
||||
# delivery methods to configuring the mail object to be sent.
|
||||
module DeliveryMethods
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
class_attribute :delivery_methods, :delivery_method
|
||||
|
||||
# Do not make this inheritable, because we always want it to propagate
|
||||
cattr_accessor :raise_delivery_errors
|
||||
self.raise_delivery_errors = true
|
||||
|
||||
cattr_accessor :perform_deliveries
|
||||
self.perform_deliveries = true
|
||||
|
||||
self.delivery_methods = {}.freeze
|
||||
self.delivery_method = :smtp
|
||||
|
||||
add_delivery_method :smtp, Mail::SMTP,
|
||||
:address => "localhost",
|
||||
:port => 25,
|
||||
:domain => 'localhost.localdomain',
|
||||
:user_name => nil,
|
||||
:password => nil,
|
||||
:authentication => nil,
|
||||
:enable_starttls_auto => true
|
||||
|
||||
add_delivery_method :file, Mail::FileDelivery,
|
||||
:location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
|
||||
|
||||
add_delivery_method :sendmail, Mail::Sendmail,
|
||||
:location => '/usr/sbin/sendmail',
|
||||
:arguments => '-i -t'
|
||||
|
||||
add_delivery_method :test, Mail::TestMailer
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Provides a list of emails that have been delivered by Mail::TestMailer
|
||||
delegate :deliveries, :deliveries=, :to => Mail::TestMailer
|
||||
|
||||
# Adds a new delivery method through the given class using the given symbol
|
||||
# as alias and the default options supplied:
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# add_delivery_method :sendmail, Mail::Sendmail,
|
||||
# :location => '/usr/sbin/sendmail',
|
||||
# :arguments => '-i -t'
|
||||
#
|
||||
def add_delivery_method(symbol, klass, default_options={})
|
||||
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
|
||||
send(:"#{symbol}_settings=", default_options)
|
||||
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
|
||||
end
|
||||
|
||||
def wrap_delivery_behavior(mail, method=nil) #:nodoc:
|
||||
method ||= self.delivery_method
|
||||
mail.delivery_handler = self
|
||||
|
||||
case method
|
||||
when NilClass
|
||||
raise "Delivery method cannot be nil"
|
||||
when Symbol
|
||||
if klass = delivery_methods[method.to_sym]
|
||||
mail.delivery_method(klass, send(:"#{method}_settings"))
|
||||
else
|
||||
raise "Invalid delivery method #{method.inspect}"
|
||||
end
|
||||
else
|
||||
mail.delivery_method(method)
|
||||
end
|
||||
|
||||
mail.perform_deliveries = perform_deliveries
|
||||
mail.raise_delivery_errors = raise_delivery_errors
|
||||
end
|
||||
end
|
||||
|
||||
def wrap_delivery_behavior!(*args) #:nodoc:
|
||||
self.class.wrap_delivery_behavior(message, *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,147 +0,0 @@
|
||||
require 'active_support/core_ext/object/try'
|
||||
|
||||
module ActionMailer
|
||||
# This is the API which is deprecated and is going to be removed on Rails 3.1 release.
|
||||
# Part of the old API will be deprecated after 3.1, for a smoother deprecation process.
|
||||
# Check those in OldApi instead.
|
||||
module DeprecatedApi #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
[:charset, :content_type, :mime_version, :implicit_parts_order].each do |method|
|
||||
class_eval <<-FILE, __FILE__, __LINE__ + 1
|
||||
def self.default_#{method}
|
||||
@@default_#{method}
|
||||
end
|
||||
|
||||
def self.default_#{method}=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionMailer::Base.default_#{method}=value is deprecated, " <<
|
||||
"use default :#{method} => value instead"
|
||||
@@default_#{method} = value
|
||||
end
|
||||
|
||||
@@default_#{method} = nil
|
||||
FILE
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Deliver the given mail object directly. This can be used to deliver
|
||||
# a preconstructed mail object, like:
|
||||
#
|
||||
# email = MyMailer.create_some_mail(parameters)
|
||||
# email.set_some_obscure_header "frobnicate"
|
||||
# MyMailer.deliver(email)
|
||||
def deliver(mail, show_warning=true)
|
||||
if show_warning
|
||||
ActiveSupport::Deprecation.warn "#{self}.deliver is deprecated, call " <<
|
||||
"deliver in the mailer instance instead", caller[0,2]
|
||||
end
|
||||
|
||||
raise "no mail object available for delivery!" unless mail
|
||||
wrap_delivery_behavior(mail)
|
||||
mail.deliver
|
||||
mail
|
||||
end
|
||||
|
||||
def template_root
|
||||
self.view_paths && self.view_paths.first
|
||||
end
|
||||
|
||||
def template_root=(root)
|
||||
ActiveSupport::Deprecation.warn "template_root= is deprecated, use prepend_view_path instead", caller[0,2]
|
||||
self.view_paths = ActionView::Base.process_view_paths(root)
|
||||
end
|
||||
|
||||
def respond_to?(method_symbol, include_private = false)
|
||||
matches_dynamic_method?(method_symbol) || super
|
||||
end
|
||||
|
||||
def method_missing(method_symbol, *parameters)
|
||||
if match = matches_dynamic_method?(method_symbol)
|
||||
case match[1]
|
||||
when 'create'
|
||||
ActiveSupport::Deprecation.warn "#{self}.create_#{match[2]} is deprecated, " <<
|
||||
"use #{self}.#{match[2]} instead", caller[0,2]
|
||||
new(match[2], *parameters).message
|
||||
when 'deliver'
|
||||
ActiveSupport::Deprecation.warn "#{self}.deliver_#{match[2]} is deprecated, " <<
|
||||
"use #{self}.#{match[2]}.deliver instead", caller[0,2]
|
||||
new(match[2], *parameters).message.deliver
|
||||
else super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matches_dynamic_method?(method_name)
|
||||
method_name = method_name.to_s
|
||||
/^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Delivers a Mail object. By default, it delivers the cached mail
|
||||
# object (from the <tt>create!</tt> method). If no cached mail object exists, and
|
||||
# no alternate has been given as the parameter, this will fail.
|
||||
def deliver!(mail = @_message)
|
||||
ActiveSupport::Deprecation.warn "Calling deliver in the AM::Base object is deprecated, " <<
|
||||
"please call deliver in the Mail instance", caller[0,2]
|
||||
self.class.deliver(mail, false)
|
||||
end
|
||||
alias :deliver :deliver!
|
||||
|
||||
def render(*args)
|
||||
options = args.last.is_a?(Hash) ? args.last : {}
|
||||
|
||||
if file = options[:file] and !file.index("/")
|
||||
ActiveSupport::Deprecation.warn("render :file is deprecated except for absolute paths. " \
|
||||
"Please use render :template instead")
|
||||
options[:prefix] = _prefix
|
||||
end
|
||||
|
||||
if options[:body].is_a?(Hash)
|
||||
ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' <<
|
||||
'variables as assigns instead', caller[0,1])
|
||||
|
||||
options[:body].each { |k,v| instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
# Render a message but does not set it as mail body. Useful for rendering
|
||||
# data for part and attachments.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# render_message "special_message"
|
||||
# render_message :template => "special_message"
|
||||
# render_message :inline => "<%= 'Hi!' %>"
|
||||
#
|
||||
def render_message(*args)
|
||||
ActiveSupport::Deprecation.warn "render_message is deprecated, use render instead", caller[0,2]
|
||||
render(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_defaults(*)
|
||||
@charset ||= self.class.default_charset.try(:dup)
|
||||
@content_type ||= self.class.default_content_type.try(:dup)
|
||||
@implicit_parts_order ||= self.class.default_implicit_parts_order.try(:dup)
|
||||
@mime_version ||= self.class.default_mime_version.try(:dup)
|
||||
super
|
||||
end
|
||||
|
||||
def create_parts
|
||||
if @body.is_a?(Hash) && !@body.empty?
|
||||
ActiveSupport::Deprecation.warn "Giving a hash to body is deprecated, please use instance variables instead", caller[0,2]
|
||||
@body.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
111
actionmailer/lib/action_mailer/helpers.rb
Normal file
111
actionmailer/lib/action_mailer/helpers.rb
Normal file
@@ -0,0 +1,111 @@
|
||||
module ActionMailer
|
||||
module Helpers #:nodoc:
|
||||
def self.included(base) #:nodoc:
|
||||
# Initialize the base module to aggregate its helpers.
|
||||
base.class_inheritable_accessor :master_helper_module
|
||||
base.master_helper_module = Module.new
|
||||
|
||||
# Extend base with class methods to declare helpers.
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.class_eval do
|
||||
# Wrap inherited to create a new master helper module for subclasses.
|
||||
class << self
|
||||
alias_method_chain :inherited, :helper
|
||||
end
|
||||
|
||||
# Wrap initialize_template_class to extend new template class
|
||||
# instances with the master helper module.
|
||||
alias_method_chain :initialize_template_class, :helper
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|
||||
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
|
||||
# available to the templates.
|
||||
def add_template_helper(helper_module) #:nodoc:
|
||||
master_helper_module.module_eval "include #{helper_module}"
|
||||
end
|
||||
|
||||
# Declare a helper:
|
||||
# helper :foo
|
||||
# requires 'foo_helper' and includes FooHelper in the template class.
|
||||
# helper FooHelper
|
||||
# includes FooHelper in the template class.
|
||||
# helper { def foo() "#{bar} is the very best" end }
|
||||
# evaluates the block in the template class, adding method #foo.
|
||||
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
||||
# does all three.
|
||||
def helper(*args, &block)
|
||||
args.flatten.each do |arg|
|
||||
case arg
|
||||
when Module
|
||||
add_template_helper(arg)
|
||||
when String, Symbol
|
||||
file_name = arg.to_s.underscore + '_helper'
|
||||
class_name = file_name.camelize
|
||||
|
||||
begin
|
||||
require_dependency(file_name)
|
||||
rescue LoadError => load_error
|
||||
requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
|
||||
msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
|
||||
raise LoadError.new(msg).copy_blame!(load_error)
|
||||
end
|
||||
|
||||
add_template_helper(class_name.constantize)
|
||||
else
|
||||
raise ArgumentError, 'helper expects String, Symbol, or Module argument'
|
||||
end
|
||||
end
|
||||
|
||||
# Evaluate block in template class if given.
|
||||
master_helper_module.module_eval(&block) if block_given?
|
||||
end
|
||||
|
||||
# Declare a controller method as a helper. For example,
|
||||
# helper_method :link_to
|
||||
# def link_to(name, options) ... end
|
||||
# makes the link_to controller method available in the view.
|
||||
def helper_method(*methods)
|
||||
methods.flatten.each do |method|
|
||||
master_helper_module.module_eval <<-end_eval
|
||||
def #{method}(*args, &block)
|
||||
controller.send!(%(#{method}), *args, &block)
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
end
|
||||
|
||||
# Declare a controller attribute as a helper. For example,
|
||||
# helper_attr :name
|
||||
# attr_accessor :name
|
||||
# makes the name and name= controller methods available in the view.
|
||||
# The is a convenience wrapper for helper_method.
|
||||
def helper_attr(*attrs)
|
||||
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
||||
end
|
||||
|
||||
private
|
||||
def inherited_with_helper(child)
|
||||
inherited_without_helper(child)
|
||||
begin
|
||||
child.master_helper_module = Module.new
|
||||
child.master_helper_module.send! :include, master_helper_module
|
||||
child.helper child.name.underscore
|
||||
rescue MissingSourceFile => e
|
||||
raise unless e.is_missing?("helpers/#{child.name.underscore}_helper")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Extend the template class instance with our controller's helper module.
|
||||
def initialize_template_class_with_helper(assigns)
|
||||
returning(template = initialize_template_class_without_helper(assigns)) do
|
||||
template.extend self.class.master_helper_module
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,22 +0,0 @@
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
|
||||
module ActionMailer
|
||||
class LogSubscriber < ActiveSupport::LogSubscriber
|
||||
def deliver(event)
|
||||
recipients = Array.wrap(event.payload[:to]).join(', ')
|
||||
info("\nSent mail to #{recipients} (%1.fms)" % event.duration)
|
||||
debug(event.payload[:mail])
|
||||
end
|
||||
|
||||
def receive(event)
|
||||
info("\nReceived mail (%.1fms)" % event.duration)
|
||||
debug(event.payload[:mail])
|
||||
end
|
||||
|
||||
def logger
|
||||
ActionMailer::Base.logger
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActionMailer::LogSubscriber.attach_to :action_mailer
|
||||
@@ -1,49 +1,19 @@
|
||||
module ActionMailer
|
||||
module MailHelper
|
||||
# Uses Text::Format to take the text and format it, indented two spaces for
|
||||
# each line, and wrapped at 72 columns.
|
||||
def block_format(text)
|
||||
formatted = text.split(/\n\r\n/).collect { |paragraph|
|
||||
simple_format(paragraph)
|
||||
}.join("\n")
|
||||
require 'text/format'
|
||||
|
||||
# Make list points stand on their own line
|
||||
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
||||
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
||||
module MailHelper
|
||||
# Uses Text::Format to take the text and format it, indented two spaces for
|
||||
# each line, and wrapped at 72 columns.
|
||||
def block_format(text)
|
||||
formatted = text.split(/\n\r\n/).collect { |paragraph|
|
||||
Text::Format.new(
|
||||
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
||||
).format
|
||||
}.join("\n")
|
||||
|
||||
# Make list points stand on their own line
|
||||
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
||||
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
|
||||
|
||||
formatted
|
||||
end
|
||||
|
||||
# Access the mailer instance.
|
||||
def mailer
|
||||
@_controller
|
||||
end
|
||||
|
||||
# Access the message instance.
|
||||
def message
|
||||
@_message
|
||||
end
|
||||
|
||||
# Access the message attachments list.
|
||||
def attachments
|
||||
@_message.attachments
|
||||
end
|
||||
|
||||
private
|
||||
def simple_format(text, len = 72, indent = 2)
|
||||
sentences = [[]]
|
||||
|
||||
text.split.each do |word|
|
||||
if (sentences.last + [word]).join(' ').length > len
|
||||
sentences << [word]
|
||||
else
|
||||
sentences.last << word
|
||||
end
|
||||
end
|
||||
|
||||
sentences.map { |sentence|
|
||||
"#{" " * indent}#{sentence.join(' ')}"
|
||||
}.join "\n"
|
||||
end
|
||||
formatted
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
require 'active_support/concern'
|
||||
require 'active_support/core_ext/object/try'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionMailer
|
||||
module OldApi #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
extend ActionMailer::AdvAttrAccessor
|
||||
|
||||
@@protected_instance_variables = %w(@parts)
|
||||
cattr_reader :protected_instance_variables
|
||||
|
||||
# Specify the BCC addresses for the message
|
||||
adv_attr_accessor :bcc
|
||||
|
||||
# Specify the CC addresses for the message.
|
||||
adv_attr_accessor :cc
|
||||
|
||||
# Specify the charset to use for the message. This defaults to the
|
||||
# +default_charset+ specified for ActionMailer::Base.
|
||||
adv_attr_accessor :charset
|
||||
|
||||
# Specify the content type for the message. This defaults to <tt>text/plain</tt>
|
||||
# in most cases, but can be automatically set in some situations.
|
||||
adv_attr_accessor :content_type
|
||||
|
||||
# Specify the from address for the message.
|
||||
adv_attr_accessor :from
|
||||
|
||||
# Specify the address (if different than the "from" address) to direct
|
||||
# replies to this message.
|
||||
adv_attr_accessor :reply_to
|
||||
|
||||
# Specify the order in which parts should be sorted, based on content-type.
|
||||
# This defaults to the value for the +default_implicit_parts_order+.
|
||||
adv_attr_accessor :implicit_parts_order
|
||||
|
||||
# Defaults to "1.0", but may be explicitly given if needed.
|
||||
adv_attr_accessor :mime_version
|
||||
|
||||
# The recipient addresses for the message, either as a string (for a single
|
||||
# address) or an array (for multiple addresses).
|
||||
adv_attr_accessor :recipients
|
||||
|
||||
# The date on which the message was sent. If not set (the default), the
|
||||
# header will be set by the delivery agent.
|
||||
adv_attr_accessor :sent_on
|
||||
|
||||
# Specify the subject of the message.
|
||||
adv_attr_accessor :subject
|
||||
|
||||
# Specify the template name to use for current message. This is the "base"
|
||||
# template name, without the extension or directory, and may be used to
|
||||
# have multiple mailer methods share the same template.
|
||||
adv_attr_accessor :template
|
||||
|
||||
# Override the mailer name, which defaults to an inflected version of the
|
||||
# mailer's class name. If you want to use a template in a non-standard
|
||||
# location, you can use this to specify that location.
|
||||
adv_attr_accessor :mailer_name
|
||||
|
||||
# Define the body of the message. This is either a Hash (in which case it
|
||||
# specifies the variables to pass to the template when it is rendered),
|
||||
# or a string, in which case it specifies the actual text of the message.
|
||||
adv_attr_accessor :body
|
||||
|
||||
# Alias controller_path to mailer_name so render :partial in views work.
|
||||
alias :controller_path :mailer_name
|
||||
end
|
||||
|
||||
def process(method_name, *args)
|
||||
initialize_defaults(method_name)
|
||||
super
|
||||
unless @mail_was_called
|
||||
create_parts
|
||||
create_mail
|
||||
end
|
||||
@_message
|
||||
end
|
||||
|
||||
# Add a part to a multipart message, with the given content-type. The
|
||||
# part itself is yielded to the block so that other properties (charset,
|
||||
# body, headers, etc.) can be set on it.
|
||||
def part(params)
|
||||
params = {:content_type => params} if String === params
|
||||
|
||||
if custom_headers = params.delete(:headers)
|
||||
params.merge!(custom_headers)
|
||||
end
|
||||
|
||||
part = Mail::Part.new(params)
|
||||
|
||||
yield part if block_given?
|
||||
@parts << part
|
||||
end
|
||||
|
||||
# Add an attachment to a multipart message. This is simply a part with the
|
||||
# content-disposition set to "attachment".
|
||||
def attachment(params, &block)
|
||||
params = { :content_type => params } if String === params
|
||||
|
||||
params[:content] ||= params.delete(:data) || params.delete(:body)
|
||||
|
||||
if params[:filename]
|
||||
params = normalize_file_hash(params)
|
||||
else
|
||||
params = normalize_nonfile_hash(params)
|
||||
end
|
||||
|
||||
part(params, &block)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def normalize_nonfile_hash(params)
|
||||
content_disposition = "attachment;"
|
||||
|
||||
mime_type = params.delete(:mime_type)
|
||||
|
||||
if content_type = params.delete(:content_type)
|
||||
content_type = "#{mime_type || content_type};"
|
||||
end
|
||||
|
||||
params[:body] = params.delete(:data) if params[:data]
|
||||
|
||||
{ :content_type => content_type,
|
||||
:content_disposition => content_disposition }.merge(params)
|
||||
end
|
||||
|
||||
def normalize_file_hash(params)
|
||||
filename = File.basename(params.delete(:filename))
|
||||
content_disposition = "attachment; filename=\"#{File.basename(filename)}\""
|
||||
|
||||
mime_type = params.delete(:mime_type)
|
||||
|
||||
if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/)
|
||||
content_type = "#{mime_type || content_type}; filename=\"#{filename}\""
|
||||
end
|
||||
|
||||
params[:body] = params.delete(:data) if params[:data]
|
||||
|
||||
{ :content_type => content_type,
|
||||
:content_disposition => content_disposition }.merge(params)
|
||||
end
|
||||
|
||||
def create_mail
|
||||
m = @_message
|
||||
|
||||
set_fields!({:subject => subject, :to => recipients, :from => from,
|
||||
:bcc => bcc, :cc => cc, :reply_to => reply_to}, charset)
|
||||
|
||||
m.mime_version = mime_version unless mime_version.nil?
|
||||
m.date = sent_on.to_time rescue sent_on if sent_on
|
||||
|
||||
@headers.each { |k, v| m[k] = v }
|
||||
|
||||
real_content_type, ctype_attrs = parse_content_type
|
||||
main_type, sub_type = split_content_type(real_content_type)
|
||||
|
||||
if @parts.size == 1 && @parts.first.parts.empty?
|
||||
m.content_type([main_type, sub_type, ctype_attrs])
|
||||
m.body = @parts.first.body.encoded
|
||||
else
|
||||
@parts.each do |p|
|
||||
m.add_part(p)
|
||||
end
|
||||
|
||||
m.body.set_sort_order(@implicit_parts_order)
|
||||
m.body.sort_parts!
|
||||
|
||||
if real_content_type =~ /multipart/
|
||||
ctype_attrs.delete "charset"
|
||||
m.content_type([main_type, sub_type, ctype_attrs])
|
||||
end
|
||||
end
|
||||
|
||||
wrap_delivery_behavior!
|
||||
m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii?
|
||||
|
||||
@_message
|
||||
end
|
||||
|
||||
# Set up the default values for the various instance variables of this
|
||||
# mailer. Subclasses may override this method to provide different
|
||||
# defaults.
|
||||
def initialize_defaults(method_name)
|
||||
@charset ||= self.class.default[:charset].try(:dup)
|
||||
@content_type ||= self.class.default[:content_type].try(:dup)
|
||||
@implicit_parts_order ||= self.class.default[:parts_order].try(:dup)
|
||||
@mime_version ||= self.class.default[:mime_version].try(:dup)
|
||||
|
||||
@mailer_name ||= self.class.mailer_name.dup
|
||||
@template ||= method_name
|
||||
@mail_was_called = false
|
||||
|
||||
@parts ||= []
|
||||
@headers ||= {}
|
||||
@sent_on ||= Time.now
|
||||
@body ||= {}
|
||||
end
|
||||
|
||||
def create_parts
|
||||
if String === @body
|
||||
@parts.unshift create_inline_part(@body)
|
||||
elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
|
||||
lookup_context.find_all(@template, @mailer_name).each do |template|
|
||||
self.formats = template.formats
|
||||
@parts << create_inline_part(render(:template => template), template.mime_type)
|
||||
end
|
||||
|
||||
if @parts.size > 1
|
||||
@content_type = "multipart/alternative" if @content_type !~ /^multipart/
|
||||
end
|
||||
|
||||
# If this is a multipart e-mail add the mime_version if it is not
|
||||
# already set.
|
||||
@mime_version ||= "1.0" if !@parts.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def create_inline_part(body, mime_type=nil)
|
||||
ct = mime_type || "text/plain"
|
||||
main_type, sub_type = split_content_type(ct.to_s)
|
||||
|
||||
Mail::Part.new(
|
||||
:content_type => [main_type, sub_type, {:charset => charset}],
|
||||
:content_disposition => "inline",
|
||||
:body => body
|
||||
)
|
||||
end
|
||||
|
||||
def set_fields!(headers, charset) #:nodoc:
|
||||
m = @_message
|
||||
m.charset = charset
|
||||
m.subject ||= headers.delete(:subject) if headers[:subject]
|
||||
m.to ||= headers.delete(:to) if headers[:to]
|
||||
m.from ||= headers.delete(:from) if headers[:from]
|
||||
m.cc ||= headers.delete(:cc) if headers[:cc]
|
||||
m.bcc ||= headers.delete(:bcc) if headers[:bcc]
|
||||
m.reply_to ||= headers.delete(:reply_to) if headers[:reply_to]
|
||||
end
|
||||
|
||||
def split_content_type(ct)
|
||||
ct.to_s.split("/")
|
||||
end
|
||||
|
||||
def parse_content_type(defaults=nil)
|
||||
if @content_type.blank?
|
||||
[ nil, {} ]
|
||||
else
|
||||
ctype, *attrs = @content_type.split(/;\s*/)
|
||||
attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
|
||||
[ctype, {"charset" => @charset}.merge(attrs)]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
110
actionmailer/lib/action_mailer/part.rb
Normal file
110
actionmailer/lib/action_mailer/part.rb
Normal file
@@ -0,0 +1,110 @@
|
||||
require 'action_mailer/adv_attr_accessor'
|
||||
require 'action_mailer/part_container'
|
||||
require 'action_mailer/utils'
|
||||
|
||||
module ActionMailer
|
||||
# Represents a subpart of an email message. It shares many similar
|
||||
# attributes of ActionMailer::Base. Although you can create parts manually
|
||||
# and add them to the #parts list of the mailer, it is easier
|
||||
# to use the helper methods in ActionMailer::PartContainer.
|
||||
class Part
|
||||
include ActionMailer::AdvAttrAccessor
|
||||
include ActionMailer::PartContainer
|
||||
|
||||
# Represents the body of the part, as a string. This should not be a
|
||||
# Hash (like ActionMailer::Base), but if you want a template to be rendered
|
||||
# into the body of a subpart you can do it with the mailer's #render method
|
||||
# and assign the result here.
|
||||
adv_attr_accessor :body
|
||||
|
||||
# Specify the charset for this subpart. By default, it will be the charset
|
||||
# of the containing part or mailer.
|
||||
adv_attr_accessor :charset
|
||||
|
||||
# The content disposition of this part, typically either "inline" or
|
||||
# "attachment".
|
||||
adv_attr_accessor :content_disposition
|
||||
|
||||
# The content type of the part.
|
||||
adv_attr_accessor :content_type
|
||||
|
||||
# The filename to use for this subpart (usually for attachments).
|
||||
adv_attr_accessor :filename
|
||||
|
||||
# Accessor for specifying additional headers to include with this part.
|
||||
adv_attr_accessor :headers
|
||||
|
||||
# The transfer encoding to use for this subpart, like "base64" or
|
||||
# "quoted-printable".
|
||||
adv_attr_accessor :transfer_encoding
|
||||
|
||||
# Create a new part from the given +params+ hash. The valid params keys
|
||||
# correspond to the accessors.
|
||||
def initialize(params)
|
||||
@content_type = params[:content_type]
|
||||
@content_disposition = params[:disposition] || "inline"
|
||||
@charset = params[:charset]
|
||||
@body = params[:body]
|
||||
@filename = params[:filename]
|
||||
@transfer_encoding = params[:transfer_encoding] || "quoted-printable"
|
||||
@headers = params[:headers] || {}
|
||||
@parts = []
|
||||
end
|
||||
|
||||
# Convert the part to a mail object which can be included in the parts
|
||||
# list of another mail object.
|
||||
def to_mail(defaults)
|
||||
part = TMail::Mail.new
|
||||
|
||||
real_content_type, ctype_attrs = parse_content_type(defaults)
|
||||
|
||||
if @parts.empty?
|
||||
part.content_transfer_encoding = transfer_encoding || "quoted-printable"
|
||||
case (transfer_encoding || "").downcase
|
||||
when "base64" then
|
||||
part.body = TMail::Base64.folding_encode(body)
|
||||
when "quoted-printable"
|
||||
part.body = [Utils.normalize_new_lines(body)].pack("M*")
|
||||
else
|
||||
part.body = body
|
||||
end
|
||||
|
||||
# Always set the content_type after setting the body and or parts!
|
||||
# Also don't set filename and name when there is none (like in
|
||||
# non-attachment parts)
|
||||
if content_disposition == "attachment"
|
||||
ctype_attrs.delete "charset"
|
||||
part.set_content_type(real_content_type, nil,
|
||||
squish("name" => filename).merge(ctype_attrs))
|
||||
part.set_content_disposition(content_disposition,
|
||||
squish("filename" => filename).merge(ctype_attrs))
|
||||
else
|
||||
part.set_content_type(real_content_type, nil, ctype_attrs)
|
||||
part.set_content_disposition(content_disposition)
|
||||
end
|
||||
else
|
||||
if String === body
|
||||
@parts.unshift Part.new(:charset => charset, :body => @body, :content_type => 'text/plain')
|
||||
@body = nil
|
||||
end
|
||||
|
||||
@parts.each do |p|
|
||||
prt = (TMail::Mail === p ? p : p.to_mail(defaults))
|
||||
part.parts << prt
|
||||
end
|
||||
|
||||
part.set_content_type(real_content_type, nil, ctype_attrs) if real_content_type =~ /multipart/
|
||||
end
|
||||
|
||||
headers.each { |k,v| part[k] = v }
|
||||
|
||||
part
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def squish(values={})
|
||||
values.delete_if { |k,v| v.nil? }
|
||||
end
|
||||
end
|
||||
end
|
||||
51
actionmailer/lib/action_mailer/part_container.rb
Normal file
51
actionmailer/lib/action_mailer/part_container.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
module ActionMailer
|
||||
# Accessors and helpers that ActionMailer::Base and ActionMailer::Part have
|
||||
# in common. Using these helpers you can easily add subparts or attachments
|
||||
# to your message:
|
||||
#
|
||||
# def my_mail_message(...)
|
||||
# ...
|
||||
# part "text/plain" do |p|
|
||||
# p.body "hello, world"
|
||||
# p.transfer_encoding "base64"
|
||||
# end
|
||||
#
|
||||
# attachment "image/jpg" do |a|
|
||||
# a.body = File.read("hello.jpg")
|
||||
# a.filename = "hello.jpg"
|
||||
# end
|
||||
# end
|
||||
module PartContainer
|
||||
# The list of subparts of this container
|
||||
attr_reader :parts
|
||||
|
||||
# Add a part to a multipart message, with the given content-type. The
|
||||
# part itself is yielded to the block so that other properties (charset,
|
||||
# body, headers, etc.) can be set on it.
|
||||
def part(params)
|
||||
params = {:content_type => params} if String === params
|
||||
part = Part.new(params)
|
||||
yield part if block_given?
|
||||
@parts << part
|
||||
end
|
||||
|
||||
# Add an attachment to a multipart message. This is simply a part with the
|
||||
# content-disposition set to "attachment".
|
||||
def attachment(params, &block)
|
||||
params = { :content_type => params } if String === params
|
||||
params = { :disposition => "attachment",
|
||||
:transfer_encoding => "base64" }.merge(params)
|
||||
part(params, &block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_content_type(defaults=nil)
|
||||
return [defaults && defaults.content_type, {}] if content_type.blank?
|
||||
ctype, *attrs = content_type.split(/;\s*/)
|
||||
attrs = attrs.inject({}) { |h,s| k,v = s.split(/=/, 2); h[k] = v; h }
|
||||
[ctype, {"charset" => charset || defaults && defaults.charset}.merge(attrs)]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
59
actionmailer/lib/action_mailer/quoting.rb
Normal file
59
actionmailer/lib/action_mailer/quoting.rb
Normal file
@@ -0,0 +1,59 @@
|
||||
module ActionMailer
|
||||
module Quoting #:nodoc:
|
||||
# Convert the given text into quoted printable format, with an instruction
|
||||
# that the text be eventually interpreted in the given charset.
|
||||
def quoted_printable(text, charset)
|
||||
text = text.gsub( /[^a-z ]/i ) { quoted_printable_encode($&) }.
|
||||
gsub( / /, "_" )
|
||||
"=?#{charset}?Q?#{text}?="
|
||||
end
|
||||
|
||||
# Convert the given character to quoted printable format, taking into
|
||||
# account multi-byte characters (if executing with $KCODE="u", for instance)
|
||||
def quoted_printable_encode(character)
|
||||
result = ""
|
||||
character.each_byte { |b| result << "=%02x" % b }
|
||||
result
|
||||
end
|
||||
|
||||
# A quick-and-dirty regexp for determining whether a string contains any
|
||||
# characters that need escaping.
|
||||
if !defined?(CHARS_NEEDING_QUOTING)
|
||||
CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
|
||||
end
|
||||
|
||||
# Quote the given text if it contains any "illegal" characters
|
||||
def quote_if_necessary(text, charset)
|
||||
(text =~ CHARS_NEEDING_QUOTING) ?
|
||||
quoted_printable(text, charset) :
|
||||
text
|
||||
end
|
||||
|
||||
# Quote any of the given strings if they contain any "illegal" characters
|
||||
def quote_any_if_necessary(charset, *args)
|
||||
args.map { |v| quote_if_necessary(v, charset) }
|
||||
end
|
||||
|
||||
# Quote the given address if it needs to be. The address may be a
|
||||
# regular email address, or it can be a phrase followed by an address in
|
||||
# brackets. The phrase is the only part that will be quoted, and only if
|
||||
# it needs to be. This allows extended characters to be used in the
|
||||
# "to", "from", "cc", and "bcc" headers.
|
||||
def quote_address_if_necessary(address, charset)
|
||||
if Array === address
|
||||
address.map { |a| quote_address_if_necessary(a, charset) }
|
||||
elsif address =~ /^(\S.*)\s+(<.*>)$/
|
||||
address = $2
|
||||
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
|
||||
"\"#{phrase}\" #{address}"
|
||||
else
|
||||
address
|
||||
end
|
||||
end
|
||||
|
||||
# Quote any of the given addresses, if they need to be.
|
||||
def quote_any_address_if_necessary(charset, *args)
|
||||
args.map { |v| quote_address_if_necessary(v, charset) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,30 +0,0 @@
|
||||
require "action_mailer"
|
||||
require "rails"
|
||||
|
||||
module ActionMailer
|
||||
class Railtie < Rails::Railtie
|
||||
config.action_mailer = ActiveSupport::OrderedOptions.new
|
||||
|
||||
initializer "action_mailer.logger" do
|
||||
ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
|
||||
end
|
||||
|
||||
initializer "action_mailer.set_configs" do |app|
|
||||
paths = app.config.paths
|
||||
options = app.config.action_mailer
|
||||
|
||||
options.assets_dir ||= paths.public.to_a.first
|
||||
options.javascripts_dir ||= paths.public.javascripts.to_a.first
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
include app.routes.url_helpers
|
||||
|
||||
register_interceptors(options.delete(:interceptors))
|
||||
register_observers(options.delete(:observers))
|
||||
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
require 'active_support/test_case'
|
||||
|
||||
module ActionMailer
|
||||
class NonInferrableMailerError < ::StandardError
|
||||
def initialize(name)
|
||||
@@ -6,71 +8,52 @@ module ActionMailer
|
||||
"test case definition"
|
||||
end
|
||||
end
|
||||
|
||||
# New Test Super class for forward compatibility.
|
||||
# To override
|
||||
class TestCase < ActiveSupport::TestCase
|
||||
module Behavior
|
||||
extend ActiveSupport::Concern
|
||||
include ActionMailer::Quoting
|
||||
|
||||
include TestHelper
|
||||
class << self
|
||||
def tests(mailer)
|
||||
write_inheritable_attribute(:mailer_class, mailer)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def tests(mailer)
|
||||
write_inheritable_attribute(:mailer_class, mailer)
|
||||
end
|
||||
|
||||
def mailer_class
|
||||
if mailer = read_inheritable_attribute(:mailer_class)
|
||||
mailer
|
||||
else
|
||||
tests determine_default_mailer(name)
|
||||
end
|
||||
end
|
||||
|
||||
def determine_default_mailer(name)
|
||||
name.sub(/Test$/, '').constantize
|
||||
rescue NameError
|
||||
raise NonInferrableMailerError.new(name)
|
||||
def mailer_class
|
||||
if mailer = read_inheritable_attribute(:mailer_class)
|
||||
mailer
|
||||
else
|
||||
tests determine_default_mailer(name)
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
protected
|
||||
|
||||
def initialize_test_deliveries
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
def set_expected_mail
|
||||
@expected = Mail.new
|
||||
@expected.content_type ["text", "plain", { "charset" => charset }]
|
||||
@expected.mime_version = '1.0'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def charset
|
||||
"UTF-8"
|
||||
end
|
||||
|
||||
def encode(subject)
|
||||
Mail::Encodings.q_value_encode(subject, charset)
|
||||
end
|
||||
|
||||
def read_fixture(action)
|
||||
IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
|
||||
end
|
||||
end
|
||||
|
||||
included do
|
||||
setup :initialize_test_deliveries
|
||||
setup :set_expected_mail
|
||||
def determine_default_mailer(name)
|
||||
name.sub(/Test$/, '').constantize
|
||||
rescue NameError => e
|
||||
raise NonInferrableMailerError.new(name)
|
||||
end
|
||||
end
|
||||
|
||||
include Behavior
|
||||
def setup
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries = []
|
||||
|
||||
@expected = TMail::Mail.new
|
||||
@expected.set_content_type "text", "plain", { "charset" => charset }
|
||||
@expected.mime_version = '1.0'
|
||||
end
|
||||
|
||||
private
|
||||
def charset
|
||||
"utf-8"
|
||||
end
|
||||
|
||||
def encode(subject)
|
||||
quoted_printable(subject, charset)
|
||||
end
|
||||
|
||||
def read_fixture(action)
|
||||
IO.readlines(File.join(RAILS_ROOT, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
module ActionMailer
|
||||
module TestHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# Asserts that the number of emails sent matches the given number.
|
||||
#
|
||||
# def test_emails
|
||||
@@ -59,3 +57,11 @@ module ActionMailer
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
class TestCase
|
||||
include ActionMailer::TestHelper
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
module Mail
|
||||
class Message
|
||||
|
||||
def set_content_type(*args)
|
||||
ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments', caller[0,2])
|
||||
content_type(*args)
|
||||
end
|
||||
|
||||
alias :old_transfer_encoding :transfer_encoding
|
||||
def transfer_encoding(value = nil)
|
||||
if value
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding with the same arguments', caller[0,2])
|
||||
content_transfer_encoding(value)
|
||||
else
|
||||
old_transfer_encoding
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_encoding=(value)
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding= is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding= with the same arguments', caller[0,2])
|
||||
self.content_transfer_encoding = value
|
||||
end
|
||||
|
||||
def original_filename
|
||||
ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' <<
|
||||
'please call Message#filename', caller[0,2])
|
||||
filename
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
8
actionmailer/lib/action_mailer/utils.rb
Normal file
8
actionmailer/lib/action_mailer/utils.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
module ActionMailer
|
||||
module Utils #:nodoc:
|
||||
def normalize_new_lines(text)
|
||||
text.to_s.gsub(/\r\n?/, "\n")
|
||||
end
|
||||
module_function :normalize_new_lines
|
||||
end
|
||||
end
|
||||
14
actionmailer/lib/action_mailer/vendor.rb
Normal file
14
actionmailer/lib/action_mailer/vendor.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
# Prefer gems to the bundled libs.
|
||||
require 'rubygems'
|
||||
|
||||
begin
|
||||
gem 'tmail', '~> 1.1.0'
|
||||
rescue Gem::LoadError
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.1.0"
|
||||
end
|
||||
|
||||
begin
|
||||
gem 'text-format', '>= 0.6.3'
|
||||
rescue Gem::LoadError
|
||||
$:.unshift "#{File.dirname(__FILE__)}/vendor/text-format-0.6.3"
|
||||
end
|
||||
1466
actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
vendored
Executable file
1466
actionmailer/lib/action_mailer/vendor/text-format-0.6.3/text/format.rb
vendored
Executable file
File diff suppressed because it is too large
Load Diff
4
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail.rb
vendored
Executable file
4
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail.rb
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
require 'tmail/version'
|
||||
require 'tmail/mail'
|
||||
require 'tmail/mailbox'
|
||||
require 'tmail/core_extensions'
|
||||
19
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/Makefile
vendored
Normal file
19
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/Makefile
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# lib/tmail/Makefile
|
||||
#
|
||||
|
||||
debug:
|
||||
rm -f parser.rb
|
||||
make parser.rb DEBUG=true
|
||||
|
||||
parser.rb: parser.y
|
||||
if [ "$(DEBUG)" = true ]; then \
|
||||
racc -v -g -o$@ parser.y ;\
|
||||
else \
|
||||
racc -E -o$@ parser.y ;\
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -f parser.rb parser.output
|
||||
|
||||
distclean: clean
|
||||
245
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/address.rb
vendored
Executable file
245
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/address.rb
vendored
Executable file
@@ -0,0 +1,245 @@
|
||||
=begin rdoc
|
||||
|
||||
= Address handling class
|
||||
|
||||
=end
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/encode'
|
||||
require 'tmail/parser'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Address
|
||||
|
||||
include TextUtils
|
||||
|
||||
def Address.parse( str )
|
||||
Parser.parse :ADDRESS, str
|
||||
end
|
||||
|
||||
def address_group?
|
||||
false
|
||||
end
|
||||
|
||||
def initialize( local, domain )
|
||||
if domain
|
||||
domain.each do |s|
|
||||
raise SyntaxError, 'empty word in domain' if s.empty?
|
||||
end
|
||||
end
|
||||
|
||||
@local = local
|
||||
@domain = domain
|
||||
@name = nil
|
||||
@routes = []
|
||||
end
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def name=( str )
|
||||
@name = str
|
||||
@name = nil if str and str.empty?
|
||||
end
|
||||
|
||||
alias phrase name
|
||||
alias phrase= name=
|
||||
|
||||
attr_reader :routes
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{address()}>"
|
||||
end
|
||||
|
||||
def local
|
||||
return nil unless @local
|
||||
return '""' if @local.size == 1 and @local[0].empty?
|
||||
@local.map {|i| quote_atom(i) }.join('.')
|
||||
end
|
||||
|
||||
def domain
|
||||
return nil unless @domain
|
||||
join_domain(@domain)
|
||||
end
|
||||
|
||||
def spec
|
||||
s = self.local
|
||||
d = self.domain
|
||||
if s and d
|
||||
s + '@' + d
|
||||
else
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
alias address spec
|
||||
|
||||
def ==( other )
|
||||
other.respond_to? :spec and self.spec == other.spec
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
@local.hash ^ @domain.hash
|
||||
end
|
||||
|
||||
def dup
|
||||
obj = self.class.new(@local.dup, @domain.dup)
|
||||
obj.name = @name.dup if @name
|
||||
obj.routes.replace @routes
|
||||
obj
|
||||
end
|
||||
|
||||
include StrategyInterface
|
||||
|
||||
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
||||
unless @local
|
||||
strategy.meta '<>' # empty return-path
|
||||
return
|
||||
end
|
||||
|
||||
spec_p = (not @name and @routes.empty?)
|
||||
if @name
|
||||
strategy.phrase @name
|
||||
strategy.space
|
||||
end
|
||||
tmp = spec_p ? '' : '<'
|
||||
unless @routes.empty?
|
||||
tmp << @routes.map {|i| '@' + i }.join(',') << ':'
|
||||
end
|
||||
tmp << self.spec
|
||||
tmp << '>' unless spec_p
|
||||
strategy.meta tmp
|
||||
strategy.lwsp ''
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class AddressGroup
|
||||
|
||||
include Enumerable
|
||||
|
||||
def address_group?
|
||||
true
|
||||
end
|
||||
|
||||
def initialize( name, addrs )
|
||||
@name = name
|
||||
@addresses = addrs
|
||||
end
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def ==( other )
|
||||
other.respond_to? :to_a and @addresses == other.to_a
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
map {|i| i.hash }.hash
|
||||
end
|
||||
|
||||
def []( idx )
|
||||
@addresses[idx]
|
||||
end
|
||||
|
||||
def size
|
||||
@addresses.size
|
||||
end
|
||||
|
||||
def empty?
|
||||
@addresses.empty?
|
||||
end
|
||||
|
||||
def each( &block )
|
||||
@addresses.each(&block)
|
||||
end
|
||||
|
||||
def to_a
|
||||
@addresses.dup
|
||||
end
|
||||
|
||||
alias to_ary to_a
|
||||
|
||||
def include?( a )
|
||||
@addresses.include? a
|
||||
end
|
||||
|
||||
def flatten
|
||||
set = []
|
||||
@addresses.each do |a|
|
||||
if a.respond_to? :flatten
|
||||
set.concat a.flatten
|
||||
else
|
||||
set.push a
|
||||
end
|
||||
end
|
||||
set
|
||||
end
|
||||
|
||||
def each_address( &block )
|
||||
flatten.each(&block)
|
||||
end
|
||||
|
||||
def add( a )
|
||||
@addresses.push a
|
||||
end
|
||||
|
||||
alias push add
|
||||
|
||||
def delete( a )
|
||||
@addresses.delete a
|
||||
end
|
||||
|
||||
include StrategyInterface
|
||||
|
||||
def accept( strategy, dummy1 = nil, dummy2 = nil )
|
||||
strategy.phrase @name
|
||||
strategy.meta ':'
|
||||
strategy.space
|
||||
first = true
|
||||
each do |mbox|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
strategy.meta ','
|
||||
end
|
||||
strategy.space
|
||||
mbox.accept strategy
|
||||
end
|
||||
strategy.meta ';'
|
||||
strategy.lwsp ''
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
47
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/attachments.rb
vendored
Normal file
47
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/attachments.rb
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
=begin rdoc
|
||||
|
||||
= Attachment handling class
|
||||
|
||||
=end
|
||||
|
||||
require 'stringio'
|
||||
|
||||
module TMail
|
||||
class Attachment < StringIO
|
||||
attr_accessor :original_filename, :content_type
|
||||
end
|
||||
|
||||
class Mail
|
||||
def has_attachments?
|
||||
multipart? && parts.any? { |part| attachment?(part) }
|
||||
end
|
||||
|
||||
def attachment?(part)
|
||||
(part['content-disposition'] && part['content-disposition'].disposition == "attachment") ||
|
||||
part.header['content-type'].main_type != "text"
|
||||
end
|
||||
|
||||
def attachments
|
||||
if multipart?
|
||||
parts.collect { |part|
|
||||
if part.multipart?
|
||||
part.attachments
|
||||
elsif attachment?(part)
|
||||
content = part.body # unquoted automatically by TMail#body
|
||||
file_name = (part['content-location'] &&
|
||||
part['content-location'].body) ||
|
||||
part.sub_header("content-type", "name") ||
|
||||
part.sub_header("content-disposition", "filename")
|
||||
|
||||
next if file_name.blank? || content.blank?
|
||||
|
||||
attachment = Attachment.new(content)
|
||||
attachment.original_filename = file_name.strip
|
||||
attachment.content_type = part.content_type
|
||||
attachment
|
||||
end
|
||||
}.flatten.compact
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
52
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/base64.rb
vendored
Executable file
52
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/base64.rb
vendored
Executable file
@@ -0,0 +1,52 @@
|
||||
# = TITLE:
|
||||
#
|
||||
# Base64
|
||||
#
|
||||
# = COPYRIGHT:
|
||||
#
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
|
||||
#
|
||||
module TMail
|
||||
|
||||
module Base64
|
||||
|
||||
module_function
|
||||
|
||||
def folding_encode( str, eol = "\n", limit = 60 )
|
||||
[str].pack('m')
|
||||
end
|
||||
|
||||
def encode( str )
|
||||
[str].pack('m').tr( "\r\n", '' )
|
||||
end
|
||||
|
||||
def decode( str, strict = false )
|
||||
str.unpack('m').first
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
39
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/compat.rb
vendored
Normal file
39
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/compat.rb
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
unless Enumerable.method_defined?(:map)
|
||||
module Enumerable
|
||||
alias map collect
|
||||
end
|
||||
end
|
||||
|
||||
unless Enumerable.method_defined?(:select)
|
||||
module Enumerable
|
||||
alias select find_all
|
||||
end
|
||||
end
|
||||
|
||||
unless Enumerable.method_defined?(:reject)
|
||||
module Enumerable
|
||||
def reject
|
||||
result = []
|
||||
each do |i|
|
||||
result.push i unless yield(i)
|
||||
end
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless Enumerable.method_defined?(:sort_by)
|
||||
module Enumerable
|
||||
def sort_by
|
||||
map {|i| [yield(i), i] }.sort.map {|val, i| i }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless File.respond_to?(:read)
|
||||
def File.read(fname)
|
||||
File.open(fname) {|f|
|
||||
return f.read
|
||||
}
|
||||
end
|
||||
end
|
||||
71
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/config.rb
vendored
Executable file
71
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/config.rb
vendored
Executable file
@@ -0,0 +1,71 @@
|
||||
=begin rdoc
|
||||
|
||||
= Configuration Class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
module TMail
|
||||
|
||||
class Config
|
||||
|
||||
def initialize( strict )
|
||||
@strict_parse = strict
|
||||
@strict_base64decode = strict
|
||||
end
|
||||
|
||||
def strict_parse?
|
||||
@strict_parse
|
||||
end
|
||||
|
||||
attr_writer :strict_parse
|
||||
|
||||
def strict_base64decode?
|
||||
@strict_base64decode
|
||||
end
|
||||
|
||||
attr_writer :strict_base64decode
|
||||
|
||||
def new_body_port( mail )
|
||||
StringPort.new
|
||||
end
|
||||
|
||||
alias new_preamble_port new_body_port
|
||||
alias new_part_port new_body_port
|
||||
|
||||
end
|
||||
|
||||
DEFAULT_CONFIG = Config.new(false)
|
||||
DEFAULT_STRICT_CONFIG = Config.new(true)
|
||||
|
||||
def Config.to_config( arg )
|
||||
return DEFAULT_STRICT_CONFIG if arg == true
|
||||
return DEFAULT_CONFIG if arg == false
|
||||
arg or DEFAULT_CONFIG
|
||||
end
|
||||
|
||||
end
|
||||
67
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/core_extensions.rb
vendored
Normal file
67
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/core_extensions.rb
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
=begin rdoc
|
||||
|
||||
= Ruby on Rails Core Extensions
|
||||
|
||||
provides .blank?
|
||||
|
||||
=end
|
||||
unless Object.respond_to?(:blank?) #:nodoc:
|
||||
# Check first to see if we are in a Rails environment, no need to
|
||||
# define these methods if we are
|
||||
class Object
|
||||
# An object is blank if it's nil, empty, or a whitespace string.
|
||||
# For example, "", " ", nil, [], and {} are blank.
|
||||
#
|
||||
# This simplifies
|
||||
# if !address.nil? && !address.empty?
|
||||
# to
|
||||
# if !address.blank?
|
||||
def blank?
|
||||
if respond_to?(:empty?) && respond_to?(:strip)
|
||||
empty? or strip.empty?
|
||||
elsif respond_to?(:empty?)
|
||||
empty?
|
||||
else
|
||||
!self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NilClass #:nodoc:
|
||||
def blank?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class FalseClass #:nodoc:
|
||||
def blank?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class TrueClass #:nodoc:
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class Array #:nodoc:
|
||||
alias_method :blank?, :empty?
|
||||
end
|
||||
|
||||
class Hash #:nodoc:
|
||||
alias_method :blank?, :empty?
|
||||
end
|
||||
|
||||
class String #:nodoc:
|
||||
def blank?
|
||||
empty? || strip.empty?
|
||||
end
|
||||
end
|
||||
|
||||
class Numeric #:nodoc:
|
||||
def blank?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
481
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/encode.rb
vendored
Executable file
481
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/encode.rb
vendored
Executable file
@@ -0,0 +1,481 @@
|
||||
=begin rdoc
|
||||
|
||||
= Text Encoding class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'nkf'
|
||||
require 'tmail/base64.rb'
|
||||
require 'tmail/stringio'
|
||||
require 'tmail/utils'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
module StrategyInterface
|
||||
|
||||
def create_dest( obj )
|
||||
case obj
|
||||
when nil
|
||||
StringOutput.new
|
||||
when String
|
||||
StringOutput.new(obj)
|
||||
when IO, StringOutput
|
||||
obj
|
||||
else
|
||||
raise TypeError, 'cannot handle this type of object for dest'
|
||||
end
|
||||
end
|
||||
module_function :create_dest
|
||||
|
||||
def encoded( eol = "\r\n", charset = 'j', dest = nil )
|
||||
accept_strategy Encoder, eol, charset, dest
|
||||
end
|
||||
|
||||
def decoded( eol = "\n", charset = 'e', dest = nil )
|
||||
# Turn the E-Mail into a string and return it with all
|
||||
# encoded characters decoded. alias for to_s
|
||||
accept_strategy Decoder, eol, charset, dest
|
||||
end
|
||||
|
||||
alias to_s decoded
|
||||
|
||||
def accept_strategy( klass, eol, charset, dest = nil )
|
||||
dest ||= ''
|
||||
accept klass.new( create_dest(dest), charset, eol )
|
||||
dest
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
### MIME B encoding decoder
|
||||
###
|
||||
|
||||
class Decoder
|
||||
|
||||
include TextUtils
|
||||
|
||||
encoded = '=\?(?:iso-2022-jp|euc-jp|shift_jis)\?[QB]\?[a-z0-9+/=]+\?='
|
||||
ENCODED_WORDS = /#{encoded}(?:\s+#{encoded})*/i
|
||||
|
||||
OUTPUT_ENCODING = {
|
||||
'EUC' => 'e',
|
||||
'SJIS' => 's',
|
||||
}
|
||||
|
||||
def self.decode( str, encoding = nil )
|
||||
encoding ||= (OUTPUT_ENCODING[$KCODE] || 'j')
|
||||
opt = '-m' + encoding
|
||||
str.gsub(ENCODED_WORDS) {|s| NKF.nkf(opt, s) }
|
||||
end
|
||||
|
||||
def initialize( dest, encoding = nil, eol = "\n" )
|
||||
@f = StrategyInterface.create_dest(dest)
|
||||
@encoding = (/\A[ejs]/ === encoding) ? encoding[0,1] : nil
|
||||
@eol = eol
|
||||
end
|
||||
|
||||
def decode( str )
|
||||
self.class.decode(str, @encoding)
|
||||
end
|
||||
private :decode
|
||||
|
||||
def terminate
|
||||
end
|
||||
|
||||
def header_line( str )
|
||||
@f << decode(str)
|
||||
end
|
||||
|
||||
def header_name( nm )
|
||||
@f << nm << ': '
|
||||
end
|
||||
|
||||
def header_body( str )
|
||||
@f << decode(str)
|
||||
end
|
||||
|
||||
def space
|
||||
@f << ' '
|
||||
end
|
||||
|
||||
alias spc space
|
||||
|
||||
def lwsp( str )
|
||||
@f << str
|
||||
end
|
||||
|
||||
def meta( str )
|
||||
@f << str
|
||||
end
|
||||
|
||||
def text( str )
|
||||
@f << decode(str)
|
||||
end
|
||||
|
||||
def phrase( str )
|
||||
@f << quote_phrase(decode(str))
|
||||
end
|
||||
|
||||
def kv_pair( k, v )
|
||||
v = dquote(v) unless token_safe?(v)
|
||||
@f << k << '=' << v
|
||||
end
|
||||
|
||||
def puts( str = nil )
|
||||
@f << str if str
|
||||
@f << @eol
|
||||
end
|
||||
|
||||
def write( str )
|
||||
@f << str
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
### MIME B-encoding encoder
|
||||
###
|
||||
|
||||
#
|
||||
# FIXME: This class can handle only (euc-jp/shift_jis -> iso-2022-jp).
|
||||
#
|
||||
class Encoder
|
||||
|
||||
include TextUtils
|
||||
|
||||
BENCODE_DEBUG = false unless defined?(BENCODE_DEBUG)
|
||||
|
||||
def Encoder.encode( str )
|
||||
e = new()
|
||||
e.header_body str
|
||||
e.terminate
|
||||
e.dest.string
|
||||
end
|
||||
|
||||
SPACER = "\t"
|
||||
MAX_LINE_LEN = 70
|
||||
|
||||
OPTIONS = {
|
||||
'EUC' => '-Ej -m0',
|
||||
'SJIS' => '-Sj -m0',
|
||||
'UTF8' => nil, # FIXME
|
||||
'NONE' => nil
|
||||
}
|
||||
|
||||
def initialize( dest = nil, encoding = nil, eol = "\r\n", limit = nil )
|
||||
@f = StrategyInterface.create_dest(dest)
|
||||
@opt = OPTIONS[$KCODE]
|
||||
@eol = eol
|
||||
@preserve_quotes = true
|
||||
reset
|
||||
end
|
||||
|
||||
def preserve_quotes=( bool )
|
||||
@preserve_quotes
|
||||
end
|
||||
|
||||
def preserve_quotes
|
||||
@preserve_quotes
|
||||
end
|
||||
|
||||
def normalize_encoding( str )
|
||||
if @opt
|
||||
then NKF.nkf(@opt, str)
|
||||
else str
|
||||
end
|
||||
end
|
||||
|
||||
def reset
|
||||
@text = ''
|
||||
@lwsp = ''
|
||||
@curlen = 0
|
||||
end
|
||||
|
||||
def terminate
|
||||
add_lwsp ''
|
||||
reset
|
||||
end
|
||||
|
||||
def dest
|
||||
@f
|
||||
end
|
||||
|
||||
def puts( str = nil )
|
||||
@f << str if str
|
||||
@f << @eol
|
||||
end
|
||||
|
||||
def write( str )
|
||||
@f << str
|
||||
end
|
||||
|
||||
#
|
||||
# add
|
||||
#
|
||||
|
||||
def header_line( line )
|
||||
scanadd line
|
||||
end
|
||||
|
||||
def header_name( name )
|
||||
add_text name.split(/-/).map {|i| i.capitalize }.join('-')
|
||||
add_text ':'
|
||||
add_lwsp ' '
|
||||
end
|
||||
|
||||
def header_body( str )
|
||||
scanadd normalize_encoding(str)
|
||||
end
|
||||
|
||||
def space
|
||||
add_lwsp ' '
|
||||
end
|
||||
|
||||
alias spc space
|
||||
|
||||
def lwsp( str )
|
||||
add_lwsp str.sub(/[\r\n]+[^\r\n]*\z/, '')
|
||||
end
|
||||
|
||||
def meta( str )
|
||||
add_text str
|
||||
end
|
||||
|
||||
def text( str )
|
||||
scanadd normalize_encoding(str)
|
||||
end
|
||||
|
||||
def phrase( str )
|
||||
str = normalize_encoding(str)
|
||||
if CONTROL_CHAR === str
|
||||
scanadd str
|
||||
else
|
||||
add_text quote_phrase(str)
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: implement line folding
|
||||
#
|
||||
def kv_pair( k, v )
|
||||
return if v.nil?
|
||||
v = normalize_encoding(v)
|
||||
if token_safe?(v)
|
||||
add_text k + '=' + v
|
||||
elsif not CONTROL_CHAR === v
|
||||
add_text k + '=' + quote_token(v)
|
||||
else
|
||||
# apply RFC2231 encoding
|
||||
kv = k + '*=' + "iso-2022-jp'ja'" + encode_value(v)
|
||||
add_text kv
|
||||
end
|
||||
end
|
||||
|
||||
def encode_value( str )
|
||||
str.gsub(TOKEN_UNSAFE) {|s| '%%%02x' % s[0] }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scanadd( str, force = false )
|
||||
types = ''
|
||||
strs = []
|
||||
|
||||
until str.empty?
|
||||
if m = /\A[^\e\t\r\n ]+/.match(str)
|
||||
types << (force ? 'j' : 'a')
|
||||
strs.push m[0]
|
||||
|
||||
elsif m = /\A[\t\r\n ]+/.match(str)
|
||||
types << 's'
|
||||
strs.push m[0]
|
||||
|
||||
elsif m = /\A\e../.match(str)
|
||||
esc = m[0]
|
||||
str = m.post_match
|
||||
if esc != "\e(B" and m = /\A[^\e]+/.match(str)
|
||||
types << 'j'
|
||||
strs.push m[0]
|
||||
end
|
||||
|
||||
else
|
||||
raise 'TMail FATAL: encoder scan fail'
|
||||
end
|
||||
(str = m.post_match) unless m.nil?
|
||||
end
|
||||
|
||||
do_encode types, strs
|
||||
end
|
||||
|
||||
def do_encode( types, strs )
|
||||
#
|
||||
# result : (A|E)(S(A|E))*
|
||||
# E : W(SW)*
|
||||
# W : (J|A)+ but must contain J # (J|A)*J(J|A)*
|
||||
# A : <<A character string not to be encoded>>
|
||||
# J : <<A character string to be encoded>>
|
||||
# S : <<LWSP>>
|
||||
#
|
||||
# An encoding unit is `E'.
|
||||
# Input (parameter `types') is (J|A)(J|A|S)*(J|A)
|
||||
#
|
||||
if BENCODE_DEBUG
|
||||
puts
|
||||
puts '-- do_encode ------------'
|
||||
puts types.split(//).join(' ')
|
||||
p strs
|
||||
end
|
||||
|
||||
e = /[ja]*j[ja]*(?:s[ja]*j[ja]*)*/
|
||||
|
||||
while m = e.match(types)
|
||||
pre = m.pre_match
|
||||
concat_A_S pre, strs[0, pre.size] unless pre.empty?
|
||||
concat_E m[0], strs[m.begin(0) ... m.end(0)]
|
||||
types = m.post_match
|
||||
strs.slice! 0, m.end(0)
|
||||
end
|
||||
concat_A_S types, strs
|
||||
end
|
||||
|
||||
def concat_A_S( types, strs )
|
||||
i = 0
|
||||
types.each_byte do |t|
|
||||
case t
|
||||
when ?a then add_text strs[i]
|
||||
when ?s then add_lwsp strs[i]
|
||||
else
|
||||
raise "TMail FATAL: unknown flag: #{t.chr}"
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
METHOD_ID = {
|
||||
?j => :extract_J,
|
||||
?e => :extract_E,
|
||||
?a => :extract_A,
|
||||
?s => :extract_S
|
||||
}
|
||||
|
||||
def concat_E( types, strs )
|
||||
if BENCODE_DEBUG
|
||||
puts '---- concat_E'
|
||||
puts "types=#{types.split(//).join(' ')}"
|
||||
puts "strs =#{strs.inspect}"
|
||||
end
|
||||
|
||||
flush() unless @text.empty?
|
||||
|
||||
chunk = ''
|
||||
strs.each_with_index do |s,i|
|
||||
mid = METHOD_ID[types[i]]
|
||||
until s.empty?
|
||||
unless c = __send__(mid, chunk.size, s)
|
||||
add_with_encode chunk unless chunk.empty?
|
||||
flush
|
||||
chunk = ''
|
||||
fold
|
||||
c = __send__(mid, 0, s)
|
||||
raise 'TMail FATAL: extract fail' unless c
|
||||
end
|
||||
chunk << c
|
||||
end
|
||||
end
|
||||
add_with_encode chunk unless chunk.empty?
|
||||
end
|
||||
|
||||
def extract_J( chunksize, str )
|
||||
size = max_bytes(chunksize, str.size) - 6
|
||||
size = (size % 2 == 0) ? (size) : (size - 1)
|
||||
return nil if size <= 0
|
||||
"\e$B#{str.slice!(0, size)}\e(B"
|
||||
end
|
||||
|
||||
def extract_A( chunksize, str )
|
||||
size = max_bytes(chunksize, str.size)
|
||||
return nil if size <= 0
|
||||
str.slice!(0, size)
|
||||
end
|
||||
|
||||
alias extract_S extract_A
|
||||
|
||||
def max_bytes( chunksize, ssize )
|
||||
(restsize() - '=?iso-2022-jp?B??='.size) / 4 * 3 - chunksize
|
||||
end
|
||||
|
||||
#
|
||||
# free length buffer
|
||||
#
|
||||
|
||||
def add_text( str )
|
||||
@text << str
|
||||
# puts '---- text -------------------------------------'
|
||||
# puts "+ #{str.inspect}"
|
||||
# puts "txt >>>#{@text.inspect}<<<"
|
||||
end
|
||||
|
||||
def add_with_encode( str )
|
||||
@text << "=?iso-2022-jp?B?#{Base64.encode(str)}?="
|
||||
end
|
||||
|
||||
def add_lwsp( lwsp )
|
||||
# puts '---- lwsp -------------------------------------'
|
||||
# puts "+ #{lwsp.inspect}"
|
||||
fold if restsize() <= 0
|
||||
flush
|
||||
@lwsp = lwsp
|
||||
end
|
||||
|
||||
def flush
|
||||
# puts '---- flush ----'
|
||||
# puts "spc >>>#{@lwsp.inspect}<<<"
|
||||
# puts "txt >>>#{@text.inspect}<<<"
|
||||
@f << @lwsp << @text
|
||||
@curlen += (@lwsp.size + @text.size)
|
||||
@text = ''
|
||||
@lwsp = ''
|
||||
end
|
||||
|
||||
def fold
|
||||
# puts '---- fold ----'
|
||||
@f << @eol
|
||||
@curlen = 0
|
||||
@lwsp = SPACER
|
||||
end
|
||||
|
||||
def restsize
|
||||
MAX_LINE_LEN - (@curlen + @lwsp.size + @text.size)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
552
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/facade.rb
vendored
Executable file
552
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/facade.rb
vendored
Executable file
@@ -0,0 +1,552 @@
|
||||
#
|
||||
# facade.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
def header_string( name, default = nil )
|
||||
h = @header[name.downcase] or return default
|
||||
h.to_s
|
||||
end
|
||||
|
||||
###
|
||||
### attributes
|
||||
###
|
||||
|
||||
include TextUtils
|
||||
|
||||
def set_string_array_attr( key, strs )
|
||||
strs.flatten!
|
||||
if strs.empty?
|
||||
@header.delete key.downcase
|
||||
else
|
||||
store key, strs.join(', ')
|
||||
end
|
||||
strs
|
||||
end
|
||||
private :set_string_array_attr
|
||||
|
||||
def set_string_attr( key, str )
|
||||
if str
|
||||
store key, str
|
||||
else
|
||||
@header.delete key.downcase
|
||||
end
|
||||
str
|
||||
end
|
||||
private :set_string_attr
|
||||
|
||||
def set_addrfield( name, arg )
|
||||
if arg
|
||||
h = HeaderField.internal_new(name, @config)
|
||||
h.addrs.replace [arg].flatten
|
||||
@header[name] = h
|
||||
else
|
||||
@header.delete name
|
||||
end
|
||||
arg
|
||||
end
|
||||
private :set_addrfield
|
||||
|
||||
def addrs2specs( addrs )
|
||||
return nil unless addrs
|
||||
list = addrs.map {|addr|
|
||||
if addr.address_group?
|
||||
then addr.map {|a| a.spec }
|
||||
else addr.spec
|
||||
end
|
||||
}.flatten
|
||||
return nil if list.empty?
|
||||
list
|
||||
end
|
||||
private :addrs2specs
|
||||
|
||||
|
||||
#
|
||||
# date time
|
||||
#
|
||||
|
||||
def date( default = nil )
|
||||
if h = @header['date']
|
||||
h.date
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def date=( time )
|
||||
if time
|
||||
store 'Date', time2str(time)
|
||||
else
|
||||
@header.delete 'date'
|
||||
end
|
||||
time
|
||||
end
|
||||
|
||||
def strftime( fmt, default = nil )
|
||||
if t = date
|
||||
t.strftime(fmt)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# destination
|
||||
#
|
||||
|
||||
def to_addrs( default = nil )
|
||||
if h = @header['to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def cc_addrs( default = nil )
|
||||
if h = @header['cc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def bcc_addrs( default = nil )
|
||||
if h = @header['bcc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def to_addrs=( arg )
|
||||
set_addrfield 'to', arg
|
||||
end
|
||||
|
||||
def cc_addrs=( arg )
|
||||
set_addrfield 'cc', arg
|
||||
end
|
||||
|
||||
def bcc_addrs=( arg )
|
||||
set_addrfield 'bcc', arg
|
||||
end
|
||||
|
||||
def to( default = nil )
|
||||
addrs2specs(to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def cc( default = nil )
|
||||
addrs2specs(cc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def bcc( default = nil )
|
||||
addrs2specs(bcc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def to=( *strs )
|
||||
set_string_array_attr 'To', strs
|
||||
end
|
||||
|
||||
def cc=( *strs )
|
||||
set_string_array_attr 'Cc', strs
|
||||
end
|
||||
|
||||
def bcc=( *strs )
|
||||
set_string_array_attr 'Bcc', strs
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# originator
|
||||
#
|
||||
|
||||
def from_addrs( default = nil )
|
||||
if h = @header['from']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def from_addrs=( arg )
|
||||
set_addrfield 'from', arg
|
||||
end
|
||||
|
||||
def from( default = nil )
|
||||
addrs2specs(from_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def from=( *strs )
|
||||
set_string_array_attr 'From', strs
|
||||
end
|
||||
|
||||
def friendly_from( default = nil )
|
||||
h = @header['from']
|
||||
a, = h.addrs
|
||||
return default unless a
|
||||
return a.phrase if a.phrase
|
||||
return h.comments.join(' ') unless h.comments.empty?
|
||||
a.spec
|
||||
end
|
||||
|
||||
|
||||
def reply_to_addrs( default = nil )
|
||||
if h = @header['reply-to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def reply_to_addrs=( arg )
|
||||
set_addrfield 'reply-to', arg
|
||||
end
|
||||
|
||||
def reply_to( default = nil )
|
||||
addrs2specs(reply_to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def reply_to=( *strs )
|
||||
set_string_array_attr 'Reply-To', strs
|
||||
end
|
||||
|
||||
|
||||
def sender_addr( default = nil )
|
||||
f = @header['sender'] or return default
|
||||
f.addr or return default
|
||||
end
|
||||
|
||||
def sender_addr=( addr )
|
||||
if addr
|
||||
h = HeaderField.internal_new('sender', @config)
|
||||
h.addr = addr
|
||||
@header['sender'] = h
|
||||
else
|
||||
@header.delete 'sender'
|
||||
end
|
||||
addr
|
||||
end
|
||||
|
||||
def sender( default )
|
||||
f = @header['sender'] or return default
|
||||
a = f.addr or return default
|
||||
a.spec
|
||||
end
|
||||
|
||||
def sender=( str )
|
||||
set_string_attr 'Sender', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# subject
|
||||
#
|
||||
|
||||
def subject( default = nil )
|
||||
if h = @header['subject']
|
||||
h.body
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
alias quoted_subject subject
|
||||
|
||||
def subject=( str )
|
||||
set_string_attr 'Subject', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# identity & threading
|
||||
#
|
||||
|
||||
def message_id( default = nil )
|
||||
if h = @header['message-id']
|
||||
h.id || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def message_id=( str )
|
||||
set_string_attr 'Message-Id', str
|
||||
end
|
||||
|
||||
def in_reply_to( default = nil )
|
||||
if h = @header['in-reply-to']
|
||||
h.ids
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def in_reply_to=( *idstrs )
|
||||
set_string_array_attr 'In-Reply-To', idstrs
|
||||
end
|
||||
|
||||
def references( default = nil )
|
||||
if h = @header['references']
|
||||
h.refs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def references=( *strs )
|
||||
set_string_array_attr 'References', strs
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# MIME headers
|
||||
#
|
||||
|
||||
def mime_version( default = nil )
|
||||
if h = @header['mime-version']
|
||||
h.version || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def mime_version=( m, opt = nil )
|
||||
if opt
|
||||
if h = @header['mime-version']
|
||||
h.major = m
|
||||
h.minor = opt
|
||||
else
|
||||
store 'Mime-Version', "#{m}.#{opt}"
|
||||
end
|
||||
else
|
||||
store 'Mime-Version', m
|
||||
end
|
||||
m
|
||||
end
|
||||
|
||||
|
||||
def content_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.content_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def main_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.main_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def sub_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.sub_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def set_content_type( str, sub = nil, param = nil )
|
||||
if sub
|
||||
main, sub = str, sub
|
||||
else
|
||||
main, sub = str.split(%r</>, 2)
|
||||
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
|
||||
end
|
||||
if h = @header['content-type']
|
||||
h.main_type = main
|
||||
h.sub_type = sub
|
||||
h.params.clear
|
||||
else
|
||||
store 'Content-Type', "#{main}/#{sub}"
|
||||
end
|
||||
@header['content-type'].params.replace param if param
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
alias content_type= set_content_type
|
||||
|
||||
def type_param( name, default = nil )
|
||||
if h = @header['content-type']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset( default = nil )
|
||||
if h = @header['content-type']
|
||||
h['charset'] or default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset=( str )
|
||||
if str
|
||||
if h = @header[ 'content-type' ]
|
||||
h['charset'] = str
|
||||
else
|
||||
store 'Content-Type', "text/plain; charset=#{str}"
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
|
||||
def transfer_encoding( default = nil )
|
||||
if h = @header['content-transfer-encoding']
|
||||
h.encoding || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_encoding=( str )
|
||||
set_string_attr 'Content-Transfer-Encoding', str
|
||||
end
|
||||
|
||||
alias encoding transfer_encoding
|
||||
alias encoding= transfer_encoding=
|
||||
alias content_transfer_encoding transfer_encoding
|
||||
alias content_transfer_encoding= transfer_encoding=
|
||||
|
||||
|
||||
def disposition( default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias content_disposition disposition
|
||||
|
||||
def set_disposition( str, params = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition = str
|
||||
h.params.clear
|
||||
else
|
||||
store('Content-Disposition', str)
|
||||
h = @header['content-disposition']
|
||||
end
|
||||
h.params.replace params if params
|
||||
end
|
||||
|
||||
alias disposition= set_disposition
|
||||
alias set_content_disposition set_disposition
|
||||
alias content_disposition= set_disposition
|
||||
|
||||
def disposition_param( name, default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
### utils
|
||||
###
|
||||
|
||||
def create_reply
|
||||
mail = TMail::Mail.parse('')
|
||||
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
|
||||
mail.to_addrs = reply_addresses([])
|
||||
mail.in_reply_to = [message_id(nil)].compact
|
||||
mail.references = references([]) + [message_id(nil)].compact
|
||||
mail.mime_version = '1.0'
|
||||
mail
|
||||
end
|
||||
|
||||
|
||||
def base64_encode
|
||||
store 'Content-Transfer-Encoding', 'Base64'
|
||||
self.body = Base64.folding_encode(self.body)
|
||||
end
|
||||
|
||||
def base64_decode
|
||||
if /base64/i === self.transfer_encoding('')
|
||||
store 'Content-Transfer-Encoding', '8bit'
|
||||
self.body = Base64.decode(self.body, @config.strict_base64decode?)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def destinations( default = nil )
|
||||
ret = []
|
||||
%w( to cc bcc ).each do |nm|
|
||||
if h = @header[nm]
|
||||
h.addrs.each {|i| ret.push i.address }
|
||||
end
|
||||
end
|
||||
ret.empty? ? default : ret
|
||||
end
|
||||
|
||||
def each_destination( &block )
|
||||
destinations([]).each do |i|
|
||||
if Address === i
|
||||
yield i
|
||||
else
|
||||
i.each(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias each_dest each_destination
|
||||
|
||||
|
||||
def reply_addresses( default = nil )
|
||||
reply_to_addrs(nil) or from_addrs(nil) or default
|
||||
end
|
||||
|
||||
def error_reply_addresses( default = nil )
|
||||
if s = sender(nil)
|
||||
[s]
|
||||
else
|
||||
from_addrs(default)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def multipart?
|
||||
main_type('').downcase == 'multipart'
|
||||
end
|
||||
|
||||
end # class Mail
|
||||
|
||||
end # module TMail
|
||||
931
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/header.rb
vendored
Executable file
931
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/header.rb
vendored
Executable file
@@ -0,0 +1,931 @@
|
||||
=begin rdoc
|
||||
|
||||
= Header handling class
|
||||
|
||||
=end
|
||||
# RFC #822 ftp://ftp.isi.edu/in-notes/rfc822.txt
|
||||
#
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/encode'
|
||||
require 'tmail/address'
|
||||
require 'tmail/parser'
|
||||
require 'tmail/config'
|
||||
require 'tmail/utils'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class HeaderField
|
||||
|
||||
include TextUtils
|
||||
|
||||
class << self
|
||||
|
||||
alias newobj new
|
||||
|
||||
def new( name, body, conf = DEFAULT_CONFIG )
|
||||
klass = FNAME_TO_CLASS[name.downcase] || UnstructuredHeader
|
||||
klass.newobj body, conf
|
||||
end
|
||||
|
||||
def new_from_port( port, name, conf = DEFAULT_CONFIG )
|
||||
re = Regep.new('\A(' + Regexp.quote(name) + '):', 'i')
|
||||
str = nil
|
||||
port.ropen {|f|
|
||||
f.each do |line|
|
||||
if m = re.match(line) then str = m.post_match.strip
|
||||
elsif str and /\A[\t ]/ === line then str << ' ' << line.strip
|
||||
elsif /\A-*\s*\z/ === line then break
|
||||
elsif str then break
|
||||
end
|
||||
end
|
||||
}
|
||||
new(name, str, Config.to_config(conf))
|
||||
end
|
||||
|
||||
def internal_new( name, conf )
|
||||
FNAME_TO_CLASS[name].newobj('', conf, true)
|
||||
end
|
||||
|
||||
end # class << self
|
||||
|
||||
def initialize( body, conf, intern = false )
|
||||
@body = body
|
||||
@config = conf
|
||||
|
||||
@illegal = false
|
||||
@parsed = false
|
||||
|
||||
if intern
|
||||
@parsed = true
|
||||
parse_init
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{@body.inspect}>"
|
||||
end
|
||||
|
||||
def illegal?
|
||||
@illegal
|
||||
end
|
||||
|
||||
def empty?
|
||||
ensure_parsed
|
||||
return true if @illegal
|
||||
isempty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_parsed
|
||||
return if @parsed
|
||||
@parsed = true
|
||||
parse
|
||||
end
|
||||
|
||||
# defabstract parse
|
||||
# end
|
||||
|
||||
def clear_parse_status
|
||||
@parsed = false
|
||||
@illegal = false
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def body
|
||||
ensure_parsed
|
||||
v = Decoder.new(s = '')
|
||||
do_accept v
|
||||
v.terminate
|
||||
s
|
||||
end
|
||||
|
||||
def body=( str )
|
||||
@body = str
|
||||
clear_parse_status
|
||||
end
|
||||
|
||||
include StrategyInterface
|
||||
|
||||
def accept( strategy )
|
||||
ensure_parsed
|
||||
do_accept strategy
|
||||
strategy.terminate
|
||||
end
|
||||
|
||||
# abstract do_accept
|
||||
|
||||
end
|
||||
|
||||
|
||||
class UnstructuredHeader < HeaderField
|
||||
|
||||
def body
|
||||
ensure_parsed
|
||||
@body
|
||||
end
|
||||
|
||||
def body=( arg )
|
||||
ensure_parsed
|
||||
@body = arg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_init
|
||||
end
|
||||
|
||||
def parse
|
||||
@body = Decoder.decode(@body.gsub(/\n|\r\n|\r/, ''))
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not @body
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
strategy.text @body
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class StructuredHeader < HeaderField
|
||||
|
||||
def comments
|
||||
ensure_parsed
|
||||
@comments
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse
|
||||
save = nil
|
||||
|
||||
begin
|
||||
parse_init
|
||||
do_parse
|
||||
rescue SyntaxError
|
||||
if not save and mime_encoded? @body
|
||||
save = @body
|
||||
@body = Decoder.decode(save)
|
||||
retry
|
||||
elsif save
|
||||
@body = save
|
||||
end
|
||||
|
||||
@illegal = true
|
||||
raise if @config.strict_parse?
|
||||
end
|
||||
end
|
||||
|
||||
def parse_init
|
||||
@comments = []
|
||||
init
|
||||
end
|
||||
|
||||
def do_parse
|
||||
quote_boundary
|
||||
obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments)
|
||||
set obj if obj
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class DateTimeHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :DATETIME
|
||||
|
||||
def date
|
||||
ensure_parsed
|
||||
@date
|
||||
end
|
||||
|
||||
def date=( arg )
|
||||
ensure_parsed
|
||||
@date = arg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@date = nil
|
||||
end
|
||||
|
||||
def set( t )
|
||||
@date = t
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not @date
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
strategy.meta time2str(@date)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class AddressHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :MADDRESS
|
||||
|
||||
def addrs
|
||||
ensure_parsed
|
||||
@addrs
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@addrs = []
|
||||
end
|
||||
|
||||
def set( a )
|
||||
@addrs = a
|
||||
end
|
||||
|
||||
def isempty?
|
||||
@addrs.empty?
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
first = true
|
||||
@addrs.each do |a|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
strategy.meta ','
|
||||
strategy.space
|
||||
end
|
||||
a.accept strategy
|
||||
end
|
||||
|
||||
@comments.each do |c|
|
||||
strategy.space
|
||||
strategy.meta '('
|
||||
strategy.text c
|
||||
strategy.meta ')'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ReturnPathHeader < AddressHeader
|
||||
|
||||
PARSE_TYPE = :RETPATH
|
||||
|
||||
def addr
|
||||
addrs()[0]
|
||||
end
|
||||
|
||||
def spec
|
||||
a = addr() or return nil
|
||||
a.spec
|
||||
end
|
||||
|
||||
def routes
|
||||
a = addr() or return nil
|
||||
a.routes
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def do_accept( strategy )
|
||||
a = addr()
|
||||
|
||||
strategy.meta '<'
|
||||
unless a.routes.empty?
|
||||
strategy.meta a.routes.map {|i| '@' + i }.join(',')
|
||||
strategy.meta ':'
|
||||
end
|
||||
spec = a.spec
|
||||
strategy.meta spec if spec
|
||||
strategy.meta '>'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class SingleAddressHeader < AddressHeader
|
||||
|
||||
def addr
|
||||
addrs()[0]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def do_accept( strategy )
|
||||
a = addr()
|
||||
a.accept strategy
|
||||
@comments.each do |c|
|
||||
strategy.space
|
||||
strategy.meta '('
|
||||
strategy.text c
|
||||
strategy.meta ')'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MessageIdHeader < StructuredHeader
|
||||
|
||||
def id
|
||||
ensure_parsed
|
||||
@id
|
||||
end
|
||||
|
||||
def id=( arg )
|
||||
ensure_parsed
|
||||
@id = arg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@id = nil
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not @id
|
||||
end
|
||||
|
||||
def do_parse
|
||||
@id = @body.slice(MESSAGE_ID) or
|
||||
raise SyntaxError, "wrong Message-ID format: #{@body}"
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
strategy.meta @id
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ReferencesHeader < StructuredHeader
|
||||
|
||||
def refs
|
||||
ensure_parsed
|
||||
@refs
|
||||
end
|
||||
|
||||
def each_id
|
||||
self.refs.each do |i|
|
||||
yield i if MESSAGE_ID === i
|
||||
end
|
||||
end
|
||||
|
||||
def ids
|
||||
ensure_parsed
|
||||
@ids
|
||||
end
|
||||
|
||||
def each_phrase
|
||||
self.refs.each do |i|
|
||||
yield i unless MESSAGE_ID === i
|
||||
end
|
||||
end
|
||||
|
||||
def phrases
|
||||
ret = []
|
||||
each_phrase {|i| ret.push i }
|
||||
ret
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@refs = []
|
||||
@ids = []
|
||||
end
|
||||
|
||||
def isempty?
|
||||
@ids.empty?
|
||||
end
|
||||
|
||||
def do_parse
|
||||
str = @body
|
||||
while m = MESSAGE_ID.match(str)
|
||||
pre = m.pre_match.strip
|
||||
@refs.push pre unless pre.empty?
|
||||
@refs.push s = m[0]
|
||||
@ids.push s
|
||||
str = m.post_match
|
||||
end
|
||||
str = str.strip
|
||||
@refs.push str unless str.empty?
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
first = true
|
||||
@ids.each do |i|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
strategy.space
|
||||
end
|
||||
strategy.meta i
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ReceivedHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :RECEIVED
|
||||
|
||||
def from
|
||||
ensure_parsed
|
||||
@from
|
||||
end
|
||||
|
||||
def from=( arg )
|
||||
ensure_parsed
|
||||
@from = arg
|
||||
end
|
||||
|
||||
def by
|
||||
ensure_parsed
|
||||
@by
|
||||
end
|
||||
|
||||
def by=( arg )
|
||||
ensure_parsed
|
||||
@by = arg
|
||||
end
|
||||
|
||||
def via
|
||||
ensure_parsed
|
||||
@via
|
||||
end
|
||||
|
||||
def via=( arg )
|
||||
ensure_parsed
|
||||
@via = arg
|
||||
end
|
||||
|
||||
def with
|
||||
ensure_parsed
|
||||
@with
|
||||
end
|
||||
|
||||
def id
|
||||
ensure_parsed
|
||||
@id
|
||||
end
|
||||
|
||||
def id=( arg )
|
||||
ensure_parsed
|
||||
@id = arg
|
||||
end
|
||||
|
||||
def _for
|
||||
ensure_parsed
|
||||
@_for
|
||||
end
|
||||
|
||||
def _for=( arg )
|
||||
ensure_parsed
|
||||
@_for = arg
|
||||
end
|
||||
|
||||
def date
|
||||
ensure_parsed
|
||||
@date
|
||||
end
|
||||
|
||||
def date=( arg )
|
||||
ensure_parsed
|
||||
@date = arg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@from = @by = @via = @with = @id = @_for = nil
|
||||
@with = []
|
||||
@date = nil
|
||||
end
|
||||
|
||||
def set( args )
|
||||
@from, @by, @via, @with, @id, @_for, @date = *args
|
||||
end
|
||||
|
||||
def isempty?
|
||||
@with.empty? and not (@from or @by or @via or @id or @_for or @date)
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
list = []
|
||||
list.push 'from ' + @from if @from
|
||||
list.push 'by ' + @by if @by
|
||||
list.push 'via ' + @via if @via
|
||||
@with.each do |i|
|
||||
list.push 'with ' + i
|
||||
end
|
||||
list.push 'id ' + @id if @id
|
||||
list.push 'for <' + @_for + '>' if @_for
|
||||
|
||||
first = true
|
||||
list.each do |i|
|
||||
strategy.space unless first
|
||||
strategy.meta i
|
||||
first = false
|
||||
end
|
||||
if @date
|
||||
strategy.meta ';'
|
||||
strategy.space
|
||||
strategy.meta time2str(@date)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class KeywordsHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :KEYWORDS
|
||||
|
||||
def keys
|
||||
ensure_parsed
|
||||
@keys
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@keys = []
|
||||
end
|
||||
|
||||
def set( a )
|
||||
@keys = a
|
||||
end
|
||||
|
||||
def isempty?
|
||||
@keys.empty?
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
first = true
|
||||
@keys.each do |i|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
strategy.meta ','
|
||||
end
|
||||
strategy.meta i
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class EncryptedHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :ENCRYPTED
|
||||
|
||||
def encrypter
|
||||
ensure_parsed
|
||||
@encrypter
|
||||
end
|
||||
|
||||
def encrypter=( arg )
|
||||
ensure_parsed
|
||||
@encrypter = arg
|
||||
end
|
||||
|
||||
def keyword
|
||||
ensure_parsed
|
||||
@keyword
|
||||
end
|
||||
|
||||
def keyword=( arg )
|
||||
ensure_parsed
|
||||
@keyword = arg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@encrypter = nil
|
||||
@keyword = nil
|
||||
end
|
||||
|
||||
def set( args )
|
||||
@encrypter, @keyword = args
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not (@encrypter or @keyword)
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
if @key
|
||||
strategy.meta @encrypter + ','
|
||||
strategy.space
|
||||
strategy.meta @keyword
|
||||
else
|
||||
strategy.meta @encrypter
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MimeVersionHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :MIMEVERSION
|
||||
|
||||
def major
|
||||
ensure_parsed
|
||||
@major
|
||||
end
|
||||
|
||||
def major=( arg )
|
||||
ensure_parsed
|
||||
@major = arg
|
||||
end
|
||||
|
||||
def minor
|
||||
ensure_parsed
|
||||
@minor
|
||||
end
|
||||
|
||||
def minor=( arg )
|
||||
ensure_parsed
|
||||
@minor = arg
|
||||
end
|
||||
|
||||
def version
|
||||
sprintf('%d.%d', major, minor)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@major = nil
|
||||
@minor = nil
|
||||
end
|
||||
|
||||
def set( args )
|
||||
@major, @minor = *args
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not (@major or @minor)
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
strategy.meta sprintf('%d.%d', @major, @minor)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ContentTypeHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :CTYPE
|
||||
|
||||
def main_type
|
||||
ensure_parsed
|
||||
@main
|
||||
end
|
||||
|
||||
def main_type=( arg )
|
||||
ensure_parsed
|
||||
@main = arg.downcase
|
||||
end
|
||||
|
||||
def sub_type
|
||||
ensure_parsed
|
||||
@sub
|
||||
end
|
||||
|
||||
def sub_type=( arg )
|
||||
ensure_parsed
|
||||
@sub = arg.downcase
|
||||
end
|
||||
|
||||
def content_type
|
||||
ensure_parsed
|
||||
@sub ? sprintf('%s/%s', @main, @sub) : @main
|
||||
end
|
||||
|
||||
def params
|
||||
ensure_parsed
|
||||
unless @params.blank?
|
||||
@params.each do |k, v|
|
||||
@params[k] = unquote(v)
|
||||
end
|
||||
end
|
||||
@params
|
||||
end
|
||||
|
||||
def []( key )
|
||||
ensure_parsed
|
||||
@params and unquote(@params[key])
|
||||
end
|
||||
|
||||
def []=( key, val )
|
||||
ensure_parsed
|
||||
(@params ||= {})[key] = val
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@main = @sub = @params = nil
|
||||
end
|
||||
|
||||
def set( args )
|
||||
@main, @sub, @params = *args
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not (@main or @sub)
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
if @sub
|
||||
strategy.meta sprintf('%s/%s', @main, @sub)
|
||||
else
|
||||
strategy.meta @main
|
||||
end
|
||||
@params.each do |k,v|
|
||||
if v
|
||||
strategy.meta ';'
|
||||
strategy.space
|
||||
strategy.kv_pair k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ContentTransferEncodingHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :CENCODING
|
||||
|
||||
def encoding
|
||||
ensure_parsed
|
||||
@encoding
|
||||
end
|
||||
|
||||
def encoding=( arg )
|
||||
ensure_parsed
|
||||
@encoding = arg
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@encoding = nil
|
||||
end
|
||||
|
||||
def set( s )
|
||||
@encoding = s
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not @encoding
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
strategy.meta @encoding.capitalize
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ContentDispositionHeader < StructuredHeader
|
||||
|
||||
PARSE_TYPE = :CDISPOSITION
|
||||
|
||||
def disposition
|
||||
ensure_parsed
|
||||
@disposition
|
||||
end
|
||||
|
||||
def disposition=( str )
|
||||
ensure_parsed
|
||||
@disposition = str.downcase
|
||||
end
|
||||
|
||||
def params
|
||||
ensure_parsed
|
||||
unless @params.blank?
|
||||
@params.each do |k, v|
|
||||
@params[k] = unquote(v)
|
||||
end
|
||||
end
|
||||
@params
|
||||
end
|
||||
|
||||
def []( key )
|
||||
ensure_parsed
|
||||
@params and unquote(@params[key])
|
||||
end
|
||||
|
||||
def []=( key, val )
|
||||
ensure_parsed
|
||||
(@params ||= {})[key] = val
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init
|
||||
@disposition = @params = nil
|
||||
end
|
||||
|
||||
def set( args )
|
||||
@disposition, @params = *args
|
||||
end
|
||||
|
||||
def isempty?
|
||||
not @disposition and (not @params or @params.empty?)
|
||||
end
|
||||
|
||||
def do_accept( strategy )
|
||||
strategy.meta @disposition
|
||||
@params.each do |k,v|
|
||||
strategy.meta ';'
|
||||
strategy.space
|
||||
strategy.kv_pair k, unquote(v)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class HeaderField # redefine
|
||||
|
||||
FNAME_TO_CLASS = {
|
||||
'date' => DateTimeHeader,
|
||||
'resent-date' => DateTimeHeader,
|
||||
'to' => AddressHeader,
|
||||
'cc' => AddressHeader,
|
||||
'bcc' => AddressHeader,
|
||||
'from' => AddressHeader,
|
||||
'reply-to' => AddressHeader,
|
||||
'resent-to' => AddressHeader,
|
||||
'resent-cc' => AddressHeader,
|
||||
'resent-bcc' => AddressHeader,
|
||||
'resent-from' => AddressHeader,
|
||||
'resent-reply-to' => AddressHeader,
|
||||
'sender' => SingleAddressHeader,
|
||||
'resent-sender' => SingleAddressHeader,
|
||||
'return-path' => ReturnPathHeader,
|
||||
'message-id' => MessageIdHeader,
|
||||
'resent-message-id' => MessageIdHeader,
|
||||
'in-reply-to' => ReferencesHeader,
|
||||
'received' => ReceivedHeader,
|
||||
'references' => ReferencesHeader,
|
||||
'keywords' => KeywordsHeader,
|
||||
'encrypted' => EncryptedHeader,
|
||||
'mime-version' => MimeVersionHeader,
|
||||
'content-type' => ContentTypeHeader,
|
||||
'content-transfer-encoding' => ContentTransferEncodingHeader,
|
||||
'content-disposition' => ContentDispositionHeader,
|
||||
'content-id' => MessageIdHeader,
|
||||
'subject' => UnstructuredHeader,
|
||||
'comments' => UnstructuredHeader,
|
||||
'content-description' => UnstructuredHeader
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
35
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/info.rb
vendored
Executable file
35
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/info.rb
vendored
Executable file
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# info.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
module TMail
|
||||
|
||||
Version = '0.10.7'
|
||||
Copyright = 'Copyright (c) 1998-2002 Minero Aoki'
|
||||
|
||||
end
|
||||
540
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/interface.rb
vendored
Normal file
540
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/interface.rb
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
=begin rdoc
|
||||
|
||||
= Facade.rb Provides an interface to the TMail object
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
def header_string( name, default = nil )
|
||||
h = @header[name.downcase] or return default
|
||||
h.to_s
|
||||
end
|
||||
|
||||
###
|
||||
### attributes
|
||||
###
|
||||
|
||||
include TextUtils
|
||||
|
||||
def set_string_array_attr( key, strs )
|
||||
strs.flatten!
|
||||
if strs.empty?
|
||||
@header.delete key.downcase
|
||||
else
|
||||
store key, strs.join(', ')
|
||||
end
|
||||
strs
|
||||
end
|
||||
private :set_string_array_attr
|
||||
|
||||
def set_string_attr( key, str )
|
||||
if str
|
||||
store key, str
|
||||
else
|
||||
@header.delete key.downcase
|
||||
end
|
||||
str
|
||||
end
|
||||
private :set_string_attr
|
||||
|
||||
def set_addrfield( name, arg )
|
||||
if arg
|
||||
h = HeaderField.internal_new(name, @config)
|
||||
h.addrs.replace [arg].flatten
|
||||
@header[name] = h
|
||||
else
|
||||
@header.delete name
|
||||
end
|
||||
arg
|
||||
end
|
||||
private :set_addrfield
|
||||
|
||||
def addrs2specs( addrs )
|
||||
return nil unless addrs
|
||||
list = addrs.map {|addr|
|
||||
if addr.address_group?
|
||||
then addr.map {|a| a.spec }
|
||||
else addr.spec
|
||||
end
|
||||
}.flatten
|
||||
return nil if list.empty?
|
||||
list
|
||||
end
|
||||
private :addrs2specs
|
||||
|
||||
#
|
||||
# date time
|
||||
#
|
||||
|
||||
def date( default = nil )
|
||||
if h = @header['date']
|
||||
h.date
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def date=( time )
|
||||
if time
|
||||
store 'Date', time2str(time)
|
||||
else
|
||||
@header.delete 'date'
|
||||
end
|
||||
time
|
||||
end
|
||||
|
||||
def strftime( fmt, default = nil )
|
||||
if t = date
|
||||
t.strftime(fmt)
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# destination
|
||||
#
|
||||
|
||||
def to_addrs( default = nil )
|
||||
if h = @header['to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def cc_addrs( default = nil )
|
||||
if h = @header['cc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def bcc_addrs( default = nil )
|
||||
if h = @header['bcc']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def to_addrs=( arg )
|
||||
set_addrfield 'to', arg
|
||||
end
|
||||
|
||||
def cc_addrs=( arg )
|
||||
set_addrfield 'cc', arg
|
||||
end
|
||||
|
||||
def bcc_addrs=( arg )
|
||||
set_addrfield 'bcc', arg
|
||||
end
|
||||
|
||||
def to( default = nil )
|
||||
addrs2specs(to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def cc( default = nil )
|
||||
addrs2specs(cc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def bcc( default = nil )
|
||||
addrs2specs(bcc_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def to=( *strs )
|
||||
set_string_array_attr 'To', strs
|
||||
end
|
||||
|
||||
def cc=( *strs )
|
||||
set_string_array_attr 'Cc', strs
|
||||
end
|
||||
|
||||
def bcc=( *strs )
|
||||
set_string_array_attr 'Bcc', strs
|
||||
end
|
||||
|
||||
#
|
||||
# originator
|
||||
#
|
||||
|
||||
def from_addrs( default = nil )
|
||||
if h = @header['from']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def from_addrs=( arg )
|
||||
set_addrfield 'from', arg
|
||||
end
|
||||
|
||||
def from( default = nil )
|
||||
addrs2specs(from_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def from=( *strs )
|
||||
set_string_array_attr 'From', strs
|
||||
end
|
||||
|
||||
def friendly_from( default = nil )
|
||||
h = @header['from']
|
||||
a, = h.addrs
|
||||
return default unless a
|
||||
return a.phrase if a.phrase
|
||||
return h.comments.join(' ') unless h.comments.empty?
|
||||
a.spec
|
||||
end
|
||||
|
||||
|
||||
def reply_to_addrs( default = nil )
|
||||
if h = @header['reply-to']
|
||||
h.addrs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def reply_to_addrs=( arg )
|
||||
set_addrfield 'reply-to', arg
|
||||
end
|
||||
|
||||
def reply_to( default = nil )
|
||||
addrs2specs(reply_to_addrs(nil)) || default
|
||||
end
|
||||
|
||||
def reply_to=( *strs )
|
||||
set_string_array_attr 'Reply-To', strs
|
||||
end
|
||||
|
||||
|
||||
def sender_addr( default = nil )
|
||||
f = @header['sender'] or return default
|
||||
f.addr or return default
|
||||
end
|
||||
|
||||
def sender_addr=( addr )
|
||||
if addr
|
||||
h = HeaderField.internal_new('sender', @config)
|
||||
h.addr = addr
|
||||
@header['sender'] = h
|
||||
else
|
||||
@header.delete 'sender'
|
||||
end
|
||||
addr
|
||||
end
|
||||
|
||||
def sender( default )
|
||||
f = @header['sender'] or return default
|
||||
a = f.addr or return default
|
||||
a.spec
|
||||
end
|
||||
|
||||
def sender=( str )
|
||||
set_string_attr 'Sender', str
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# subject
|
||||
#
|
||||
|
||||
def subject( default = nil )
|
||||
if h = @header['subject']
|
||||
h.body
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
alias quoted_subject subject
|
||||
|
||||
def subject=( str )
|
||||
set_string_attr 'Subject', str
|
||||
end
|
||||
|
||||
#
|
||||
# identity & threading
|
||||
#
|
||||
|
||||
def message_id( default = nil )
|
||||
if h = @header['message-id']
|
||||
h.id || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def message_id=( str )
|
||||
set_string_attr 'Message-Id', str
|
||||
end
|
||||
|
||||
def in_reply_to( default = nil )
|
||||
if h = @header['in-reply-to']
|
||||
h.ids
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def in_reply_to=( *idstrs )
|
||||
set_string_array_attr 'In-Reply-To', idstrs
|
||||
end
|
||||
|
||||
def references( default = nil )
|
||||
if h = @header['references']
|
||||
h.refs
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def references=( *strs )
|
||||
set_string_array_attr 'References', strs
|
||||
end
|
||||
|
||||
#
|
||||
# MIME headers
|
||||
#
|
||||
|
||||
def mime_version( default = nil )
|
||||
if h = @header['mime-version']
|
||||
h.version || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def mime_version=( m, opt = nil )
|
||||
if opt
|
||||
if h = @header['mime-version']
|
||||
h.major = m
|
||||
h.minor = opt
|
||||
else
|
||||
store 'Mime-Version', "#{m}.#{opt}"
|
||||
end
|
||||
else
|
||||
store 'Mime-Version', m
|
||||
end
|
||||
m
|
||||
end
|
||||
|
||||
def content_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.content_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def main_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.main_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def sub_type( default = nil )
|
||||
if h = @header['content-type']
|
||||
h.sub_type || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def set_content_type( str, sub = nil, param = nil )
|
||||
if sub
|
||||
main, sub = str, sub
|
||||
else
|
||||
main, sub = str.split(%r</>, 2)
|
||||
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
|
||||
end
|
||||
if h = @header['content-type']
|
||||
h.main_type = main
|
||||
h.sub_type = sub
|
||||
h.params.clear
|
||||
else
|
||||
store 'Content-Type', "#{main}/#{sub}"
|
||||
end
|
||||
@header['content-type'].params.replace param if param
|
||||
str
|
||||
end
|
||||
|
||||
alias content_type= set_content_type
|
||||
|
||||
def type_param( name, default = nil )
|
||||
if h = @header['content-type']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset( default = nil )
|
||||
if h = @header['content-type']
|
||||
h['charset'] or default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def charset=( str )
|
||||
if str
|
||||
if h = @header[ 'content-type' ]
|
||||
h['charset'] = str
|
||||
else
|
||||
store 'Content-Type', "text/plain; charset=#{str}"
|
||||
end
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
def transfer_encoding( default = nil )
|
||||
if h = @header['content-transfer-encoding']
|
||||
h.encoding || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_encoding=( str )
|
||||
set_string_attr 'Content-Transfer-Encoding', str
|
||||
end
|
||||
|
||||
alias encoding transfer_encoding
|
||||
alias encoding= transfer_encoding=
|
||||
alias content_transfer_encoding transfer_encoding
|
||||
alias content_transfer_encoding= transfer_encoding=
|
||||
|
||||
def disposition( default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias content_disposition disposition
|
||||
|
||||
def set_disposition( str, params = nil )
|
||||
if h = @header['content-disposition']
|
||||
h.disposition = str
|
||||
h.params.clear
|
||||
else
|
||||
store('Content-Disposition', str)
|
||||
h = @header['content-disposition']
|
||||
end
|
||||
h.params.replace params if params
|
||||
end
|
||||
|
||||
alias disposition= set_disposition
|
||||
alias set_content_disposition set_disposition
|
||||
alias content_disposition= set_disposition
|
||||
|
||||
def disposition_param( name, default = nil )
|
||||
if h = @header['content-disposition']
|
||||
h[name] || default
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
### utils
|
||||
###
|
||||
|
||||
def create_reply
|
||||
mail = TMail::Mail.parse('')
|
||||
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
|
||||
mail.to_addrs = reply_addresses([])
|
||||
mail.in_reply_to = [message_id(nil)].compact
|
||||
mail.references = references([]) + [message_id(nil)].compact
|
||||
mail.mime_version = '1.0'
|
||||
mail
|
||||
end
|
||||
|
||||
def base64_encode
|
||||
store 'Content-Transfer-Encoding', 'Base64'
|
||||
self.body = Base64.folding_encode(self.body)
|
||||
end
|
||||
|
||||
def base64_decode
|
||||
if /base64/i === self.transfer_encoding('')
|
||||
store 'Content-Transfer-Encoding', '8bit'
|
||||
self.body = Base64.decode(self.body, @config.strict_base64decode?)
|
||||
end
|
||||
end
|
||||
|
||||
def destinations( default = nil )
|
||||
ret = []
|
||||
%w( to cc bcc ).each do |nm|
|
||||
if h = @header[nm]
|
||||
h.addrs.each {|i| ret.push i.address }
|
||||
end
|
||||
end
|
||||
ret.empty? ? default : ret
|
||||
end
|
||||
|
||||
def each_destination( &block )
|
||||
destinations([]).each do |i|
|
||||
if Address === i
|
||||
yield i
|
||||
else
|
||||
i.each(&block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias each_dest each_destination
|
||||
|
||||
def reply_addresses( default = nil )
|
||||
reply_to_addrs(nil) or from_addrs(nil) or default
|
||||
end
|
||||
|
||||
def error_reply_addresses( default = nil )
|
||||
if s = sender(nil)
|
||||
[s]
|
||||
else
|
||||
from_addrs(default)
|
||||
end
|
||||
end
|
||||
|
||||
def multipart?
|
||||
main_type('').downcase == 'multipart'
|
||||
end
|
||||
|
||||
end # class Mail
|
||||
|
||||
end # module TMail
|
||||
1
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/loader.rb
vendored
Executable file
1
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/loader.rb
vendored
Executable file
@@ -0,0 +1 @@
|
||||
require 'tmail/mailbox'
|
||||
462
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/mail.rb
vendored
Executable file
462
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/mail.rb
vendored
Executable file
@@ -0,0 +1,462 @@
|
||||
=begin rdoc
|
||||
|
||||
= Mail class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/interface'
|
||||
require 'tmail/encode'
|
||||
require 'tmail/header'
|
||||
require 'tmail/port'
|
||||
require 'tmail/config'
|
||||
require 'tmail/utils'
|
||||
require 'tmail/attachments'
|
||||
require 'tmail/quoting'
|
||||
require 'socket'
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
class << self
|
||||
def load( fname )
|
||||
new(FilePort.new(fname))
|
||||
end
|
||||
|
||||
alias load_from load
|
||||
alias loadfrom load
|
||||
|
||||
def parse( str )
|
||||
new(StringPort.new(str))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize( port = nil, conf = DEFAULT_CONFIG )
|
||||
@port = port || StringPort.new
|
||||
@config = Config.to_config(conf)
|
||||
|
||||
@header = {}
|
||||
@body_port = nil
|
||||
@body_parsed = false
|
||||
@epilogue = ''
|
||||
@parts = []
|
||||
|
||||
@port.ropen {|f|
|
||||
parse_header f
|
||||
parse_body f unless @port.reproducible?
|
||||
}
|
||||
end
|
||||
|
||||
attr_reader :port
|
||||
|
||||
def inspect
|
||||
"\#<#{self.class} port=#{@port.inspect} bodyport=#{@body_port.inspect}>"
|
||||
end
|
||||
|
||||
#
|
||||
# to_s interfaces
|
||||
#
|
||||
|
||||
public
|
||||
|
||||
include StrategyInterface
|
||||
|
||||
def write_back( eol = "\n", charset = 'e' )
|
||||
parse_body
|
||||
@port.wopen {|stream| encoded eol, charset, stream }
|
||||
end
|
||||
|
||||
def accept( strategy )
|
||||
with_multipart_encoding(strategy) {
|
||||
ordered_each do |name, field|
|
||||
next if field.empty?
|
||||
strategy.header_name canonical(name)
|
||||
field.accept strategy
|
||||
strategy.puts
|
||||
end
|
||||
strategy.puts
|
||||
body_port().ropen {|r|
|
||||
strategy.write r.read
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def canonical( name )
|
||||
name.split(/-/).map {|s| s.capitalize }.join('-')
|
||||
end
|
||||
|
||||
def with_multipart_encoding( strategy )
|
||||
if parts().empty? # DO NOT USE @parts
|
||||
yield
|
||||
|
||||
else
|
||||
bound = ::TMail.new_boundary
|
||||
if @header.key? 'content-type'
|
||||
@header['content-type'].params['boundary'] = bound
|
||||
else
|
||||
store 'Content-Type', %<multipart/mixed; boundary="#{bound}">
|
||||
end
|
||||
|
||||
yield
|
||||
|
||||
parts().each do |tm|
|
||||
strategy.puts
|
||||
strategy.puts '--' + bound
|
||||
tm.accept strategy
|
||||
end
|
||||
strategy.puts
|
||||
strategy.puts '--' + bound + '--'
|
||||
strategy.write epilogue()
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
### header
|
||||
###
|
||||
|
||||
public
|
||||
|
||||
ALLOW_MULTIPLE = {
|
||||
'received' => true,
|
||||
'resent-date' => true,
|
||||
'resent-from' => true,
|
||||
'resent-sender' => true,
|
||||
'resent-to' => true,
|
||||
'resent-cc' => true,
|
||||
'resent-bcc' => true,
|
||||
'resent-message-id' => true,
|
||||
'comments' => true,
|
||||
'keywords' => true
|
||||
}
|
||||
USE_ARRAY = ALLOW_MULTIPLE
|
||||
|
||||
def header
|
||||
@header.dup
|
||||
end
|
||||
|
||||
def []( key )
|
||||
@header[key.downcase]
|
||||
end
|
||||
|
||||
def sub_header(key, param)
|
||||
(hdr = self[key]) ? hdr[param] : nil
|
||||
end
|
||||
|
||||
alias fetch []
|
||||
|
||||
def []=( key, val )
|
||||
dkey = key.downcase
|
||||
|
||||
if val.nil?
|
||||
@header.delete dkey
|
||||
return nil
|
||||
end
|
||||
|
||||
case val
|
||||
when String
|
||||
header = new_hf(key, val)
|
||||
when HeaderField
|
||||
;
|
||||
when Array
|
||||
ALLOW_MULTIPLE.include? dkey or
|
||||
raise ArgumentError, "#{key}: Header must not be multiple"
|
||||
@header[dkey] = val
|
||||
return val
|
||||
else
|
||||
header = new_hf(key, val.to_s)
|
||||
end
|
||||
if ALLOW_MULTIPLE.include? dkey
|
||||
(@header[dkey] ||= []).push header
|
||||
else
|
||||
@header[dkey] = header
|
||||
end
|
||||
|
||||
val
|
||||
end
|
||||
|
||||
alias store []=
|
||||
|
||||
def each_header
|
||||
@header.each do |key, val|
|
||||
[val].flatten.each {|v| yield key, v }
|
||||
end
|
||||
end
|
||||
|
||||
alias each_pair each_header
|
||||
|
||||
def each_header_name( &block )
|
||||
@header.each_key(&block)
|
||||
end
|
||||
|
||||
alias each_key each_header_name
|
||||
|
||||
def each_field( &block )
|
||||
@header.values.flatten.each(&block)
|
||||
end
|
||||
|
||||
alias each_value each_field
|
||||
|
||||
FIELD_ORDER = %w(
|
||||
return-path received
|
||||
resent-date resent-from resent-sender resent-to
|
||||
resent-cc resent-bcc resent-message-id
|
||||
date from sender reply-to to cc bcc
|
||||
message-id in-reply-to references
|
||||
subject comments keywords
|
||||
mime-version content-type content-transfer-encoding
|
||||
content-disposition content-description
|
||||
)
|
||||
|
||||
def ordered_each
|
||||
list = @header.keys
|
||||
FIELD_ORDER.each do |name|
|
||||
if list.delete(name)
|
||||
[@header[name]].flatten.each {|v| yield name, v }
|
||||
end
|
||||
end
|
||||
list.each do |name|
|
||||
[@header[name]].flatten.each {|v| yield name, v }
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
@header.clear
|
||||
end
|
||||
|
||||
def delete( key )
|
||||
@header.delete key.downcase
|
||||
end
|
||||
|
||||
def delete_if
|
||||
@header.delete_if do |key,val|
|
||||
if Array === val
|
||||
val.delete_if {|v| yield key, v }
|
||||
val.empty?
|
||||
else
|
||||
yield key, val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def keys
|
||||
@header.keys
|
||||
end
|
||||
|
||||
def key?( key )
|
||||
@header.key? key.downcase
|
||||
end
|
||||
|
||||
def values_at( *args )
|
||||
args.map {|k| @header[k.downcase] }.flatten
|
||||
end
|
||||
|
||||
alias indexes values_at
|
||||
alias indices values_at
|
||||
|
||||
private
|
||||
|
||||
def parse_header( f )
|
||||
name = field = nil
|
||||
unixfrom = nil
|
||||
|
||||
while line = f.gets
|
||||
case line
|
||||
when /\A[ \t]/ # continue from prev line
|
||||
raise SyntaxError, 'mail is began by space' unless field
|
||||
field << ' ' << line.strip
|
||||
|
||||
when /\A([^\: \t]+):\s*/ # new header line
|
||||
add_hf name, field if field
|
||||
name = $1
|
||||
field = $' #.strip
|
||||
|
||||
when /\A\-*\s*\z/ # end of header
|
||||
add_hf name, field if field
|
||||
name = field = nil
|
||||
break
|
||||
|
||||
when /\AFrom (\S+)/
|
||||
unixfrom = $1
|
||||
|
||||
when /^charset=.*/
|
||||
|
||||
else
|
||||
raise SyntaxError, "wrong mail header: '#{line.inspect}'"
|
||||
end
|
||||
end
|
||||
add_hf name, field if name
|
||||
|
||||
if unixfrom
|
||||
add_hf 'Return-Path', "<#{unixfrom}>" unless @header['return-path']
|
||||
end
|
||||
end
|
||||
|
||||
def add_hf( name, field )
|
||||
key = name.downcase
|
||||
field = new_hf(name, field)
|
||||
|
||||
if ALLOW_MULTIPLE.include? key
|
||||
(@header[key] ||= []).push field
|
||||
else
|
||||
@header[key] = field
|
||||
end
|
||||
end
|
||||
|
||||
def new_hf( name, field )
|
||||
HeaderField.new(name, field, @config)
|
||||
end
|
||||
|
||||
###
|
||||
### body
|
||||
###
|
||||
|
||||
public
|
||||
|
||||
def body_port
|
||||
parse_body
|
||||
@body_port
|
||||
end
|
||||
|
||||
def each( &block )
|
||||
body_port().ropen {|f| f.each(&block) }
|
||||
end
|
||||
|
||||
def quoted_body
|
||||
parse_body
|
||||
@body_port.ropen {|f|
|
||||
return f.read
|
||||
}
|
||||
end
|
||||
|
||||
def body=( str )
|
||||
# Sets the body of the email to a new (encoded) string.
|
||||
#
|
||||
# We also reparses the email if the body is ever reassigned, this is a performance hit, however when
|
||||
# you assign the body, you usually want to be able to make sure that you can access the attachments etc.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# mail.body = "Hello, this is\nthe body text"
|
||||
# # => "Hello, this is\nthe body"
|
||||
# mail.body
|
||||
# # => "Hello, this is\nthe body"
|
||||
@body_parsed = false
|
||||
parse_body(StringInput.new(str))
|
||||
parse_body
|
||||
@body_port.wopen {|f| f.write str }
|
||||
str
|
||||
end
|
||||
|
||||
alias preamble body
|
||||
alias preamble= body=
|
||||
|
||||
def epilogue
|
||||
parse_body
|
||||
@epilogue.dup
|
||||
end
|
||||
|
||||
def epilogue=( str )
|
||||
parse_body
|
||||
@epilogue = str
|
||||
str
|
||||
end
|
||||
|
||||
def parts
|
||||
parse_body
|
||||
@parts
|
||||
end
|
||||
|
||||
def each_part( &block )
|
||||
parts().each(&block)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_body( f = nil )
|
||||
return if @body_parsed
|
||||
if f
|
||||
parse_body_0 f
|
||||
else
|
||||
@port.ropen {|f|
|
||||
skip_header f
|
||||
parse_body_0 f
|
||||
}
|
||||
end
|
||||
@body_parsed = true
|
||||
end
|
||||
|
||||
def skip_header( f )
|
||||
while line = f.gets
|
||||
return if /\A[\r\n]*\z/ === line
|
||||
end
|
||||
end
|
||||
|
||||
def parse_body_0( f )
|
||||
if multipart?
|
||||
read_multipart f
|
||||
else
|
||||
@body_port = @config.new_body_port(self)
|
||||
@body_port.wopen {|w|
|
||||
w.write f.read
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def read_multipart( src )
|
||||
bound = @header['content-type'].params['boundary']
|
||||
is_sep = /\A--#{Regexp.quote bound}(?:--)?[ \t]*(?:\n|\r\n|\r)/
|
||||
lastbound = "--#{bound}--"
|
||||
|
||||
ports = [ @config.new_preamble_port(self) ]
|
||||
begin
|
||||
f = ports.last.wopen
|
||||
while line = src.gets
|
||||
if is_sep === line
|
||||
f.close
|
||||
break if line.strip == lastbound
|
||||
ports.push @config.new_part_port(self)
|
||||
f = ports.last.wopen
|
||||
else
|
||||
f << line
|
||||
end
|
||||
end
|
||||
@epilogue = (src.read || '')
|
||||
ensure
|
||||
f.close if f and not f.closed?
|
||||
end
|
||||
|
||||
@body_port = ports.shift
|
||||
@parts = ports.map {|p| self.class.new(p, @config) }
|
||||
end
|
||||
|
||||
end # class Mail
|
||||
|
||||
end # module TMail
|
||||
435
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/mailbox.rb
vendored
Executable file
435
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/mailbox.rb
vendored
Executable file
@@ -0,0 +1,435 @@
|
||||
=begin rdoc
|
||||
|
||||
= Mailbox and Mbox interaction class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/port'
|
||||
require 'socket'
|
||||
require 'mutex_m'
|
||||
|
||||
|
||||
unless [].respond_to?(:sort_by)
|
||||
module Enumerable#:nodoc:
|
||||
def sort_by
|
||||
map {|i| [yield(i), i] }.sort {|a,b| a.first <=> b.first }.map {|i| i[1] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class MhMailbox
|
||||
|
||||
PORT_CLASS = MhPort
|
||||
|
||||
def initialize( dir )
|
||||
edir = File.expand_path(dir)
|
||||
raise ArgumentError, "not directory: #{dir}"\
|
||||
unless FileTest.directory? edir
|
||||
@dirname = edir
|
||||
@last_file = nil
|
||||
@last_atime = nil
|
||||
end
|
||||
|
||||
def directory
|
||||
@dirname
|
||||
end
|
||||
|
||||
alias dirname directory
|
||||
|
||||
attr_accessor :last_atime
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{@dirname}>"
|
||||
end
|
||||
|
||||
def close
|
||||
end
|
||||
|
||||
def new_port
|
||||
PORT_CLASS.new(next_file_name())
|
||||
end
|
||||
|
||||
def each_port
|
||||
mail_files().each do |path|
|
||||
yield PORT_CLASS.new(path)
|
||||
end
|
||||
@last_atime = Time.now
|
||||
end
|
||||
|
||||
alias each each_port
|
||||
|
||||
def reverse_each_port
|
||||
mail_files().reverse_each do |path|
|
||||
yield PORT_CLASS.new(path)
|
||||
end
|
||||
@last_atime = Time.now
|
||||
end
|
||||
|
||||
alias reverse_each reverse_each_port
|
||||
|
||||
# old #each_mail returns Port
|
||||
#def each_mail
|
||||
# each_port do |port|
|
||||
# yield Mail.new(port)
|
||||
# end
|
||||
#end
|
||||
|
||||
def each_new_port( mtime = nil, &block )
|
||||
mtime ||= @last_atime
|
||||
return each_port(&block) unless mtime
|
||||
return unless File.mtime(@dirname) >= mtime
|
||||
|
||||
mail_files().each do |path|
|
||||
yield PORT_CLASS.new(path) if File.mtime(path) > mtime
|
||||
end
|
||||
@last_atime = Time.now
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mail_files
|
||||
Dir.entries(@dirname)\
|
||||
.select {|s| /\A\d+\z/ === s }\
|
||||
.map {|s| s.to_i }\
|
||||
.sort\
|
||||
.map {|i| "#{@dirname}/#{i}" }\
|
||||
.select {|path| FileTest.file? path }
|
||||
end
|
||||
|
||||
def next_file_name
|
||||
unless n = @last_file
|
||||
n = 0
|
||||
Dir.entries(@dirname)\
|
||||
.select {|s| /\A\d+\z/ === s }\
|
||||
.map {|s| s.to_i }.sort\
|
||||
.each do |i|
|
||||
next unless FileTest.file? "#{@dirname}/#{i}"
|
||||
n = i
|
||||
end
|
||||
end
|
||||
begin
|
||||
n += 1
|
||||
end while FileTest.exist? "#{@dirname}/#{n}"
|
||||
@last_file = n
|
||||
|
||||
"#{@dirname}/#{n}"
|
||||
end
|
||||
|
||||
end # MhMailbox
|
||||
|
||||
MhLoader = MhMailbox
|
||||
|
||||
|
||||
class UNIXMbox
|
||||
|
||||
def UNIXMbox.lock( fname )
|
||||
begin
|
||||
f = File.open(fname)
|
||||
f.flock File::LOCK_EX
|
||||
yield f
|
||||
ensure
|
||||
f.flock File::LOCK_UN
|
||||
f.close if f and not f.closed?
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
alias newobj new
|
||||
end
|
||||
|
||||
def UNIXMbox.new( fname, tmpdir = nil, readonly = false )
|
||||
tmpdir = ENV['TEMP'] || ENV['TMP'] || '/tmp'
|
||||
newobj(fname, "#{tmpdir}/ruby_tmail_#{$$}_#{rand()}", readonly, false)
|
||||
end
|
||||
|
||||
def UNIXMbox.static_new( fname, dir, readonly = false )
|
||||
newobj(fname, dir, readonly, true)
|
||||
end
|
||||
|
||||
def initialize( fname, mhdir, readonly, static )
|
||||
@filename = fname
|
||||
@readonly = readonly
|
||||
@closed = false
|
||||
|
||||
Dir.mkdir mhdir
|
||||
@real = MhMailbox.new(mhdir)
|
||||
@finalizer = UNIXMbox.mkfinal(@real, @filename, !@readonly, !static)
|
||||
ObjectSpace.define_finalizer self, @finalizer
|
||||
end
|
||||
|
||||
def UNIXMbox.mkfinal( mh, mboxfile, writeback_p, cleanup_p )
|
||||
lambda {
|
||||
if writeback_p
|
||||
lock(mboxfile) {|f|
|
||||
mh.each_port do |port|
|
||||
f.puts create_from_line(port)
|
||||
port.ropen {|r|
|
||||
f.puts r.read
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
if cleanup_p
|
||||
Dir.foreach(mh.dirname) do |fname|
|
||||
next if /\A\.\.?\z/ === fname
|
||||
File.unlink "#{mh.dirname}/#{fname}"
|
||||
end
|
||||
Dir.rmdir mh.dirname
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# make _From line
|
||||
def UNIXMbox.create_from_line( port )
|
||||
sprintf 'From %s %s',
|
||||
fromaddr(), TextUtils.time2str(File.mtime(port.filename))
|
||||
end
|
||||
|
||||
def UNIXMbox.fromaddr
|
||||
h = HeaderField.new_from_port(port, 'Return-Path') ||
|
||||
HeaderField.new_from_port(port, 'From') or return 'nobody'
|
||||
a = h.addrs[0] or return 'nobody'
|
||||
a.spec
|
||||
end
|
||||
private_class_method :fromaddr
|
||||
|
||||
def close
|
||||
return if @closed
|
||||
|
||||
ObjectSpace.undefine_finalizer self
|
||||
@finalizer.call
|
||||
@finalizer = nil
|
||||
@real = nil
|
||||
@closed = true
|
||||
@updated = nil
|
||||
end
|
||||
|
||||
def each_port( &block )
|
||||
close_check
|
||||
update
|
||||
@real.each_port(&block)
|
||||
end
|
||||
|
||||
alias each each_port
|
||||
|
||||
def reverse_each_port( &block )
|
||||
close_check
|
||||
update
|
||||
@real.reverse_each_port(&block)
|
||||
end
|
||||
|
||||
alias reverse_each reverse_each_port
|
||||
|
||||
# old #each_mail returns Port
|
||||
#def each_mail( &block )
|
||||
# each_port do |port|
|
||||
# yield Mail.new(port)
|
||||
# end
|
||||
#end
|
||||
|
||||
def each_new_port( mtime = nil )
|
||||
close_check
|
||||
update
|
||||
@real.each_new_port(mtime) {|p| yield p }
|
||||
end
|
||||
|
||||
def new_port
|
||||
close_check
|
||||
@real.new_port
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def close_check
|
||||
@closed and raise ArgumentError, 'accessing already closed mbox'
|
||||
end
|
||||
|
||||
def update
|
||||
return if FileTest.zero?(@filename)
|
||||
return if @updated and File.mtime(@filename) < @updated
|
||||
w = nil
|
||||
port = nil
|
||||
time = nil
|
||||
UNIXMbox.lock(@filename) {|f|
|
||||
begin
|
||||
f.each do |line|
|
||||
if /\AFrom / === line
|
||||
w.close if w
|
||||
File.utime time, time, port.filename if time
|
||||
|
||||
port = @real.new_port
|
||||
w = port.wopen
|
||||
time = fromline2time(line)
|
||||
else
|
||||
w.print line if w
|
||||
end
|
||||
end
|
||||
ensure
|
||||
if w and not w.closed?
|
||||
w.close
|
||||
File.utime time, time, port.filename if time
|
||||
end
|
||||
end
|
||||
f.truncate(0) unless @readonly
|
||||
@updated = Time.now
|
||||
}
|
||||
end
|
||||
|
||||
def fromline2time( line )
|
||||
m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) \
|
||||
or return nil
|
||||
Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i)
|
||||
end
|
||||
|
||||
end # UNIXMbox
|
||||
|
||||
MboxLoader = UNIXMbox
|
||||
|
||||
|
||||
class Maildir
|
||||
|
||||
extend Mutex_m
|
||||
|
||||
PORT_CLASS = MaildirPort
|
||||
|
||||
@seq = 0
|
||||
def Maildir.unique_number
|
||||
synchronize {
|
||||
@seq += 1
|
||||
return @seq
|
||||
}
|
||||
end
|
||||
|
||||
def initialize( dir = nil )
|
||||
@dirname = dir || ENV['MAILDIR']
|
||||
raise ArgumentError, "not directory: #{@dirname}"\
|
||||
unless FileTest.directory? @dirname
|
||||
@new = "#{@dirname}/new"
|
||||
@tmp = "#{@dirname}/tmp"
|
||||
@cur = "#{@dirname}/cur"
|
||||
end
|
||||
|
||||
def directory
|
||||
@dirname
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{@dirname}>"
|
||||
end
|
||||
|
||||
def close
|
||||
end
|
||||
|
||||
def each_port
|
||||
mail_files(@cur).each do |path|
|
||||
yield PORT_CLASS.new(path)
|
||||
end
|
||||
end
|
||||
|
||||
alias each each_port
|
||||
|
||||
def reverse_each_port
|
||||
mail_files(@cur).reverse_each do |path|
|
||||
yield PORT_CLASS.new(path)
|
||||
end
|
||||
end
|
||||
|
||||
alias reverse_each reverse_each_port
|
||||
|
||||
def new_port
|
||||
fname = nil
|
||||
tmpfname = nil
|
||||
newfname = nil
|
||||
|
||||
begin
|
||||
fname = "#{Time.now.to_i}.#{$$}_#{Maildir.unique_number}.#{Socket.gethostname}"
|
||||
|
||||
tmpfname = "#{@tmp}/#{fname}"
|
||||
newfname = "#{@new}/#{fname}"
|
||||
end while FileTest.exist? tmpfname
|
||||
|
||||
if block_given?
|
||||
File.open(tmpfname, 'w') {|f| yield f }
|
||||
File.rename tmpfname, newfname
|
||||
PORT_CLASS.new(newfname)
|
||||
else
|
||||
File.open(tmpfname, 'w') {|f| f.write "\n\n" }
|
||||
PORT_CLASS.new(tmpfname)
|
||||
end
|
||||
end
|
||||
|
||||
def each_new_port
|
||||
mail_files(@new).each do |path|
|
||||
dest = @cur + '/' + File.basename(path)
|
||||
File.rename path, dest
|
||||
yield PORT_CLASS.new(dest)
|
||||
end
|
||||
|
||||
check_tmp
|
||||
end
|
||||
|
||||
TOO_OLD = 60 * 60 * 36 # 36 hour
|
||||
|
||||
def check_tmp
|
||||
old = Time.now.to_i - TOO_OLD
|
||||
|
||||
each_filename(@tmp) do |full, fname|
|
||||
if FileTest.file? full and
|
||||
File.stat(full).mtime.to_i < old
|
||||
File.unlink full
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mail_files( dir )
|
||||
Dir.entries(dir)\
|
||||
.select {|s| s[0] != ?. }\
|
||||
.sort_by {|s| s.slice(/\A\d+/).to_i }\
|
||||
.map {|s| "#{dir}/#{s}" }\
|
||||
.select {|path| FileTest.file? path }
|
||||
end
|
||||
|
||||
def each_filename( dir )
|
||||
Dir.foreach(dir) do |fname|
|
||||
path = "#{dir}/#{fname}"
|
||||
if fname[0] != ?. and FileTest.file? path
|
||||
yield path, fname
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end # Maildir
|
||||
|
||||
MaildirLoader = Maildir
|
||||
|
||||
end # module TMail
|
||||
1
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/mbox.rb
vendored
Executable file
1
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/mbox.rb
vendored
Executable file
@@ -0,0 +1 @@
|
||||
require 'tmail/mailbox'
|
||||
282
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/net.rb
vendored
Executable file
282
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/net.rb
vendored
Executable file
@@ -0,0 +1,282 @@
|
||||
=begin rdoc
|
||||
|
||||
= Net provides SMTP wrapping
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'nkf'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Mail
|
||||
|
||||
def send_to( smtp )
|
||||
do_send_to(smtp) do
|
||||
ready_to_send
|
||||
end
|
||||
end
|
||||
|
||||
def send_text_to( smtp )
|
||||
do_send_to(smtp) do
|
||||
ready_to_send
|
||||
mime_encode
|
||||
end
|
||||
end
|
||||
|
||||
def do_send_to( smtp )
|
||||
from = from_address or raise ArgumentError, 'no from address'
|
||||
(dests = destinations).empty? and raise ArgumentError, 'no receipient'
|
||||
yield
|
||||
send_to_0 smtp, from, dests
|
||||
end
|
||||
private :do_send_to
|
||||
|
||||
def send_to_0( smtp, from, to )
|
||||
smtp.ready(from, to) do |f|
|
||||
encoded "\r\n", 'j', f, ''
|
||||
end
|
||||
end
|
||||
|
||||
def ready_to_send
|
||||
delete_no_send_fields
|
||||
add_message_id
|
||||
add_date
|
||||
end
|
||||
|
||||
NOSEND_FIELDS = %w(
|
||||
received
|
||||
bcc
|
||||
)
|
||||
|
||||
def delete_no_send_fields
|
||||
NOSEND_FIELDS.each do |nm|
|
||||
delete nm
|
||||
end
|
||||
delete_if {|n,v| v.empty? }
|
||||
end
|
||||
|
||||
def add_message_id( fqdn = nil )
|
||||
self.message_id = ::TMail::new_message_id(fqdn)
|
||||
end
|
||||
|
||||
def add_date
|
||||
self.date = Time.now
|
||||
end
|
||||
|
||||
def mime_encode
|
||||
if parts.empty?
|
||||
mime_encode_singlepart
|
||||
else
|
||||
mime_encode_multipart true
|
||||
end
|
||||
end
|
||||
|
||||
def mime_encode_singlepart
|
||||
self.mime_version = '1.0'
|
||||
b = body
|
||||
if NKF.guess(b) != NKF::BINARY
|
||||
mime_encode_text b
|
||||
else
|
||||
mime_encode_binary b
|
||||
end
|
||||
end
|
||||
|
||||
def mime_encode_text( body )
|
||||
self.body = NKF.nkf('-j -m0', body)
|
||||
self.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
|
||||
self.encoding = '7bit'
|
||||
end
|
||||
|
||||
def mime_encode_binary( body )
|
||||
self.body = [body].pack('m')
|
||||
self.set_content_type 'application', 'octet-stream'
|
||||
self.encoding = 'Base64'
|
||||
end
|
||||
|
||||
def mime_encode_multipart( top = true )
|
||||
self.mime_version = '1.0' if top
|
||||
self.set_content_type 'multipart', 'mixed'
|
||||
e = encoding(nil)
|
||||
if e and not /\A(?:7bit|8bit|binary)\z/i === e
|
||||
raise ArgumentError,
|
||||
'using C.T.Encoding with multipart mail is not permitted'
|
||||
end
|
||||
end
|
||||
|
||||
def create_empty_mail
|
||||
self.class.new(StringPort.new(''), @config)
|
||||
end
|
||||
|
||||
def create_reply
|
||||
setup_reply create_empty_mail()
|
||||
end
|
||||
|
||||
def setup_reply( m )
|
||||
if tmp = reply_addresses(nil)
|
||||
m.to_addrs = tmp
|
||||
end
|
||||
|
||||
mid = message_id(nil)
|
||||
tmp = references(nil) || []
|
||||
tmp.push mid if mid
|
||||
m.in_reply_to = [mid] if mid
|
||||
m.references = tmp unless tmp.empty?
|
||||
m.subject = 'Re: ' + subject('').sub(/\A(?:\s*re:)+/i, '')
|
||||
|
||||
m
|
||||
end
|
||||
|
||||
def create_forward
|
||||
setup_forward create_empty_mail()
|
||||
end
|
||||
|
||||
def setup_forward( mail )
|
||||
m = Mail.new(StringPort.new(''))
|
||||
m.body = decoded
|
||||
m.set_content_type 'message', 'rfc822'
|
||||
m.encoding = encoding('7bit')
|
||||
mail.parts.push m
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class DeleteFields
|
||||
|
||||
NOSEND_FIELDS = %w(
|
||||
received
|
||||
bcc
|
||||
)
|
||||
|
||||
def initialize( nosend = nil, delempty = true )
|
||||
@no_send_fields = nosend || NOSEND_FIELDS.dup
|
||||
@delete_empty_fields = delempty
|
||||
end
|
||||
|
||||
attr :no_send_fields
|
||||
attr :delete_empty_fields, true
|
||||
|
||||
def exec( mail )
|
||||
@no_send_fields.each do |nm|
|
||||
delete nm
|
||||
end
|
||||
delete_if {|n,v| v.empty? } if @delete_empty_fields
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class AddMessageId
|
||||
|
||||
def initialize( fqdn = nil )
|
||||
@fqdn = fqdn
|
||||
end
|
||||
|
||||
attr :fqdn, true
|
||||
|
||||
def exec( mail )
|
||||
mail.message_id = ::TMail::new_msgid(@fqdn)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class AddDate
|
||||
|
||||
def exec( mail )
|
||||
mail.date = Time.now
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MimeEncodeAuto
|
||||
|
||||
def initialize( s = nil, m = nil )
|
||||
@singlepart_composer = s || MimeEncodeSingle.new
|
||||
@multipart_composer = m || MimeEncodeMulti.new
|
||||
end
|
||||
|
||||
attr :singlepart_composer
|
||||
attr :multipart_composer
|
||||
|
||||
def exec( mail )
|
||||
if mail._builtin_multipart?
|
||||
then @multipart_composer
|
||||
else @singlepart_composer end.exec mail
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MimeEncodeSingle
|
||||
|
||||
def exec( mail )
|
||||
mail.mime_version = '1.0'
|
||||
b = mail.body
|
||||
if NKF.guess(b) != NKF::BINARY
|
||||
on_text b
|
||||
else
|
||||
on_binary b
|
||||
end
|
||||
end
|
||||
|
||||
def on_text( body )
|
||||
mail.body = NKF.nkf('-j -m0', body)
|
||||
mail.set_content_type 'text', 'plain', {'charset' => 'iso-2022-jp'}
|
||||
mail.encoding = '7bit'
|
||||
end
|
||||
|
||||
def on_binary( body )
|
||||
mail.body = [body].pack('m')
|
||||
mail.set_content_type 'application', 'octet-stream'
|
||||
mail.encoding = 'Base64'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MimeEncodeMulti
|
||||
|
||||
def exec( mail, top = true )
|
||||
mail.mime_version = '1.0' if top
|
||||
mail.set_content_type 'multipart', 'mixed'
|
||||
e = encoding(nil)
|
||||
if e and not /\A(?:7bit|8bit|binary)\z/i === e
|
||||
raise ArgumentError,
|
||||
'using C.T.Encoding with multipart mail is not permitted'
|
||||
end
|
||||
mail.parts.each do |m|
|
||||
exec m, false if m._builtin_multipart?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
137
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/obsolete.rb
vendored
Executable file
137
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/obsolete.rb
vendored
Executable file
@@ -0,0 +1,137 @@
|
||||
=begin rdoc
|
||||
|
||||
= Obsolete methods that are depriciated
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
module TMail
|
||||
|
||||
# mail.rb
|
||||
class Mail
|
||||
alias include? key?
|
||||
alias has_key? key?
|
||||
|
||||
def values
|
||||
ret = []
|
||||
each_field {|v| ret.push v }
|
||||
ret
|
||||
end
|
||||
|
||||
def value?( val )
|
||||
HeaderField === val or return false
|
||||
|
||||
[ @header[val.name.downcase] ].flatten.include? val
|
||||
end
|
||||
|
||||
alias has_value? value?
|
||||
end
|
||||
|
||||
|
||||
# facade.rb
|
||||
class Mail
|
||||
def from_addr( default = nil )
|
||||
addr, = from_addrs(nil)
|
||||
addr || default
|
||||
end
|
||||
|
||||
def from_address( default = nil )
|
||||
if a = from_addr(nil)
|
||||
a.spec
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias from_address= from_addrs=
|
||||
|
||||
def from_phrase( default = nil )
|
||||
if a = from_addr(nil)
|
||||
a.phrase
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
alias msgid message_id
|
||||
alias msgid= message_id=
|
||||
|
||||
alias each_dest each_destination
|
||||
end
|
||||
|
||||
|
||||
# address.rb
|
||||
class Address
|
||||
alias route routes
|
||||
alias addr spec
|
||||
|
||||
def spec=( str )
|
||||
@local, @domain = str.split(/@/,2).map {|s| s.split(/\./) }
|
||||
end
|
||||
|
||||
alias addr= spec=
|
||||
alias address= spec=
|
||||
end
|
||||
|
||||
|
||||
# mbox.rb
|
||||
class MhMailbox
|
||||
alias new_mail new_port
|
||||
alias each_mail each_port
|
||||
alias each_newmail each_new_port
|
||||
end
|
||||
class UNIXMbox
|
||||
alias new_mail new_port
|
||||
alias each_mail each_port
|
||||
alias each_newmail each_new_port
|
||||
end
|
||||
class Maildir
|
||||
alias new_mail new_port
|
||||
alias each_mail each_port
|
||||
alias each_newmail each_new_port
|
||||
end
|
||||
|
||||
|
||||
# utils.rb
|
||||
extend TextUtils
|
||||
|
||||
class << self
|
||||
alias msgid? message_id?
|
||||
alias boundary new_boundary
|
||||
alias msgid new_message_id
|
||||
alias new_msgid new_message_id
|
||||
end
|
||||
|
||||
def Mail.boundary
|
||||
::TMail.new_boundary
|
||||
end
|
||||
|
||||
def Mail.msgid
|
||||
::TMail.new_message_id
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
1475
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/parser.rb
vendored
Executable file
1475
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/parser.rb
vendored
Executable file
File diff suppressed because it is too large
Load Diff
381
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/parser.y
vendored
Normal file
381
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/parser.y
vendored
Normal file
@@ -0,0 +1,381 @@
|
||||
#
|
||||
# parser.y
|
||||
#
|
||||
# Copyright (c) 1998-2007 Minero Aoki
|
||||
#
|
||||
# This program is free software.
|
||||
# You can distribute/modify this program under the terms of
|
||||
# the GNU Lesser General Public License version 2.1.
|
||||
#
|
||||
|
||||
class TMail::Parser
|
||||
|
||||
options no_result_var
|
||||
|
||||
rule
|
||||
|
||||
content : DATETIME datetime { val[1] }
|
||||
| RECEIVED received { val[1] }
|
||||
| MADDRESS addrs_TOP { val[1] }
|
||||
| RETPATH retpath { val[1] }
|
||||
| KEYWORDS keys { val[1] }
|
||||
| ENCRYPTED enc { val[1] }
|
||||
| MIMEVERSION version { val[1] }
|
||||
| CTYPE ctype { val[1] }
|
||||
| CENCODING cencode { val[1] }
|
||||
| CDISPOSITION cdisp { val[1] }
|
||||
| ADDRESS addr_TOP { val[1] }
|
||||
| MAILBOX mbox { val[1] }
|
||||
|
||||
datetime : day DIGIT ATOM DIGIT hour zone
|
||||
# 0 1 2 3 4 5
|
||||
# date month year
|
||||
{
|
||||
t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0)
|
||||
(t + val[4] - val[5]).localtime
|
||||
}
|
||||
|
||||
day : /* none */
|
||||
| ATOM ','
|
||||
|
||||
hour : DIGIT ':' DIGIT
|
||||
{
|
||||
(val[0].to_i * 60 * 60) +
|
||||
(val[2].to_i * 60)
|
||||
}
|
||||
| DIGIT ':' DIGIT ':' DIGIT
|
||||
{
|
||||
(val[0].to_i * 60 * 60) +
|
||||
(val[2].to_i * 60) +
|
||||
(val[4].to_i)
|
||||
}
|
||||
|
||||
zone : ATOM
|
||||
{
|
||||
timezone_string_to_unixtime(val[0])
|
||||
}
|
||||
|
||||
received : from by via with id for received_datetime
|
||||
{
|
||||
val
|
||||
}
|
||||
|
||||
from : /* none */
|
||||
| FROM received_domain
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
by : /* none */
|
||||
| BY received_domain
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
received_domain
|
||||
: domain
|
||||
{
|
||||
join_domain(val[0])
|
||||
}
|
||||
| domain '@' domain
|
||||
{
|
||||
join_domain(val[2])
|
||||
}
|
||||
| domain DOMLIT
|
||||
{
|
||||
join_domain(val[0])
|
||||
}
|
||||
|
||||
via : /* none */
|
||||
| VIA ATOM
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
with : /* none */
|
||||
{
|
||||
[]
|
||||
}
|
||||
| with WITH ATOM
|
||||
{
|
||||
val[0].push val[2]
|
||||
val[0]
|
||||
}
|
||||
|
||||
id : /* none */
|
||||
| ID msgid
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
| ID ATOM
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
for : /* none */
|
||||
| FOR received_addrspec
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
received_addrspec
|
||||
: routeaddr
|
||||
{
|
||||
val[0].spec
|
||||
}
|
||||
| spec
|
||||
{
|
||||
val[0].spec
|
||||
}
|
||||
|
||||
received_datetime
|
||||
: /* none */
|
||||
| ';' datetime
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
addrs_TOP : addrs
|
||||
| group_bare
|
||||
| addrs commas group_bare
|
||||
|
||||
addr_TOP : mbox
|
||||
| group
|
||||
| group_bare
|
||||
|
||||
retpath : addrs_TOP
|
||||
| '<' '>' { [ Address.new(nil, nil) ] }
|
||||
|
||||
addrs : addr
|
||||
{
|
||||
val
|
||||
}
|
||||
| addrs commas addr
|
||||
{
|
||||
val[0].push val[2]
|
||||
val[0]
|
||||
}
|
||||
|
||||
addr : mbox
|
||||
| group
|
||||
|
||||
mboxes : mbox
|
||||
{
|
||||
val
|
||||
}
|
||||
| mboxes commas mbox
|
||||
{
|
||||
val[0].push val[2]
|
||||
val[0]
|
||||
}
|
||||
|
||||
mbox : spec
|
||||
| routeaddr
|
||||
| addr_phrase routeaddr
|
||||
{
|
||||
val[1].phrase = Decoder.decode(val[0])
|
||||
val[1]
|
||||
}
|
||||
|
||||
group : group_bare ';'
|
||||
|
||||
group_bare: addr_phrase ':' mboxes
|
||||
{
|
||||
AddressGroup.new(val[0], val[2])
|
||||
}
|
||||
| addr_phrase ':' { AddressGroup.new(val[0], []) }
|
||||
|
||||
addr_phrase
|
||||
: local_head { val[0].join('.') }
|
||||
| addr_phrase local_head { val[0] << ' ' << val[1].join('.') }
|
||||
|
||||
routeaddr : '<' routes spec '>'
|
||||
{
|
||||
val[2].routes.replace val[1]
|
||||
val[2]
|
||||
}
|
||||
| '<' spec '>'
|
||||
{
|
||||
val[1]
|
||||
}
|
||||
|
||||
routes : at_domains ':'
|
||||
|
||||
at_domains: '@' domain { [ val[1].join('.') ] }
|
||||
| at_domains ',' '@' domain { val[0].push val[3].join('.'); val[0] }
|
||||
|
||||
spec : local '@' domain { Address.new( val[0], val[2] ) }
|
||||
| local { Address.new( val[0], nil ) }
|
||||
|
||||
local: local_head
|
||||
| local_head '.' { val[0].push ''; val[0] }
|
||||
|
||||
local_head: word
|
||||
{ val }
|
||||
| local_head dots word
|
||||
{
|
||||
val[1].times do
|
||||
val[0].push ''
|
||||
end
|
||||
val[0].push val[2]
|
||||
val[0]
|
||||
}
|
||||
|
||||
domain : domword
|
||||
{ val }
|
||||
| domain dots domword
|
||||
{
|
||||
val[1].times do
|
||||
val[0].push ''
|
||||
end
|
||||
val[0].push val[2]
|
||||
val[0]
|
||||
}
|
||||
|
||||
dots : '.' { 0 }
|
||||
| '.' '.' { 1 }
|
||||
|
||||
word : atom
|
||||
| QUOTED
|
||||
| DIGIT
|
||||
|
||||
domword : atom
|
||||
| DOMLIT
|
||||
| DIGIT
|
||||
|
||||
commas : ','
|
||||
| commas ','
|
||||
|
||||
msgid : '<' spec '>'
|
||||
{
|
||||
val[1] = val[1].spec
|
||||
val.join('')
|
||||
}
|
||||
|
||||
keys : phrase { val }
|
||||
| keys ',' phrase { val[0].push val[2]; val[0] }
|
||||
|
||||
phrase : word
|
||||
| phrase word { val[0] << ' ' << val[1] }
|
||||
|
||||
enc : word
|
||||
{
|
||||
val.push nil
|
||||
val
|
||||
}
|
||||
| word word
|
||||
{
|
||||
val
|
||||
}
|
||||
|
||||
version : DIGIT '.' DIGIT
|
||||
{
|
||||
[ val[0].to_i, val[2].to_i ]
|
||||
}
|
||||
|
||||
ctype : TOKEN '/' TOKEN params opt_semicolon
|
||||
{
|
||||
[ val[0].downcase, val[2].downcase, decode_params(val[3]) ]
|
||||
}
|
||||
| TOKEN params opt_semicolon
|
||||
{
|
||||
[ val[0].downcase, nil, decode_params(val[1]) ]
|
||||
}
|
||||
|
||||
params : /* none */
|
||||
{
|
||||
{}
|
||||
}
|
||||
| params ';' TOKEN '=' QUOTED
|
||||
{
|
||||
val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"')
|
||||
val[0]
|
||||
}
|
||||
| params ';' TOKEN '=' TOKEN
|
||||
{
|
||||
val[0][ val[2].downcase ] = val[4]
|
||||
val[0]
|
||||
}
|
||||
|
||||
cencode : TOKEN
|
||||
{
|
||||
val[0].downcase
|
||||
}
|
||||
|
||||
cdisp : TOKEN params opt_semicolon
|
||||
{
|
||||
[ val[0].downcase, decode_params(val[1]) ]
|
||||
}
|
||||
|
||||
opt_semicolon
|
||||
:
|
||||
| ';'
|
||||
|
||||
atom : ATOM
|
||||
| FROM
|
||||
| BY
|
||||
| VIA
|
||||
| WITH
|
||||
| ID
|
||||
| FOR
|
||||
|
||||
end
|
||||
|
||||
|
||||
---- header
|
||||
#
|
||||
# parser.rb
|
||||
#
|
||||
# Copyright (c) 1998-2007 Minero Aoki
|
||||
#
|
||||
# This program is free software.
|
||||
# You can distribute/modify this program under the terms of
|
||||
# the GNU Lesser General Public License version 2.1.
|
||||
#
|
||||
|
||||
require 'tmail/scanner'
|
||||
require 'tmail/utils'
|
||||
|
||||
---- inner
|
||||
|
||||
include TextUtils
|
||||
|
||||
def self.parse( ident, str, cmt = nil )
|
||||
new.parse(ident, str, cmt)
|
||||
end
|
||||
|
||||
MAILP_DEBUG = false
|
||||
|
||||
def initialize
|
||||
self.debug = MAILP_DEBUG
|
||||
end
|
||||
|
||||
def debug=( flag )
|
||||
@yydebug = flag && Racc_debug_parser
|
||||
@scanner_debug = flag
|
||||
end
|
||||
|
||||
def debug
|
||||
@yydebug
|
||||
end
|
||||
|
||||
def parse( ident, str, comments = nil )
|
||||
@scanner = Scanner.new(str, ident, comments)
|
||||
@scanner.debug = @scanner_debug
|
||||
@first = [ident, ident]
|
||||
result = yyparse(self, :parse_in)
|
||||
comments.map! {|c| to_kcode(c) } if comments
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_in( &block )
|
||||
yield @first
|
||||
@scanner.scan(&block)
|
||||
end
|
||||
|
||||
def on_error( t, val, vstack )
|
||||
raise SyntaxError, "parse error on token #{racc_token2str t}"
|
||||
end
|
||||
|
||||
379
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/port.rb
vendored
Executable file
379
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/port.rb
vendored
Executable file
@@ -0,0 +1,379 @@
|
||||
=begin rdoc
|
||||
|
||||
= Port class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/stringio'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Port
|
||||
def reproducible?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
### FilePort
|
||||
###
|
||||
|
||||
class FilePort < Port
|
||||
|
||||
def initialize( fname )
|
||||
@filename = File.expand_path(fname)
|
||||
super()
|
||||
end
|
||||
|
||||
attr_reader :filename
|
||||
|
||||
alias ident filename
|
||||
|
||||
def ==( other )
|
||||
other.respond_to?(:filename) and @filename == other.filename
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
@filename.hash
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:#{@filename}>"
|
||||
end
|
||||
|
||||
def reproducible?
|
||||
true
|
||||
end
|
||||
|
||||
def size
|
||||
File.size @filename
|
||||
end
|
||||
|
||||
|
||||
def ropen( &block )
|
||||
File.open(@filename, &block)
|
||||
end
|
||||
|
||||
def wopen( &block )
|
||||
File.open(@filename, 'w', &block)
|
||||
end
|
||||
|
||||
def aopen( &block )
|
||||
File.open(@filename, 'a', &block)
|
||||
end
|
||||
|
||||
|
||||
def read_all
|
||||
ropen {|f|
|
||||
return f.read
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
def remove
|
||||
File.unlink @filename
|
||||
end
|
||||
|
||||
def move_to( port )
|
||||
begin
|
||||
File.link @filename, port.filename
|
||||
rescue Errno::EXDEV
|
||||
copy_to port
|
||||
end
|
||||
File.unlink @filename
|
||||
end
|
||||
|
||||
alias mv move_to
|
||||
|
||||
def copy_to( port )
|
||||
if FilePort === port
|
||||
copy_file @filename, port.filename
|
||||
else
|
||||
File.open(@filename) {|r|
|
||||
port.wopen {|w|
|
||||
while s = r.sysread(4096)
|
||||
w.write << s
|
||||
end
|
||||
} }
|
||||
end
|
||||
end
|
||||
|
||||
alias cp copy_to
|
||||
|
||||
private
|
||||
|
||||
# from fileutils.rb
|
||||
def copy_file( src, dest )
|
||||
st = r = w = nil
|
||||
|
||||
File.open(src, 'rb') {|r|
|
||||
File.open(dest, 'wb') {|w|
|
||||
st = r.stat
|
||||
begin
|
||||
while true
|
||||
w.write r.sysread(st.blksize)
|
||||
end
|
||||
rescue EOFError
|
||||
end
|
||||
} }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
module MailFlags
|
||||
|
||||
def seen=( b )
|
||||
set_status 'S', b
|
||||
end
|
||||
|
||||
def seen?
|
||||
get_status 'S'
|
||||
end
|
||||
|
||||
def replied=( b )
|
||||
set_status 'R', b
|
||||
end
|
||||
|
||||
def replied?
|
||||
get_status 'R'
|
||||
end
|
||||
|
||||
def flagged=( b )
|
||||
set_status 'F', b
|
||||
end
|
||||
|
||||
def flagged?
|
||||
get_status 'F'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def procinfostr( str, tag, true_p )
|
||||
a = str.upcase.split(//)
|
||||
a.push true_p ? tag : nil
|
||||
a.delete tag unless true_p
|
||||
a.compact.sort.join('').squeeze
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MhPort < FilePort
|
||||
|
||||
include MailFlags
|
||||
|
||||
private
|
||||
|
||||
def set_status( tag, flag )
|
||||
begin
|
||||
tmpfile = @filename + '.tmailtmp.' + $$.to_s
|
||||
File.open(tmpfile, 'w') {|f|
|
||||
write_status f, tag, flag
|
||||
}
|
||||
File.unlink @filename
|
||||
File.link tmpfile, @filename
|
||||
ensure
|
||||
File.unlink tmpfile
|
||||
end
|
||||
end
|
||||
|
||||
def write_status( f, tag, flag )
|
||||
stat = ''
|
||||
File.open(@filename) {|r|
|
||||
while line = r.gets
|
||||
if line.strip.empty?
|
||||
break
|
||||
elsif m = /\AX-TMail-Status:/i.match(line)
|
||||
stat = m.post_match.strip
|
||||
else
|
||||
f.print line
|
||||
end
|
||||
end
|
||||
|
||||
s = procinfostr(stat, tag, flag)
|
||||
f.puts 'X-TMail-Status: ' + s unless s.empty?
|
||||
f.puts
|
||||
|
||||
while s = r.read(2048)
|
||||
f.write s
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def get_status( tag )
|
||||
File.foreach(@filename) {|line|
|
||||
return false if line.strip.empty?
|
||||
if m = /\AX-TMail-Status:/i.match(line)
|
||||
return m.post_match.strip.include?(tag[0])
|
||||
end
|
||||
}
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class MaildirPort < FilePort
|
||||
|
||||
def move_to_new
|
||||
new = replace_dir(@filename, 'new')
|
||||
File.rename @filename, new
|
||||
@filename = new
|
||||
end
|
||||
|
||||
def move_to_cur
|
||||
new = replace_dir(@filename, 'cur')
|
||||
File.rename @filename, new
|
||||
@filename = new
|
||||
end
|
||||
|
||||
def replace_dir( path, dir )
|
||||
"#{File.dirname File.dirname(path)}/#{dir}/#{File.basename path}"
|
||||
end
|
||||
private :replace_dir
|
||||
|
||||
|
||||
include MailFlags
|
||||
|
||||
private
|
||||
|
||||
MAIL_FILE = /\A(\d+\.[\d_]+\.[^:]+)(?:\:(\d),(\w+)?)?\z/
|
||||
|
||||
def set_status( tag, flag )
|
||||
if m = MAIL_FILE.match(File.basename(@filename))
|
||||
s, uniq, type, info, = m.to_a
|
||||
return if type and type != '2' # do not change anything
|
||||
newname = File.dirname(@filename) + '/' +
|
||||
uniq + ':2,' + procinfostr(info.to_s, tag, flag)
|
||||
else
|
||||
newname = @filename + ':2,' + tag
|
||||
end
|
||||
|
||||
File.link @filename, newname
|
||||
File.unlink @filename
|
||||
@filename = newname
|
||||
end
|
||||
|
||||
def get_status( tag )
|
||||
m = MAIL_FILE.match(File.basename(@filename)) or return false
|
||||
m[2] == '2' and m[3].to_s.include?(tag[0])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
###
|
||||
### StringPort
|
||||
###
|
||||
|
||||
class StringPort < Port
|
||||
|
||||
def initialize( str = '' )
|
||||
@buffer = str
|
||||
super()
|
||||
end
|
||||
|
||||
def string
|
||||
@buffer
|
||||
end
|
||||
|
||||
def to_s
|
||||
@buffer.dup
|
||||
end
|
||||
|
||||
alias read_all to_s
|
||||
|
||||
def size
|
||||
@buffer.size
|
||||
end
|
||||
|
||||
def ==( other )
|
||||
StringPort === other and @buffer.equal? other.string
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def hash
|
||||
@buffer.object_id.hash
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:id=#{sprintf '0x%x', @buffer.object_id}>"
|
||||
end
|
||||
|
||||
def reproducible?
|
||||
true
|
||||
end
|
||||
|
||||
def ropen( &block )
|
||||
@buffer or raise Errno::ENOENT, "#{inspect} is already removed"
|
||||
StringInput.open(@buffer, &block)
|
||||
end
|
||||
|
||||
def wopen( &block )
|
||||
@buffer = ''
|
||||
StringOutput.new(@buffer, &block)
|
||||
end
|
||||
|
||||
def aopen( &block )
|
||||
@buffer ||= ''
|
||||
StringOutput.new(@buffer, &block)
|
||||
end
|
||||
|
||||
def remove
|
||||
@buffer = nil
|
||||
end
|
||||
|
||||
alias rm remove
|
||||
|
||||
def copy_to( port )
|
||||
port.wopen {|f|
|
||||
f.write @buffer
|
||||
}
|
||||
end
|
||||
|
||||
alias cp copy_to
|
||||
|
||||
def move_to( port )
|
||||
if StringPort === port
|
||||
str = @buffer
|
||||
port.instance_eval { @buffer = str }
|
||||
else
|
||||
copy_to port
|
||||
end
|
||||
remove
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
142
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/quoting.rb
vendored
Normal file
142
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/quoting.rb
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
=begin rdoc
|
||||
|
||||
= Quoting methods
|
||||
|
||||
=end
|
||||
module TMail
|
||||
class Mail
|
||||
def subject(to_charset = 'utf-8')
|
||||
Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
|
||||
end
|
||||
|
||||
def unquoted_body(to_charset = 'utf-8')
|
||||
from_charset = sub_header("content-type", "charset")
|
||||
case (content_transfer_encoding || "7bit").downcase
|
||||
when "quoted-printable"
|
||||
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
|
||||
# This is needed as many mailer do not set the charset but send in ISO. This is only used if no charset is set.
|
||||
if !from_charset.blank? && from_charset.downcase == 'us-ascii'
|
||||
from_charset = 'iso-8859-1'
|
||||
end
|
||||
|
||||
Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
|
||||
to_charset, from_charset, true)
|
||||
when "base64"
|
||||
Unquoter.unquote_base64_and_convert_to(quoted_body, to_charset,
|
||||
from_charset)
|
||||
when "7bit", "8bit"
|
||||
Unquoter.convert_to(quoted_body, to_charset, from_charset)
|
||||
when "binary"
|
||||
quoted_body
|
||||
else
|
||||
quoted_body
|
||||
end
|
||||
end
|
||||
|
||||
def body(to_charset = 'utf-8', &block)
|
||||
attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
|
||||
|
||||
if multipart?
|
||||
parts.collect { |part|
|
||||
header = part["content-type"]
|
||||
|
||||
if part.multipart?
|
||||
part.body(to_charset, &attachment_presenter)
|
||||
elsif header.nil?
|
||||
""
|
||||
elsif !attachment?(part)
|
||||
part.unquoted_body(to_charset)
|
||||
else
|
||||
attachment_presenter.call(header["name"] || "(unnamed)")
|
||||
end
|
||||
}.join
|
||||
else
|
||||
unquoted_body(to_charset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Unquoter
|
||||
class << self
|
||||
def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1", preserve_underscores=false)
|
||||
return "" if text.nil?
|
||||
text.gsub(/(.*?)(?:(?:=\?(.*?)\?(.)\?(.*?)\?=)|$)/) do
|
||||
before = $1
|
||||
from_charset = $2
|
||||
quoting_method = $3
|
||||
text = $4
|
||||
|
||||
before = convert_to(before, to_charset, from_charset) if before.length > 0
|
||||
before + case quoting_method
|
||||
when "q", "Q" then
|
||||
unquote_quoted_printable_and_convert_to(text, to_charset, from_charset, preserve_underscores)
|
||||
when "b", "B" then
|
||||
unquote_base64_and_convert_to(text, to_charset, from_charset)
|
||||
when nil then
|
||||
# will be nil at the end of the string, due to the nature of
|
||||
# the regex used.
|
||||
""
|
||||
else
|
||||
raise "unknown quoting method #{quoting_method.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
|
||||
text = text.gsub(/_/, " ") unless preserve_underscores
|
||||
text = text.gsub(/\r\n|\r/, "\n") # normalize newlines
|
||||
convert_to(text.unpack("M*").first, to, from)
|
||||
end
|
||||
|
||||
def unquote_base64_and_convert_to(text, to, from)
|
||||
convert_to(Base64.decode(text), to, from)
|
||||
end
|
||||
|
||||
begin
|
||||
require 'iconv'
|
||||
def convert_to(text, to, from)
|
||||
return text unless to && from
|
||||
text ? Iconv.iconv(to, from, text).first : ""
|
||||
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
|
||||
# the 'from' parameter specifies a charset other than what the text
|
||||
# actually is...not much we can do in this case but just return the
|
||||
# unconverted text.
|
||||
#
|
||||
# Ditto if either parameter represents an unknown charset, like
|
||||
# X-UNKNOWN.
|
||||
text
|
||||
end
|
||||
rescue LoadError
|
||||
# Not providing quoting support
|
||||
def convert_to(text, to, from)
|
||||
warn "Action Mailer: iconv not loaded; ignoring conversion from #{from} to #{to} (#{__FILE__}:#{__LINE__})"
|
||||
text
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
require 'test/unit'
|
||||
|
||||
class TC_Unquoter < Test::Unit::TestCase
|
||||
def test_unquote_quoted_printable
|
||||
a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
|
||||
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
||||
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
|
||||
end
|
||||
|
||||
def test_unquote_base64
|
||||
a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
|
||||
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
||||
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
|
||||
end
|
||||
|
||||
def test_unquote_without_charset
|
||||
a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
|
||||
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
||||
assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
|
||||
end
|
||||
end
|
||||
end
|
||||
43
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/scanner.rb
vendored
Executable file
43
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/scanner.rb
vendored
Executable file
@@ -0,0 +1,43 @@
|
||||
=begin rdoc
|
||||
|
||||
= Scanner for TMail
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/utils'
|
||||
|
||||
module TMail
|
||||
require 'tmail/scanner_r.rb'
|
||||
begin
|
||||
raise LoadError, 'Turn off Ruby extention by user choice' if ENV['NORUBYEXT']
|
||||
require 'tmail/scanner_c.so'
|
||||
Scanner = Scanner_C
|
||||
rescue LoadError
|
||||
Scanner = Scanner_R
|
||||
end
|
||||
end
|
||||
263
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/scanner_r.rb
vendored
Executable file
263
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/scanner_r.rb
vendored
Executable file
@@ -0,0 +1,263 @@
|
||||
#
|
||||
# scanner_r.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
require 'tmail/config'
|
||||
|
||||
|
||||
module TMail
|
||||
|
||||
class Scanner_R
|
||||
|
||||
Version = '0.10.7'
|
||||
Version.freeze
|
||||
|
||||
MIME_HEADERS = {
|
||||
:CTYPE => true,
|
||||
:CENCODING => true,
|
||||
:CDISPOSITION => true
|
||||
}
|
||||
|
||||
alnum = 'a-zA-Z0-9'
|
||||
atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
|
||||
tokensyms = %q[ _#!$%&`'*+-{|}~^@. ].strip
|
||||
|
||||
atomchars = alnum + Regexp.quote(atomsyms)
|
||||
tokenchars = alnum + Regexp.quote(tokensyms)
|
||||
iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'
|
||||
|
||||
eucstr = '(?:[\xa1-\xfe][\xa1-\xfe])+'
|
||||
sjisstr = '(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc])+'
|
||||
utf8str = '(?:[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf])+'
|
||||
|
||||
quoted_with_iso2022 = /\A(?:[^\\\e"]+|#{iso2022str})+/n
|
||||
domlit_with_iso2022 = /\A(?:[^\\\e\]]+|#{iso2022str})+/n
|
||||
comment_with_iso2022 = /\A(?:[^\\\e()]+|#{iso2022str})+/n
|
||||
|
||||
quoted_without_iso2022 = /\A[^\\"]+/n
|
||||
domlit_without_iso2022 = /\A[^\\\]]+/n
|
||||
comment_without_iso2022 = /\A[^\\()]+/n
|
||||
|
||||
PATTERN_TABLE = {}
|
||||
PATTERN_TABLE['EUC'] =
|
||||
[
|
||||
/\A(?:[#{atomchars}]+|#{iso2022str}|#{eucstr})+/n,
|
||||
/\A(?:[#{tokenchars}]+|#{iso2022str}|#{eucstr})+/n,
|
||||
quoted_with_iso2022,
|
||||
domlit_with_iso2022,
|
||||
comment_with_iso2022
|
||||
]
|
||||
PATTERN_TABLE['SJIS'] =
|
||||
[
|
||||
/\A(?:[#{atomchars}]+|#{iso2022str}|#{sjisstr})+/n,
|
||||
/\A(?:[#{tokenchars}]+|#{iso2022str}|#{sjisstr})+/n,
|
||||
quoted_with_iso2022,
|
||||
domlit_with_iso2022,
|
||||
comment_with_iso2022
|
||||
]
|
||||
PATTERN_TABLE['UTF8'] =
|
||||
[
|
||||
/\A(?:[#{atomchars}]+|#{utf8str})+/n,
|
||||
/\A(?:[#{tokenchars}]+|#{utf8str})+/n,
|
||||
quoted_without_iso2022,
|
||||
domlit_without_iso2022,
|
||||
comment_without_iso2022
|
||||
]
|
||||
PATTERN_TABLE['NONE'] =
|
||||
[
|
||||
/\A[#{atomchars}]+/n,
|
||||
/\A[#{tokenchars}]+/n,
|
||||
quoted_without_iso2022,
|
||||
domlit_without_iso2022,
|
||||
comment_without_iso2022
|
||||
]
|
||||
|
||||
|
||||
def initialize( str, scantype, comments )
|
||||
init_scanner str
|
||||
@comments = comments || []
|
||||
@debug = false
|
||||
|
||||
# fix scanner mode
|
||||
@received = (scantype == :RECEIVED)
|
||||
@is_mime_header = MIME_HEADERS[scantype]
|
||||
|
||||
atom, token, @quoted_re, @domlit_re, @comment_re = PATTERN_TABLE[$KCODE]
|
||||
@word_re = (MIME_HEADERS[scantype] ? token : atom)
|
||||
end
|
||||
|
||||
attr_accessor :debug
|
||||
|
||||
def scan( &block )
|
||||
if @debug
|
||||
scan_main do |arr|
|
||||
s, v = arr
|
||||
printf "%7d %-10s %s\n",
|
||||
rest_size(),
|
||||
s.respond_to?(:id2name) ? s.id2name : s.inspect,
|
||||
v.inspect
|
||||
yield arr
|
||||
end
|
||||
else
|
||||
scan_main(&block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
RECV_TOKEN = {
|
||||
'from' => :FROM,
|
||||
'by' => :BY,
|
||||
'via' => :VIA,
|
||||
'with' => :WITH,
|
||||
'id' => :ID,
|
||||
'for' => :FOR
|
||||
}
|
||||
|
||||
def scan_main
|
||||
until eof?
|
||||
if skip(/\A[\n\r\t ]+/n) # LWSP
|
||||
break if eof?
|
||||
end
|
||||
|
||||
if s = readstr(@word_re)
|
||||
if @is_mime_header
|
||||
yield :TOKEN, s
|
||||
else
|
||||
# atom
|
||||
if /\A\d+\z/ === s
|
||||
yield :DIGIT, s
|
||||
elsif @received
|
||||
yield RECV_TOKEN[s.downcase] || :ATOM, s
|
||||
else
|
||||
yield :ATOM, s
|
||||
end
|
||||
end
|
||||
|
||||
elsif skip(/\A"/)
|
||||
yield :QUOTED, scan_quoted_word()
|
||||
|
||||
elsif skip(/\A\[/)
|
||||
yield :DOMLIT, scan_domain_literal()
|
||||
|
||||
elsif skip(/\A\(/)
|
||||
@comments.push scan_comment()
|
||||
|
||||
else
|
||||
c = readchar()
|
||||
yield c, c
|
||||
end
|
||||
end
|
||||
|
||||
yield false, '$'
|
||||
end
|
||||
|
||||
def scan_quoted_word
|
||||
scan_qstr(@quoted_re, /\A"/, 'quoted-word')
|
||||
end
|
||||
|
||||
def scan_domain_literal
|
||||
'[' + scan_qstr(@domlit_re, /\A\]/, 'domain-literal') + ']'
|
||||
end
|
||||
|
||||
def scan_qstr( pattern, terminal, type )
|
||||
result = ''
|
||||
until eof?
|
||||
if s = readstr(pattern) then result << s
|
||||
elsif skip(terminal) then return result
|
||||
elsif skip(/\A\\/) then result << readchar()
|
||||
else
|
||||
raise "TMail FATAL: not match in #{type}"
|
||||
end
|
||||
end
|
||||
scan_error! "found unterminated #{type}"
|
||||
end
|
||||
|
||||
def scan_comment
|
||||
result = ''
|
||||
nest = 1
|
||||
content = @comment_re
|
||||
|
||||
until eof?
|
||||
if s = readstr(content) then result << s
|
||||
elsif skip(/\A\)/) then nest -= 1
|
||||
return result if nest == 0
|
||||
result << ')'
|
||||
elsif skip(/\A\(/) then nest += 1
|
||||
result << '('
|
||||
elsif skip(/\A\\/) then result << readchar()
|
||||
else
|
||||
raise 'TMail FATAL: not match in comment'
|
||||
end
|
||||
end
|
||||
scan_error! 'found unterminated comment'
|
||||
end
|
||||
|
||||
# string scanner
|
||||
|
||||
def init_scanner( str )
|
||||
@src = str
|
||||
end
|
||||
|
||||
def eof?
|
||||
@src.empty?
|
||||
end
|
||||
|
||||
def rest_size
|
||||
@src.size
|
||||
end
|
||||
|
||||
def readstr( re )
|
||||
if m = re.match(@src)
|
||||
@src = m.post_match
|
||||
m[0]
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def readchar
|
||||
readstr(/\A./)
|
||||
end
|
||||
|
||||
def skip( re )
|
||||
if m = re.match(@src)
|
||||
@src = m.post_match
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def scan_error!( msg )
|
||||
raise SyntaxError, msg
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module TMail
|
||||
279
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/stringio.rb
vendored
Executable file
279
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/stringio.rb
vendored
Executable file
@@ -0,0 +1,279 @@
|
||||
=begin rdoc
|
||||
|
||||
= String handling class
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
class StringInput#:nodoc:
|
||||
|
||||
include Enumerable
|
||||
|
||||
class << self
|
||||
|
||||
def new( str )
|
||||
if block_given?
|
||||
begin
|
||||
f = super
|
||||
yield f
|
||||
ensure
|
||||
f.close if f
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
alias open new
|
||||
|
||||
end
|
||||
|
||||
def initialize( str )
|
||||
@src = str
|
||||
@pos = 0
|
||||
@closed = false
|
||||
@lineno = 0
|
||||
end
|
||||
|
||||
attr_reader :lineno
|
||||
|
||||
def string
|
||||
@src
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@src[0,30].inspect}>"
|
||||
end
|
||||
|
||||
def close
|
||||
stream_check!
|
||||
@pos = nil
|
||||
@closed = true
|
||||
end
|
||||
|
||||
def closed?
|
||||
@closed
|
||||
end
|
||||
|
||||
def pos
|
||||
stream_check!
|
||||
[@pos, @src.size].min
|
||||
end
|
||||
|
||||
alias tell pos
|
||||
|
||||
def seek( offset, whence = IO::SEEK_SET )
|
||||
stream_check!
|
||||
case whence
|
||||
when IO::SEEK_SET
|
||||
@pos = offset
|
||||
when IO::SEEK_CUR
|
||||
@pos += offset
|
||||
when IO::SEEK_END
|
||||
@pos = @src.size - offset
|
||||
else
|
||||
raise ArgumentError, "unknown seek flag: #{whence}"
|
||||
end
|
||||
@pos = 0 if @pos < 0
|
||||
@pos = [@pos, @src.size + 1].min
|
||||
offset
|
||||
end
|
||||
|
||||
def rewind
|
||||
stream_check!
|
||||
@pos = 0
|
||||
end
|
||||
|
||||
def eof?
|
||||
stream_check!
|
||||
@pos > @src.size
|
||||
end
|
||||
|
||||
def each( &block )
|
||||
stream_check!
|
||||
begin
|
||||
@src.each(&block)
|
||||
ensure
|
||||
@pos = 0
|
||||
end
|
||||
end
|
||||
|
||||
def gets
|
||||
stream_check!
|
||||
if idx = @src.index(?\n, @pos)
|
||||
idx += 1 # "\n".size
|
||||
line = @src[ @pos ... idx ]
|
||||
@pos = idx
|
||||
@pos += 1 if @pos == @src.size
|
||||
else
|
||||
line = @src[ @pos .. -1 ]
|
||||
@pos = @src.size + 1
|
||||
end
|
||||
@lineno += 1
|
||||
|
||||
line
|
||||
end
|
||||
|
||||
def getc
|
||||
stream_check!
|
||||
ch = @src[@pos]
|
||||
@pos += 1
|
||||
@pos += 1 if @pos == @src.size
|
||||
ch
|
||||
end
|
||||
|
||||
def read( len = nil )
|
||||
stream_check!
|
||||
return read_all unless len
|
||||
str = @src[@pos, len]
|
||||
@pos += len
|
||||
@pos += 1 if @pos == @src.size
|
||||
str
|
||||
end
|
||||
|
||||
alias sysread read
|
||||
|
||||
def read_all
|
||||
stream_check!
|
||||
return nil if eof?
|
||||
rest = @src[@pos ... @src.size]
|
||||
@pos = @src.size + 1
|
||||
rest
|
||||
end
|
||||
|
||||
def stream_check!
|
||||
@closed and raise IOError, 'closed stream'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class StringOutput#:nodoc:
|
||||
|
||||
class << self
|
||||
|
||||
def new( str = '' )
|
||||
if block_given?
|
||||
begin
|
||||
f = super
|
||||
yield f
|
||||
ensure
|
||||
f.close if f
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
alias open new
|
||||
|
||||
end
|
||||
|
||||
def initialize( str = '' )
|
||||
@dest = str
|
||||
@closed = false
|
||||
end
|
||||
|
||||
def close
|
||||
@closed = true
|
||||
end
|
||||
|
||||
def closed?
|
||||
@closed
|
||||
end
|
||||
|
||||
def string
|
||||
@dest
|
||||
end
|
||||
|
||||
alias value string
|
||||
alias to_str string
|
||||
|
||||
def size
|
||||
@dest.size
|
||||
end
|
||||
|
||||
alias pos size
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:#{@dest ? 'open' : 'closed'},#{object_id}>"
|
||||
end
|
||||
|
||||
def print( *args )
|
||||
stream_check!
|
||||
raise ArgumentError, 'wrong # of argument (0 for >1)' if args.empty?
|
||||
args.each do |s|
|
||||
raise ArgumentError, 'nil not allowed' if s.nil?
|
||||
@dest << s.to_s
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def puts( *args )
|
||||
stream_check!
|
||||
args.each do |str|
|
||||
@dest << (s = str.to_s)
|
||||
@dest << "\n" unless s[-1] == ?\n
|
||||
end
|
||||
@dest << "\n" if args.empty?
|
||||
nil
|
||||
end
|
||||
|
||||
def putc( ch )
|
||||
stream_check!
|
||||
@dest << ch.chr
|
||||
nil
|
||||
end
|
||||
|
||||
def printf( *args )
|
||||
stream_check!
|
||||
@dest << sprintf(*args)
|
||||
nil
|
||||
end
|
||||
|
||||
def write( str )
|
||||
stream_check!
|
||||
s = str.to_s
|
||||
@dest << s
|
||||
s.size
|
||||
end
|
||||
|
||||
alias syswrite write
|
||||
|
||||
def <<( str )
|
||||
stream_check!
|
||||
@dest << str.to_s
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stream_check!
|
||||
@closed and raise IOError, 'closed stream'
|
||||
end
|
||||
|
||||
end
|
||||
1
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/tmail.rb
vendored
Executable file
1
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/tmail.rb
vendored
Executable file
@@ -0,0 +1 @@
|
||||
require 'tmail'
|
||||
281
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/utils.rb
vendored
Executable file
281
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/utils.rb
vendored
Executable file
@@ -0,0 +1,281 @@
|
||||
=begin rdoc
|
||||
|
||||
= General Purpose TMail Utilities
|
||||
|
||||
=end
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
module TMail
|
||||
|
||||
class SyntaxError < StandardError; end
|
||||
|
||||
|
||||
def TMail.new_boundary
|
||||
'mimepart_' + random_tag
|
||||
end
|
||||
|
||||
def TMail.new_message_id( fqdn = nil )
|
||||
fqdn ||= ::Socket.gethostname
|
||||
"<#{random_tag()}@#{fqdn}.tmail>"
|
||||
end
|
||||
|
||||
def TMail.random_tag
|
||||
@uniq += 1
|
||||
t = Time.now
|
||||
sprintf('%x%x_%x%x%d%x',
|
||||
t.to_i, t.tv_usec,
|
||||
$$, Thread.current.object_id, @uniq, rand(255))
|
||||
end
|
||||
private_class_method :random_tag
|
||||
|
||||
@uniq = 0
|
||||
|
||||
module TextUtils
|
||||
# Defines characters per RFC that are OK for TOKENs, ATOMs, PHRASEs and CONTROL characters.
|
||||
|
||||
aspecial = '()<>[]:;.\\,"'
|
||||
tspecial = '()<>[];:\\,"/?='
|
||||
lwsp = " \t\r\n"
|
||||
control = '\x00-\x1f\x7f-\xff'
|
||||
|
||||
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
|
||||
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
|
||||
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
|
||||
CONTROL_CHAR = /[#{control}]/n
|
||||
|
||||
def atom_safe?( str )
|
||||
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
||||
not ATOM_UNSAFE === str
|
||||
end
|
||||
|
||||
def quote_atom( str )
|
||||
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
||||
# in double quotes, otherwise returns the string unmodified
|
||||
(ATOM_UNSAFE === str) ? dquote(str) : str
|
||||
end
|
||||
|
||||
def quote_phrase( str )
|
||||
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
||||
# in double quotes, otherwise returns the string unmodified
|
||||
(PHRASE_UNSAFE === str) ? dquote(str) : str
|
||||
end
|
||||
|
||||
def token_safe?( str )
|
||||
# Returns true if the string supplied is free from characters not allowed as a TOKEN
|
||||
not TOKEN_UNSAFE === str
|
||||
end
|
||||
|
||||
def quote_token( str )
|
||||
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
||||
# in double quotes, otherwise returns the string unmodified
|
||||
(TOKEN_UNSAFE === str) ? dquote(str) : str
|
||||
end
|
||||
|
||||
def dquote( str )
|
||||
# Wraps supplied string in double quotes unless it is already wrapped
|
||||
# Returns double quoted string
|
||||
unless str =~ /^".*?"$/
|
||||
'"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
|
||||
else
|
||||
str
|
||||
end
|
||||
end
|
||||
private :dquote
|
||||
|
||||
def unquote( str )
|
||||
# Unwraps supplied string from inside double quotes
|
||||
# Returns unquoted string
|
||||
str =~ /^"(.*?)"$/ ? $1 : str
|
||||
end
|
||||
|
||||
def join_domain( arr )
|
||||
arr.map {|i|
|
||||
if /\A\[.*\]\z/ === i
|
||||
i
|
||||
else
|
||||
quote_atom(i)
|
||||
end
|
||||
}.join('.')
|
||||
end
|
||||
|
||||
|
||||
ZONESTR_TABLE = {
|
||||
'jst' => 9 * 60,
|
||||
'eet' => 2 * 60,
|
||||
'bst' => 1 * 60,
|
||||
'met' => 1 * 60,
|
||||
'gmt' => 0,
|
||||
'utc' => 0,
|
||||
'ut' => 0,
|
||||
'nst' => -(3 * 60 + 30),
|
||||
'ast' => -4 * 60,
|
||||
'edt' => -4 * 60,
|
||||
'est' => -5 * 60,
|
||||
'cdt' => -5 * 60,
|
||||
'cst' => -6 * 60,
|
||||
'mdt' => -6 * 60,
|
||||
'mst' => -7 * 60,
|
||||
'pdt' => -7 * 60,
|
||||
'pst' => -8 * 60,
|
||||
'a' => -1 * 60,
|
||||
'b' => -2 * 60,
|
||||
'c' => -3 * 60,
|
||||
'd' => -4 * 60,
|
||||
'e' => -5 * 60,
|
||||
'f' => -6 * 60,
|
||||
'g' => -7 * 60,
|
||||
'h' => -8 * 60,
|
||||
'i' => -9 * 60,
|
||||
# j not use
|
||||
'k' => -10 * 60,
|
||||
'l' => -11 * 60,
|
||||
'm' => -12 * 60,
|
||||
'n' => 1 * 60,
|
||||
'o' => 2 * 60,
|
||||
'p' => 3 * 60,
|
||||
'q' => 4 * 60,
|
||||
'r' => 5 * 60,
|
||||
's' => 6 * 60,
|
||||
't' => 7 * 60,
|
||||
'u' => 8 * 60,
|
||||
'v' => 9 * 60,
|
||||
'w' => 10 * 60,
|
||||
'x' => 11 * 60,
|
||||
'y' => 12 * 60,
|
||||
'z' => 0 * 60
|
||||
}
|
||||
|
||||
def timezone_string_to_unixtime( str )
|
||||
# Takes a time zone string from an EMail and converts it to Unix Time (seconds)
|
||||
if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
|
||||
sec = (m[2].to_i * 60 + m[3].to_i) * 60
|
||||
m[1] == '-' ? -sec : sec
|
||||
else
|
||||
min = ZONESTR_TABLE[str.downcase] or
|
||||
raise SyntaxError, "wrong timezone format '#{str}'"
|
||||
min * 60
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG )
|
||||
MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun
|
||||
Jul Aug Sep Oct Nov Dec TMailBUG )
|
||||
|
||||
def time2str( tm )
|
||||
# [ruby-list:7928]
|
||||
gmt = Time.at(tm.to_i)
|
||||
gmt.gmtime
|
||||
offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i
|
||||
|
||||
# DO NOT USE strftime: setlocale() breaks it
|
||||
sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d',
|
||||
WDAY[tm.wday], tm.mday, MONTH[tm.month],
|
||||
tm.year, tm.hour, tm.min, tm.sec,
|
||||
*(offset / 60).divmod(60)
|
||||
end
|
||||
|
||||
|
||||
MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
|
||||
|
||||
def message_id?( str )
|
||||
MESSAGE_ID === str
|
||||
end
|
||||
|
||||
|
||||
MIME_ENCODED = /=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i
|
||||
|
||||
def mime_encoded?( str )
|
||||
MIME_ENCODED === str
|
||||
end
|
||||
|
||||
|
||||
def decode_params( hash )
|
||||
new = Hash.new
|
||||
encoded = nil
|
||||
hash.each do |key, value|
|
||||
if m = /\*(?:(\d+)\*)?\z/.match(key)
|
||||
((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value
|
||||
else
|
||||
new[key] = to_kcode(value)
|
||||
end
|
||||
end
|
||||
if encoded
|
||||
encoded.each do |key, strings|
|
||||
new[key] = decode_RFC2231(strings.join(''))
|
||||
end
|
||||
end
|
||||
|
||||
new
|
||||
end
|
||||
|
||||
NKF_FLAGS = {
|
||||
'EUC' => '-e -m',
|
||||
'SJIS' => '-s -m'
|
||||
}
|
||||
|
||||
def to_kcode( str )
|
||||
flag = NKF_FLAGS[$KCODE] or return str
|
||||
NKF.nkf(flag, str)
|
||||
end
|
||||
|
||||
RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
|
||||
|
||||
def decode_RFC2231( str )
|
||||
m = RFC2231_ENCODED.match(str) or return str
|
||||
begin
|
||||
NKF.nkf(NKF_FLAGS[$KCODE],
|
||||
m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
|
||||
rescue
|
||||
m.post_match.gsub(/%[\da-f]{2}/in, "")
|
||||
end
|
||||
end
|
||||
|
||||
def quote_boundary
|
||||
# Make sure the Content-Type boundary= parameter is quoted if it contains illegal characters
|
||||
# (to ensure any special characters in the boundary text are escaped from the parser
|
||||
# (such as = in MS Outlook's boundary text))
|
||||
if @body =~ /^(.*)boundary=(.*)$/m
|
||||
preamble = $1
|
||||
remainder = $2
|
||||
if remainder =~ /;/
|
||||
remainder =~ /^(.*)(;.*)$/m
|
||||
boundary_text = $1
|
||||
post = $2.chomp
|
||||
else
|
||||
boundary_text = remainder.chomp
|
||||
end
|
||||
if boundary_text =~ /[\/\?\=]/
|
||||
boundary_text = "\"#{boundary_text}\"" unless boundary_text =~ /^".*?"$/
|
||||
@body = "#{preamble}boundary=#{boundary_text}#{post}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
38
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/version.rb
vendored
Normal file
38
actionmailer/lib/action_mailer/vendor/tmail-1.1.0/tmail/version.rb
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# version.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
||||
# with permission of Minero Aoki.
|
||||
#++
|
||||
|
||||
module TMail #:nodoc:
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 1
|
||||
MINOR = 1
|
||||
TINY = 1
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,9 @@
|
||||
module ActionMailer
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 3
|
||||
MAJOR = 2
|
||||
MINOR = 0
|
||||
TINY = 20
|
||||
PRE = nil
|
||||
TINY = 2
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
||||
1
actionmailer/lib/actionmailer.rb
Normal file
1
actionmailer/lib/actionmailer.rb
Normal file
@@ -0,0 +1 @@
|
||||
require 'action_mailer'
|
||||
@@ -1,16 +0,0 @@
|
||||
module Rails
|
||||
module Generators
|
||||
class MailerGenerator < NamedBase
|
||||
source_root File.expand_path("../templates", __FILE__)
|
||||
|
||||
argument :actions, :type => :array, :default => [], :banner => "method method"
|
||||
check_class_collision
|
||||
|
||||
def create_mailer_file
|
||||
template "mailer.rb", File.join('app/mailers', class_path, "#{file_name}.rb")
|
||||
end
|
||||
|
||||
hook_for :template_engine, :test_framework
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,16 +0,0 @@
|
||||
class <%= class_name %> < ActionMailer::Base
|
||||
default :from => "from@example.com"
|
||||
<% for action in actions -%>
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
#
|
||||
# en.<%= file_path.gsub("/",".") %>.<%= action %>.subject
|
||||
#
|
||||
def <%= action %>
|
||||
@greeting = "Hi"
|
||||
|
||||
mail :to => "to@example.org"
|
||||
end
|
||||
<% end -%>
|
||||
end
|
||||
@@ -1,49 +1,14 @@
|
||||
# Pathname has a warning, so require it first while silencing
|
||||
# warnings to shut it up.
|
||||
#
|
||||
# Also, in 1.9, Bundler creates warnings due to overriding
|
||||
# Rubygems methods
|
||||
begin
|
||||
old, $VERBOSE = $VERBOSE, nil
|
||||
require 'pathname'
|
||||
require File.expand_path('../../../load_paths', __FILE__)
|
||||
ensure
|
||||
$VERBOSE = old
|
||||
end
|
||||
|
||||
require 'active_support/core_ext/kernel/reporting'
|
||||
|
||||
require 'active_support/core_ext/string/encoding'
|
||||
if "ruby".encoding_aware?
|
||||
# These are the normal settings that will be set up by Railties
|
||||
# TODO: Have these tests support other combinations of these values
|
||||
silence_warnings do
|
||||
Encoding.default_internal = "UTF-8"
|
||||
Encoding.default_external = "UTF-8"
|
||||
end
|
||||
end
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'mail'
|
||||
end
|
||||
|
||||
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
|
||||
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
|
||||
|
||||
require 'test/unit'
|
||||
|
||||
$:.unshift "#{File.dirname(__FILE__)}/../lib"
|
||||
require 'action_mailer'
|
||||
require 'action_mailer/test_case'
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
||||
# Bogus template processors
|
||||
ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!".inspect }
|
||||
ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup".inspect }
|
||||
|
||||
FIXTURE_LOAD_PATH = File.expand_path('fixtures', File.dirname(__FILE__))
|
||||
ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH
|
||||
$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers"
|
||||
ActionMailer::Base.template_root = "#{File.dirname(__FILE__)}/fixtures"
|
||||
|
||||
class MockSMTP
|
||||
def self.deliveries
|
||||
@@ -57,21 +22,26 @@ class MockSMTP
|
||||
def sendmail(mail, from, to)
|
||||
@@deliveries << [mail, from, to]
|
||||
end
|
||||
|
||||
def start(*args)
|
||||
yield self
|
||||
end
|
||||
end
|
||||
|
||||
class Net::SMTP
|
||||
def self.new(*args)
|
||||
MockSMTP.new
|
||||
def self.start(*args)
|
||||
yield MockSMTP.new
|
||||
end
|
||||
end
|
||||
|
||||
def set_delivery_method(method)
|
||||
# Wrap tests that use Mocha and skip if unavailable.
|
||||
def uses_mocha(test_name)
|
||||
gem 'mocha', ">=0.5"
|
||||
require 'stubba'
|
||||
yield
|
||||
rescue Gem::LoadError
|
||||
$stderr.puts "Skipping #{test_name} tests (Mocha >= 0.5 is required). `gem install mocha` and try again."
|
||||
end
|
||||
|
||||
def set_delivery_method(delivery_method)
|
||||
@old_delivery_method = ActionMailer::Base.delivery_method
|
||||
ActionMailer::Base.delivery_method = method
|
||||
ActionMailer::Base.delivery_method = delivery_method
|
||||
end
|
||||
|
||||
def restore_delivery_method
|
||||
|
||||
@@ -1,605 +0,0 @@
|
||||
# encoding: utf-8
|
||||
require 'abstract_unit'
|
||||
require 'active_support/time'
|
||||
|
||||
require 'mailers/base_mailer'
|
||||
require 'mailers/proc_mailer'
|
||||
require 'mailers/asset_mailer'
|
||||
|
||||
class BaseTest < ActiveSupport::TestCase
|
||||
# TODO Add some tests for implicity layout render and url helpers
|
||||
# so we can get rid of old base tests altogether with old base.
|
||||
|
||||
def teardown
|
||||
ActionMailer::Base.asset_host = nil
|
||||
ActionMailer::Base.assets_dir = nil
|
||||
end
|
||||
|
||||
test "method call to mail does not raise error" do
|
||||
assert_nothing_raised { BaseMailer.welcome }
|
||||
end
|
||||
|
||||
# Basic mail usage without block
|
||||
test "mail() should set the headers of the mail message" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal(['system@test.lindsaar.net'], email.to)
|
||||
assert_equal(['jose@test.plataformatec.com'], email.from)
|
||||
assert_equal('The first email on new API!', email.subject)
|
||||
end
|
||||
|
||||
test "mail() with from overwrites the class level default" do
|
||||
email = BaseMailer.welcome(:from => 'someone@example.com',
|
||||
:to => 'another@example.org')
|
||||
assert_equal(['someone@example.com'], email.from)
|
||||
assert_equal(['another@example.org'], email.to)
|
||||
end
|
||||
|
||||
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
|
||||
@time = Time.now.beginning_of_day.to_datetime
|
||||
email = BaseMailer.welcome(:bcc => 'bcc@test.lindsaar.net',
|
||||
:cc => 'cc@test.lindsaar.net',
|
||||
:content_type => 'multipart/mixed',
|
||||
:charset => 'iso-8559-1',
|
||||
:mime_version => '2.0',
|
||||
:reply_to => 'reply-to@test.lindsaar.net',
|
||||
:date => @time)
|
||||
assert_equal(['bcc@test.lindsaar.net'], email.bcc)
|
||||
assert_equal(['cc@test.lindsaar.net'], email.cc)
|
||||
assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
|
||||
assert_equal('iso-8559-1', email.charset)
|
||||
assert_equal('2.0', email.mime_version)
|
||||
assert_equal(['reply-to@test.lindsaar.net'], email.reply_to)
|
||||
assert_equal(@time, email.date)
|
||||
end
|
||||
|
||||
test "mail() renders the template using the method being processed" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("Welcome", email.body.encoded)
|
||||
end
|
||||
|
||||
test "can pass in :body to the mail method hash" do
|
||||
email = BaseMailer.welcome(:body => "Hello there")
|
||||
assert_equal("text/plain", email.mime_type)
|
||||
assert_equal("Hello there", email.body.encoded)
|
||||
end
|
||||
|
||||
test "should set template content type if mail has only one part" do
|
||||
mail = BaseMailer.html_only
|
||||
assert_equal('text/html', mail.mime_type)
|
||||
mail = BaseMailer.plain_text_only
|
||||
assert_equal('text/plain', mail.mime_type)
|
||||
end
|
||||
|
||||
# Custom headers
|
||||
test "custom headers" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("Not SPAM", email['X-SPAM'].decoded)
|
||||
end
|
||||
|
||||
test "deprecated non-String custom headers" do
|
||||
email = assert_deprecated { BaseMailer.welcome_with_fixnum_header }
|
||||
assert_equal("2", email['X-SPAM-COUNT'].decoded)
|
||||
end
|
||||
|
||||
test "can pass random headers in as a hash to mail" do
|
||||
hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
|
||||
'In-Reply-To' => '1234@mikel.me.com' }
|
||||
mail = BaseMailer.welcome(hash)
|
||||
assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
|
||||
assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
|
||||
end
|
||||
|
||||
test "can pass random headers in as a hash to headers" do
|
||||
hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
|
||||
'In-Reply-To' => '1234@mikel.me.com' }
|
||||
mail = BaseMailer.welcome_with_headers(hash)
|
||||
assert_equal('SecretValue', mail['X-Special-Domain-Specific-Header'].decoded)
|
||||
assert_equal('1234@mikel.me.com', mail['In-Reply-To'].decoded)
|
||||
end
|
||||
|
||||
# Attachments
|
||||
test "attachment with content" do
|
||||
email = BaseMailer.attachment_with_content
|
||||
assert_equal(1, email.attachments.length)
|
||||
assert_equal('invoice.pdf', email.attachments[0].filename)
|
||||
assert_equal('This is test File content', email.attachments['invoice.pdf'].decoded)
|
||||
end
|
||||
|
||||
test "attachment gets content type from filename" do
|
||||
email = BaseMailer.attachment_with_content
|
||||
assert_equal('invoice.pdf', email.attachments[0].filename)
|
||||
end
|
||||
|
||||
test "attachment with hash" do
|
||||
skip "failed already"
|
||||
email = BaseMailer.attachment_with_hash
|
||||
assert_equal(1, email.attachments.length)
|
||||
assert_equal('invoice.jpg', email.attachments[0].filename)
|
||||
expected = "\312\213\254\232)b"
|
||||
expected.force_encoding(Encoding::BINARY) if '1.9'.respond_to?(:force_encoding)
|
||||
assert_equal expected, email.attachments['invoice.jpg'].decoded
|
||||
end
|
||||
|
||||
test "attachment with hash using default mail encoding" do
|
||||
email = BaseMailer.attachment_with_hash_default_encoding
|
||||
assert_equal(1, email.attachments.length)
|
||||
assert_equal('invoice.jpg', email.attachments[0].filename)
|
||||
expected = "\312\213\254\232)b"
|
||||
expected.force_encoding(Encoding::BINARY) if '1.9'.respond_to?(:force_encoding)
|
||||
assert_equal expected, email.attachments['invoice.jpg'].decoded
|
||||
end
|
||||
|
||||
test "sets mime type to multipart/mixed when attachment is included" do
|
||||
email = BaseMailer.attachment_with_content
|
||||
assert_equal(1, email.attachments.length)
|
||||
assert_equal("multipart/mixed", email.mime_type)
|
||||
end
|
||||
|
||||
test "adds the rendered template as part" do
|
||||
email = BaseMailer.attachment_with_content
|
||||
assert_equal(2, email.parts.length)
|
||||
assert_equal("multipart/mixed", email.mime_type)
|
||||
assert_equal("text/html", email.parts[0].mime_type)
|
||||
assert_equal("Attachment with content", email.parts[0].body.encoded)
|
||||
assert_equal("application/pdf", email.parts[1].mime_type)
|
||||
assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "adds the given :body as part" do
|
||||
email = BaseMailer.attachment_with_content(:body => "I'm the eggman")
|
||||
assert_equal(2, email.parts.length)
|
||||
assert_equal("multipart/mixed", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("I'm the eggman", email.parts[0].body.encoded)
|
||||
assert_equal("application/pdf", email.parts[1].mime_type)
|
||||
assert_equal("VGhpcyBpcyB0ZXN0IEZpbGUgY29udGVudA==\r\n", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "can embed an inline attachment" do
|
||||
email = BaseMailer.inline_attachment
|
||||
# Need to call #encoded to force the JIT sort on parts
|
||||
email.encoded
|
||||
assert_equal(2, email.parts.length)
|
||||
assert_equal("multipart/related", email.mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[0].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("logo.png", email.parts[1].filename)
|
||||
end
|
||||
|
||||
# Defaults values
|
||||
test "uses default charset from class" do
|
||||
with_default BaseMailer, :charset => "US-ASCII" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("US-ASCII", email.charset)
|
||||
|
||||
email = BaseMailer.welcome(:charset => "iso-8559-1")
|
||||
assert_equal("iso-8559-1", email.charset)
|
||||
end
|
||||
end
|
||||
|
||||
test "uses default content type from class" do
|
||||
with_default BaseMailer, :content_type => "text/html" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("text/html", email.mime_type)
|
||||
|
||||
email = BaseMailer.welcome(:content_type => "text/plain")
|
||||
assert_equal("text/plain", email.mime_type)
|
||||
end
|
||||
end
|
||||
|
||||
test "uses default mime version from class" do
|
||||
with_default BaseMailer, :mime_version => "2.0" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("2.0", email.mime_version)
|
||||
|
||||
email = BaseMailer.welcome(:mime_version => "1.0")
|
||||
assert_equal("1.0", email.mime_version)
|
||||
end
|
||||
end
|
||||
|
||||
test "uses random default headers from class" do
|
||||
with_default BaseMailer, "X-Custom" => "Custom" do
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("Custom", email["X-Custom"].decoded)
|
||||
end
|
||||
end
|
||||
|
||||
test "subject gets default from I18n" do
|
||||
BaseMailer.default :subject => nil
|
||||
email = BaseMailer.welcome(:subject => nil)
|
||||
assert_equal "Welcome", email.subject
|
||||
|
||||
I18n.backend.store_translations('en', :base_mailer => {:welcome => {:subject => "New Subject!"}})
|
||||
email = BaseMailer.welcome(:subject => nil)
|
||||
assert_equal "New Subject!", email.subject
|
||||
end
|
||||
|
||||
test "translations are scoped properly" do
|
||||
I18n.backend.store_translations('en', :base_mailer => {:email_with_translations => {:greet_user => "Hello %{name}!"}})
|
||||
email = BaseMailer.email_with_translations
|
||||
assert_equal 'Hello lifo!', email.body.encoded
|
||||
end
|
||||
|
||||
# Implicit multipart
|
||||
test "implicit multipart" do
|
||||
email = BaseMailer.implicit_multipart
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("TEXT Implicit Multipart", email.parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
assert_equal("HTML Implicit Multipart", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "implicit multipart with sort order" do
|
||||
order = ["text/html", "text/plain"]
|
||||
with_default BaseMailer, :parts_order => order do
|
||||
email = BaseMailer.implicit_multipart
|
||||
assert_equal("text/html", email.parts[0].mime_type)
|
||||
assert_equal("text/plain", email.parts[1].mime_type)
|
||||
|
||||
email = BaseMailer.implicit_multipart(:parts_order => order.reverse)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
end
|
||||
end
|
||||
|
||||
test "implicit multipart with attachments creates nested parts" do
|
||||
email = BaseMailer.implicit_multipart(:attachments => true)
|
||||
assert_equal("application/pdf", email.parts[0].mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[1].mime_type)
|
||||
assert_equal("text/plain", email.parts[1].parts[0].mime_type)
|
||||
assert_equal("TEXT Implicit Multipart", email.parts[1].parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].parts[1].mime_type)
|
||||
assert_equal("HTML Implicit Multipart", email.parts[1].parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "implicit multipart with attachments and sort order" do
|
||||
order = ["text/html", "text/plain"]
|
||||
with_default BaseMailer, :parts_order => order do
|
||||
email = BaseMailer.implicit_multipart(:attachments => true)
|
||||
assert_equal("application/pdf", email.parts[0].mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[1].mime_type)
|
||||
assert_equal("text/plain", email.parts[1].parts[1].mime_type)
|
||||
assert_equal("text/html", email.parts[1].parts[0].mime_type)
|
||||
end
|
||||
end
|
||||
|
||||
test "implicit multipart with default locale" do
|
||||
email = BaseMailer.implicit_with_locale
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("Implicit with locale TEXT", email.parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
assert_equal("Implicit with locale EN HTML", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "implicit multipart with other locale" do
|
||||
swap I18n, :locale => :pl do
|
||||
email = BaseMailer.implicit_with_locale
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("Implicit with locale PL TEXT", email.parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
assert_equal("Implicit with locale HTML", email.parts[1].body.encoded)
|
||||
end
|
||||
end
|
||||
|
||||
test "implicit multipart with several view paths uses the first one with template" do
|
||||
old = BaseMailer.view_paths
|
||||
begin
|
||||
BaseMailer.view_paths = [File.join(FIXTURE_LOAD_PATH, "another.path")] + old.dup
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("Welcome from another path", email.body.encoded)
|
||||
ensure
|
||||
BaseMailer.view_paths = old
|
||||
end
|
||||
end
|
||||
|
||||
test "implicit multipart with inexistent templates uses the next view path" do
|
||||
old = BaseMailer.view_paths
|
||||
begin
|
||||
BaseMailer.view_paths = [File.join(FIXTURE_LOAD_PATH, "unknown")] + old.dup
|
||||
email = BaseMailer.welcome
|
||||
assert_equal("Welcome", email.body.encoded)
|
||||
ensure
|
||||
BaseMailer.view_paths = old
|
||||
end
|
||||
end
|
||||
|
||||
# Explicit multipart
|
||||
test "explicit multipart" do
|
||||
email = BaseMailer.explicit_multipart
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("TEXT Explicit Multipart", email.parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
assert_equal("HTML Explicit Multipart", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "explicit multipart have a boundary" do
|
||||
mail = BaseMailer.explicit_multipart
|
||||
assert_not_nil(mail.content_type_parameters[:boundary])
|
||||
end
|
||||
|
||||
test "explicit multipart does not sort order" do
|
||||
order = ["text/html", "text/plain"]
|
||||
with_default BaseMailer, :parts_order => order do
|
||||
email = BaseMailer.explicit_multipart
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
|
||||
email = BaseMailer.explicit_multipart(:parts_order => order.reverse)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
end
|
||||
end
|
||||
|
||||
test "explicit multipart with attachments creates nested parts" do
|
||||
email = BaseMailer.explicit_multipart(:attachments => true)
|
||||
assert_equal("application/pdf", email.parts[0].mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[1].mime_type)
|
||||
assert_equal("text/plain", email.parts[1].parts[0].mime_type)
|
||||
assert_equal("TEXT Explicit Multipart", email.parts[1].parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].parts[1].mime_type)
|
||||
assert_equal("HTML Explicit Multipart", email.parts[1].parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "explicit multipart with templates" do
|
||||
email = BaseMailer.explicit_multipart_templates
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/html", email.parts[0].mime_type)
|
||||
assert_equal("HTML Explicit Multipart Templates", email.parts[0].body.encoded)
|
||||
assert_equal("text/plain", email.parts[1].mime_type)
|
||||
assert_equal("TEXT Explicit Multipart Templates", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "explicit multipart with format.any" do
|
||||
email = BaseMailer.explicit_multipart_with_any
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("Format with any!", email.parts[0].body.encoded)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
assert_equal("Format with any!", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
test "explicit multipart with format(Hash)" do
|
||||
email = BaseMailer.explicit_multipart_with_options(true)
|
||||
email.ready_to_send!
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/plain", email.parts[0].mime_type)
|
||||
assert_equal("base64", email.parts[0].content_transfer_encoding)
|
||||
assert_equal("text/html", email.parts[1].mime_type)
|
||||
assert_equal("7bit", email.parts[1].content_transfer_encoding)
|
||||
end
|
||||
|
||||
test "explicit multipart with one part is rendered as body and options are merged" do
|
||||
email = BaseMailer.explicit_multipart_with_options
|
||||
assert_equal(0, email.parts.size)
|
||||
assert_equal("text/plain", email.mime_type)
|
||||
assert_equal("base64", email.content_transfer_encoding)
|
||||
end
|
||||
|
||||
test "explicit multipart with one template has the expected format" do
|
||||
email = BaseMailer.explicit_multipart_with_one_template
|
||||
assert_equal(2, email.parts.size)
|
||||
assert_equal("multipart/alternative", email.mime_type)
|
||||
assert_equal("text/html", email.parts[0].mime_type)
|
||||
assert_equal("[:html]", email.parts[0].body.encoded)
|
||||
assert_equal("text/plain", email.parts[1].mime_type)
|
||||
assert_equal("[:text]", email.parts[1].body.encoded)
|
||||
end
|
||||
|
||||
# Class level API with method missing
|
||||
test "should respond to action methods" do
|
||||
assert_respond_to BaseMailer, :welcome
|
||||
assert_respond_to BaseMailer, :implicit_multipart
|
||||
assert !BaseMailer.respond_to?(:mail)
|
||||
assert !BaseMailer.respond_to?(:headers)
|
||||
end
|
||||
|
||||
test "calling just the action should return the generated mail object" do
|
||||
BaseMailer.deliveries.clear
|
||||
email = BaseMailer.welcome
|
||||
assert_equal(0, BaseMailer.deliveries.length)
|
||||
assert_equal('The first email on new API!', email.subject)
|
||||
end
|
||||
|
||||
test "calling deliver on the action should deliver the mail object" do
|
||||
BaseMailer.deliveries.clear
|
||||
BaseMailer.expects(:deliver_mail).once
|
||||
mail = BaseMailer.welcome.deliver
|
||||
assert_instance_of Mail::Message, mail
|
||||
end
|
||||
|
||||
test "calling deliver on the action should increment the deliveries collection if using the test mailer" do
|
||||
BaseMailer.delivery_method = :test
|
||||
BaseMailer.deliveries.clear
|
||||
BaseMailer.welcome.deliver
|
||||
assert_equal(1, BaseMailer.deliveries.length)
|
||||
end
|
||||
|
||||
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
|
||||
mail = Mail::Message.new
|
||||
mail.expects(:do_delivery).once
|
||||
BaseMailer.expects(:welcome).returns(mail)
|
||||
BaseMailer.welcome.deliver
|
||||
end
|
||||
|
||||
# Rendering
|
||||
test "you can specify a different template for implicit render" do
|
||||
mail = BaseMailer.implicit_different_template('implicit_multipart').deliver
|
||||
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
|
||||
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "render :file uses render :template semantics and is deprecated" do
|
||||
mail = nil
|
||||
assert_deprecated { mail = BaseMailer.implicit_different_template_with_file('implicit_multipart').deliver }
|
||||
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
|
||||
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "you can specify a different template for explicit render" do
|
||||
mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver
|
||||
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
|
||||
assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "you can specify a different layout" do
|
||||
mail = BaseMailer.different_layout('different_layout').deliver
|
||||
assert_equal("HTML -- HTML", mail.html_part.body.decoded)
|
||||
assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "you can specify the template path for implicit lookup" do
|
||||
mail = BaseMailer.welcome_from_another_path('another.path/base_mailer').deliver
|
||||
assert_equal("Welcome from another path", mail.body.encoded)
|
||||
|
||||
mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer']).deliver
|
||||
assert_equal("Welcome from another path", mail.body.encoded)
|
||||
end
|
||||
|
||||
test "assets tags should use ActionMailer's asset_host settings" do
|
||||
ActionMailer::Base.config.asset_host = "http://global.com"
|
||||
ActionMailer::Base.config.assets_dir = "global/"
|
||||
|
||||
mail = AssetMailer.welcome
|
||||
|
||||
assert_equal(%{<img alt="Dummy" src="http://global.com/images/dummy.png" />}, mail.body.to_s.strip)
|
||||
end
|
||||
|
||||
test "assets tags should use a Mailer's asset_host settings when available" do
|
||||
ActionMailer::Base.config.asset_host = "global.com"
|
||||
ActionMailer::Base.config.assets_dir = "global/"
|
||||
|
||||
AssetMailer.asset_host = "http://local.com"
|
||||
|
||||
mail = AssetMailer.welcome
|
||||
|
||||
assert_equal(%{<img alt="Dummy" src="http://local.com/images/dummy.png" />}, mail.body.to_s.strip)
|
||||
end
|
||||
|
||||
# Before and After hooks
|
||||
|
||||
class MyObserver
|
||||
def self.delivered_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
class MySecondObserver
|
||||
def self.delivered_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
test "you can register an observer to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer(MyObserver)
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register an observer using its stringified name to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer("BaseTest::MyObserver")
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register multiple observers to the mail object that both get informed on email delivery" do
|
||||
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
MySecondObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
class MyInterceptor
|
||||
def self.delivering_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
class MySecondInterceptor
|
||||
def self.delivering_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
test "you can register an interceptor to the mail object that gets passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptor(MyInterceptor)
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register multiple interceptors to the mail object that both get passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
MySecondInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
|
||||
mail1 = ProcMailer.welcome
|
||||
yesterday = 1.day.ago
|
||||
Time.stubs(:now).returns(yesterday)
|
||||
mail2 = ProcMailer.welcome
|
||||
assert(mail1['X-Proc-Method'].to_s.to_i > mail2['X-Proc-Method'].to_s.to_i)
|
||||
end
|
||||
|
||||
test "we can call other defined methods on the class as needed" do
|
||||
mail = ProcMailer.welcome
|
||||
assert_equal("Thanks for signing up this afternoon", mail.subject)
|
||||
end
|
||||
|
||||
test "action methods should be refreshed after defining new method" do
|
||||
class FooMailer < ActionMailer::Base
|
||||
# this triggers action_methods
|
||||
self.respond_to?(:foo)
|
||||
|
||||
def notify
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal ["notify"], FooMailer.action_methods
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Execute the block setting the given values and restoring old values after
|
||||
# the block is executed.
|
||||
def swap(klass, new_values)
|
||||
old_values = {}
|
||||
new_values.each do |key, value|
|
||||
old_values[key] = klass.send key
|
||||
klass.send :"#{key}=", value
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
old_values.each do |key, value|
|
||||
klass.send :"#{key}=", value
|
||||
end
|
||||
end
|
||||
|
||||
def with_default(klass, new_values)
|
||||
old = klass.default_params
|
||||
klass.default(new_values)
|
||||
yield
|
||||
ensure
|
||||
klass.default_params = old
|
||||
end
|
||||
end
|
||||
51
actionmailer/test/delivery_method_test.rb
Normal file
51
actionmailer/test/delivery_method_test.rb
Normal file
@@ -0,0 +1,51 @@
|
||||
require "#{File.dirname(__FILE__)}/abstract_unit"
|
||||
|
||||
class DefaultDeliveryMethodMailer < ActionMailer::Base
|
||||
end
|
||||
|
||||
class NonDefaultDeliveryMethodMailer < ActionMailer::Base
|
||||
self.delivery_method = :sendmail
|
||||
end
|
||||
|
||||
class ActionMailerBase_delivery_method_Test < Test::Unit::TestCase
|
||||
def setup
|
||||
set_delivery_method :smtp
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_should_be_the_default_smtp
|
||||
assert_equal :smtp, ActionMailer::Base.delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
|
||||
def setup
|
||||
set_delivery_method :smtp
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_should_be_the_default_smtp
|
||||
assert_equal :smtp, DefaultDeliveryMethodMailer.delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
class NonDefaultDeliveryMethodMailer_delivery_method_Test < Test::Unit::TestCase
|
||||
def setup
|
||||
set_delivery_method :smtp
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_should_be_the_set_delivery_method
|
||||
assert_equal :sendmail, NonDefaultDeliveryMethodMailer.delivery_method
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
require 'abstract_unit'
|
||||
require 'mail'
|
||||
|
||||
class MyCustomDelivery
|
||||
end
|
||||
|
||||
class BogusDelivery
|
||||
def initialize(*)
|
||||
end
|
||||
|
||||
def deliver!(mail)
|
||||
raise "failed"
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase
|
||||
test "default smtp settings" do
|
||||
settings = { :address => "localhost",
|
||||
:port => 25,
|
||||
:domain => 'localhost.localdomain',
|
||||
:user_name => nil,
|
||||
:password => nil,
|
||||
:authentication => nil,
|
||||
:enable_starttls_auto => true }
|
||||
assert_equal settings, ActionMailer::Base.smtp_settings
|
||||
end
|
||||
|
||||
test "default file delivery settings" do
|
||||
settings = {:location => "#{Dir.tmpdir}/mails"}
|
||||
assert_equal settings, ActionMailer::Base.file_settings
|
||||
end
|
||||
|
||||
test "default sendmail settings" do
|
||||
settings = {:location => '/usr/sbin/sendmail',
|
||||
:arguments => '-i -t'}
|
||||
assert_equal settings, ActionMailer::Base.sendmail_settings
|
||||
end
|
||||
end
|
||||
|
||||
class CustomDeliveryMethodsTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@old_delivery_method = ActionMailer::Base.delivery_method
|
||||
ActionMailer::Base.add_delivery_method :custom, MyCustomDelivery
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActionMailer::Base.delivery_method = @old_delivery_method
|
||||
new = ActionMailer::Base.delivery_methods.dup
|
||||
new.delete(:custom)
|
||||
ActionMailer::Base.delivery_methods = new
|
||||
end
|
||||
|
||||
test "allow to add custom delivery method" do
|
||||
ActionMailer::Base.delivery_method = :custom
|
||||
assert_equal :custom, ActionMailer::Base.delivery_method
|
||||
end
|
||||
|
||||
test "allow to customize custom settings" do
|
||||
ActionMailer::Base.custom_settings = { :foo => :bar }
|
||||
assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings
|
||||
end
|
||||
|
||||
test "respond to custom settings" do
|
||||
assert_respond_to ActionMailer::Base, :custom_settings
|
||||
assert_respond_to ActionMailer::Base, :custom_settings=
|
||||
end
|
||||
|
||||
test "does not respond to unknown settings" do
|
||||
assert_raise NoMethodError do
|
||||
ActionMailer::Base.another_settings
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class MailDeliveryTest < ActiveSupport::TestCase
|
||||
class DeliveryMailer < ActionMailer::Base
|
||||
DEFAULT_HEADERS = {
|
||||
:to => 'mikel@test.lindsaar.net',
|
||||
:from => 'jose@test.plataformatec.com'
|
||||
}
|
||||
|
||||
def welcome(hash={})
|
||||
mail(DEFAULT_HEADERS.merge(hash))
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
ActionMailer::Base.delivery_method = :smtp
|
||||
end
|
||||
|
||||
def teardown
|
||||
DeliveryMailer.delivery_method = :smtp
|
||||
DeliveryMailer.perform_deliveries = true
|
||||
DeliveryMailer.raise_delivery_errors = true
|
||||
end
|
||||
|
||||
test "ActionMailer should be told when Mail gets delivered" do
|
||||
DeliveryMailer.deliveries.clear
|
||||
DeliveryMailer.expects(:deliver_mail).once
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
|
||||
test "delivery method can be customized per instance" do
|
||||
email = DeliveryMailer.welcome.deliver
|
||||
assert_instance_of Mail::SMTP, email.delivery_method
|
||||
email = DeliveryMailer.welcome(:delivery_method => :test).deliver
|
||||
assert_instance_of Mail::TestMailer, email.delivery_method
|
||||
end
|
||||
|
||||
test "delivery method can be customized in subclasses not changing the parent" do
|
||||
DeliveryMailer.delivery_method = :test
|
||||
assert_equal :smtp, ActionMailer::Base.delivery_method
|
||||
$BREAK = true
|
||||
email = DeliveryMailer.welcome.deliver
|
||||
assert_instance_of Mail::TestMailer, email.delivery_method
|
||||
end
|
||||
|
||||
test "non registered delivery methods raises errors" do
|
||||
DeliveryMailer.delivery_method = :unknown
|
||||
assert_raise RuntimeError do
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
end
|
||||
|
||||
test "does not perform deliveries if requested" do
|
||||
DeliveryMailer.perform_deliveries = false
|
||||
DeliveryMailer.deliveries.clear
|
||||
Mail::Message.any_instance.expects(:deliver!).never
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
|
||||
test "does not append the deliveries collection if told not to perform the delivery" do
|
||||
DeliveryMailer.perform_deliveries = false
|
||||
DeliveryMailer.deliveries.clear
|
||||
DeliveryMailer.welcome.deliver
|
||||
assert_equal(0, DeliveryMailer.deliveries.length)
|
||||
end
|
||||
|
||||
test "raise errors on bogus deliveries" do
|
||||
DeliveryMailer.delivery_method = BogusDelivery
|
||||
DeliveryMailer.deliveries.clear
|
||||
assert_raise RuntimeError do
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
end
|
||||
|
||||
test "does not increment the deliveries collection on error" do
|
||||
DeliveryMailer.delivery_method = BogusDelivery
|
||||
DeliveryMailer.deliveries.clear
|
||||
assert_raise RuntimeError do
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
assert_equal(0, DeliveryMailer.deliveries.length)
|
||||
end
|
||||
|
||||
test "does not raise errors on bogus deliveries if set" do
|
||||
DeliveryMailer.delivery_method = BogusDelivery
|
||||
DeliveryMailer.raise_delivery_errors = false
|
||||
assert_nothing_raised do
|
||||
DeliveryMailer.welcome.deliver
|
||||
end
|
||||
end
|
||||
|
||||
test "does not increment the deliveries collection on bogus deliveries" do
|
||||
DeliveryMailer.delivery_method = BogusDelivery
|
||||
DeliveryMailer.raise_delivery_errors = false
|
||||
DeliveryMailer.deliveries.clear
|
||||
DeliveryMailer.welcome.deliver
|
||||
assert_equal(0, DeliveryMailer.deliveries.length)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
Welcome from another path
|
||||
@@ -1 +0,0 @@
|
||||
<%= image_tag "somelogo.png" %>
|
||||
@@ -1 +0,0 @@
|
||||
<%= image_tag "dummy.png" %>
|
||||
BIN
actionmailer/test/fixtures/attachments/foo.jpg
vendored
BIN
actionmailer/test/fixtures/attachments/foo.jpg
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
BIN
actionmailer/test/fixtures/attachments/test.jpg
vendored
BIN
actionmailer/test/fixtures/attachments/test.jpg
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1 +0,0 @@
|
||||
Inside
|
||||
@@ -1 +0,0 @@
|
||||
text/html multipart
|
||||
@@ -1 +0,0 @@
|
||||
text/plain multipart
|
||||
@@ -1 +0,0 @@
|
||||
Attachment with content
|
||||
@@ -1 +0,0 @@
|
||||
HTML
|
||||
@@ -1 +0,0 @@
|
||||
PLAIN
|
||||
@@ -1 +0,0 @@
|
||||
body_text
|
||||
@@ -1 +0,0 @@
|
||||
<%= t('.greet_user', :name => 'lifo') %>
|
||||
@@ -1 +0,0 @@
|
||||
HTML Explicit Multipart Templates
|
||||
@@ -1 +0,0 @@
|
||||
TEXT Explicit Multipart Templates
|
||||
@@ -1 +0,0 @@
|
||||
<%= self.formats.inspect %>
|
||||
@@ -1 +0,0 @@
|
||||
<h1>Testing</h1>
|
||||
@@ -1 +0,0 @@
|
||||
HTML Implicit Multipart
|
||||
@@ -1 +0,0 @@
|
||||
TEXT Implicit Multipart
|
||||
@@ -1 +0,0 @@
|
||||
Implicit with locale EN HTML
|
||||
@@ -1 +0,0 @@
|
||||
Implicit with locale HTML
|
||||
@@ -1 +0,0 @@
|
||||
Implicit with locale PL TEXT
|
||||
@@ -1 +0,0 @@
|
||||
Implicit with locale TEXT
|
||||
@@ -1,5 +0,0 @@
|
||||
<h1>Inline Image</h1>
|
||||
|
||||
<%= image_tag attachments['logo.png'].url %>
|
||||
|
||||
<p>This is an image that is inline</p>
|
||||
@@ -1,4 +0,0 @@
|
||||
Inline Image
|
||||
|
||||
No image for you
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Testing
|
||||
@@ -1 +0,0 @@
|
||||
Welcome
|
||||
@@ -1 +0,0 @@
|
||||
You logged out
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user