mime 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +192 -140
- data/lib/mime/composite_media.rb +230 -0
- data/lib/mime/content_formats/text_flowed.rb +6 -6
- data/lib/mime/discrete_media.rb +73 -0
- data/lib/mime/discrete_media_factory.rb +19 -13
- data/lib/mime/header.rb +48 -0
- data/lib/mime/headers/internet.rb +124 -27
- data/lib/mime/headers/mime.rb +38 -27
- data/lib/mime/mail.rb +51 -0
- data/lib/mime/media.rb +30 -0
- data/lib/mime/parser.rb +1 -1
- data/lib/mime.rb +27 -9
- data/test/scaffold/application.msg +2 -5
- data/test/scaffold/audio.msg +2 -5
- data/test/scaffold/image.msg +0 -0
- data/test/scaffold/multipart_alternative.msg +7 -7
- data/test/scaffold/multipart_alternative_related.msg +0 -0
- data/test/scaffold/multipart_form_data_file_and_text.msg +0 -0
- data/test/scaffold/multipart_form_data_mixed.msg +0 -0
- data/test/scaffold/multipart_form_data_text.msg +12 -12
- data/test/scaffold/multipart_mixed_inline_and_attachment.msg +0 -0
- data/test/scaffold/multipart_mixed_inline_and_attachment2.msg +0 -0
- data/test/scaffold/multipart_related.msg +0 -0
- data/test/scaffold/rfc822_composite.msg +0 -0
- data/test/scaffold/{plain_text_email.msg → rfc822_discrete.msg} +5 -5
- data/test/scaffold/text.msg +2 -5
- data/test/scaffold/video.msg +2 -5
- data/test/test_mime.rb +323 -150
- data/test/test_text_flowed.rb +1 -1
- metadata +13 -12
- data/lib/mime/composite_media_type.rb +0 -169
- data/lib/mime/discrete_media_type.rb +0 -78
- data/lib/mime/header_container.rb +0 -32
- data/lib/mime/media_type.rb +0 -51
- data/lib/mime/message.rb +0 -61
data/README.rdoc
CHANGED
@@ -9,80 +9,80 @@ multipart/form-data transactions.
|
|
9
9
|
== See
|
10
10
|
|
11
11
|
* MIME for RFCs used to implement the library (other RFCs scattered throughout)
|
12
|
-
* MIME::
|
13
|
-
* MIME::
|
12
|
+
* MIME::CompositeMedia for a description of composite media types
|
13
|
+
* MIME::DiscreteMedia for a description of discrete media types
|
14
14
|
* MIME::DiscreteMediaFactory for easy programming of discrete media types
|
15
15
|
* MIME::ContentFormats for ways to encode/decode discrete media types
|
16
16
|
|
17
17
|
|
18
|
-
== Media
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
== MIME Message
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
Each <em>MIME Entity</em> must be a discrete (MIME::
|
85
|
-
composite (MIME::
|
18
|
+
== Media Inheritance Heirarchy
|
19
|
+
|
20
|
+
Media*
|
21
|
+
^
|
22
|
+
|
|
23
|
+
|--DiscreteMedia*
|
24
|
+
| ^
|
25
|
+
| |
|
26
|
+
| |--Application
|
27
|
+
| |--Audio
|
28
|
+
| |--Image
|
29
|
+
| |--Text
|
30
|
+
| +--Video
|
31
|
+
|
|
32
|
+
+--CompositeMedia*
|
33
|
+
^
|
34
|
+
|
|
35
|
+
|--Message**
|
36
|
+
| ^
|
37
|
+
| |
|
38
|
+
| |--ExternalBody**
|
39
|
+
| |--Partial**
|
40
|
+
| +--RFC822**
|
41
|
+
|
|
42
|
+
+--Multipart*
|
43
|
+
^
|
44
|
+
|
|
45
|
+
|--Alternative
|
46
|
+
|--Digest**
|
47
|
+
|--Encrypted**
|
48
|
+
|--FormData
|
49
|
+
|--Mixed
|
50
|
+
|--Parallel**
|
51
|
+
|--Related
|
52
|
+
|--Report**
|
53
|
+
+--Signed**
|
54
|
+
|
55
|
+
* Abstract Class
|
56
|
+
** Not implemented
|
57
|
+
|
58
|
+
|
59
|
+
== MIME Message Format
|
60
|
+
|
61
|
+
|
62
|
+
________________ -------------------+
|
63
|
+
| | |
|
64
|
+
| RFC822 & MIME | |
|
65
|
+
| Headers | |
|
66
|
+
|________________| |
|
67
|
+
________________ |
|
68
|
+
| | |
|
69
|
+
| MIME Headers | |
|
70
|
+
|~~~~~~~~~~~~~~~~| <-- MIME Entity* |--- RFC822/MIME
|
71
|
+
| Body* | (N) | Message
|
72
|
+
|________________| |
|
73
|
+
________________ |
|
74
|
+
| | |
|
75
|
+
| MIME Headers | |
|
76
|
+
|~~~~~~~~~~~~~~~~| <-- MIME Entity* |
|
77
|
+
| Body* | (N+1) |
|
78
|
+
|________________| |
|
79
|
+
-------------------+
|
80
|
+
|
81
|
+
* Optional
|
82
|
+
|
83
|
+
|
84
|
+
Each <em>MIME Entity</em> must be a discrete (MIME::DiscreteMedia) or
|
85
|
+
composite (MIME::CompositeMedia) media type. Because MIME is recursive,
|
86
86
|
composite entity bodies may contain other composite or discrete entities and so
|
87
87
|
on. However, discrete entities are non-recursive and contain only non-MIME
|
88
88
|
bodies.
|
@@ -96,7 +96,7 @@ bodies.
|
|
96
96
|
include MIME # allow ommision of "MIME::" namespace in examples below
|
97
97
|
|
98
98
|
|
99
|
-
=== Instantiate a
|
99
|
+
=== Instantiate a DiscreteMedia object
|
100
100
|
|
101
101
|
Discrete media objects, such as text or video, can be created directly using a
|
102
102
|
specific discrete media *class* or indirectly via the *factory*. If the media is
|
@@ -105,8 +105,8 @@ file and determine the MIME type for you.
|
|
105
105
|
|
106
106
|
file = '/tmp/data.xml'
|
107
107
|
|
108
|
-
text_media =
|
109
|
-
text_media = DiscreteMediaFactory.create(file)
|
108
|
+
text_media = Text.new(File.read(file), 'xml') # media class
|
109
|
+
text_media = DiscreteMediaFactory.create(file) # media factory
|
110
110
|
|
111
111
|
Discrete media objects can then be embedded in MIME messages as we will see in
|
112
112
|
the next example.
|
@@ -118,34 +118,52 @@ Create a well-formed email message with multiple recipients. The string
|
|
118
118
|
representation of the message (i.e. to_s) can then be sent directly via an
|
119
119
|
SMTP client.
|
120
120
|
|
121
|
-
msg =
|
122
|
-
msg.date
|
121
|
+
msg = Mail.new # blank message with current date and message ID headers
|
122
|
+
msg.date -= 3600 # change date to 1 hour ago
|
123
123
|
msg.subject = 'This is important' # add subject
|
124
|
-
msg.headers.
|
124
|
+
msg.headers.set('Priority', 'urgent') # add custom header
|
125
125
|
|
126
|
-
msg.body =
|
126
|
+
msg.body = Text.new('hello, world!', 'plain', 'charset' => 'us-ascii')
|
127
127
|
#
|
128
|
-
# The
|
128
|
+
# The previous line is equivalent to the following snippet:
|
129
129
|
#
|
130
|
-
# msg.body =
|
131
|
-
# msg.
|
132
|
-
|
133
|
-
|
134
|
-
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
'
|
139
|
-
'
|
140
|
-
'clint@example.com' => 'Clint Pachl',
|
141
|
-
}
|
142
|
-
msg.from = {
|
143
|
-
'boss@example.com' => 'Boss Man'
|
130
|
+
# msg.body = 'hello, world!'
|
131
|
+
# msg.headers.set('Content-Type', 'text/plain; charset=us-ascii')
|
132
|
+
|
133
|
+
msg.from = {'boss@example.com' => 'Boss Man'} # mailbox hash
|
134
|
+
msg.bcc = 'boss+home@example.com' # mailbox string
|
135
|
+
msg.cc = %w(secretary@example.com manager@example.com) # mailbox array
|
136
|
+
msg.to = {
|
137
|
+
'list@example.com' => nil, # no name display
|
138
|
+
'john@example.com' => 'John Doe',
|
139
|
+
'jane@example.com' => 'Jane Doe',
|
144
140
|
}
|
145
141
|
|
146
142
|
msg.to_s # ready to be sent via SMTP
|
147
143
|
|
148
144
|
|
145
|
+
=== RFC822 email message with image body
|
146
|
+
|
147
|
+
Embedding a single image within an email message requires a single discrete
|
148
|
+
media image. However, embedding multiple images requires a multipart/mixed
|
149
|
+
composite media type to encapsulate all of the discrete media images.
|
150
|
+
|
151
|
+
==== Email body with single image
|
152
|
+
|
153
|
+
img = Image.new(File.read('screenshot.png'), 'png')
|
154
|
+
img.disposition = 'inline'
|
155
|
+
img.description = 'My screenshot'
|
156
|
+
email = Mail.new(img)
|
157
|
+
|
158
|
+
==== Email body with multiple images
|
159
|
+
|
160
|
+
msg = Multipart::Mixed.new
|
161
|
+
msg.inline Image.new(File.read('screenshot1.png'), 'png')
|
162
|
+
msg.inline Image.new(File.read('screenshot2.png'), 'png')
|
163
|
+
msg.description = 'My screenshots'
|
164
|
+
email = Mail.new(msg)
|
165
|
+
|
166
|
+
|
149
167
|
=== Plain text multipart/mixed message with a file attachment
|
150
168
|
|
151
169
|
The multipart/mixed content type can be used to aggregate multiple unrelated
|
@@ -154,9 +172,9 @@ entities, such as text and an image.
|
|
154
172
|
text = DiscreteMediaFactory.create('/tmp/data.txt')
|
155
173
|
image = DiscreteMediaFactory.create('/tmp/ruby.png')
|
156
174
|
|
157
|
-
mixed_msg =
|
158
|
-
mixed_msg.
|
159
|
-
mixed_msg.
|
175
|
+
mixed_msg = Multipart::Mixed.new
|
176
|
+
mixed_msg.add(text)
|
177
|
+
mixed_msg.attach(image)
|
160
178
|
mixed_msg.to_s
|
161
179
|
|
162
180
|
|
@@ -166,13 +184,13 @@ The multipart/alternative content type allows for multiple, alternatively
|
|
166
184
|
formatted versions of the same content, such as plain text and HTML. Clients are
|
167
185
|
then responsible for choosing the most suitable version for display.
|
168
186
|
|
169
|
-
text_msg =
|
187
|
+
text_msg = Text.new(<<TEXT_DATA, 'plain')
|
170
188
|
**Hello, world!**
|
171
189
|
|
172
190
|
Ruby is cool!
|
173
191
|
TEXT_DATA
|
174
192
|
|
175
|
-
html_msg =
|
193
|
+
html_msg = Text.new(<<HTML_DATA, 'html')
|
176
194
|
<html>
|
177
195
|
<body>
|
178
196
|
<h1>Hello, world!</h1>
|
@@ -181,10 +199,10 @@ then responsible for choosing the most suitable version for display.
|
|
181
199
|
</html>
|
182
200
|
HTML_DATA
|
183
201
|
|
184
|
-
msg =
|
185
|
-
msg.
|
186
|
-
msg.
|
187
|
-
msg.to_s
|
202
|
+
msg = Multipart::Alternative.new
|
203
|
+
msg.add(text_msg) # add the simplest representations first
|
204
|
+
msg.add(html_msg)
|
205
|
+
msg.to_s # or send in an email: Mail.new(msg)
|
188
206
|
|
189
207
|
|
190
208
|
=== HTML multipart/related MIME email with embedded image
|
@@ -194,35 +212,33 @@ parts. For example, an HTML page with embedded images. The multipart/related
|
|
194
212
|
content type aggregates all the parts and creates the means for the root entity
|
195
213
|
to reference the other entities.
|
196
214
|
|
197
|
-
Notice the _img_ tag
|
215
|
+
Notice the _src_ of the _img_ tag.
|
198
216
|
|
199
217
|
image = DiscreteMediaFactory.create('/tmp/ruby.png')
|
200
|
-
image.
|
218
|
+
image.transfer_encoding = 'binary' # could base64 encode the image instead
|
201
219
|
|
202
|
-
html_msg =
|
220
|
+
html_msg = Text.new(<<EOF, 'html', 'charset' => 'iso-8859-1')
|
203
221
|
<html>
|
204
222
|
<body>
|
205
223
|
<h1>Ruby Image</h1>
|
206
224
|
<p>
|
207
225
|
Check out this cool pic.
|
208
|
-
<img alt="ruby is cool" src="cid:#{image.
|
226
|
+
<img alt="ruby is cool" src="cid:#{image.id}">
|
209
227
|
</p>
|
210
228
|
<p>Wasn't it cool?</p>
|
211
229
|
</body>
|
212
230
|
</html>
|
213
231
|
EOF
|
214
232
|
|
215
|
-
|
216
|
-
|
217
|
-
related_msg
|
218
|
-
related_msg.inline_entity(image)
|
219
|
-
related_msg.add_entity(html_msg)
|
233
|
+
related_msg = Multipart::Related.new
|
234
|
+
related_msg.add(html_msg)
|
235
|
+
related_msg.inline(image)
|
220
236
|
|
221
|
-
email_msg
|
222
|
-
email_msg.to
|
223
|
-
email_msg.from
|
237
|
+
email_msg = Mail.new(related_msg)
|
238
|
+
email_msg.to = 'jane@example.com'
|
239
|
+
email_msg.from = 'john@example.com'
|
224
240
|
email_msg.subject = 'Ruby is cool, checkout the picture'
|
225
|
-
email_msg.to_s
|
241
|
+
email_msg.to_s # ready to send HTML email with image
|
226
242
|
|
227
243
|
|
228
244
|
=== HTML form with file upload using multipart/form-data encoding
|
@@ -230,39 +246,34 @@ Notice the _img_ tag _src_.
|
|
230
246
|
This example builds a representation of an HTML form that can be POSTed to an
|
231
247
|
HTTP server. It contains a single text input and a file input.
|
232
248
|
|
233
|
-
name_field
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
portrait_field = open(portrait_filename) do |f|
|
238
|
-
ImageMedia.new(f.read, 'jpeg') # explicit content type
|
239
|
-
end
|
240
|
-
portrait_field.content_transfer_encoding = 'binary'
|
249
|
+
name_field = Text.new('Joe Blow')
|
250
|
+
portrait_path = '/tmp/joe_portrait.jpg'
|
251
|
+
portrait_field = Image.new(File.read(portrait_path), 'jpeg')
|
252
|
+
portrait_field.transfer_encoding = 'binary'
|
241
253
|
|
242
|
-
form_data =
|
243
|
-
form_data.
|
244
|
-
|
245
|
-
form_data.
|
246
|
-
|
247
|
-
|
248
|
-
form_data.to_s
|
254
|
+
form_data = Multipart::FormData.new
|
255
|
+
form_data.add(name_field, # field value
|
256
|
+
'name') # field name, i.e. HTML input type=text
|
257
|
+
form_data.add(portrait_field, # field value
|
258
|
+
'portrait', # field name, i.e. HTML input type=file
|
259
|
+
portrait_path) # suggest image filename to server
|
260
|
+
form_data.to_s # ready to POST via HTTP
|
249
261
|
|
250
262
|
|
251
263
|
=== HTML form with file upload via DiscreteMediaFactory
|
252
264
|
|
253
265
|
The outcome of this example is identical to the previous one. The only semantic
|
254
266
|
difference is that the DiscreteMediaFactory module is used to instantiate the
|
255
|
-
image object.
|
267
|
+
image object and automatically set the content type and FormData filename.
|
256
268
|
|
257
|
-
name_field
|
269
|
+
name_field = Text.new('Joe Blow')
|
270
|
+
portrait_path = '/tmp/joe_portrait.jpg'
|
271
|
+
portrait_field = DiscreteMediaFactory.create(portrait_path)
|
272
|
+
portrait_field.transfer_encoding = 'binary'
|
258
273
|
|
259
|
-
|
260
|
-
|
261
|
-
portrait_field
|
262
|
-
|
263
|
-
form_data = MultipartMedia::FormData.new
|
264
|
-
form_data.add_entity(name_field, 'name')
|
265
|
-
form_data.add_entity(portrait_field, 'portrait') # automatic file name
|
274
|
+
form_data = Multipart::FormData.new
|
275
|
+
form_data.add(name_field, 'name')
|
276
|
+
form_data.add(portrait_field, 'portrait')
|
266
277
|
form_data.to_s
|
267
278
|
|
268
279
|
|
@@ -284,19 +295,27 @@ appropriate.
|
|
284
295
|
"80 characters and will be soft line wrapped after the word '80'.\n\n"
|
285
296
|
|
286
297
|
flowed_txt = ContentFormats::TextFlowed.encode(long_paragraph * 2)
|
287
|
-
flowed_msg =
|
288
|
-
flowed_msg.to_s # neatly formatted text compatible with
|
298
|
+
flowed_msg = Text.new(flowed_txt, 'plain', 'format' => 'flowed')
|
299
|
+
flowed_msg.to_s # neatly formatted text compatible with small to large screens
|
289
300
|
|
290
301
|
|
291
302
|
== More Examples
|
292
303
|
|
293
304
|
For many more examples, check the test class MIMETest.
|
294
305
|
|
306
|
+
|
295
307
|
== Links
|
296
308
|
|
297
309
|
Ruby Gem :: https://rubygems.org/gems/mime
|
298
310
|
Source Code :: https://bitbucket.org/pachl/mime/src
|
299
311
|
Documentation :: http://ecentryx.com/gems/mime
|
312
|
+
Validators :: Please check *all* of your messages using the following lint
|
313
|
+
tools and report errors to pachl@ecentryx.com with "Ruby MIME"
|
314
|
+
in the subject.
|
315
|
+
|
316
|
+
- http://www.apps.ietf.org/content/message-lint
|
317
|
+
- http://tools.ietf.org/tools/msglint
|
318
|
+
|
300
319
|
|
301
320
|
== History
|
302
321
|
|
@@ -313,6 +332,39 @@ Documentation :: http://ecentryx.com/gems/mime
|
|
313
332
|
* Simplify API of DiscreteMediaType subclasses.
|
314
333
|
* Disallow Content-Type changes after instantiating DiscreteMediaType.
|
315
334
|
* Add flowed format support for text/plain (RFC 2646).
|
335
|
+
4. 2014-04-18, v0.4.0
|
336
|
+
* <b>Major API disruption!</b>
|
337
|
+
* Rename classes:
|
338
|
+
- HeaderContainter => Header
|
339
|
+
- remove "Media" suffix from the 5 DiscreteMedia and 2 CompositeMedia
|
340
|
+
subclasses. See revision 4876b1390624 for details.
|
341
|
+
- MIME::Message => MIME::Mail
|
342
|
+
* Rename methods:
|
343
|
+
- remove "_entity" suffix from add, inline, and attach in CompositeMedia.
|
344
|
+
- remove "content_" prefix from id, disposition, description, and
|
345
|
+
transfer_encoding in Headers::MIME.
|
346
|
+
* Remove methods:
|
347
|
+
- Header#add
|
348
|
+
* Add methods:
|
349
|
+
- Header#set (replace Header#add)
|
350
|
+
- Header#get
|
351
|
+
- Header#delete
|
352
|
+
* Reverse order of entities in CompositeMedia::Body, which most likely
|
353
|
+
reverses all CompositeMedia #add, #inline, and #attach method calls.
|
354
|
+
See revision a51745f346b5 for details.
|
355
|
+
* Other changes
|
356
|
+
* Use From header field domain in the Message-ID header.
|
357
|
+
* Add more randomness when generating header IDs.
|
358
|
+
* Header field names are now case-insensitive to comply with RFCs.
|
359
|
+
* Accept String, Array, and Hash for originator and destination mailboxes.
|
360
|
+
* Add CompositeMedia::Body class for nesting MIME entities.
|
361
|
+
* Improve docs and examples for Content-Disposition (inline/attachment).
|
362
|
+
* FIX: remove trailing CRLF in Mail#to_s and update tests.
|
363
|
+
* Add README links to message lint tools on IETF.org.
|
364
|
+
* Add Send, In-Reply-To, and References RFC 5322 header fields.
|
365
|
+
* Comply with RFC regarding parameter quoting in header field bodies.
|
366
|
+
I.e., do not quote atom/dot-atom parameter values.
|
367
|
+
* Many fixes and improvements in code, tests, documentation, and examples.
|
316
368
|
|
317
369
|
|
318
370
|
== License
|