mailfactory 0.5.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,309 @@
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
+
30
+
31
+ require 'base64'
32
+ require 'pathname'
33
+
34
+ # try to bring in the mime/types module, make a dummy module if it can't be found
35
+ begin
36
+ begin
37
+ require 'rubygems'
38
+ rescue LoadError
39
+ end
40
+ require 'mime/types'
41
+ rescue LoadError
42
+ module MIME
43
+ class Types
44
+ def Types::type_for(filename)
45
+ return('')
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ # An easy class for creating a mail message
52
+ class MailFactory
53
+
54
+ def initialize()
55
+ @headers = Array.new()
56
+ @attachments = Array.new()
57
+ @attachmentboundary = generate_boundary()
58
+ @bodyboundary = generate_boundary()
59
+ end
60
+
61
+
62
+ # adds a header to the bottom of the headers
63
+ def add_header(header, value)
64
+ @headers << "#{header}: #{value}"
65
+ end
66
+
67
+
68
+ # removes the named header - case insensitive
69
+ def remove_header(header)
70
+ @headers.each_index() { |i|
71
+ if(@headers[i] =~ /^#{Regexp.escape(header)}:/i)
72
+ @headers.delete_at(i)
73
+ end
74
+ }
75
+ end
76
+
77
+
78
+ # sets a header (removing any other versions of that header)
79
+ def set_header(header, value)
80
+ remove_header(header)
81
+ add_header(header, value)
82
+ end
83
+
84
+
85
+ def to=(newto)
86
+ remove_header("To")
87
+ add_header("To", newto)
88
+ end
89
+
90
+
91
+ def to()
92
+ return(get_header("To")[0])
93
+ end
94
+
95
+
96
+ def from=(newfrom)
97
+ remove_header("From")
98
+ add_header("From", newfrom)
99
+ end
100
+
101
+
102
+ def from()
103
+ return(get_header("From")[0])
104
+ end
105
+
106
+
107
+ def subject=(newsubject)
108
+ remove_header("Subject")
109
+ add_header("Subject", newsubject)
110
+ end
111
+
112
+
113
+ def subject()
114
+ return(get_header("Subject")[0])
115
+ end
116
+
117
+
118
+ def cc=(newcc)
119
+ remove_header("CC")
120
+ add_header("CC", newcc)
121
+ end
122
+
123
+
124
+ def cc()
125
+ return(get_header("CC")[0])
126
+ end
127
+
128
+
129
+ # sets the plain text body of the message
130
+ def text=(newtext)
131
+ @text = newtext
132
+ end
133
+
134
+
135
+ # sets the HTML body of the message. Only the body of the
136
+ # html should be provided
137
+ def html=(newhtml)
138
+ @html = "<html>\n<head>\n<meta content=\"text/html;charset=ISO-8859-1\" http-equiv=\"Content-Type\">\n</head>\n<body bgcolor=\"#ffffff\" text=\"#000000\">\n#{newhtml}\n</body>\n</html>"
139
+ end
140
+
141
+
142
+ # sets the HTML body of the message. The entire HTML section should be provided
143
+ def rawhtml=(newhtml)
144
+ @html = newhtml
145
+ end
146
+
147
+
148
+ # returns the value (or values) of the named header in an array
149
+ def get_header(header)
150
+ headers = Array.new()
151
+ headerregex = /^#{Regexp.escape(header)}:/i
152
+ @headers.each() { |h|
153
+ if(headerregex.match(h))
154
+ headers << h[/^[^:]+:(.*)/i, 1].strip()
155
+ end
156
+ }
157
+
158
+ return(headers)
159
+ end
160
+
161
+
162
+ # returns true if the email is multipart
163
+ def multipart?()
164
+ if(@attachments.length > 0 or @html != nil)
165
+ return(true)
166
+ else
167
+ return(false)
168
+ end
169
+ end
170
+
171
+
172
+ # returns a formatted email
173
+ def to_s()
174
+ if(get_header("Date").length == 0)
175
+ add_header("Date", Time.now.strftime("%a, %d %B %Y %H:%M:%S %Z"))
176
+ end
177
+
178
+ # Add a mime header if we don't already have one and we have multiple parts
179
+ if(multipart?())
180
+ if(get_header("MIME-Version").length == 0)
181
+ add_header("MIME-Version", "1.0")
182
+ end
183
+
184
+ if(get_header("Content-Type").length == 0)
185
+ if(@attachments.length == 0)
186
+ add_header("Content-Type", "multipart/alternative;boundary=\"#{@bodyboundary}\"")
187
+ else
188
+ add_header("Content-Type", "multipart/mixed; boundary=\"#{@attachmentboundary}\"")
189
+ end
190
+ end
191
+ end
192
+
193
+ return("#{headers_to_s()}#{body_to_s()}")
194
+ end
195
+
196
+
197
+ # generates a unique boundary string
198
+ def generate_boundary()
199
+ randomstring = Array.new()
200
+ 1.upto(25) {
201
+ whichglyph = rand(100)
202
+ if(whichglyph < 40)
203
+ randomstring << (rand(25) + 65).chr()
204
+ elsif(whichglyph < 70)
205
+ randomstring << (rand(25) + 97).chr()
206
+ elsif(whichglyph < 90)
207
+ randomstring << (rand(10) + 48).chr()
208
+ elsif(whichglyph < 95)
209
+ randomstring << '.'
210
+ else
211
+ randomstring << '_'
212
+ end
213
+ }
214
+ return("----=_NextPart_#{randomstring.join()}")
215
+ end
216
+
217
+
218
+ # adds an attachment to the mail. Type may be given as a mime type. If it
219
+ # is left off and the MIME::Types module is available it will be determined automagically.
220
+ def add_attachment(filename, type=nil)
221
+ attachment = Array.new()
222
+ attachment[0] = Pathname.new(filename).basename
223
+ attachment[1] = MIME::Types.type_for(filename).to_s
224
+ File.open(filename, File::RDONLY) { |fp|
225
+ attachment[2] = Base64.b64encode(fp.read())
226
+ }
227
+ @attachments << attachment
228
+ end
229
+
230
+
231
+ # adds an attachment to the mail as emailfilename. Type may be given as a mime type. If it
232
+ # is left off and the MIME::Types module is available it will be determined automagically.
233
+ def add_attachment_as(filename, emailfilename, type=nil)
234
+ attachment = Array.new()
235
+ attachment[0] = emailfilename
236
+ attachment[1] = MIME::Types.type_for(filename).to_s
237
+ File.open(filename, File::RDONLY) { |fp|
238
+ attachment[2] = Base64.b64encode(fp.read())
239
+ }
240
+ @attachments << attachment
241
+ end
242
+
243
+
244
+ alias attach add_attachment
245
+ alias attach_as add_attachment_as
246
+
247
+ protected
248
+
249
+ # returns the @headers as a properly formatted string
250
+ def headers_to_s()
251
+ return("#{@headers.join("\r\n")}\r\n\r\n")
252
+ end
253
+
254
+
255
+ # returns the body as a properly formatted string
256
+ def body_to_s()
257
+ body = Array.new()
258
+
259
+ # simple message with one part
260
+ if(!multipart?())
261
+ return(@text)
262
+ else
263
+ body << "This is a multi-part message in MIME format.\r\n\r\n--#{@attachmentboundary}\r\nContent-Type: multipart/alternative; boundary=\"#{@bodyboundary}\""
264
+
265
+ if(@attachments.length > 0)
266
+ # text part
267
+ body << "#{buildbodyboundary('text/plain; charset=ISO-8859-1; format=flowed', '7bit')}\r\n\r\n#{@text}"
268
+
269
+ # html part
270
+ body << "#{buildbodyboundary('text/html; charset=ISO-8859-1', '7bit')}\r\n\r\n#{@html}"
271
+
272
+ body << "--#{@bodyboundary}--"
273
+
274
+ # and, the attachments
275
+ if(@attachments.length > 0)
276
+ @attachments.each() { |attachment|
277
+ body << "#{buildattachmentboundary(attachment[1], 'base64', attachment[0])}\r\n\r\n#{attachment[2]}"
278
+ }
279
+ body << "\r\n--#{@attachmentboundary}--"
280
+ end
281
+ else
282
+ # text part
283
+ body << "#{buildbodyboundary('text/plain; charset=ISO-8859-1; format=flowed', '7bit')}\r\n\r\n#{@text}"
284
+
285
+ # html part
286
+ body << "#{buildbodyboundary('text/html; charset=ISO-8859-1', '7bit')}\r\n\r\n#{@html}"
287
+
288
+ body << "--#{@bodyboundary}--"
289
+ end
290
+
291
+ return(body.join("\r\n\r\n"))
292
+ end
293
+ end
294
+
295
+
296
+ # builds a boundary string for including attachments in the body
297
+ def buildattachmentboundary(type, encoding, filename)
298
+ disposition = "\r\nContent-Disposition: inline; filename=\"#{filename}\""
299
+ return("--#{@attachmentboundary}\r\nContent-Type: #{type}; name=\"#{filename}\"\r\nContent-Transfer-Encoding: #{encoding}#{disposition}")
300
+ end
301
+
302
+
303
+ # builds a boundary string for inclusion in the body of a message
304
+ def buildbodyboundary(type, encoding)
305
+ return("--#{@bodyboundary}\r\nContent-Type: #{type}\r\nContent-Transfer-Encoding: #{encoding}")
306
+ end
307
+
308
+ end
309
+
data/lib/test.rb ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'net/smtp'
4
+ require 'rubygems'
5
+ require 'mailfactory'
6
+
7
+
8
+ mail = MailFactory.new()
9
+ mail.to = "test@test.com"
10
+ mail.from = "sender@sender.com"
11
+ mail.subject = "Here are some files for you!"
12
+ mail.text = "This is what people with plain text mail readers will see"
13
+ mail.html = "A little something <b>special</b> for people with HTML readers"
14
+ mail.attach("/etc/fstab")
15
+ mail.attach("/some/other/file")
16
+
17
+ Net::SMTP.start('smtp1.testmailer.com', 25, 'mail.from.domain', fromaddress, password, :cram_md5) { |smtp|
18
+ mail.to = toaddress
19
+ smtp.send_message(mail.to_s(), fromaddress, toaddress)
20
+ }
21
+
@@ -0,0 +1,96 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'test/unit'
4
+ require '../lib/mailfactory.rb'
5
+
6
+ class TC_MailFactory < Test::Unit::TestCase
7
+
8
+ def setup()
9
+ @mail = MailFactory.new
10
+ end
11
+
12
+
13
+ def test_set_to
14
+ assert_nothing_raised("exception raised while setting to=") {
15
+ @mail.to = "test@test.com"
16
+ }
17
+
18
+ assert_equal(@mail.to, "test@test.com", "to does not equal what it was set to")
19
+
20
+ assert_nothing_raised("exception raised while setting to=") {
21
+ @mail.to = "test@test2.com"
22
+ }
23
+
24
+ # count to headers in the final message to make sure we have only one
25
+ count = 0
26
+ @mail.to_s().each_line() { |line|
27
+ if(line =~ /^To:/i)
28
+ count = count + 1
29
+ end
30
+ }
31
+ assert_equal(1, count, "Count of To: headers expected to be 1, but was #{count}")
32
+ end
33
+
34
+ def test_set_from
35
+ assert_nothing_raised("exception raised while setting from=") {
36
+ @mail.from = "test@test.com"
37
+ }
38
+
39
+ assert_equal(@mail.from, "test@test.com", "from does not equal what it was set to")
40
+
41
+ assert_nothing_raised("exception raised while setting from=") {
42
+ @mail.from = "test@test2.com"
43
+ }
44
+
45
+ # count to headers in the final message to make sure we have only one
46
+ count = 0
47
+ @mail.to_s().each_line() { |line|
48
+ if(line =~ /^From:/i)
49
+ count = count + 1
50
+ end
51
+ }
52
+ assert_equal(1, count, "Count of From: headers expected to be 1, but was #{count}")
53
+ end
54
+
55
+ def test_set_subject
56
+ assert_nothing_raised("exception raised while setting subject=") {
57
+ @mail.subject = "Test Subject"
58
+ }
59
+
60
+ assert_equal(@mail.subject, "Test Subject", "subject does not equal what it was set to")
61
+
62
+ assert_nothing_raised("exception raised while setting subject=") {
63
+ @mail.subject = "A Different Subject"
64
+ }
65
+
66
+ # count to headers in the final message to make sure we have only one
67
+ count = 0
68
+ @mail.to_s().each_line() { |line|
69
+ if(line =~ /^Subject:/i)
70
+ count = count + 1
71
+ end
72
+ }
73
+ assert_equal(1, count, "Count of Subject: headers expected to be 1, but was #{count}")
74
+ end
75
+
76
+ def test_set_header
77
+ assert_nothing_raised("exception raised while setting arbitrary header") {
78
+ @mail.set_header("arbitrary", "some value")
79
+ }
80
+
81
+ assert_equal("some value", @mail.get_header("arbitrary")[0], "arbitrary header does not equal \"some value\"")
82
+ end
83
+
84
+ def test_boundary_generator
85
+ 1.upto(50) {
86
+ assert_match(/^----=_NextPart_[a-zA-Z0-9\._]{25}$/, @mail.generate_boundary(), "illegal message boundary generated")
87
+ }
88
+ end
89
+
90
+ def test_email
91
+ @mail.to="test@test.com"
92
+ @mail.from="test@othertest.com"
93
+ @mail.subject="This is a test"
94
+ @mail.text = "This is a test message with\na few\n\nlines."
95
+ end
96
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: mailfactory
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.5.0
7
+ date: 2005-05-21
8
+ summary: MailFactory is a pure-ruby MIME mail generator
9
+ require_paths:
10
+ - lib
11
+ email: david@grayskies.net
12
+ homepage: http://mailfactory.rubyforge.org
13
+ rubyforge_project: mailfactory
14
+ description: "MailFactory is s simple module for producing RFC compliant mail that can include
15
+ multiple attachments, multiple body parts, and arbitrary headers"
16
+ autorequire: mailfactory
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ authors:
29
+ - David Powers
30
+ files:
31
+ - lib/mailfactory.rb
32
+ - lib/test.rb
33
+ test_files:
34
+ - tests/test_mailfactory.rb
35
+ rdoc_options: []
36
+ extra_rdoc_files: []
37
+ executables: []
38
+ extensions: []
39
+ requirements: []
40
+ dependencies:
41
+ - !ruby/object:Gem::Dependency
42
+ name: mime-types
43
+ version_requirement:
44
+ version_requirements: !ruby/object:Gem::Version::Requirement
45
+ requirements:
46
+ -
47
+ - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.13.1
50
+ version: