mailit 2009.08
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +6 -0
- data/CHANGELOG +76 -0
- data/MANIFEST +28 -0
- data/README.md +63 -0
- data/Rakefile +84 -0
- data/lib/mailit.rb +8 -0
- data/lib/mailit/mail.rb +367 -0
- data/lib/mailit/mailer.rb +131 -0
- data/lib/mailit/mime.rb +49 -0
- data/lib/mailit/version.rb +3 -0
- data/lib/version.rb +3 -0
- data/mailit.gemspec +27 -0
- data/spec/helper.rb +4 -0
- data/spec/mailit/mail.rb +108 -0
- data/spec/mailit/mailer.rb +48 -0
- data/tasks/authors.rake +33 -0
- data/tasks/bacon.rake +70 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/copyright.rake +38 -0
- data/tasks/gem.rake +23 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/install_dependencies.rake +6 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +23 -0
- data/tasks/release.rake +52 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/setup.rake +15 -0
- data/tasks/yard.rake +4 -0
- metadata +82 -0
data/AUTHORS
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
[78c844d | 2009-08-25 21:40:40 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
2
|
+
|
3
|
+
* Another fix
|
4
|
+
|
5
|
+
[fb25c19 | 2009-08-25 21:40:01 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
6
|
+
|
7
|
+
* Version 2009.08
|
8
|
+
|
9
|
+
[2e6d77a | 2009-08-25 21:38:53 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
10
|
+
|
11
|
+
* Fix Rakefile
|
12
|
+
|
13
|
+
[585554d | 2009-08-25 05:11:42 UTC] Kevin Berry <kevin@opensourcealchemist.com>
|
14
|
+
|
15
|
+
* Take in Pistos's patch, make my own patch to get running for current project, and add seedling structure.
|
16
|
+
|
17
|
+
[db73002 | 2009-08-06 06:50:35 UTC] Jake Douglas <jakecdouglas@gmail.com>
|
18
|
+
|
19
|
+
* NetSMTP and EventMachine version both actually send mail now. Previous configuration didn't override the #send method correctly so the EM methods weren't loaded. The EM module may only be included now, not used with extend.
|
20
|
+
|
21
|
+
Signed-off-by: Michael Fellinger <m.fellinger@gmail.com>
|
22
|
+
|
23
|
+
[9a36e2f | 2009-08-06 05:39:26 UTC] Jake Douglas <jakecdouglas@gmail.com>
|
24
|
+
|
25
|
+
* get the value of :noop from the options hash in NetSmtp#send
|
26
|
+
|
27
|
+
Signed-off-by: Michael Fellinger <m.fellinger@gmail.com>
|
28
|
+
|
29
|
+
[c6da60b | 2009-06-08 13:52:33 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
30
|
+
|
31
|
+
* Release new version
|
32
|
+
|
33
|
+
[185cf45 | 2009-06-08 13:51:07 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
34
|
+
|
35
|
+
* Fix long outstanding issues
|
36
|
+
|
37
|
+
[e429f3b | 2009-04-12 13:38:16 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
38
|
+
|
39
|
+
* Major refactor of Mailer, add support for EventMachine::SmtpClient and remove render_send
|
40
|
+
|
41
|
+
[341d76a | 2009-04-12 13:23:33 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
42
|
+
|
43
|
+
* Make Mail#header_string and Mail#body_string public
|
44
|
+
|
45
|
+
[5716124 | 2009-03-15 14:26:27 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
46
|
+
|
47
|
+
* Add auth_type and username options for Mailer#send, should work on windows now
|
48
|
+
|
49
|
+
[ff38041 | 2009-03-10 21:02:58 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
50
|
+
|
51
|
+
* Add gemspec
|
52
|
+
|
53
|
+
[07b92d0 | 2009-03-10 20:49:36 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
54
|
+
|
55
|
+
* Add Mailit::Mime - detects and uses available mime providers
|
56
|
+
|
57
|
+
[584398c | 2009-03-10 20:48:49 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
58
|
+
|
59
|
+
* Little improvment to style
|
60
|
+
|
61
|
+
[194c990 | 2009-03-10 20:19:08 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
62
|
+
|
63
|
+
* Add warning if no mime-type detection is possible
|
64
|
+
|
65
|
+
[184b20d | 2009-03-10 20:18:54 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
66
|
+
|
67
|
+
* Add Version
|
68
|
+
|
69
|
+
[74e9ca3 | 2009-03-10 20:18:35 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
70
|
+
|
71
|
+
* Add readme
|
72
|
+
|
73
|
+
[0a4bcad | 2009-03-10 20:02:28 UTC] Michael Fellinger <m.fellinger@gmail.com>
|
74
|
+
|
75
|
+
* Initial commit
|
76
|
+
|
data/MANIFEST
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
AUTHORS
|
2
|
+
CHANGELOG
|
3
|
+
MANIFEST
|
4
|
+
README.md
|
5
|
+
Rakefile
|
6
|
+
lib/mailit.rb
|
7
|
+
lib/mailit/mail.rb
|
8
|
+
lib/mailit/mailer.rb
|
9
|
+
lib/mailit/mime.rb
|
10
|
+
lib/mailit/version.rb
|
11
|
+
lib/version.rb
|
12
|
+
mailit.gemspec
|
13
|
+
spec/helper.rb
|
14
|
+
spec/mailit/mail.rb
|
15
|
+
spec/mailit/mailer.rb
|
16
|
+
tasks/authors.rake
|
17
|
+
tasks/bacon.rake
|
18
|
+
tasks/changelog.rake
|
19
|
+
tasks/copyright.rake
|
20
|
+
tasks/gem.rake
|
21
|
+
tasks/gem_installer.rake
|
22
|
+
tasks/install_dependencies.rake
|
23
|
+
tasks/manifest.rake
|
24
|
+
tasks/rcov.rake
|
25
|
+
tasks/release.rake
|
26
|
+
tasks/reversion.rake
|
27
|
+
tasks/setup.rake
|
28
|
+
tasks/yard.rake
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Mailit
|
2
|
+
|
3
|
+
Mailit is a simple to use library to create and send MIME compliant e-mail with
|
4
|
+
attachments and various encodings.
|
5
|
+
|
6
|
+
This is a fork of MailFactory and provides a mostly identical API but has been
|
7
|
+
cleaned up, simplified, and made compliant to common Ruby idioms. I would like
|
8
|
+
to thank David Powers for the original MailFactory, it served me well for many
|
9
|
+
years.
|
10
|
+
|
11
|
+
Copyright (c) 2005-2008 David Powers.
|
12
|
+
Copyright (c) 2009 Michael Fellinger.
|
13
|
+
|
14
|
+
This program is free software. You can re-distribute and/or modify this program
|
15
|
+
under the same terms as Ruby itself.
|
16
|
+
|
17
|
+
|
18
|
+
## Dependencies
|
19
|
+
|
20
|
+
Any Ruby since 1.8.4 should work.
|
21
|
+
Mailit can use the Rack or Mime::Types libraries to determine the mime-type of
|
22
|
+
attachments automatically, but they are optional.
|
23
|
+
|
24
|
+
|
25
|
+
## Usage of Mailit::Mail
|
26
|
+
|
27
|
+
require 'net/smtp'
|
28
|
+
require 'mailit'
|
29
|
+
|
30
|
+
mail = Mailit::Mail.new
|
31
|
+
mail.to = 'test@test.com'
|
32
|
+
mail.from = 'sender@sender.com'
|
33
|
+
mail.subject 'Here are some files for you!'
|
34
|
+
mail.text = 'This is what people with plain text mail readers will see'
|
35
|
+
mail.html = "A little something <b>special</b> for people with HTML readers'
|
36
|
+
mail.attach('/etc/fstab')
|
37
|
+
mail.attach('/home/manveru/.vimrc')
|
38
|
+
|
39
|
+
puts mail
|
40
|
+
|
41
|
+
|
42
|
+
## Usage of Mailit::Mailer
|
43
|
+
|
44
|
+
Using the mail variable from above example
|
45
|
+
|
46
|
+
mailer = Mailit::Mailer.new
|
47
|
+
|
48
|
+
mailer.send(mail, :server => 'smtp.example.com', :port => 25,
|
49
|
+
:domain => 'example.com', :password => 'foo')
|
50
|
+
|
51
|
+
|
52
|
+
## Todo:
|
53
|
+
|
54
|
+
MailFactory has a method_missing that handles getting and setting of arbitrary
|
55
|
+
headers.
|
56
|
+
I went for the less magical #[] and #[]= methods, maybe someone can add the
|
57
|
+
MailFactory behaviour.
|
58
|
+
|
59
|
+
## Thanks to
|
60
|
+
|
61
|
+
* Michael Thompson (AKA:nylon)
|
62
|
+
|
63
|
+
Making mailer work on windows
|
data/Rakefile
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
begin; require 'rubygems'; rescue LoadError; end
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/clean'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'time'
|
7
|
+
require 'date'
|
8
|
+
require './lib/mailit'
|
9
|
+
|
10
|
+
PROJECT_SPECS = FileList[
|
11
|
+
'spec/*/**/*.rb'
|
12
|
+
]
|
13
|
+
|
14
|
+
PROJECT_MODULE = 'Mailit'
|
15
|
+
PROJECT_README = 'README'
|
16
|
+
#PROJECT_RUBYFORGE_GROUP_ID = 3034
|
17
|
+
PROJECT_COPYRIGHT_SUMMARY = [
|
18
|
+
"# Copyright (c) 2008-#{Time.now.year} The Rubyists, LLC (effortless systems) <rubyists@rubyists.com>",
|
19
|
+
"# Distributed under the terms of the MIT license.",
|
20
|
+
"# See the LICENSE file which accompanies this software for the full text",
|
21
|
+
"#"
|
22
|
+
]
|
23
|
+
PROJECT_COPYRIGHT = PROJECT_COPYRIGHT_SUMMARY + [
|
24
|
+
"# Permission is hereby granted, free of charge, to any person obtaining a copy",
|
25
|
+
'# of this software and associated documentation files (the "Software"), to deal',
|
26
|
+
"# in the Software without restriction, including without limitation the rights",
|
27
|
+
"# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
28
|
+
"# copies of the Software, and to permit persons to whom the Software is",
|
29
|
+
"# furnished to do so, subject to the following conditions:",
|
30
|
+
"#",
|
31
|
+
"# The above copyright notice and this permission notice shall be included in",
|
32
|
+
"# all copies or substantial portions of the Software.",
|
33
|
+
"#",
|
34
|
+
'# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR',
|
35
|
+
"# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
36
|
+
"# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
37
|
+
"# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
38
|
+
"# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
39
|
+
"# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN",
|
40
|
+
"# THE SOFTWARE."
|
41
|
+
]
|
42
|
+
|
43
|
+
PROJECT_VERSION =
|
44
|
+
if version = ENV['PROJECT_VERSION'] || ENV['VERSION']
|
45
|
+
version
|
46
|
+
else
|
47
|
+
::VERSION rescue Date.today.strftime("%Y.%m.%d")
|
48
|
+
end
|
49
|
+
|
50
|
+
# To release the monthly version do:
|
51
|
+
# $ PROJECT_VERSION=2009.03 rake release
|
52
|
+
|
53
|
+
PROJECT_FILES = FileList[`git ls-files`.split("\n")].exclude('.gitignore')
|
54
|
+
|
55
|
+
GEMSPEC = Gem::Specification.new{|s|
|
56
|
+
s.name = "mailit"
|
57
|
+
s.author = "Kevin Berry"
|
58
|
+
s.summary = "The Mailit library, by Kevin Berry"
|
59
|
+
s.description = "The Mailit library, by Kevin Berry"
|
60
|
+
s.email = "kevinberry@nrs.us"
|
61
|
+
s.homepage = "http://github.com/manveru/mailit"
|
62
|
+
s.platform = Gem::Platform::RUBY
|
63
|
+
s.version = PROJECT_VERSION
|
64
|
+
s.files = PROJECT_FILES
|
65
|
+
s.has_rdoc = true
|
66
|
+
s.require_path = "lib"
|
67
|
+
}
|
68
|
+
|
69
|
+
Dir.glob('tasks/*.rake'){|f| import(f) }
|
70
|
+
|
71
|
+
task :default => [:bacon]
|
72
|
+
|
73
|
+
CLEAN.include %w[
|
74
|
+
**/.*.sw?
|
75
|
+
*.gem
|
76
|
+
.config
|
77
|
+
**/*~
|
78
|
+
**/{data.db,cache.yaml}
|
79
|
+
*.yaml
|
80
|
+
pkg
|
81
|
+
rdoc
|
82
|
+
ydoc
|
83
|
+
*coverage*
|
84
|
+
]
|
data/lib/mailit.rb
ADDED
data/lib/mailit/mail.rb
ADDED
@@ -0,0 +1,367 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'pathname'
|
3
|
+
require 'enumerator' unless 'String'.respond_to?(:enum_for)
|
4
|
+
|
5
|
+
module Mailit
|
6
|
+
|
7
|
+
# = Overview:
|
8
|
+
#
|
9
|
+
# A simple to use class to generate RFC compliant MIME email.
|
10
|
+
#
|
11
|
+
# MailIt is a fork of MailFactory and provides a mostly identical API but has
|
12
|
+
# been cleaned up, simplified, and made compliant to common Ruby idioms.
|
13
|
+
#
|
14
|
+
# Copyright (c) 2005-2008 David Powers.
|
15
|
+
# Copyright (c) 2009 Michael Fellinger.
|
16
|
+
#
|
17
|
+
# This program is free software. You can re-distribute and/or
|
18
|
+
# modify this program under the same terms as Ruby itself.
|
19
|
+
#
|
20
|
+
# = Usage:
|
21
|
+
#
|
22
|
+
# require 'net/smtp'
|
23
|
+
# require 'mailit'
|
24
|
+
#
|
25
|
+
# mail = Mailit::Mail.new
|
26
|
+
# mail.to = 'test@test.com'
|
27
|
+
# mail.from = 'sender@sender.com'
|
28
|
+
# mail.subject 'Here are some files for you!'
|
29
|
+
# mail.text = 'This is what people with plain text mail readers will see'
|
30
|
+
# mail.html = "A little something <b>special</b> for people with HTML readers'
|
31
|
+
# mail.attach('/etc/fstab')
|
32
|
+
# mail.attach('/home/manveru/.vimrc')
|
33
|
+
#
|
34
|
+
# server = 'smtp1.testmailer.com'
|
35
|
+
# port = 25
|
36
|
+
# domain = 'mail.from.domain'
|
37
|
+
# password = 'foo'
|
38
|
+
#
|
39
|
+
# Net::SMTP.start(server, port, domain, mail.from, password, :cram_md5) do |smtp|
|
40
|
+
# smtp.send_message(mail.to_s, mail.from, mail.to)
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# = Todo:
|
44
|
+
#
|
45
|
+
# * MailFactory has a method_missing that handles getting and setting of
|
46
|
+
# arbitrary headers.
|
47
|
+
# I went for the less magical #[] and #[]= methods.
|
48
|
+
# Maybe someone can add the MailFactory behaviour.
|
49
|
+
class Mail
|
50
|
+
VERSION = '2009.03.02'
|
51
|
+
|
52
|
+
BOUNDARY_CHARS = [*'a'..'z'] + [*'A'..'Z'] + [*'0'..'9'] + ['.', '_']
|
53
|
+
BOUNDARY_PREFIX = "----=_NextPart_"
|
54
|
+
|
55
|
+
# body_boundary, encoding
|
56
|
+
BODY_BOUNDARY = "--%s\r\nContent-Type: %s\r\nContent-Transfer-Encoding: %s"
|
57
|
+
|
58
|
+
# attachment_boundary, mimetype, filename, filename
|
59
|
+
ATTACHMENT_BOUNDARY = "--%s\r\nContent-Type: %s; name=%p\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: inline; filename=%p"
|
60
|
+
|
61
|
+
HTML_BODY = <<BODY.strip
|
62
|
+
<html>
|
63
|
+
<head>
|
64
|
+
<meta http-equiv="Content-Type" content="text/html; charset=%s">
|
65
|
+
</head>
|
66
|
+
<body bgcolor="#ffffff" text="#000000">
|
67
|
+
%s
|
68
|
+
</body>
|
69
|
+
</html>
|
70
|
+
BODY
|
71
|
+
|
72
|
+
OPTIONS = {
|
73
|
+
:date => true,
|
74
|
+
:message_id => lambda{|mail|
|
75
|
+
time = Time.now
|
76
|
+
domain = mail['from'].first.to_s.split('@').last || 'localhost'
|
77
|
+
message_id = "<%f.%d.%d@%s>" % [time, $$, time.object_id, domain]
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
attr_accessor :charset, :text, :html, :attachment_boundary, :body_boundary
|
83
|
+
attr_reader :headers, :attachments
|
84
|
+
|
85
|
+
# Create an instance of {Mailit::Mailer}.
|
86
|
+
#
|
87
|
+
# @option options [String] :to
|
88
|
+
# @option options [String] :from
|
89
|
+
# @option options [String] :subject
|
90
|
+
# @option options [String] :text
|
91
|
+
# @option options [String] :html
|
92
|
+
# @author manveru
|
93
|
+
def initialize(options = {})
|
94
|
+
@headers = []
|
95
|
+
@attachments = []
|
96
|
+
@attachment_boundary = self.class.generate_boundary
|
97
|
+
@body_boundary = self.class.generate_boundary
|
98
|
+
@charset = 'utf-8'
|
99
|
+
@html = @text = nil
|
100
|
+
|
101
|
+
options.each{|key, value| __send__("#{key}=", value) }
|
102
|
+
end
|
103
|
+
|
104
|
+
def construct(options = {})
|
105
|
+
options = OPTIONS.merge(options)
|
106
|
+
time = Time.now
|
107
|
+
|
108
|
+
if message_id = options[:message_id]
|
109
|
+
self['Message-ID'] = message_id.call(self) unless self['Message-Id'].any?
|
110
|
+
end
|
111
|
+
|
112
|
+
if options[:date]
|
113
|
+
self['Date'] = time.rfc2822 unless self['Date'].any?
|
114
|
+
end
|
115
|
+
|
116
|
+
if multipart?
|
117
|
+
self['MIME-Version'] = '1.0' unless self['MIME-Version'].any?
|
118
|
+
|
119
|
+
unless self['Content-Type'].any?
|
120
|
+
if @attachments.any?
|
121
|
+
content_type = ('multipart/alternative; boundary=%p' % body_boundary)
|
122
|
+
else
|
123
|
+
content_type = ('multipart/mixed; boundary=%p' % attachment_boundary)
|
124
|
+
end
|
125
|
+
|
126
|
+
self['Content-Type'] = content_type
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
"#{header_string}#{body_string}"
|
131
|
+
end
|
132
|
+
alias to_s construct
|
133
|
+
|
134
|
+
## Attachments
|
135
|
+
|
136
|
+
def add_attachment(filename, mimetype = nil, headers = nil)
|
137
|
+
container = {
|
138
|
+
:filename => Pathname.new(filename).basename,
|
139
|
+
:mimetype => (mimetype || mime_type_for(filename)),
|
140
|
+
}
|
141
|
+
|
142
|
+
add_attachment_common(container, filename, headers)
|
143
|
+
end
|
144
|
+
alias attach add_attachment
|
145
|
+
|
146
|
+
def add_attachment_as(file, filename, mimetype = nil, headers = nil)
|
147
|
+
container = {
|
148
|
+
:filename => filename,
|
149
|
+
:mimetype => (mimetype || mime_type_for(file))
|
150
|
+
}
|
151
|
+
|
152
|
+
add_attachment_common(container, file, headers)
|
153
|
+
end
|
154
|
+
alias attach_as add_attachment_as
|
155
|
+
|
156
|
+
## Shortcuts
|
157
|
+
|
158
|
+
def multipart?
|
159
|
+
html || attachments.size > 0
|
160
|
+
end
|
161
|
+
|
162
|
+
def html=(html)
|
163
|
+
@html = HTML_BODY % [charset, html]
|
164
|
+
end
|
165
|
+
|
166
|
+
def raw_html=(html)
|
167
|
+
@html = html
|
168
|
+
end
|
169
|
+
|
170
|
+
def message_id=(id)
|
171
|
+
self['Message-ID'] = id
|
172
|
+
end
|
173
|
+
|
174
|
+
def send(options = {})
|
175
|
+
Mailer.send(self, options)
|
176
|
+
end
|
177
|
+
|
178
|
+
def defer_send(options = {})
|
179
|
+
Mailer.defer_send(self, options)
|
180
|
+
end
|
181
|
+
|
182
|
+
## Header handling
|
183
|
+
|
184
|
+
def add_header(header, value)
|
185
|
+
case header.to_s.downcase
|
186
|
+
when /^subject$/i
|
187
|
+
value = quoted_printable_with_instruction(value)
|
188
|
+
when /^(from|to|bcc|reply-to)$/i
|
189
|
+
value = quote_address_if_necessary(value, charset)
|
190
|
+
end
|
191
|
+
|
192
|
+
@headers << [header, value]
|
193
|
+
end
|
194
|
+
|
195
|
+
def set_header(header, value)
|
196
|
+
remove_header(header)
|
197
|
+
add_header(header, value)
|
198
|
+
end
|
199
|
+
alias []= set_header
|
200
|
+
|
201
|
+
def remove_header(header)
|
202
|
+
regex = /^#{Regexp.escape(header)}/i
|
203
|
+
|
204
|
+
@headers.reject!{|key, value| key =~ regex }
|
205
|
+
end
|
206
|
+
|
207
|
+
def get_header(header)
|
208
|
+
regex = /^#{Regexp.escape(header)}/i
|
209
|
+
|
210
|
+
@headers.map{|key, value| value if regex =~ key }.compact
|
211
|
+
end
|
212
|
+
alias [] get_header
|
213
|
+
|
214
|
+
def header_string
|
215
|
+
headers.map{|key,value| "#{key}: #{value}"}.join("\r\n") << "\r\n\r\n"
|
216
|
+
end
|
217
|
+
|
218
|
+
MIME_INDICATOR = "This is a multi-part message in MIME format.\r\n\r\n--%s\r\nContent-Type: multipart/alternative; boundary=%p"
|
219
|
+
|
220
|
+
def body_string
|
221
|
+
return text unless multipart?
|
222
|
+
|
223
|
+
body = [ MIME_INDICATOR % [attachment_boundary, body_boundary] ]
|
224
|
+
body << build_body_boundary("text/plain; charset=#{charset} format=flowed")
|
225
|
+
body << "\r\n\r\n" << quote_if_necessary(text, charset)
|
226
|
+
|
227
|
+
if html
|
228
|
+
body << build_body_boundary("text/html; charset=#{charset}")
|
229
|
+
body << "\r\n\r\n" << quote_if_necessary(html, charset)
|
230
|
+
end
|
231
|
+
|
232
|
+
body << "--#{body_boundary}--"
|
233
|
+
|
234
|
+
attachments.each do |attachment|
|
235
|
+
body << build_attachment_boundary(attachment)
|
236
|
+
body << "\r\n\r\n" << attachment[:attachment]
|
237
|
+
body << "\r\n--#{attachment_boundary}--"
|
238
|
+
end
|
239
|
+
|
240
|
+
body.join("\r\n\r\n")
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
def add_attachment_common(container, file, headers)
|
246
|
+
container[:attachment] = file_read(file)
|
247
|
+
container[:headers] = headers_prepare(headers)
|
248
|
+
self.attachments << container
|
249
|
+
end
|
250
|
+
|
251
|
+
def headers_prepare(headers)
|
252
|
+
case headers
|
253
|
+
when Array
|
254
|
+
container[:headers] = headers
|
255
|
+
else
|
256
|
+
container[:headers] = headers.split(/\r?\n/)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def quoted_printable_with_instruction(text)
|
261
|
+
text = encode_quoted_printable_rfc2047(text)
|
262
|
+
"=?#{charset}?Q?#{text}?="
|
263
|
+
end
|
264
|
+
|
265
|
+
def quote_if_necessary(text, charset, instruction = false)
|
266
|
+
return unless text
|
267
|
+
|
268
|
+
if text.respond_to?(:force_encoding)
|
269
|
+
text = text.dup.force_encoding(Encoding::ASCII_8BIT)
|
270
|
+
end
|
271
|
+
|
272
|
+
if instruction
|
273
|
+
quoted_printable_with_instruction(text)
|
274
|
+
else
|
275
|
+
encode_quoted_printable_rfc2045(text)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def quote_address_if_necessary(address, charset)
|
280
|
+
case address
|
281
|
+
when Array
|
282
|
+
address.map{|addr| quote_address_if_necessary(addr, charset) }
|
283
|
+
when /^(\S.*)\s+(<.*>)$/
|
284
|
+
phrase = $1.gsub(/^['"](.*)['"]$/, '\1')
|
285
|
+
address = $2
|
286
|
+
|
287
|
+
phrase = quote_if_necessary(phrase, charset, true)
|
288
|
+
|
289
|
+
"%p %s" % [phrase, address]
|
290
|
+
else
|
291
|
+
address
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def encode_file(string)
|
296
|
+
[string].pack('m')
|
297
|
+
end
|
298
|
+
|
299
|
+
def file_read(file)
|
300
|
+
case file
|
301
|
+
when String, Pathname
|
302
|
+
File.open(file.to_s, 'rb') do |io|
|
303
|
+
encode_file(io.read)
|
304
|
+
end
|
305
|
+
else
|
306
|
+
encode_file(file.read)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def encode_quoted_printable_rfc2045(string)
|
311
|
+
[string].pack('M').gsub(/\n/, "\r\n").chomp.gsub(/=$/, '')
|
312
|
+
end
|
313
|
+
|
314
|
+
def encode_quoted_printable_rfc2047(string)
|
315
|
+
string.enum_for(:each_byte).map{|ord|
|
316
|
+
if ord < 128 and ord != 61 # 61 is ascii '='
|
317
|
+
ord.chr
|
318
|
+
else
|
319
|
+
'=%X' % ord
|
320
|
+
end
|
321
|
+
}.join('').chomp.
|
322
|
+
gsub(/=$/,'').gsub('?', '=3F').gsub('_', '=5F').gsub(/ /, '_')
|
323
|
+
end
|
324
|
+
|
325
|
+
def build_body_boundary(type, encoding = 'quoted-printable')
|
326
|
+
BODY_BOUNDARY % [ body_boundary, type, encoding ]
|
327
|
+
end
|
328
|
+
|
329
|
+
def build_attachment_boundary(attachment)
|
330
|
+
mime, file, headers = attachment.values_at(:mimetype, :filename, :headers)
|
331
|
+
|
332
|
+
boundary = ATTACHMENT_BOUNDARY % [attachment_boundary, mime, file, file]
|
333
|
+
boundary << "\r\n%s" % headers.join("\r\n") if headers
|
334
|
+
|
335
|
+
boundary
|
336
|
+
end
|
337
|
+
|
338
|
+
# Try to get the
|
339
|
+
def mime_type_for(filename, override = nil)
|
340
|
+
override || Mime.type_for(filename)
|
341
|
+
end
|
342
|
+
|
343
|
+
## Header shortcuts
|
344
|
+
|
345
|
+
def self.header_accessors(hash)
|
346
|
+
hash.each{|k,v| header_accessor(k, v) }
|
347
|
+
end
|
348
|
+
|
349
|
+
def self.header_accessor(method, header)
|
350
|
+
public
|
351
|
+
eval("def %s; self[%p].first; end" % [method, header.to_s])
|
352
|
+
eval("def %s=(o); self[%p] = o; end" % [method, header.to_s])
|
353
|
+
end
|
354
|
+
|
355
|
+
header_accessors(:reply_to => 'Reply-To', :to => :to, :from => :from,
|
356
|
+
:subject => :subject, :bcc => :bcc)
|
357
|
+
|
358
|
+
def self.generate_boundary(size = 25, chars = BOUNDARY_CHARS)
|
359
|
+
char_count = chars.size
|
360
|
+
postfix = Array.new(size){
|
361
|
+
chars[rand(char_count)]
|
362
|
+
}.join
|
363
|
+
|
364
|
+
return "#{BOUNDARY_PREFIX}#{postfix}"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|