mime 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4cfd511f4221d7e04909d860f85eddb218d46e99
4
- data.tar.gz: 3e43d9945ffdee1a5fe5e4c8f0365ea64707c36e
3
+ metadata.gz: 521f5a6513abe39c177b7a104f3daadffe034fe9
4
+ data.tar.gz: f86cd41fcd40b2d0874c191743e917ded58d85a9
5
5
  SHA512:
6
- metadata.gz: bd1a127ac7d9f8fef49a346ccc64c10f4f1f028357ac36a254960aa61e48168b95d202d4acb1a741c897bc34697a21041fa19e4586862c24b8ffa5697855cec0
7
- data.tar.gz: d70123f3578684bbe430fe85a5d440d2a393a74e1b633941f610de737a51fa8acf2863af7fdc415f591c055b7cdccf5c44b5259deb334b357db0b60cd970c283
6
+ metadata.gz: 5131353ba887c7aac163d87147fefac233eabc4e3f6ab9b65a3b746c45d4352739f6d4ce51ecbdcf3c9cee4e3273083836babc45d8b3f2d6c12f524498679c21
7
+ data.tar.gz: 33782e5a37696c00faf723de25cfd6afc05542e95c08ccf86e2707eb77f400ba6fd85baa8d8095ec372542898c3aedbce555149db03bb2d368058aa67599eb9d
@@ -217,22 +217,18 @@ Notice the _src_ of the _img_ tag.
217
217
  image = DiscreteMediaFactory.create('/tmp/ruby.png')
218
218
  image.transfer_encoding = 'binary' # could base64 encode the image instead
219
219
 
220
- html_msg = Text.new(<<EOF, 'html', 'charset' => 'iso-8859-1')
220
+ html_msg = Text.new(<<EOF, 'html', 'charset' => 'utf-8')
221
221
  <html>
222
222
  <body>
223
- <h1>Ruby Image</h1>
224
- <p>
225
- Check out this cool pic.
226
- <img alt="ruby is cool" src="cid:#{image.id}">
227
- </p>
228
- <p>Wasn't it cool?</p>
223
+ <h1>A Shiny Red Ruby</h1>
224
+ <img alt="a ruby gem" src="cid:#{image.id}">
229
225
  </body>
230
226
  </html>
231
227
  EOF
232
228
 
233
229
  related_msg = Multipart::Related.new
234
- related_msg.add(html_msg)
235
- related_msg.inline(image)
230
+ related_msg.add(html_msg) # first entity added becomes the root object
231
+ related_msg.inline(image) # the root object references the inline image
236
232
 
237
233
  email_msg = Mail.new(related_msg)
238
234
  email_msg.to = 'jane@example.com'
@@ -373,7 +369,15 @@ Validators :: Please check *all* of your messages using the following lint
373
369
  * Add bug tracker URL.
374
370
  * Link to referenced commit messages.
375
371
  6. 2014-06-13, v0.4.2
376
- * Ruby 1.8.7 compatibility fix.
372
+ * FIX: remove header field when set to nil.
373
+ * RFC compliance: set Sender field to first From address if multiple From
374
+ addresses and the Sender header is not already set.
375
+ * Improve TextFlowed tests.
376
+ * Add TODO.mkd.
377
+ * FIX: Ruby 1.8.7 compatibility issue.
378
+ 7. 2015-11-05, v0.4.3
379
+ * FIX: Add "type" parameter to Multipart/Related messages (RFC conformance).
380
+ * FIX: Documention bug regarding Multipart/Alternative entity order.
377
381
 
378
382
 
379
383
  == License
data/Rakefile CHANGED
@@ -16,3 +16,12 @@ end
16
16
  Rake::TestTask.new do |t|
17
17
  t.warning = true
18
18
  end
19
+
20
+ desc 'Upload RDoc HTML files to server'
21
+ task :upload_rdoc => :rdoc do
22
+ %x[
23
+ rdir=/var/www/sites/ecentryx.com/public/gems/mime
24
+ rsvr=hercules
25
+ cd ./doc && pax -w . | ssh $rsvr "cd $rdir && rm -rf ./*; pax -r"
26
+ ]
27
+ end
@@ -18,7 +18,7 @@
18
18
  #
19
19
  module MIME
20
20
 
21
- VERSION = '0.4.2'
21
+ VERSION = '0.4.3'
22
22
 
23
23
  # Defined in RFC: https://tools.ietf.org/html/rfc5322#section-2.1.1
24
24
  MAX_LINE_LENGTH = 998
@@ -67,7 +67,7 @@ module MIME
67
67
  # msg.add(MIME::Text.new('<html>html text</html>', 'html'))
68
68
  #
69
69
  # The order in which the entities are added is significant. Add the simplest
70
- # representations first.
70
+ # representations first or in increasing order of complexity.
71
71
  #
72
72
  def add entity
73
73
  raise Error.new('can only add Media objects') unless entity.is_a? Media
@@ -143,9 +143,13 @@ module MIME
143
143
 
144
144
  #
145
145
  # The Alternative subtype indicates that each contained entity is an
146
- # alternatively formatted version of the same content. The most complex
147
- # version should be added to the message first, i.e. it will be sequentially
148
- # last in the message.
146
+ # alternatively formatted version of the same content.
147
+ #
148
+ # In general, when composing multipart/alternative entities, add the body
149
+ # parts in increasing order of preference; that is, with the preferred format
150
+ # last. For example, to prioritize for fancy text, add the plainest format
151
+ # first and the richest format last. Typically, receiving user agents select
152
+ # the last format they are capable of displaying or interpreting.
149
153
  #
150
154
  class Multipart::Alternative < Multipart
151
155
 
@@ -225,6 +229,21 @@ module MIME
225
229
  super('related')
226
230
  end
227
231
 
232
+ #
233
+ # The first entity added becomes the root object. The related message
234
+ # type is set to the value of the root object media type.
235
+ #--
236
+ # The "type" parameter is required and should equal the root media type.
237
+ # https://tools.ietf.org/html/rfc2387#section-3.1
238
+ #
239
+ def add entity
240
+ unless type.include? '; type='
241
+ root_type = entity.type.partition(';').first # omit parameters
242
+ self.type = append_field_params(type, 'type' => root_type)
243
+ end
244
+ super
245
+ end
246
+
228
247
  end
229
248
 
230
249
  end
@@ -1,8 +1,7 @@
1
- gem 'minitest' # minitest in 1.9 stdlib is crufty
2
1
  require 'minitest/autorun'
3
2
  require 'mime'
4
3
 
5
- Encoding.default_external = 'ASCII-8BIT' # may be able to remove in 2.0
4
+ Encoding.default_external = 'ASCII-8BIT'
6
5
 
7
6
 
8
7
  class MIMETest < Minitest::Test
@@ -319,6 +318,29 @@ EOF
319
318
  assert_equal sender, msg.sender
320
319
  end
321
320
 
321
+ # Related type parameter: https://tools.ietf.org/html/rfc2387#section-3.1
322
+ def test_related_message_inherits_type_from_root
323
+ msg = MIME::Multipart::Related.new
324
+ refute_includes(msg.type, 'type')
325
+ msg.add(MIME::Text.new('body', 'plain')) # Root object (first entity)
326
+ assert_includes(msg.type, 'type=text/plain') # sets the related msg type.
327
+ msg.add(MIME::Text.new('body', 'html')) # Additional entities have
328
+ refute_includes(msg.type, 'type=text/html') # no effect on msg type.
329
+ assert_includes(msg.type, 'type=text/plain')
330
+ end
331
+
332
+ def test_related_message_inherits_parameterized_type_from_root
333
+ msg = MIME::Multipart::Related.new
334
+ refute_includes(msg.type, 'type')
335
+
336
+ root = MIME::Text.new('a message', 'plain', 'charset' => 'us-ascii')
337
+ assert_equal(root.type, 'text/plain; charset=us-ascii')
338
+
339
+ msg.add(root)
340
+ assert_includes(msg.type, 'type=text/plain') # Inherits root mime type,
341
+ refute_includes(msg.type, 'charset=us-ascii') # but params are omitted.
342
+ end
343
+
322
344
  def test_rfc2822_date_format
323
345
  msg = MIME::Mail.new
324
346
  assert_kind_of(Time, msg.date)
@@ -0,0 +1,616 @@
1
+ # THIS IS STILL HERE BECAUSE A LOT OF WORK WENT INTO MAKING TESTS AGNOSTIC OF MIME HEADER ORDER.
2
+ # HOWEVER, THINGS GOT MESSY SO REVERTED BACK TO COMPARING AGAINST KNOWN GOOD MESSAGES.
3
+ # THIS IS HERE JUST IN CASE. IN THE FUTURE, REMOVE IT IF THERE IS NO NEED FOR IT.
4
+
5
+ gem 'minitest' # minitest in 1.9 stdlib is crufty
6
+ require 'minitest/autorun'
7
+ require 'mime'
8
+
9
+ # may be able to remove in 2.0
10
+ Encoding.default_external = 'ASCII-8BIT'
11
+
12
+ class MIMETest < Minitest::Test
13
+
14
+ CRLF = "\r\n"
15
+ HDR_BDY_SEP = CRLF * 2
16
+ MESSAGE_ID = /^Message-ID: <\d+@\d+>\r\n/
17
+ CONTENT_ID = /^Content-ID: <\d+\.\d+>\r\n/
18
+
19
+ CDISPOSITION = /^Content-Disposition: .*\r\n/
20
+ CTYPE = /^Content-Type: .*\r\n/
21
+
22
+ DATE = /^Date: ..., \d{1,2} ... \d{4} \d\d:\d\d:\d\d -\d{4}\r\n/
23
+ VERSION = /^MIME-Version: 1.0 \(Ruby MIME v\d\.\d\)\r\n/
24
+ #BOUNDARY = /^--Boundary_\d+\.\d+\r\n/
25
+ BOUNDARY = /^--Boundary_\d+\.\d+(--)?\r\n/
26
+ BOUNDARY_LAST = /^--Boundary_\d+\.\d+--\r\n/
27
+ CID = /cid:\d+\.\d+/
28
+
29
+ CTYPE_TEXT_PLAIN = /^Content-Type: text\/plain; charset=us-ascii\r\n/
30
+ CTYPE_TEXT_HTML = /^Content-Type: text\/html\r\n/
31
+ CTYPE_TEXT_XML = /^Content-Type: text\/xml\r\n/
32
+ CTYPE_IMAGE_JPEG = /^Content-Type: image\/jpeg\r\n/
33
+ CTYPE_IMAGE_PNG = /^Content-Type: image\/png\r\n/
34
+ CTYPE_VIDEO_MPEG = /^Content-Type: video\/mpeg\r\n/
35
+ CTYPE_AUDIO_MIDI = /^Content-Type: audio\/midi\r\n/
36
+ CTYPE_APPLICATION = /^Content-Type: application\/octet-stream\r\n/
37
+
38
+ CTYPE_MPART_FORM = /^Content-Type: multipart\/form-data; boundary=Boundary_\d+\.\d+\r\n/
39
+ CTYPE_MPART_ALT = /^Content-Type: multipart\/alternative; boundary=Boundary_\d+\.\d+\r\n/
40
+ CTYPE_MPART_MIXED = /^Content-Type: multipart\/mixed; boundary=Boundary_\d+\.\d+\r\n/
41
+
42
+ XFER_ENC_BINARY = /^Content-Transfer-Encoding: binary\r\n/
43
+ XFER_ENC_8BIT = /^Content-Transfer-Encoding: 8bit\r\n/
44
+
45
+ BINARY_DATA = '0110000101110101011001000110100101101111'
46
+
47
+ def test_make_top_level_rfc2822_message
48
+ rfc2822_msg = MIME::Message.new
49
+ rfc2822_msg.body = "\r\nmessage body"
50
+ msg = rfc2822_msg.to_s
51
+
52
+ assert_match MESSAGE_ID, msg
53
+ assert_match DATE, msg
54
+ assert_match VERSION, msg
55
+ assert_equal_num_headers 3, msg
56
+ assert_equal_body "message body", msg
57
+ end
58
+
59
+ # TODO remove audio.msg
60
+ def test_make_audio_message
61
+ audio_media = MIME::AudioMedia.new(BINARY_DATA, 'audio/midi')
62
+ audio_media.content_transfer_encoding = 'binary'
63
+ msg = MIME::Message.new(audio_media).to_s
64
+
65
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_AUDIO_MIDI].each do |header|
66
+ assert_match header, msg
67
+ end
68
+ assert_equal_num_headers 6, msg
69
+ assert_equal_body BINARY_DATA, msg
70
+ end
71
+
72
+ # TODO remove expected_mime_msg = IO.read(sd('/application.msg'))
73
+ def test_make_application_message
74
+ application_media = MIME::ApplicationMedia.new(BINARY_DATA)
75
+ application_media.content_transfer_encoding = 'binary'
76
+ msg = MIME::Message.new(application_media).to_s
77
+
78
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_APPLICATION].each do |header|
79
+ assert_match header, msg
80
+ end
81
+ assert_equal_num_headers 6, msg
82
+ assert_equal_body BINARY_DATA, msg
83
+ end
84
+
85
+ # TODO remove image.msg
86
+ def test_make_image_message
87
+ image = IO.read(sd('/image.jpg'))
88
+ image_media = MIME::ImageMedia.new(image)
89
+ image_media.content_type = 'image/jpeg'
90
+ image_media.content_transfer_encoding = 'binary'
91
+ msg = MIME::Message.new(image_media).to_s
92
+
93
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_IMAGE_JPEG].each do |header|
94
+ assert_match header, msg
95
+ end
96
+
97
+ assert_equal_num_headers 6, msg
98
+ assert_equal_body image, msg
99
+ end
100
+
101
+ # TODO remove text.msg
102
+ def test_make_text_message
103
+ body = 'a plain text message'
104
+ msg = MIME::Message.new(MIME::TextMedia.new(body)).to_s
105
+
106
+ [CTYPE_TEXT_PLAIN].each do |header|
107
+ assert_match header, msg
108
+ end
109
+ end
110
+
111
+ # TODO remove video.msg
112
+ def test_make_video_message
113
+ video_media = MIME::VideoMedia.new(BINARY_DATA)
114
+ video_media.content_type = 'video/mpeg'
115
+ video_media.content_transfer_encoding = 'binary'
116
+ msg = MIME::Message.new(video_media).to_s
117
+
118
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_VIDEO_MPEG].each do |header|
119
+ assert_match header, msg
120
+ end
121
+ assert_equal_num_headers 6, msg
122
+ assert_equal_body BINARY_DATA, msg
123
+ end
124
+
125
+ def test_no_instantiation_of_abstract_classes
126
+ e = MIME::AbstractClassError
127
+ assert_raises(e) {MIME::MediaType.new(nil, nil)}
128
+ assert_raises(e) {MIME::DiscreteMediaType.new(nil)}
129
+ assert_raises(e) {MIME::CompositeMediaType.new(nil)}
130
+ assert_raises(e) {MIME::MultipartMedia.new(nil)}
131
+ end
132
+
133
+ # def test_boundaries
134
+ # # CREATE a multipart message; take simplified version of next form data
135
+ # # test
136
+ # e = msg.scan(BOUNDARY).each
137
+ # first_boundary = e.next
138
+ # assert_equal first_boundary, e.next
139
+ # assert_equal first_boundary, e.next
140
+ # last_boundary = e.next
141
+ # refute_equal first_boundary, last_boundary
142
+ # assert_match /^--Boundary_\d+\.\d+\r\n/, first_boundary
143
+ # assert_match /^--Boundary_\d+\.\d+--\r\n/, last_boundary
144
+ # end
145
+ #
146
+ # def test_unique_content_ids_in_multipart_message
147
+ #
148
+ # content_ids = msg.scan(/^Content-ID: <(\d+.\d+)>/)
149
+ # assert_equal 4, content_ids.flatten.uniq.size # all IDs must be unique
150
+ # end
151
+
152
+ # TODO rm multipart_form_data_text.msg
153
+ def test_multipart_form_data_with_text_entities
154
+ txt_data = 'text body'
155
+ htm_data = IO.read(sd('data.htm'))
156
+ xml_data = IO.read(sd('data.xml'))
157
+
158
+ txt = MIME::TextMedia.new('text body')
159
+ htm = MIME::TextMedia.new(htm_data, 'text/html')
160
+ xml = MIME::TextMedia.new(xml_data, 'text/xml')
161
+
162
+ form_data = MIME::MultipartMedia::FormData.new
163
+ form_data.add_entity txt, 'txt'
164
+ form_data.add_entity htm, 'htm'
165
+ form_data.add_entity xml, 'xml'
166
+ msg = form_data.to_s
167
+
168
+ parts = msg.split(BOUNDARY)
169
+ assert_equal 5, parts.size, 'main header, 3 entities, last boundary'
170
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
171
+
172
+ # main header
173
+ assert_match CONTENT_ID, parts[0]
174
+ assert_match CTYPE_MPART_FORM, parts[0]
175
+ assert_equal_num_headers 2, parts[0]
176
+ assert_equal_body '', parts[0]
177
+
178
+ # xml entity
179
+ assert_match CONTENT_ID, parts[1]
180
+ assert_match CTYPE_TEXT_XML, parts[1]
181
+ assert_match /^Content-Disposition: form-data; name="xml"\r\n/, parts[1]
182
+ assert_equal_num_headers 3, parts[1]
183
+ assert_equal_body xml_data, parts[1]
184
+
185
+ # html entity
186
+ assert_match CONTENT_ID, parts[2]
187
+ assert_match CTYPE_TEXT_HTML, parts[2]
188
+ assert_match /^Content-Disposition: form-data; name="htm"\r\n/, parts[2]
189
+ assert_equal_num_headers 3, parts[2]
190
+ assert_equal_body htm_data, parts[2]
191
+
192
+ # text entity
193
+ assert_match CONTENT_ID, parts[3]
194
+ assert_match CTYPE_TEXT_PLAIN, parts[3]
195
+ assert_match /^Content-Disposition: form-data; name="txt"\r\n/, parts[3]
196
+ assert_equal_num_headers 3, parts[3]
197
+ assert_equal_body txt_data, parts[3]
198
+ end
199
+
200
+ # TODO test that only basename of the file is included in header
201
+ # test if no filename is used, then probably no filename param
202
+ # see next test for ideas
203
+ def test_content_disposition_filename
204
+
205
+ pass
206
+ end
207
+
208
+ # rm multipart_form_data_file.msg
209
+ # rm multipart_form_data_file_and_text.msg
210
+ def test_multipart_form_data_with_text_and_file_entities
211
+ img1_filename = 'image.jpg'
212
+ img2_filename = 'ruby.png'
213
+ img1_data = IO.read(sd(img1_filename))
214
+ img2_data = IO.read(sd(img2_filename))
215
+ img1 = MIME::ImageMedia.new(img1_data, 'image/jpeg')
216
+ img2 = MIME::ImageMedia.new(img2_data, 'image/png')
217
+ img1.content_transfer_encoding = '8bit'
218
+ img2.content_transfer_encoding = '8bit'
219
+
220
+ desc_data = 'This is plain text description of images.'
221
+ desc = MIME::TextMedia.new(desc_data)
222
+
223
+ form_data = MIME::MultipartMedia::FormData.new
224
+ form_data.add_entity desc, 'description'
225
+ form_data.add_entity img2, 'image_2', img2_filename
226
+ form_data.add_entity img1, 'image_1', img1_filename
227
+ msg = form_data.to_s
228
+
229
+ parts = msg.split(BOUNDARY)
230
+ assert_equal 5, parts.size, 'main header, 3 entities, last boundary'
231
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
232
+
233
+ # main header
234
+ assert_match CONTENT_ID, parts[0]
235
+ assert_match CTYPE_MPART_FORM, parts[0]
236
+ assert_equal_num_headers 2, parts[0]
237
+ assert_equal_body '', parts[0]
238
+
239
+ # first image entity
240
+ assert_match CONTENT_ID, parts[1]
241
+ assert_match CTYPE_IMAGE_JPEG, parts[1]
242
+ assert_match XFER_ENC_8BIT, parts[1]
243
+ assert_match /^Content-Disposition: form-data; name="image_1"; filename="#{img1_filename}"\r\n/, parts[1]
244
+ assert_equal_num_headers 4, parts[1]
245
+ assert_equal_body img1_data, parts[1]
246
+
247
+ # second image entity
248
+ assert_match CONTENT_ID, parts[2]
249
+ assert_match CTYPE_IMAGE_PNG, parts[2]
250
+ assert_match XFER_ENC_8BIT, parts[2]
251
+ assert_match /^Content-Disposition: form-data; name="image_2"; filename="#{img2_filename}"\r\n/, parts[2]
252
+ assert_equal_num_headers 4, parts[2]
253
+ assert_equal_body img2_data, parts[2]
254
+
255
+ # plain text entity
256
+ assert_match CONTENT_ID, parts[3]
257
+ assert_match CTYPE_TEXT_PLAIN, parts[3]
258
+ assert_match /^Content-Disposition: form-data; name="description"\r\n/, parts[3]
259
+ assert_equal_num_headers 3, parts[3]
260
+ assert_equal_body desc_data, parts[3]
261
+ end
262
+
263
+ # rm plain_text_email.msg
264
+ def test_construction_of_plain_text_email_message
265
+ subject = 'This is an important email'
266
+ body = 'This is the all important email body.'
267
+
268
+ email_msg = MIME::Message.new
269
+ email_msg.to = {
270
+ 'john@example.com' => 'John',
271
+ 'paul@example.com' => nil,
272
+ 'mary@example.com' => 'Mary'
273
+ }
274
+ email_msg.cc = {'Head Honcho' => 'boss@example.com'}
275
+ email_msg.from = {'jane@example.com' => nil}
276
+ email_msg.subject = subject
277
+ email_msg.body = MIME::TextMedia.new(body)
278
+
279
+ msg = email_msg.to_s
280
+
281
+ assert_equal_num_headers 9, msg
282
+ assert_match MESSAGE_ID, msg
283
+ assert_match CONTENT_ID, msg
284
+ assert_match CTYPE_TEXT_PLAIN, msg
285
+ assert_match DATE, msg
286
+ assert_match VERSION, msg
287
+ assert_match /^Subject: #{subject}\r\n/, msg
288
+ assert_match /^From: jane@example.com\r\n/, msg
289
+ assert_match /^To: .+, .+, .+\r\n/, msg # 3 addresses
290
+ assert_match /^To: .*John <john@example.com>.*\r\n/, msg
291
+ assert_match /^To: .*Mary <mary@example.com>.*\r\n/, msg
292
+ assert_match /^To: .*paul@example.com.*\r\n/, msg
293
+ assert_match /^Cc: boss@example.com <Head Honcho>\r\n/, msg
294
+ assert_equal_body body, msg
295
+ end
296
+
297
+ def test_content_type_detection
298
+ (o = Object.new).extend(MIME::ContentTypes)
299
+
300
+ # test using file path, relative and absolute
301
+ assert_equal 'application/pdf', o.file_type('book.pdf')
302
+ assert_equal 'video/quicktime', o.file_type('mini.mov')
303
+ assert_equal 'application/octet-stream', o.file_type('dsk.iso')
304
+ assert_equal 'audio/mpeg', o.file_type('/tmp/song.mp3')
305
+ assert_equal 'text/css', o.file_type('/tmp/main.css')
306
+ assert_equal nil, o.file_type('unknown.yyy')
307
+
308
+ # test using file object
309
+ img_type = open(sd('ruby.png')) {|f| o.file_type(f)}
310
+ assert_equal 'image/png', img_type
311
+ refute_equal 'image/jpeg', img_type
312
+ end
313
+
314
+ def test_object_instantiation_using_discrete_media_factory
315
+ app_file = sd('book.pdf')
316
+ audio_file = sd('song.mp3')
317
+ text_file = sd('data.xml')
318
+ video_file = sd('mini.mov')
319
+ image_file = sd('image.jpg')
320
+ unknown_file = sd('unknown.yyy')
321
+
322
+ dmf = MIME::DiscreteMediaFactory
323
+
324
+ # test using file path
325
+ assert_kind_of MIME::ApplicationMedia, dmf.create(app_file)
326
+ assert_kind_of MIME::AudioMedia, dmf.create(audio_file)
327
+ assert_kind_of MIME::TextMedia, dmf.create(text_file)
328
+ assert_kind_of MIME::VideoMedia, dmf.create(video_file)
329
+ assert_kind_of MIME::ImageMedia, dmf.create(image_file)
330
+
331
+ # test using file object
332
+ open(image_file) do |image_file_obj|
333
+ assert_kind_of MIME::ImageMedia, dmf.create(image_file_obj)
334
+ end
335
+ open(text_file) do |text_file_obj|
336
+ assert_kind_of MIME::TextMedia, dmf.create(text_file_obj)
337
+ end
338
+
339
+ # raise for unknown file path and File object
340
+ assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file)}
341
+ open(unknown_file) do |unknown_file_obj|
342
+ assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file_obj)}
343
+ end
344
+ end
345
+
346
+ def test_discrete_media_factory_creates_path_singleton_method
347
+ pdf_file_path = sd('book.pdf')
348
+
349
+ media_obj = MIME::DiscreteMediaFactory.create(pdf_file_path)
350
+ assert_equal pdf_file_path, media_obj.path
351
+
352
+ open(pdf_file_path) do |pdf_file_obj|
353
+ media_obj = MIME::DiscreteMediaFactory.create(pdf_file_obj)
354
+ assert_equal pdf_file_path, media_obj.path
355
+ end
356
+ end
357
+
358
+ # rm multipart_alternative.msg
359
+ def test_multipart_alternative_message_construction
360
+ txt_data = "*Header*\nmessage"
361
+ htm_data = "<html><body><h1>Header</h1><p>message</p></body></html>"
362
+ txt_msg = MIME::TextMedia.new(txt_data)
363
+ htm_msg = MIME::TextMedia.new(htm_data)
364
+
365
+ txt_msg.content_type = (txt_type = 'text/enhanced; charset=us-ascii')
366
+ htm_msg.content_type = (htm_type = 'text/html; charset=iso-8859-1')
367
+
368
+ alt_msg = MIME::MultipartMedia::Alternative.new
369
+ alt_msg.add_entity htm_msg
370
+ alt_msg.add_entity txt_msg
371
+ msg = alt_msg.to_s
372
+
373
+ parts = msg.split(BOUNDARY)
374
+ assert_equal 4, parts.size, 'main header, 2 entities, last boundary'
375
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
376
+
377
+ # main header
378
+ assert_match CONTENT_ID, parts[0]
379
+ assert_match CTYPE_MPART_ALT, parts[0]
380
+ assert_equal_num_headers 2, parts[0]
381
+ assert_equal_body '', parts[0]
382
+
383
+ # text entity
384
+ assert_match CONTENT_ID, parts[1]
385
+ assert_match /^Content-Type: #{txt_type}\r\n/, parts[1]
386
+ assert_equal_num_headers 2, parts[1]
387
+ assert_equal_body txt_data, parts[1]
388
+
389
+ # html entity
390
+ assert_match CONTENT_ID, parts[2]
391
+ assert_match /^Content-Type: #{htm_type}\r\n/, parts[2]
392
+ assert_equal_num_headers 2, parts[2]
393
+ assert_equal_body htm_data, parts[2]
394
+ end
395
+
396
+ # rm multipart_mixed_inline_and_attachment.msg
397
+ def test_multipart_mixed_with_inline_and_attachment
398
+ mixed_msg = MIME::MultipartMedia::Mixed.new
399
+
400
+ img_filename = 'image.jpg'
401
+ img_data = ''
402
+ open(sd(img_filename)) do |img_file|
403
+ img_data = img_file.read
404
+ img_msg = MIME::ImageMedia.new(img_data, 'image/jpeg')
405
+ mixed_msg.attach_entity(img_msg, 'filename' => img_file.path)
406
+ end
407
+
408
+ txt_data = 'This is plain text.'
409
+ mixed_msg.inline_entity(MIME::TextMedia.new(txt_data))
410
+ msg = mixed_msg.to_s
411
+
412
+ parts = msg.split(BOUNDARY)
413
+ assert_equal 4, parts.size, 'main header, 2 entities, last boundary'
414
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
415
+
416
+ # main header
417
+ assert_match CONTENT_ID, parts[0]
418
+ assert_match CTYPE_MPART_MIXED, parts[0]
419
+ assert_equal_num_headers 2, parts[0]
420
+ assert_equal_body '', parts[0]
421
+
422
+ # text entity
423
+ assert_match CONTENT_ID, parts[1]
424
+ assert_match CTYPE_TEXT_PLAIN, parts[1]
425
+ assert_match /^Content-Disposition: inline\r\n/, parts[1]
426
+ assert_equal_num_headers 3, parts[1]
427
+ assert_equal_body txt_data, parts[1]
428
+
429
+ # image entity
430
+ assert_match CONTENT_ID, parts[2]
431
+ assert_match CTYPE_IMAGE_JPEG, parts[2]
432
+ assert_match /^Content-Disposition: attachment; filename="#{img_filename}"\r\n/, parts[2]
433
+ assert_equal_num_headers 3, parts[2]
434
+ assert_equal_body img_data, parts[2]
435
+ end
436
+
437
+ # rm multipart_mixed_inline_and_attachment2.msg
438
+ def test_multipart_mixed_message_construction_using_media_factory
439
+ img1 = sd(img1_filename = 'image.jpg')
440
+ img2 = sd(img2_filename = 'ruby.png')
441
+ txt = sd(txt_filename = 'data.htm')
442
+ bot_img = MIME::DiscreteMediaFactory.create(img1)
443
+ top_img = MIME::DiscreteMediaFactory.create(img2)
444
+ top_txt = MIME::DiscreteMediaFactory.create(txt)
445
+
446
+ mixed_msg = MIME::MultipartMedia::Mixed.new
447
+ mixed_msg.attach_entity(bot_img)
448
+ mixed_msg.attach_entity(top_img)
449
+ mixed_msg.inline_entity(top_txt)
450
+ msg = mixed_msg.to_s
451
+
452
+ parts = msg.split(BOUNDARY)
453
+ assert_equal 5, parts.size, 'main header, 2 entities, last boundary'
454
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
455
+
456
+ # main header
457
+ assert_match CONTENT_ID, parts[0]
458
+ assert_match CTYPE_MPART_MIXED, parts[0]
459
+ assert_equal_num_headers 2, parts[0]
460
+ assert_equal_body '', parts[0]
461
+
462
+ # html entity
463
+ assert_match CONTENT_ID, parts[1]
464
+ assert_match CTYPE_TEXT_HTML, parts[1]
465
+ assert_match /^Content-Disposition: inline; filename="#{txt_filename}"\r\n/, parts[1]
466
+ assert_equal_num_headers 3, parts[1]
467
+ assert_equal_body top_txt.send(:body), parts[1]
468
+
469
+ # png image entity
470
+ assert_match CONTENT_ID, parts[2]
471
+ assert_match CTYPE_IMAGE_PNG, parts[2]
472
+ assert_match /^Content-Disposition: attachment; filename="#{img2_filename}"\r\n/, parts[2]
473
+ assert_equal_num_headers 3, parts[2]
474
+ assert_equal_body top_img.send(:body), parts[2]
475
+
476
+ # jpg image entity
477
+ assert_match CONTENT_ID, parts[3]
478
+ assert_match CTYPE_IMAGE_JPEG, parts[3]
479
+ assert_match /^Content-Disposition: attachment; filename="#{img1_filename}"\r\n/, parts[3]
480
+ assert_equal_num_headers 3, parts[3]
481
+ assert_equal_body bot_img.send(:body), parts[3]
482
+ end
483
+
484
+ def test_multipart_form_data_with_mixed_entity
485
+ txt = MIME::TextMedia.new('Joe Blow')
486
+ img1 = MIME::DiscreteMediaFactory.create(sd('image.jpg'))
487
+ img2 = MIME::DiscreteMediaFactory.create(sd('ruby.png'))
488
+
489
+ mixed_msg = MIME::MultipartMedia::Mixed.new
490
+ mixed_msg.attach_entity(img2)
491
+ mixed_msg.attach_entity(img1)
492
+
493
+ form = MIME::MultipartMedia::FormData.new
494
+ form.add_entity(mixed_msg, 'pics')
495
+ form.add_entity(txt, 'field1')
496
+
497
+ # similar to example 6 in RFC1867
498
+ expected = IO.read(sd('multipart_form_data_mixed.msg'))
499
+
500
+ assert_equal_mime_message expected, form.to_s
501
+
502
+ #IO.write('/tmp/mime.out', msg)
503
+ end
504
+
505
+ # def test_multipart_related_html_message_with_embedded_image
506
+ # img = DiscreteMediaFactory.create(sd('/ruby.png'))
507
+ # img.content_transfer_encoding = 'binary'
508
+ #
509
+ # html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
510
+ # <html>
511
+ # <body>
512
+ # <h1>HTML multipart/related message</h1>
513
+ # <p>txt before pix</p>
514
+ # <img alt="cool ruby" src="cid:#{img.content_id}"/>
515
+ # <p>txt after pix</p>
516
+ # </body>
517
+ # </html>
518
+ # html
519
+ # html_msg.content_transfer_encoding = '7bit'
520
+ #
521
+ # related_msg = MultipartMedia::Related.new
522
+ # related_msg.inline_entity(img)
523
+ # related_msg.add_entity(html_msg)
524
+ #
525
+ # expected = IO.read(sd('/multipart_related.msg'))
526
+ #
527
+ # assert_equal_mime_message expected, related_msg.to_s
528
+ # end
529
+ #
530
+ # def test_multipart_alternative_with_related_html_entity
531
+ # img = DiscreteMediaFactory.create(sd('/ruby.png'))
532
+ # img.content_transfer_encoding = 'binary'
533
+ #
534
+ # html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
535
+ # <html>
536
+ # <body>
537
+ # <h1>HTML multipart/alternative message</h1>
538
+ # <p>txt before pix</p>
539
+ # <img alt="cool ruby" src="cid:#{img.content_id}"/>
540
+ # <p>txt after pix</p>
541
+ # </body>
542
+ # </html>
543
+ # html
544
+ # html_msg.content_transfer_encoding = '7bit'
545
+ #
546
+ # text_msg = TextMedia.new(<<-text)
547
+ # *HTML multipart/alternative message*
548
+ # txt before pix
549
+ # <cool ruby image>
550
+ # txt after pix
551
+ # text
552
+ # text_msg.content_transfer_encoding = '7bit'
553
+ #
554
+ # related_msg = MultipartMedia::Related.new
555
+ # related_msg.inline_entity(img)
556
+ # related_msg.add_entity(html_msg)
557
+ #
558
+ # alt_msg = MultipartMedia::Alternative.new
559
+ # alt_msg.add_entity(related_msg)
560
+ # alt_msg.add_entity(text_msg)
561
+ #
562
+ # expected = IO.read(sd('/multipart_alternative_related.msg'))
563
+ #
564
+ # assert_equal_mime_message expected, alt_msg.to_s
565
+ # end
566
+
567
+
568
+ private
569
+
570
+ #
571
+ # Test the equality of the normalized +expected+ and +actual+ MIME messages.
572
+ #
573
+ def assert_equal_mime_message expected, actual
574
+ assert_equal normalize_message(expected), normalize_message(actual)
575
+ end
576
+
577
+ #
578
+ # Make messages comparable by removing *-ID header values, library version
579
+ # comment in MIME-Version header, Date header value, multipart/related
580
+ # content IDs, and boundaries.
581
+ #
582
+ def normalize_message message
583
+ # these are very delicate REs that are inter-dependent, be careful
584
+ match_id_headers = /-ID: <[^>]+>\r\n/
585
+ match_boundaries = /Boundary_\d+\.\d+/
586
+ match_lib_version = / \(Ruby MIME v\d\.\d\)/
587
+ match_date_header = /^Date: .*\d{4}\r\n/
588
+ match_related_cid = /cid:\d+\.\d+/
589
+
590
+ message.
591
+ gsub(match_related_cid, "cid").
592
+ gsub(match_id_headers, "-ID:\r\n").
593
+ gsub(match_boundaries, "Boundary_").
594
+ sub(match_date_header, "Date:\r\n").
595
+ sub(match_lib_version, " (Ruby MIME v0.0)")
596
+ end
597
+
598
+ def assert_equal_num_headers num, msg
599
+ assert_equal num, msg.split(HDR_BDY_SEP).first.split(CRLF).count
600
+ end
601
+
602
+ def assert_equal_body expected_body, msg
603
+ # FIXME the next line should be a fix
604
+ headers, actual_body = msg.split(HDR_BDY_SEP)
605
+ assert_equal expected_body, actual_body.to_s.chomp
606
+ #assert_equal body, msg.split("\r\n").last
607
+ end
608
+
609
+ #
610
+ # Return the absolute path of +file+ under the test/scaffold directory.
611
+ #
612
+ def sd file
613
+ File.join(File.dirname(__FILE__), 'scaffold', file)
614
+ end
615
+
616
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clint Pachl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-13 00:00:00.000000000 Z
11
+ date: 2015-11-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  A library for building RFC compliant Multipurpose Internet Mail Extensions
@@ -61,6 +61,7 @@ files:
61
61
  - test/scaffold/unknown.yyy
62
62
  - test/scaffold/video.msg
63
63
  - test/test_mime.rb
64
+ - test/test_mime.rb-try
64
65
  - test/test_text_flowed.rb
65
66
  homepage: http://ecentryx.com/gems/mime
66
67
  licenses:
@@ -82,10 +83,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
83
  version: '0'
83
84
  requirements: []
84
85
  rubyforge_project:
85
- rubygems_version: 2.2.0
86
+ rubygems_version: 2.4.5
86
87
  signing_key:
87
88
  specification_version: 4
88
89
  summary: Multipurpose Internet Mail Extensions (MIME) Library
89
90
  test_files:
90
- - test/test_text_flowed.rb
91
91
  - test/test_mime.rb
92
+ - test/test_text_flowed.rb