fotonauts-mailfactory 1.4.0

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.
@@ -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