fotonauts-mailfactory 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,481 @@
1
+ # = Overview:
2
+ # A simple to use module for generating RFC compliant MIME mail
3
+ # ---
4
+ # = License:
5
+ # Author:: David Powers
6
+ # Copyright:: May, 2005
7
+ # License:: Ruby License
8
+ # ---
9
+ # = Usage:
10
+ # require 'net/smtp'
11
+ # require 'rubygems'
12
+ # require 'mailfactory'
13
+ #
14
+ #
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
+ #
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
+ # }
28
+
29
+ require 'pathname'
30
+
31
+ # try to bring in the mime/types module, make a dummy module if it can't be found
32
+ begin
33
+ begin
34
+ require 'rubygems'
35
+ rescue LoadError
36
+ end
37
+ require 'mime/types'
38
+ rescue LoadError
39
+ module MIME
40
+ class Types
41
+ def Types::type_for(filename)
42
+ return('')
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ # An easy class for creating a mail message
49
+ class MailFactory
50
+
51
+ def initialize()
52
+ @headers = Array.new()
53
+ @attachments = Array.new()
54
+ @attachmentboundary = generate_boundary()
55
+ @bodyboundary = generate_boundary()
56
+ @html = nil
57
+ @text = nil
58
+ @charset = 'utf-8'
59
+ @quoted_printable_body = true
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
+ # use 'quoted-printable' encoding fot body ? (default: true)
119
+ # if false, then '8bit' will be used
120
+ def quoted_printable_body=(value)
121
+ @quoted_printable_body = value
122
+ end
123
+
124
+
125
+ # implement method missing to provide helper methods for setting and getting headers.
126
+ # Headers with '-' characters may be set/gotten as 'x_mailer' or 'XMailer' (splitting
127
+ # will occur between capital letters or on '_' chracters)
128
+ def method_missing(methId, *args)
129
+ name = methId.id2name()
130
+
131
+ # mangle the name if we have to
132
+ if(name =~ /_/)
133
+ name = name.gsub(/_/, '-')
134
+ elsif(name =~ /[A-Z]/)
135
+ name = name.gsub(/([a-zA-Z])([A-Z])/, '\1-\2')
136
+ end
137
+
138
+ # determine if it sets or gets, and do the right thing
139
+ if(name =~ /=$/)
140
+ if(args.length != 1)
141
+ super(methId, args)
142
+ end
143
+ set_header(name[/^(.*)=$/, 1], args[0])
144
+ else
145
+ if(args.length != 0)
146
+ super(methId, args)
147
+ end
148
+ headers = get_header(name)
149
+ return(get_header(name))
150
+ end
151
+ end
152
+
153
+
154
+ # returns the value (or values) of the named header in an array
155
+ def get_header(header)
156
+ headers = Array.new()
157
+ headerregex = /^#{Regexp.escape(header)}:/i
158
+ @headers.each() { |h|
159
+ if(headerregex.match(h))
160
+ headers << h[/^[^:]+:(.*)/i, 1].strip()
161
+ end
162
+ }
163
+
164
+ return(headers)
165
+ end
166
+
167
+
168
+ # returns true if the email is multipart
169
+ def multipart?()
170
+ if(@attachments.length > 0 or @html != nil)
171
+ return(true)
172
+ else
173
+ return(false)
174
+ end
175
+ end
176
+
177
+
178
+ # builds an email and returns it as a string. Takes the following options:
179
+ # <tt>:messageid</tt>:: Adds a message id to the message based on the from header (defaults to false)
180
+ # <tt>:date</tt>:: Adds a date to the message if one is not present (defaults to true)
181
+ def construct(options = Hash.new)
182
+ if(options[:date] == nil)
183
+ options[:date] = true
184
+ end
185
+
186
+ if(options[:messageid])
187
+ # add a unique message-id
188
+ remove_header("Message-ID")
189
+ sendingdomain = get_header('from')[0].to_s()[/@([-a-zA-Z0-9._]+)/,1].to_s()
190
+ add_header("Message-ID", "<#{Time.now.to_f()}.#{Process.euid()}.#{String.new.object_id()}@#{sendingdomain}>")
191
+ end
192
+
193
+ if(options[:date])
194
+ if(get_header("Date").length == 0)
195
+ add_header("Date", Time.now.strftime("%a, %d %b %Y %H:%M:%S %z"))
196
+ end
197
+ end
198
+
199
+ # Add a mime header if we don't already have one and we have multiple parts
200
+ if(multipart?())
201
+ if(get_header("MIME-Version").length == 0)
202
+ add_header("MIME-Version", "1.0")
203
+ end
204
+
205
+ if(get_header("Content-Type").length == 0)
206
+ if(@attachments.length == 0)
207
+ add_header("Content-Type", "multipart/alternative;boundary=\"#{@bodyboundary}\"")
208
+ else
209
+ add_header("Content-Type", "multipart/mixed; boundary=\"#{@attachmentboundary}\"")
210
+ end
211
+ end
212
+ end
213
+
214
+ return("#{headers_to_s()}#{body_to_s()}")
215
+ end
216
+
217
+
218
+ # returns a formatted email - equivalent to construct(:messageid => true)
219
+ def to_s()
220
+ return(construct(:messageid => true))
221
+ end
222
+
223
+
224
+ # generates a unique boundary string
225
+ def generate_boundary()
226
+ randomstring = Array.new()
227
+ 1.upto(25) {
228
+ whichglyph = rand(100)
229
+ if(whichglyph < 40)
230
+ randomstring << (rand(25) + 65).chr()
231
+ elsif(whichglyph < 70)
232
+ randomstring << (rand(25) + 97).chr()
233
+ elsif(whichglyph < 90)
234
+ randomstring << (rand(10) + 48).chr()
235
+ elsif(whichglyph < 95)
236
+ randomstring << '.'
237
+ else
238
+ randomstring << '_'
239
+ end
240
+ }
241
+ return("----=_NextPart_#{randomstring.join()}")
242
+ end
243
+
244
+
245
+ # adds an attachment to the mail. Type may be given as a mime type. If it
246
+ # is left off and the MIME::Types module is available it will be determined automagically.
247
+ # If the optional attachemntheaders is given, then they will be added to the attachment
248
+ # boundary in the email, which can be used to produce Content-ID markers. attachmentheaders
249
+ # can be given as an Array or a String.
250
+ def add_attachment(filename, type=nil, attachmentheaders = nil)
251
+ attachment = Hash.new()
252
+ attachment['filename'] = Pathname.new(filename).basename
253
+ if(type == nil)
254
+ attachment['mimetype'] = MIME::Types.type_for(filename).to_s
255
+ else
256
+ attachment['mimetype'] = type
257
+ end
258
+
259
+ # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
260
+ File.open(filename, "rb") { |fp|
261
+ attachment['attachment'] = file_encode(fp.read())
262
+ }
263
+
264
+ if(attachmentheaders != nil)
265
+ if(!attachmentheaders.kind_of?(Array))
266
+ attachmentheaders = attachmentheaders.split(/\r?\n/)
267
+ end
268
+ attachment['headers'] = attachmentheaders
269
+ end
270
+
271
+ @attachments << attachment
272
+ end
273
+
274
+
275
+ # adds an attachment to the mail as emailfilename. Type may be given as a mime type. If it
276
+ # is left off and the MIME::Types module is available it will be determined automagically.
277
+ # file may be given as an IO stream (which will be read until the end) or as a filename.
278
+ # If the optional attachemntheaders is given, then they will be added to the attachment
279
+ # boundary in the email, which can be used to produce Content-ID markers. attachmentheaders
280
+ # can be given as an Array of a String.
281
+ def add_attachment_as(file, emailfilename, type=nil, attachmentheaders = nil)
282
+ attachment = Hash.new()
283
+ attachment['filename'] = emailfilename
284
+
285
+ if(type != nil)
286
+ attachment['mimetype'] = type.to_s()
287
+ elsif(file.kind_of?(String) or file.kind_of?(Pathname))
288
+ attachment['mimetype'] = MIME::Types.type_for(file.to_s()).to_s
289
+ else
290
+ attachment['mimetype'] = ''
291
+ end
292
+
293
+ if(file.kind_of?(String) or file.kind_of?(Pathname))
294
+ # Open in rb mode to handle Windows, which mangles binary files opened in a text mode
295
+ File.open(file.to_s(), "rb") { |fp|
296
+ attachment['attachment'] = file_encode(fp.read())
297
+ }
298
+ elsif(file.respond_to?(:read))
299
+ attachment['attachment'] = file_encode(file.read())
300
+ else
301
+ raise(Exception, "file is not a supported type (must be a String, Pathnamem, or support read method)")
302
+ end
303
+
304
+ if(attachmentheaders != nil)
305
+ if(!attachmentheaders.kind_of?(Array))
306
+ attachmentheaders = attachmentheaders.split(/\r?\n/)
307
+ end
308
+ attachment['headers'] = attachmentheaders
309
+ end
310
+
311
+ @attachments << attachment
312
+ end
313
+
314
+
315
+ alias attach add_attachment
316
+ alias attach_as add_attachment_as
317
+
318
+ protected
319
+
320
+ # returns the @headers as a properly formatted string
321
+ def headers_to_s()
322
+ return("#{@headers.join("\r\n")}\r\n\r\n")
323
+ end
324
+
325
+
326
+ # returns the body as a properly formatted string
327
+ def body_to_s()
328
+ body = Array.new()
329
+
330
+ # simple message with one part
331
+ if(!multipart?())
332
+ return(@text)
333
+ else
334
+ body << "This is a multi-part message in MIME format.\r\n\r\n--#{@attachmentboundary}\r\nContent-Type: multipart/alternative; boundary=\"#{@bodyboundary}\""
335
+
336
+ if @quoted_printable_body
337
+ boundary_encoding = 'quoted-printable'
338
+ body_text = quote_if_necessary(@text, @charset)
339
+ body_html = quote_if_necessary(@html, @charset)
340
+ else
341
+ boundary_encoding = '8bit'
342
+ body_text = @text
343
+ body_html = @html
344
+ end
345
+
346
+ if(@attachments.length > 0)
347
+ # text part
348
+ body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", boundary_encoding)}\r\n\r\n#{body_text}"
349
+
350
+ # html part if one is provided
351
+ if @html
352
+ body << "#{buildbodyboundary("text/html; charset=#{@charset}", boundary_encoding)}\r\n\r\n#{body_html}"
353
+ end
354
+
355
+ body << "--#{@bodyboundary}--"
356
+
357
+ # and, the attachments
358
+ if(@attachments.length > 0)
359
+ @attachments.each() { |attachment|
360
+ body << "#{buildattachmentboundary(attachment)}\r\n\r\n#{attachment['attachment']}"
361
+ }
362
+ body << "\r\n--#{@attachmentboundary}--"
363
+ end
364
+ else
365
+ # text part
366
+ body << "#{buildbodyboundary("text/plain; charset=#{@charset}; format=flowed", boundary_encoding)}\r\n\r\n#{body_text}"
367
+
368
+ # html part
369
+ body << "#{buildbodyboundary("text/html; charset=#{@charset}", boundary_encoding)}\r\n\r\n#{body_html}"
370
+
371
+ body << "--#{@bodyboundary}--"
372
+ end
373
+
374
+ return(body.join("\r\n\r\n"))
375
+ end
376
+ end
377
+
378
+
379
+ # builds a boundary string for including attachments in the body, expects an attachment hash as built by
380
+ # add_attachment and add_attachment_as
381
+ def buildattachmentboundary(attachment)
382
+ disposition = "Content-Disposition: inline; filename=\"#{attachment['filename']}\""
383
+ boundary = "--#{@attachmentboundary}\r\nContent-Type: #{attachment['mimetype']}; name=\"#{attachment['filename']}\"\r\nContent-Transfer-Encoding: base64\r\n#{disposition}"
384
+ if(attachment['headers'])
385
+ boundary = boundary + "\r\n#{attachment['headers'].join("\r\n")}"
386
+ end
387
+
388
+ return(boundary)
389
+ end
390
+
391
+
392
+ # builds a boundary string for inclusion in the body of a message
393
+ def buildbodyboundary(type, encoding)
394
+ return("--#{@bodyboundary}\r\nContent-Type: #{type}\r\nContent-Transfer-Encoding: #{encoding}")
395
+ end
396
+
397
+
398
+ # returns a base64 encoded version of the contents of str
399
+ def file_encode(str)
400
+ collection = Array.new()
401
+ enc = [str].pack('m')
402
+ # while(enc.length > 60)
403
+ # collection << enc.slice!(0..59)
404
+ # end
405
+ # collection << enc
406
+ # return(collection.join("\n"))
407
+ return(enc)
408
+ end
409
+
410
+
411
+ # Convert the given text into quoted printable format, with an instruction
412
+ # that the text be eventually interpreted in the given charset.
413
+
414
+ def quoted_printable_with_instruction(text, charset)
415
+ text = quoted_printable_encode_header(text)
416
+ "=?#{charset}?Q?#{text}?="
417
+ end
418
+
419
+ # rfc2045 compatible. use rfc2047 for headers (such as the Subject line) instead
420
+ def quoted_printable_encode(text)
421
+ [text].pack('M').gsub(/\n/, "\r\n").chomp.gsub(/=$/, '')
422
+ end
423
+
424
+ # Convert the given character to quoted printable format
425
+ # see http://tools.ietf.org/html/rfc2047
426
+
427
+ require 'enumerator' unless ''.respond_to? :enum_for
428
+
429
+ def quoted_printable_encode_header(text)
430
+ text.enum_for(:each_byte).map do |ord|
431
+ if ord < 128 and ord != 61 # 61 is ascii '='
432
+ ord.chr
433
+ else
434
+ '=%X' % ord
435
+ end
436
+ end.join('').
437
+ chomp.
438
+ gsub(/=$/,'').
439
+ gsub('?', '=3F').
440
+ gsub('_', '=5F').
441
+ gsub(/ /, '_')
442
+ end
443
+
444
+
445
+ # A quick-and-dirty regexp for determining whether a string contains any
446
+ # characters that need escaping.
447
+ #--
448
+ # Jun18-08: deprecated, since all multipart blocks are marked quoted-printable, quoting is required
449
+
450
+ # if !defined?(CHARS_NEEDING_QUOTING)
451
+ # CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
452
+ # end
453
+
454
+
455
+ # Quote the given text if it contains any "illegal" characters
456
+ def quote_if_necessary(text, charset, instruction = false)
457
+ return unless text
458
+ text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
459
+ #(text =~ CHARS_NEEDING_QUOTING) ? (instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)) : text
460
+ instruction ? quoted_printable_with_instruction(text, charset) : quoted_printable_encode(text)
461
+ end
462
+
463
+
464
+ # Quote the given address if it needs to be. The address may be a
465
+ # regular email address, or it can be a phrase followed by an address in
466
+ # brackets. The phrase is the only part that will be quoted, and only if
467
+ # it needs to be. This allows extended characters to be used in the
468
+ # "to", "from", "cc", and "bcc" headers.
469
+ def quote_address_if_necessary(address, charset)
470
+ if Array === address
471
+ address.map { |a| quote_address_if_necessary(a, charset) }
472
+ elsif address =~ /^(\S.*)\s+(<.*>)$/
473
+ address = $2
474
+ phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset, true)
475
+ "\"#{phrase}\" #{address}"
476
+ else
477
+ address
478
+ end
479
+ end
480
+
481
+ end
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env ruby
2
+ # coding:utf-8
3
+
4
+ require 'test/unit/ui/console/testrunner'
5
+ require File.dirname(__FILE__) + '/../lib/mailfactory.rb'
6
+
7
+
8
+ def get_options()
9
+ options = Hash.new()
10
+
11
+ opts = OptionParser.new() { |opts|
12
+ opts.on_tail("-h", "--help", "Print this message") {
13
+ print(opts)
14
+ exit()
15
+ }
16
+
17
+ opts.on("-s", "--smtpserver SERVER", "SMTP server to use for remote tests") { |server|
18
+ options['smtpserver'] = server
19
+ }
20
+
21
+ opts.on("-f", "--from ADDRESS", "address to send the mail from") { |address|
22
+ options['from'] = address
23
+ }
24
+
25
+ opts.on("-t", "--to ADDRESS", "address to send the mail to") { |address|
26
+ options['to'] = address
27
+ }
28
+
29
+ opts.on("-u", "--username USERNAME", "username for smtp auth (required)") { |username|
30
+ options['username'] = username
31
+ }
32
+
33
+ opts.on("-p", "--password PASSWORD", "password for smtp auth (required)") { |password|
34
+ options['password'] = password
35
+ }
36
+
37
+ }
38
+
39
+ opts.parse(ARGV)
40
+
41
+ return(options)
42
+ end
43
+
44
+
45
+
46
+ class TC_MailFactory < Test::Unit::TestCase
47
+
48
+ def setup()
49
+ @mail = MailFactory.new
50
+ end
51
+
52
+
53
+ def test_set_to
54
+ assert_nothing_raised("exception raised while setting to=") {
55
+ @mail.to = "test@test.com"
56
+ }
57
+
58
+ assert_equal(@mail.to, ["test@test.com"], "to does not equal what it was set to")
59
+
60
+ assert_nothing_raised("exception raised while setting to=") {
61
+ @mail.to = "test@test2.com"
62
+ }
63
+
64
+ # count to headers in the final message to make sure we have only one
65
+ count = 0
66
+ @mail.to_s().each_line() { |line|
67
+ if(line =~ /^To:/i)
68
+ count = count + 1
69
+ end
70
+ }
71
+ assert_equal(1, count, "Count of To: headers expected to be 1, but was #{count}")
72
+ end
73
+
74
+
75
+ def test_set_from
76
+ assert_nothing_raised("exception raised while setting from=") {
77
+ @mail.from = "test@test.com"
78
+ }
79
+
80
+ assert_equal(@mail.from, ["test@test.com"], "from does not equal what it was set to")
81
+
82
+ assert_nothing_raised("exception raised while setting from=") {
83
+ @mail.from = "test@test2.com"
84
+ }
85
+
86
+ # count to headers in the final message to make sure we have only one
87
+ count = 0
88
+ @mail.to_s().each_line() { |line|
89
+ if(line =~ /^From:/i)
90
+ count = count + 1
91
+ end
92
+ }
93
+ assert_equal(1, count, "Count of From: headers expected to be 1, but was #{count}")
94
+ end
95
+
96
+
97
+ def test_set_subject
98
+ assert_nothing_raised("exception raised while setting subject=") {
99
+ @mail.subject = "Test Subject"
100
+ }
101
+
102
+ assert_equal(["=?utf-8?Q?Test_Subject?="], @mail.subject, "subject does not equal what it was set to")
103
+
104
+ assert_nothing_raised("exception raised while setting subject=") {
105
+ @mail.subject = "A Different Subject"
106
+ }
107
+
108
+ # count to headers in the final message to make sure we have only one
109
+ count = 0
110
+ @mail.to_s().each_line() { |line|
111
+ if(line =~ /^Subject:/i)
112
+ count = count + 1
113
+ end
114
+ }
115
+ assert_equal(1, count, "Count of Subject: headers expected to be 1, but was #{count}")
116
+ end
117
+
118
+
119
+ def test_set_header
120
+ assert_nothing_raised("exception raised while setting arbitrary header") {
121
+ @mail.set_header("arbitrary", "some value")
122
+ }
123
+
124
+ assert_equal("some value", @mail.get_header("arbitrary")[0], "arbitrary header does not equal \"some value\"")
125
+
126
+ assert_nothing_raised("exception raised while setting arbitrary header with _") {
127
+ @mail.arbitrary_header = "some _ value"
128
+ }
129
+
130
+ assert_equal(["some _ value"], @mail.get_header("arbitrary-header"), "arbitrary header does not equal \"some _ value\"")
131
+ assert_equal(["some _ value"], @mail.arbitrary_header, "arbitrary header does not equal \"some _ value\"")
132
+
133
+
134
+ assert_nothing_raised("exception raised while setting arbitraryHeader") {
135
+ @mail.arbitraryHeader = "someValue"
136
+ }
137
+
138
+ assert_equal(["someValue"], @mail.get_header("arbitrary-header"), "arbitrary header does not equal \"someValue\"")
139
+ assert_equal(["someValue"], @mail.arbitraryHeader, "arbitrary header does not equal \"someValue\"")
140
+ end
141
+
142
+
143
+ def test_boundary_generator
144
+ 1.upto(50) {
145
+ assert_match(/^----=_NextPart_[a-zA-Z0-9\._]{25}$/, @mail.generate_boundary(), "illegal message boundary generated")
146
+ }
147
+ end
148
+
149
+
150
+ def test_email
151
+ @mail.to="test@test.com"
152
+ @mail.from="test@othertest.com"
153
+ @mail.subject="This is a test"
154
+ @mail.text = "This is a test message with\na few\n\nlines."
155
+
156
+ @mail.attach(File.dirname(__FILE__) + '/testfile.txt')
157
+ @mail.attach(File.dirname(__FILE__) + '/testsheet.xls')
158
+
159
+ if($options['smtpserver'] != nil and $options['to'] != nil and $options['from'] != nil)
160
+ assert_nothing_raised() {
161
+ require('net/smtp')
162
+ Net::SMTP.start($options['smtpserver'], 25, 'mail.from.domain', $options['username'], $options['password'], :cram_md5) { |smtp|
163
+ smtp.send_message(@mail.to_s(), $options['from'], $options['to'])
164
+ }
165
+ }
166
+ end
167
+ end
168
+
169
+
170
+ def test_attach_as
171
+ @mail.to="test@test.com"
172
+ @mail.from="test@othertest.com"
173
+ @mail.subject="This is a test"
174
+ @mail.text = "This is a test message with\na few\n\nlines."
175
+
176
+ @mail.add_attachment_as(File.dirname(__FILE__) + '/testfile.txt', 'newname.txt')
177
+ @mail.add_attachment_as(File.open(File.dirname(__FILE__) + '/testsheet.xls', 'rb'), 'newname.xls', 'application/vnd.ms-excel')
178
+
179
+ if($options['smtpserver'] != nil and $options['to'] != nil and $options['from'] != nil)
180
+ assert_nothing_raised() {
181
+ require('net/smtp')
182
+ Net::SMTP.start($options['smtpserver'], 25, 'mail.from.domain', $options['username'], $options['password'], :cram_md5) { |smtp|
183
+ smtp.send_message(@mail.to_s(), $options['from'], $options['to'])
184
+ }
185
+ }
186
+ end
187
+ end
188
+
189
+ def test_quoted_printable_with_instruction
190
+ @mail.to="test@test.com"
191
+ @mail.from="test@othertest.com"
192
+ @mail.subject="My email subject has a ? in it and also an = and a _ too... Also some non-quoted junk ()!@\#\{\$\%\}"
193
+ @mail.text = "This is a test message with\na few\n\nlines."
194
+ assert_equal(["=?utf-8?Q?My_email_subject_has_a_=3F_in_it_and_also_an_=3D_and_a_=5F_too..._Also_some_non-quoted_junk_()!@\#\{\$\%\}?="], @mail.subject)
195
+ end
196
+
197
+ def test_scandinavian_subject_quoting
198
+ @mail.to="test@test.com"
199
+ @mail.from="test@othertest.com"
200
+ # Three a with dots and three o with dots.
201
+ @mail.subject="\303\244\303\244\303\244\303\266\303\266\303\266"
202
+ @mail.text = "This is a test message with\na few\n\nlines."
203
+ assert_equal(["=?utf-8?Q?=C3=A4=C3=A4=C3=A4=C3=B6=C3=B6=C3=B6?="], @mail.subject)
204
+ end
205
+
206
+ def test_utf8_quoted_printable_with_instruction
207
+ @mail.to="test@test.com"
208
+ @mail.from="test@othertest.com"
209
+ @mail.subject="My email subject has a à which is utf8."
210
+ @mail.text = "This is a test message with\na few\n\nlines."
211
+ assert_equal(["=?utf-8?Q?My_email_subject_has_a_=C3=83_which_is_utf8.?="], @mail.subject)
212
+ end
213
+
214
+ def test_quoted_printable_html
215
+ @mail.to="test@test.com"
216
+ @mail.from="test@othertest.com"
217
+ @mail.subject="some html"
218
+ @mail.html="<a href=\"http://google.com\">click here</a>"
219
+ assert_match('<a href=3D"http://google.com">click here</a>', @mail.to_s)
220
+ end
221
+
222
+ end
223
+
224
+ $options = get_options()
225
+ Test::Unit::UI::Console::TestRunner.run(TC_MailFactory)
@@ -0,0 +1 @@
1
+ This is a test text file
Binary file
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fotonauts-mailfactory
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
+ platform: ruby
6
+ authors:
7
+ - David Powers
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-08 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mime-types
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.13.1
24
+ version:
25
+ description: MailFactory is s simple module for producing RFC compliant mail that can include multiple attachments, multiple body parts, and arbitrary headers
26
+ email: david@grayskies.net
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/mailfactory.rb
35
+ - tests/test_mailfactory.rb
36
+ - tests/testfile.txt
37
+ - tests/testsheet.xls
38
+ has_rdoc: true
39
+ homepage: http://mailfactory.rubyforge.org
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: mailfactory
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: MailFactory is a pure-ruby MIME mail generator
64
+ test_files:
65
+ - ./tests/test_mailfactory.rb