mime 0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,303 @@
1
+ == Multipurpose Internet Mail Extensions (MIME)
2
+
3
+ A library for building RFC compliant Multipurpose Internet Mail Extensions
4
+ (MIME) messages. It can be used to construct standardized MIME messages for use
5
+ in client/server communications, such as Internet mail or HTTP
6
+ multipart/form-data transactions.
7
+
8
+
9
+ == See
10
+
11
+ * MIME for RFCs used to implement the library (other RFCs scattered throughout)
12
+ * MIME::CompositeMediaType for a description of composite media types
13
+ * MIME::DiscreteMediaType for a description of discrete media types
14
+ * MIME::DiscreteMediaFactory for easy programming of discrete media types
15
+
16
+
17
+ == Media Type Inheritance Heirarchy
18
+
19
+ MediaType*
20
+ ^
21
+ |
22
+ |--DiscreteMediaType*
23
+ | ^
24
+ | |
25
+ | |--ApplicationMedia
26
+ | |--AudioMedia
27
+ | |--ImageMedia
28
+ | |--TextMedia
29
+ | +--VideoMedia
30
+ |
31
+ +--CompositeMediaType*
32
+ ^
33
+ |
34
+ |--MessageMedia**
35
+ | ^
36
+ | |
37
+ | |--ExternalBody**
38
+ | |--Partial**
39
+ | +--RFC822**
40
+ |
41
+ +--MultipartMedia*
42
+ ^
43
+ |
44
+ |--Alternative
45
+ |--Digest**
46
+ |--Encrypted**
47
+ |--FormData
48
+ |--Mixed
49
+ |--Parallel**
50
+ |--Related
51
+ |--Report**
52
+ +--Signed**
53
+
54
+ * Abstract Class
55
+ ** Not implemented
56
+
57
+
58
+ == MIME Message Structure
59
+
60
+
61
+ ________________ -------------------+
62
+ | | |
63
+ | RFC822 & MIME | |
64
+ | Message Headers| |
65
+ |________________| |
66
+ ________________ |
67
+ | | |
68
+ | MIME Headers | |
69
+ |~~~~~~~~~~~~~~~~| <-- MIME Entity |
70
+ | Body | (N) |
71
+ | (optional) | |--- RFC822 Message
72
+ |________________| |
73
+ ________________ |
74
+ | | |
75
+ | MIME Headers | |
76
+ |~~~~~~~~~~~~~~~~| <-- MIME Entity |
77
+ | Body | (N+1) |
78
+ | (optional) | |
79
+ |________________| |
80
+ -------------------+
81
+
82
+
83
+ Each <em>MIME Entity</em> must be a discrete (MIME::DiscreteMediaType) or
84
+ composite (MIME::CompositeMediaType) media type. Because MIME is recursive,
85
+ composite entity bodies may contain other composite or discrete entities and so
86
+ on. However, discrete entities are non-recursive and contain only non-MIME
87
+ bodies.
88
+
89
+
90
+ == Examples
91
+
92
+ <em>First things first!</em>
93
+
94
+ require 'mime'
95
+
96
+
97
+ === Instantiate a DiscreteMediaType object
98
+
99
+ Discrete media objects, such as text or video, can be created directly using a
100
+ specific discrete media *class* or indirectly via the *factory*. If the media is
101
+ file backed, like the example below, the factory will open and read the data
102
+ file and determine the MIME type for you.
103
+
104
+ file = '/tmp/data.xml'
105
+
106
+ text_media = TextMedia.new(File.read(file), 'text/xml')} # media class
107
+ text_media = DiscreteMediaFactory.create(file) # media factory
108
+
109
+ Discrete media objects can then be embedded in MIME messages as we will see in
110
+ the next example.
111
+
112
+
113
+ === Simple text/plain RFC822 email message
114
+
115
+ Create a well-formed email message with multiple recipients. The string
116
+ representation of the message (i.e. to_s) can then be sent directly via an
117
+ SMTP client.
118
+
119
+ msg = Message.new # blank message with current date and message ID headers
120
+ msg.date = (Time.now - 3600).rfc2822 # change date
121
+ msg.subject = 'This is important' # add subject
122
+ msg.headers.add('Priority', 'urgent') # add custom header
123
+
124
+ msg.body = TextMedia.new('hello, world!')
125
+ #
126
+ # The following two snippets are equivalent to the previous line.
127
+ #
128
+ # msg.body = "\r\nhello, world!"
129
+ # msg.header.add('Content-Type', 'text/plain; charset=us-ascii')
130
+ #
131
+ # --OR-- (notice the header must come first, followed by CRLFx2)
132
+ #
133
+ # msg.body = "Content-Type: text/plain; charset=us-ascii\r\n\r\nhello, world!"
134
+
135
+ msg.to = {
136
+ 'robot@example.com' => nil, # no name display
137
+ 'james@example.com' => 'James Smith',
138
+ 'clint@example.com' => 'Clint Pachl',
139
+ }
140
+ msg.from = {
141
+ 'boss@example.com' => 'Boss Man'
142
+ }
143
+
144
+ msg.to_s # ready to be sent via SMTP
145
+
146
+
147
+ === Plain text multipart/mixed message with a file attachment
148
+
149
+ The multipart/mixed content type can be used to aggregate multiple unrelated
150
+ entities, such as text and an image.
151
+
152
+ text = DiscreteMediaFactory.create('/tmp/data.txt')
153
+ image = DiscreteMediaFactory.create('/tmp/ruby.png')
154
+
155
+ mixed_msg = MultipartMedia::Mixed.new
156
+ mixed_msg.attach_entity(image)
157
+ mixed_msg.add_entity(text)
158
+ mixed_msg.to_s
159
+
160
+
161
+ === Plain text and HTML multipart/alternative MIME message
162
+
163
+ The multipart/alternative content type allows for multiple, alternatively
164
+ formatted versions of the same content, such as plain text and HTML. Clients are
165
+ then responsible for choosing the most suitable version for display.
166
+
167
+ text_msg = TextMedia.new(<<TEXT_DATA, 'text/plain')
168
+ **Hello, world!**
169
+
170
+ Ruby is cool!
171
+ TEXT_DATA
172
+
173
+ html_msg = TextMedia.new(<<HTML_DATA, 'text/html')
174
+ <html>
175
+ <body>
176
+ <h1>Hello, world!</h1>
177
+ <p>Ruby is cool!</p>
178
+ </body>
179
+ </html>
180
+ HTML_DATA
181
+
182
+ msg = MultipartMedia::Alternative.new
183
+ msg.add_entity(html_msg) # most complex representations must be added first
184
+ msg.add_entity(text_msg)
185
+ msg.to_s
186
+
187
+
188
+ === HTML multipart/related MIME email with embedded image
189
+
190
+ Sometimes it is desirable to send a document that is made up of many separate
191
+ parts. For example, an HTML page with embedded images. The multipart/related
192
+ content type aggregates all the parts and creates the means for the root entity
193
+ to reference the other entities.
194
+
195
+ Notice the _img_ tag _src_.
196
+
197
+ image = DiscreteMediaFactory.create('/tmp/ruby.png')
198
+ image.content_transfer_encoding = 'binary'
199
+
200
+ html_msg = TextMedia.new(<<EOF, 'text/html; charset=iso-8859-1')
201
+ <html>
202
+ <body>
203
+ <h1>Ruby Image</h1>
204
+ <p>
205
+ Check out this cool pic.
206
+ <img alt="ruby is cool" src="cid:#{image.content_id}">
207
+ </p>
208
+ <p>Wasn't it cool?</p>
209
+ </body>
210
+ </html>
211
+ EOF
212
+
213
+ html_msg.content_transfer_encoding = '7bit'
214
+
215
+ related_msg = MultipartMedia::Related.new
216
+ related_msg.inline_entity(image)
217
+ related_msg.add_entity(html_msg)
218
+
219
+ email_msg = Message.new(related_msg)
220
+ email_msg.to = {'joe@example.com' => 'Joe Schmo'}
221
+ email_msg.from = {'john@example.com' => 'John Doe'}
222
+ email_msg.subject = 'Ruby is cool, checkout the picture'
223
+ email_msg.to_s # ready to send HTML email with image
224
+
225
+
226
+ === HTML form with file upload using multipart/form-data encoding
227
+
228
+ This example builds a representation of an HTML form that can be POSTed to an
229
+ HTTP server. It contains a single text input and a file input.
230
+
231
+ name_field = TextMedia.new('Joe Blow')
232
+
233
+ portrait_filename = '/tmp/joe_portrait.jpg'
234
+
235
+ portrait_field = open(portrait_filename) do |f|
236
+ ImageMedia.new(f.read, 'image/jpeg') # explicit content type
237
+ end
238
+ portrait_field.content_transfer_encoding = 'binary'
239
+
240
+ form_data = MultipartMedia::FormData.new
241
+ form_data.add_entity(name_field, # TextMedia object
242
+ 'name') # field name, i.e. HTML input type=text
243
+ form_data.add_entity(portrait_field, # ImageMedia object
244
+ 'portrait', # field name, i.e. HTML input type=file
245
+ portrait_filename) # suggest filename to server
246
+ form_data.to_s # ready to POST via HTTP
247
+
248
+
249
+ === HTML form with file upload via DiscreteMediaFactory
250
+
251
+ The outcome of this example is identical to the previous one. The only semantic
252
+ difference is that the DiscreteMediaFactory class is used to instantiate the
253
+ image object.
254
+
255
+ name_field = TextMedia.new('Joe Blow')
256
+
257
+ img = '/tmp/joe_portrait.jpg'
258
+ portrait_field = DiscreteMediaFactory.create(img) # automatic content type
259
+ portrait_field.content_transfer_encoding = 'binary'
260
+
261
+ form_data = MultipartMedia::FormData.new
262
+ form_data.add_entity(name_field, 'name')
263
+ form_data.add_entity(portrait_field, 'portrait') # automatic file name
264
+ form_data.to_s
265
+
266
+
267
+ == More Examples
268
+
269
+ For many more examples, check the test class MIMETest.
270
+
271
+ == Links
272
+
273
+ Ruby Gem :: https://rubygems.org/gems/mime
274
+ Source Code :: https://bitbucket.org/pachl/mime/src
275
+ Documentation :: http://ecentryx.com/gems/mime
276
+
277
+ == History
278
+
279
+ 1. 2008-11-05, v0.1: First public release
280
+ 2. 2013-11-17, v0.2.0: Update for Ruby 1.9.3
281
+ * Update Rakefile test, package, and rdoc tasks.
282
+ * Change test suite from Test::Unit to Minitest.
283
+ * Cleanup existing and add new tests cases.
284
+ * Clarify code comments and README examples.
285
+ * Fix content type detection.
286
+
287
+ == License
288
+
289
+ ({ISC License}[http://opensource.org/licenses/ISC])
290
+
291
+ Copyright (c) 2014, Clint Pachl <pachl@ecentryx.com>
292
+
293
+ Permission to use, copy, modify, and/or distribute this software for any purpose
294
+ with or without fee is hereby granted, provided that the above copyright notice
295
+ and this permission notice appear in all copies.
296
+
297
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
298
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
299
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
300
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
301
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
302
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
303
+ THIS SOFTWARE.
data/Rakefile CHANGED
@@ -1,34 +1,18 @@
1
- require 'rake/rdoctask'
1
+ require 'rdoc/task'
2
+ require 'rubygems/package_task'
2
3
  require 'rake/testtask'
3
- require 'rake/gempackagetask'
4
4
 
5
- gem_spec = Gem::Specification.new do |s|
6
- s.name = 'mime'
7
- s.version = '0.1'
8
- s.summary = 'Multipurpose Internet Mail Extensions (MIME) Library'
9
- s.test_files = FileList['test/*.rb']
10
- s.files = FileList['README', 'Rakefile', 'lib/**/*.rb', 'test/**/*']
11
- s.author = 'Clint Pachl'
12
- s.email = 'pachl@ecentryx.com'
13
- s.homepage = 'mime.rubyforge.org'
14
- s.rubyforge_project = 'mime'
15
- s.has_rdoc = true
16
- end
17
-
18
-
19
- Rake::GemPackageTask.new(gem_spec) do |pkg|
5
+ Gem::PackageTask.new(eval File.read('mime.gemspec')) do |pkg|
20
6
  pkg.need_tar_gz = true
21
7
  end
22
8
 
23
- Rake::RDocTask.new do |rd|
24
- rd.rdoc_files.include('README', 'lib/')
25
- rd.rdoc_dir = 'doc'
26
- rd.main = 'README'
27
- rd.options << '--all' << '--inline-source'
9
+ RDoc::Task.new do |rdoc|
10
+ rdoc.main = 'README.rdoc'
11
+ rdoc.rdoc_dir = 'doc'
12
+ rdoc.rdoc_files.include('README.rdoc', 'lib/')
13
+ rdoc.options << "--all"
28
14
  end
29
15
 
30
16
  Rake::TestTask.new do |t|
31
- t.libs << 'lib'
32
- t.pattern = 'test/*_test.rb'
33
17
  t.warning = true
34
18
  end
@@ -18,7 +18,7 @@
18
18
  #
19
19
  module MIME
20
20
 
21
- VERSION = '0.1'
21
+ VERSION = '0.2.0'
22
22
 
23
23
  end
24
24
 
@@ -42,15 +42,14 @@ module MIME
42
42
  fname = file
43
43
  end
44
44
 
45
- raise UnknownContentError unless ctype
46
-
47
45
  media_obj =
48
- case ctype.match(/^(\w+)\//)[1]
46
+ case ctype.to_s[/^(\w+)\//, 1]
49
47
  when 'application'; ApplicationMedia.new(cntnt, ctype)
50
48
  when 'audio' ; AudioMedia.new(cntnt, ctype)
51
49
  when 'image' ; ImageMedia.new(cntnt, ctype)
52
50
  when 'text' ; TextMedia.new(cntnt, ctype)
53
51
  when 'video' ; VideoMedia.new(cntnt, ctype)
52
+ else raise UnknownContentError, "invalid content type: #{ctype}"
54
53
  end
55
54
 
56
55
  class << media_obj; attr_accessor :path end
@@ -6,14 +6,21 @@ module MIME
6
6
  #
7
7
  module Internet
8
8
 
9
- attr_reader :date, :from, :to
10
- attr_reader :cc, :bcc, :reply_to, :message_id
11
- attr_reader :comments, :keywords, :subject
12
-
13
-
14
- #
15
- # Required Headers
16
- #
9
+ attr_reader(
10
+ # Required Headers
11
+ :to,
12
+ :from,
13
+ :date,
14
+
15
+ # Optional Headers
16
+ :cc,
17
+ :bcc,
18
+ :reply_to,
19
+ :message_id,
20
+ :comments,
21
+ :keywords,
22
+ :subject
23
+ )
17
24
 
18
25
  def date= date
19
26
  @date = date
@@ -30,11 +37,6 @@ module MIME
30
37
  headers.add('To', @to)
31
38
  end
32
39
 
33
-
34
- #
35
- # Optional Headers
36
- #
37
-
38
40
  def cc= list
39
41
  @cc = stringify_email_list(list)
40
42
  headers.add('Cc', @cc)
@@ -6,13 +6,14 @@ module MIME
6
6
  #
7
7
  module MIME
8
8
 
9
- attr_reader :mime_version,
9
+ attr_reader(
10
+ :mime_version,
10
11
  :content_description,
11
12
  :content_disposition,
12
13
  :content_id,
13
14
  :content_transfer_encoding,
14
15
  :content_type
15
-
16
+ )
16
17
 
17
18
  #
18
19
  # Describes the content, which can be useful for non-MIME clients.
@@ -47,11 +48,11 @@ module MIME
47
48
  # The mechanism used for encoding the top-level message content.
48
49
  #
49
50
  # Common Encoding Mechanisms
50
- # 1. 7bit
51
- # 2. 8bit
52
- # 3. binary
53
- # 4. quoted-printable
54
- # 5. base64
51
+ # * 7bit
52
+ # * 8bit
53
+ # * binary
54
+ # * quoted-printable
55
+ # * base64
55
56
  #
56
57
  def content_transfer_encoding= encoding
57
58
  @content_transfer_encoding = encoding
@@ -63,11 +64,11 @@ module MIME
63
64
  # the form <em>media-type/subtype</em>.
64
65
  #
65
66
  # Common Content Types
66
- # 1. application/octet-stream
67
- # 2. audio/mpeg
68
- # 3. image/jpeg
69
- # 4. text/plain
70
- # 5. video/mpeg
67
+ # * application/octet-stream
68
+ # * audio/mpeg
69
+ # * image/jpeg
70
+ # * text/plain
71
+ # * video/mpeg
71
72
  #
72
73
  def content_type= type
73
74
  @content_type = type
@@ -88,11 +89,11 @@ module MIME
88
89
  # +type+ is the disposition type of either "inline" or "attachment".
89
90
  # +params+ is a Hash with zero or more of the following keys:
90
91
  #
91
- # * filename => name of file
92
- # * creation-date => RFC2822 data-time
93
- # * modification-date => RFC2822 data-time
94
- # * read-date => RFC2822 data-time
95
- # * size => file size in octets
92
+ # +filename+ :: name of file
93
+ # +creation-date+ :: RFC2822 data-time
94
+ # +modification-date+ :: RFC2822 data-time
95
+ # +read-date+ :: RFC2822 data-time
96
+ # +size+ :: file size in octets
96
97
  #
97
98
  # The values for the *-date keys may use Time::rfc2822.
98
99
  #
@@ -113,6 +114,5 @@ module MIME
113
114
  end
114
115
 
115
116
  end
116
-
117
117
  end
118
118
  end