mailfactory 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/mailfactory.rb +383 -371
- metadata +2 -2
data/lib/mailfactory.rb
CHANGED
|
@@ -7,421 +7,433 @@
|
|
|
7
7
|
# License:: Ruby License
|
|
8
8
|
# ---
|
|
9
9
|
# = Usage:
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
10
|
+
# require 'net/smtp'
|
|
11
|
+
# require 'rubygems'
|
|
12
|
+
# require 'mailfactory'
|
|
13
13
|
#
|
|
14
14
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
15
|
+
# mail = MailFactory.new()
|
|
16
|
+
# mail.to = "test@test.com"
|
|
17
|
+
# mail.from = "sender@sender.com"
|
|
18
|
+
# mail.subject = "Here are some files for you!"
|
|
19
|
+
# mail.text = "This is what people with plain text mail readers will see"
|
|
20
|
+
# mail.html = "A little something <b>special</b> for people with HTML readers"
|
|
21
|
+
# mail.attach("/etc/fstab")
|
|
22
|
+
# mail.attach("/some/other/file")
|
|
23
23
|
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
# Net::SMTP.start('smtp1.testmailer.com', 25, 'mail.from.domain', fromaddress, password, :cram_md5) { |smtp|
|
|
25
|
+
# mail.to = toaddress
|
|
26
|
+
# smtp.send_message(mail.to_s(), fromaddress, toaddress)
|
|
27
|
+
# }
|
|
30
28
|
|
|
31
29
|
require 'base64'
|
|
32
30
|
require 'pathname'
|
|
33
31
|
|
|
34
32
|
# try to bring in the mime/types module, make a dummy module if it can't be found
|
|
35
33
|
begin
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
begin
|
|
35
|
+
require 'rubygems'
|
|
36
|
+
rescue LoadError
|
|
37
|
+
end
|
|
38
|
+
require 'mime/types'
|
|
41
39
|
rescue LoadError
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
module MIME
|
|
41
|
+
class Types
|
|
42
|
+
def Types::type_for(filename)
|
|
43
|
+
return('')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
49
47
|
end
|
|
50
48
|
|
|
51
49
|
# An easy class for creating a mail message
|
|
52
50
|
class MailFactory
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
end
|
|
52
|
+
def initialize()
|
|
53
|
+
@headers = Array.new()
|
|
54
|
+
@attachments = Array.new()
|
|
55
|
+
@attachmentboundary = generate_boundary()
|
|
56
|
+
@bodyboundary = generate_boundary()
|
|
57
|
+
@html = nil
|
|
58
|
+
@text = nil
|
|
59
|
+
@charset = 'utf-8'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# adds a header to the bottom of the headers
|
|
64
|
+
def add_header(header, value)
|
|
65
|
+
value = quoted_printable_with_instruction(value, @charset) if header == 'subject'
|
|
66
|
+
value = quote_address_if_necessary(value, @charset) if %w[from to cc bcc reply-to].include?(header.downcase)
|
|
67
|
+
@headers << "#{header}: #{value}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# removes the named header - case insensitive
|
|
72
|
+
def remove_header(header)
|
|
73
|
+
@headers.each_index() { |i|
|
|
74
|
+
if(@headers[i] =~ /^#{Regexp.escape(header)}:/i)
|
|
75
|
+
@headers.delete_at(i)
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# sets a header (removing any other versions of that header)
|
|
82
|
+
def set_header(header, value)
|
|
83
|
+
remove_header(header)
|
|
84
|
+
add_header(header, value)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def replyto=(newreplyto)
|
|
89
|
+
remove_header("Reply-To")
|
|
90
|
+
add_header("Reply-To", newreplyto)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def replyto()
|
|
95
|
+
return(get_header("Reply-To")[0])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# sets the plain text body of the message
|
|
100
|
+
def text=(newtext)
|
|
101
|
+
@text = newtext
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# sets the HTML body of the message. Only the body of the
|
|
106
|
+
# html should be provided
|
|
107
|
+
def html=(newhtml)
|
|
108
|
+
@html = "<html>\n<head>\n<meta content=\"text/html;charset=#{@charset}\" http-equiv=\"Content-Type\">\n</head>\n<body bgcolor=\"#ffffff\" text=\"#000000\">\n#{newhtml}\n</body>\n</html>"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# sets the HTML body of the message. The entire HTML section should be provided
|
|
113
|
+
def rawhtml=(newhtml)
|
|
114
|
+
@html = newhtml
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# implement method missing to provide helper methods for setting and getting headers.
|
|
119
|
+
# Headers with '-' characters may be set/gotten as 'x_mailer' or 'XMailer' (splitting
|
|
120
|
+
# will occur between capital letters or on '_' chracters)
|
|
121
|
+
def method_missing(methId, *args)
|
|
122
|
+
name = methId.id2name()
|
|
123
|
+
|
|
124
|
+
# mangle the name if we have to
|
|
125
|
+
if(name =~ /_/)
|
|
126
|
+
name = name.gsub(/_/, '-')
|
|
127
|
+
elsif(name =~ /[A-Z]/)
|
|
128
|
+
name = name.gsub(/([a-zA-Z])([A-Z])/, '\1-\2')
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# determine if it sets or gets, and do the right thing
|
|
132
|
+
if(name =~ /=$/)
|
|
133
|
+
if(args.length != 1)
|
|
134
|
+
super(methId, args)
|
|
135
|
+
end
|
|
136
|
+
set_header(name[/^(.*)=$/, 1], args[0])
|
|
137
|
+
else
|
|
138
|
+
if(args.length != 0)
|
|
139
|
+
super(methId, args)
|
|
140
|
+
end
|
|
141
|
+
headers = get_header(name)
|
|
142
|
+
return(get_header(name))
|
|
143
|
+
end
|
|
144
|
+
end
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
146
|
+
|
|
147
|
+
# returns the value (or values) of the named header in an array
|
|
148
|
+
def get_header(header)
|
|
149
|
+
headers = Array.new()
|
|
150
|
+
headerregex = /^#{Regexp.escape(header)}:/i
|
|
151
|
+
@headers.each() { |h|
|
|
152
|
+
if(headerregex.match(h))
|
|
153
|
+
headers << h[/^[^:]+:(.*)/i, 1].strip()
|
|
154
|
+
end
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return(headers)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# returns true if the email is multipart
|
|
162
|
+
def multipart?()
|
|
163
|
+
if(@attachments.length > 0 or @html != nil)
|
|
164
|
+
return(true)
|
|
165
|
+
else
|
|
166
|
+
return(false)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
173
170
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
# builds an email and returns it as a string. Takes the following options:
|
|
172
|
+
# <tt>:messageid</tt>:: Adds a message id to the message based on the from header (defaults to false)
|
|
173
|
+
# <tt>:date</tt>:: Adds a date to the message if one is not present (defaults to true)
|
|
174
|
+
def construct(options = Hash.new)
|
|
175
|
+
if(options[:date] == nil)
|
|
176
|
+
options[:date] = true
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
if(options[:messageid])
|
|
180
|
+
# add a unique message-id
|
|
181
|
+
remove_header("Message-ID")
|
|
182
|
+
sendingdomain = get_header('from')[0].to_s()[/@([-a-zA-Z0-9._]+)/,1].to_s()
|
|
183
|
+
add_header("Message-ID", "<#{Time.now.to_f()}.#{Process.euid()}.#{String.new.object_id()}@#{sendingdomain}>")
|
|
184
|
+
end
|
|
188
185
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
186
|
+
if(options[:date])
|
|
187
|
+
if(get_header("Date").length == 0)
|
|
188
|
+
add_header("Date", Time.now.strftime("%a, %d %b %Y %H:%M:%S %z"))
|
|
189
|
+
end
|
|
190
|
+
end
|
|
194
191
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
192
|
+
# Add a mime header if we don't already have one and we have multiple parts
|
|
193
|
+
if(multipart?())
|
|
194
|
+
if(get_header("MIME-Version").length == 0)
|
|
195
|
+
add_header("MIME-Version", "1.0")
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if(get_header("Content-Type").length == 0)
|
|
199
|
+
if(@attachments.length == 0)
|
|
200
|
+
add_header("Content-Type", "multipart/alternative;boundary=\"#{@bodyboundary}\"")
|
|
201
|
+
else
|
|
202
|
+
add_header("Content-Type", "multipart/mixed; boundary=\"#{@attachmentboundary}\"")
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
return("#{headers_to_s()}#{body_to_s()}")
|
|
208
|
+
end
|
|
212
209
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
210
|
+
|
|
211
|
+
# returns a formatted email - equivalent to construct(:messageid => true)
|
|
212
|
+
def to_s()
|
|
213
|
+
return(construct(:messageid => true))
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# generates a unique boundary string
|
|
218
|
+
def generate_boundary()
|
|
219
|
+
randomstring = Array.new()
|
|
220
|
+
1.upto(25) {
|
|
221
|
+
whichglyph = rand(100)
|
|
222
|
+
if(whichglyph < 40)
|
|
223
|
+
randomstring << (rand(25) + 65).chr()
|
|
224
|
+
elsif(whichglyph < 70)
|
|
225
|
+
randomstring << (rand(25) + 97).chr()
|
|
226
|
+
elsif(whichglyph < 90)
|
|
227
|
+
randomstring << (rand(10) + 48).chr()
|
|
228
|
+
elsif(whichglyph < 95)
|
|
229
|
+
randomstring << '.'
|
|
230
|
+
else
|
|
231
|
+
randomstring << '_'
|
|
232
|
+
end
|
|
233
|
+
}
|
|
234
|
+
return("----=_NextPart_#{randomstring.join()}")
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# adds an attachment to the mail. Type may be given as a mime type. If it
|
|
239
|
+
# is left off and the MIME::Types module is available it will be determined automagically.
|
|
240
|
+
# If the optional attachemntheaders is given, then they will be added to the attachment
|
|
241
|
+
# boundary in the email, which can be used to produce Content-ID markers. attachmentheaders
|
|
242
|
+
# can be given as an Array or a String.
|
|
243
|
+
def add_attachment(filename, type=nil, attachmentheaders = nil)
|
|
244
|
+
attachment = Hash.new()
|
|
245
|
+
attachment['filename'] = Pathname.new(filename).basename
|
|
246
|
+
if(type == nil)
|
|
247
|
+
attachment['mimetype'] = MIME::Types.type_for(filename).to_s
|
|
248
|
+
else
|
|
249
|
+
attachment['mimetype'] = type
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Open in rb mode to handle Windows, which mangles binary files opened in a text mode
|
|
253
|
+
File.open(filename, "rb") { |fp|
|
|
254
|
+
attachment['attachment'] = file_encode(fp.read())
|
|
255
|
+
}
|
|
259
256
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
257
|
+
if(attachmentheaders != nil)
|
|
258
|
+
if(!attachmentheaders.kind_of?(Array))
|
|
259
|
+
attachmentheaders = attachmentheaders.split(/\r?\n/)
|
|
260
|
+
end
|
|
261
|
+
attachment['headers'] = attachmentheaders
|
|
262
|
+
end
|
|
266
263
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
264
|
+
@attachments << attachment
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
# adds an attachment to the mail as emailfilename. Type may be given as a mime type. If it
|
|
269
|
+
# is left off and the MIME::Types module is available it will be determined automagically.
|
|
270
|
+
# file may be given as an IO stream (which will be read until the end) or as a filename.
|
|
271
|
+
# If the optional attachemntheaders is given, then they will be added to the attachment
|
|
272
|
+
# boundary in the email, which can be used to produce Content-ID markers. attachmentheaders
|
|
273
|
+
# can be given as an Array of a String.
|
|
274
|
+
def add_attachment_as(file, emailfilename, type=nil, attachmentheaders = nil)
|
|
275
|
+
attachment = Hash.new()
|
|
276
|
+
attachment['filename'] = emailfilename
|
|
280
277
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
278
|
+
if(type != nil)
|
|
279
|
+
attachment['mimetype'] = type.to_s()
|
|
280
|
+
elsif(file.kind_of?(String) or file.kind_of?(Pathname))
|
|
281
|
+
attachment['mimetype'] = MIME::Types.type_for(file.to_s()).to_s
|
|
282
|
+
else
|
|
283
|
+
attachment['mimetype'] = ''
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
if(file.kind_of?(String) or file.kind_of?(Pathname))
|
|
287
|
+
# Open in rb mode to handle Windows, which mangles binary files opened in a text mode
|
|
288
|
+
File.open(file.to_s(), "rb") { |fp|
|
|
289
|
+
attachment['attachment'] = file_encode(fp.read())
|
|
290
|
+
}
|
|
291
|
+
elsif(file.respond_to?(:read))
|
|
292
|
+
attachment['attachment'] = file_encode(file.read())
|
|
293
|
+
else
|
|
294
|
+
raise(Exception, "file is not a supported type (must be a String, Pathnamem, or support read method)")
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
if(attachmentheaders != nil)
|
|
298
|
+
if(!attachmentheaders.kind_of?(Array))
|
|
299
|
+
attachmentheaders = attachmentheaders.split(/\r?\n/)
|
|
300
|
+
end
|
|
301
|
+
attachment['headers'] = attachmentheaders
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
@attachments << attachment
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
alias attach add_attachment
|
|
309
|
+
alias attach_as add_attachment_as
|
|
310
|
+
|
|
314
311
|
protected
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
312
|
+
|
|
313
|
+
# returns the @headers as a properly formatted string
|
|
314
|
+
def headers_to_s()
|
|
315
|
+
return("#{@headers.join("\r\n")}\r\n\r\n")
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
# returns the body as a properly formatted string
|
|
320
|
+
def body_to_s()
|
|
321
|
+
body = Array.new()
|
|
322
|
+
|
|
323
|
+
# simple message with one part
|
|
324
|
+
if(!multipart?())
|
|
325
|
+
return(@text)
|
|
326
|
+
else
|
|
327
|
+
body << "This is a multi-part message in MIME format.\r\n\r\n--#{@attachmentboundary}\r\nContent-Type: multipart/alternative; boundary=\"#{@bodyboundary}\""
|
|
328
|
+
|
|
329
|
+
if(@attachments.length > 0)
|
|
330
|
+
# text part
|
|
331
|
+
body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@text, @charset)}"
|
|
332
|
+
|
|
333
|
+
# html part if one is provided
|
|
334
|
+
if @html
|
|
335
|
+
body << "#{buildbodyboundary("text/html; charset=#{@charset}", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@html, @charset)}"
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
body << "--#{@bodyboundary}--"
|
|
339
|
+
|
|
340
|
+
# and, the attachments
|
|
341
|
+
if(@attachments.length > 0)
|
|
342
|
+
@attachments.each() { |attachment|
|
|
343
|
+
body << "#{buildattachmentboundary(attachment)}\r\n\r\n#{attachment['attachment']}"
|
|
344
|
+
}
|
|
345
|
+
body << "\r\n--#{@attachmentboundary}--"
|
|
346
|
+
end
|
|
347
|
+
else
|
|
348
|
+
# text part
|
|
349
|
+
body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@text, @charset)}"
|
|
350
|
+
|
|
351
|
+
# html part
|
|
352
|
+
body << "#{buildbodyboundary("text/html; charset=#{@charset}", 'quoted-printable')}\r\n\r\n#{quote_if_necessary(@html, @charset)}"
|
|
353
|
+
|
|
354
|
+
body << "--#{@bodyboundary}--"
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
return(body.join("\r\n\r\n"))
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
# builds a boundary string for including attachments in the body, expects an attachment hash as built by
|
|
363
|
+
# add_attachment and add_attachment_as
|
|
364
|
+
def buildattachmentboundary(attachment)
|
|
365
|
+
disposition = "Content-Disposition: inline; filename=\"#{attachment['filename']}\""
|
|
366
|
+
boundary = "--#{@attachmentboundary}\r\nContent-Type: #{attachment['mimetype']}; name=\"#{attachment['filename']}\"\r\nContent-Transfer-Encoding: base64\r\n#{disposition}"
|
|
367
|
+
if(attachment['headers'])
|
|
368
|
+
boundary = boundary + "\r\n#{attachment['headers'].join("\r\n")}"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
return(boundary)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# builds a boundary string for inclusion in the body of a message
|
|
376
|
+
def buildbodyboundary(type, encoding)
|
|
377
|
+
return("--#{@bodyboundary}\r\nContent-Type: #{type}\r\nContent-Transfer-Encoding: #{encoding}")
|
|
378
|
+
end
|
|
382
379
|
|
|
383
380
|
|
|
384
381
|
# returns a base64 encoded version of the contents of str
|
|
385
382
|
def file_encode(str)
|
|
386
383
|
collection = Array.new()
|
|
387
384
|
enc = Base64.encode64(str)
|
|
388
|
-
#
|
|
389
|
-
#
|
|
390
|
-
#
|
|
391
|
-
#
|
|
392
|
-
#
|
|
385
|
+
# while(enc.length > 60)
|
|
386
|
+
# collection << enc.slice!(0..59)
|
|
387
|
+
# end
|
|
388
|
+
# collection << enc
|
|
389
|
+
# return(collection.join("\n"))
|
|
393
390
|
return(enc)
|
|
394
391
|
end
|
|
395
392
|
|
|
396
|
-
def quote_text_if_necessary(text)
|
|
397
|
-
text = text.gsub( /[^a-z ]/i ) { quote_char($&) }.
|
|
398
|
-
gsub( / /, "_" )
|
|
399
|
-
text
|
|
400
|
-
end
|
|
401
393
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
394
|
+
# Convert the given text into quoted printable format, with an instruction
|
|
395
|
+
# that the text be eventually interpreted in the given charset.
|
|
396
|
+
def quoted_printable_with_instruction(text, charset)
|
|
397
|
+
text = [text].pack("M").gsub(/\n/, "\r\n").chomp.gsub(/ /, "_")
|
|
405
398
|
"=?#{charset}?Q?#{text}?="
|
|
406
399
|
end
|
|
407
|
-
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# Convert the given character to quoted printable format
|
|
403
|
+
def quoted_printable_encode(text)
|
|
404
|
+
[text].pack("M").gsub(/\n/, "\r\n").chomp
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
# A quick-and-dirty regexp for determining whether a string contains any
|
|
409
|
+
# characters that need escaping.
|
|
410
|
+
if !defined?(CHARS_NEEDING_QUOTING)
|
|
411
|
+
CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# Quote the given text if it contains any "illegal" characters
|
|
416
|
+
def quote_if_necessary(text, charset, instruction = false)
|
|
417
|
+
text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
|
|
418
|
+
(text =~ CHARS_NEEDING_QUOTING) ? (instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)) : text
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# Quote the given address if it needs to be. The address may be a
|
|
423
|
+
# regular email address, or it can be a phrase followed by an address in
|
|
424
|
+
# brackets. The phrase is the only part that will be quoted, and only if
|
|
425
|
+
# it needs to be. This allows extended characters to be used in the
|
|
426
|
+
# "to", "from", "cc", and "bcc" headers.
|
|
408
427
|
def quote_address_if_necessary(address, charset)
|
|
409
428
|
if Array === address
|
|
410
429
|
address.map { |a| quote_address_if_necessary(a, charset) }
|
|
411
430
|
elsif address =~ /^(\S.*)\s+(<.*>)$/
|
|
412
431
|
address = $2
|
|
413
|
-
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
|
|
432
|
+
phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset, true)
|
|
414
433
|
"\"#{phrase}\" #{address}"
|
|
415
434
|
else
|
|
416
435
|
address
|
|
417
436
|
end
|
|
418
437
|
end
|
|
419
438
|
|
|
420
|
-
|
|
421
|
-
result = ""
|
|
422
|
-
character.each_byte { |b| result << "=%02x" % b }
|
|
423
|
-
result
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
end
|
|
427
|
-
|
|
439
|
+
end
|
metadata
CHANGED
|
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
|
3
3
|
specification_version: 1
|
|
4
4
|
name: mailfactory
|
|
5
5
|
version: !ruby/object:Gem::Version
|
|
6
|
-
version: 1.3.
|
|
7
|
-
date: 2008-04-
|
|
6
|
+
version: 1.3.1
|
|
7
|
+
date: 2008-04-28 00:00:00 +01:00
|
|
8
8
|
summary: MailFactory is a pure-ruby MIME mail generator
|
|
9
9
|
require_paths:
|
|
10
10
|
- lib
|