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