mime 0.3.0 → 0.4.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.
- 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
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-04-18 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'A library for building RFC compliant Multipurpose Internet Mail Extensions
|
15
15
|
|
@@ -32,14 +32,14 @@ files:
|
|
32
32
|
- lib/mime/headers/internet.rb
|
33
33
|
- lib/mime/discrete_media_factory.rb
|
34
34
|
- lib/mime/error.rb
|
35
|
-
- lib/mime/header_container.rb
|
36
35
|
- lib/mime/parser.rb
|
37
36
|
- lib/mime/content_types.rb
|
38
|
-
- lib/mime/
|
39
|
-
- lib/mime/
|
40
|
-
- lib/mime/discrete_media_type.rb
|
41
|
-
- lib/mime/message.rb
|
37
|
+
- lib/mime/media.rb
|
38
|
+
- lib/mime/composite_media.rb
|
42
39
|
- lib/mime/content_formats/text_flowed.rb
|
40
|
+
- lib/mime/header.rb
|
41
|
+
- lib/mime/mail.rb
|
42
|
+
- lib/mime/discrete_media.rb
|
43
43
|
- lib/mime.rb
|
44
44
|
- test/scaffold/application.msg
|
45
45
|
- test/scaffold/unknown.yyy
|
@@ -47,23 +47,24 @@ files:
|
|
47
47
|
- test/scaffold/mini.mov
|
48
48
|
- test/scaffold/image.jpg
|
49
49
|
- test/scaffold/book.pdf
|
50
|
-
- test/scaffold/plain_text_email.msg
|
51
50
|
- test/scaffold/song.mp3
|
52
51
|
- test/scaffold/ruby.png
|
53
52
|
- test/scaffold/multipart_alternative.msg
|
54
|
-
- test/scaffold/multipart_alternative_related.msg
|
55
|
-
- test/scaffold/multipart_related.msg
|
56
53
|
- test/scaffold/multipart_mixed_inline_and_attachment2.msg
|
57
54
|
- test/scaffold/image.msg
|
58
55
|
- test/scaffold/multipart_form_data_file_and_text.msg
|
59
56
|
- test/scaffold/data.xml
|
60
|
-
- test/scaffold/
|
57
|
+
- test/scaffold/rfc822_composite.msg
|
61
58
|
- test/scaffold/text.msg
|
62
59
|
- test/scaffold/main.css
|
63
|
-
- test/scaffold/
|
60
|
+
- test/scaffold/multipart_alternative_related.msg
|
64
61
|
- test/scaffold/multipart_form_data_text.msg
|
65
62
|
- test/scaffold/audio.msg
|
63
|
+
- test/scaffold/multipart_related.msg
|
64
|
+
- test/scaffold/rfc822_discrete.msg
|
66
65
|
- test/scaffold/data.htm
|
66
|
+
- test/scaffold/multipart_form_data_mixed.msg
|
67
|
+
- test/scaffold/multipart_mixed_inline_and_attachment.msg
|
67
68
|
- test/test_mime.rb-try
|
68
69
|
- test/test_mime.rb
|
69
70
|
- test/test_text_flowed.rb
|
@@ -1,169 +0,0 @@
|
|
1
|
-
module MIME
|
2
|
-
|
3
|
-
#
|
4
|
-
# Composite entities are handled using MIME mechanisms. A MIME processor must
|
5
|
-
# handle the body directly. A CompositeMediaType object is composed of one or
|
6
|
-
# more CompositeMediaType and/or DiscreteMediaType objects.
|
7
|
-
#
|
8
|
-
# This class is abstract.
|
9
|
-
#
|
10
|
-
class CompositeMediaType < MediaType
|
11
|
-
|
12
|
-
def initialize content_type
|
13
|
-
AbstractClassError.no_instantiation(self, CompositeMediaType)
|
14
|
-
|
15
|
-
super(nil, content_type)
|
16
|
-
@entities = Array.new
|
17
|
-
end
|
18
|
-
|
19
|
-
#
|
20
|
-
# Add a MediaType object to the message.
|
21
|
-
#
|
22
|
-
def add_entity entity
|
23
|
-
raise Error.new('can only add MediaType objects') unless entity.is_a? MediaType
|
24
|
-
@entities.unshift(entity)
|
25
|
-
end
|
26
|
-
|
27
|
-
#
|
28
|
-
# Attach a MediaType object to the message.
|
29
|
-
#
|
30
|
-
def attach_entity entity, params = {}
|
31
|
-
entity.set_content_disposition('attachment', params)
|
32
|
-
add_entity(entity)
|
33
|
-
end
|
34
|
-
|
35
|
-
#
|
36
|
-
# Inline a MediaType object in the message.
|
37
|
-
#
|
38
|
-
def inline_entity entity, params = {}
|
39
|
-
entity.set_content_disposition('inline', params)
|
40
|
-
add_entity(entity)
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
#
|
46
|
-
# MessageMedia is intended to encapsulate another message. In particular,
|
47
|
-
# the <em>message/rfc822</em> content type is used to encapsulate RFC 822
|
48
|
-
# messages.
|
49
|
-
#
|
50
|
-
# TODO Implement
|
51
|
-
#
|
52
|
-
class MessageMedia < CompositeMediaType
|
53
|
-
end
|
54
|
-
|
55
|
-
#
|
56
|
-
# The abstract base class for all multipart message subtypes. The entities of
|
57
|
-
# a multipart message are delimited by a unique boundary.
|
58
|
-
#
|
59
|
-
class MultipartMedia < CompositeMediaType
|
60
|
-
|
61
|
-
def initialize content_type
|
62
|
-
AbstractClassError.no_instantiation(self, MultipartMedia)
|
63
|
-
super
|
64
|
-
end
|
65
|
-
|
66
|
-
#
|
67
|
-
# The boundary used to separate the message entities.
|
68
|
-
#
|
69
|
-
def boundary
|
70
|
-
@boundary ||= "Boundary_#{unique_id}"
|
71
|
-
end
|
72
|
-
|
73
|
-
#
|
74
|
-
# Return the multipart representation of the body.
|
75
|
-
#
|
76
|
-
def body
|
77
|
-
all_entities = @entities.join("\r\n--#{boundary}\r\n")
|
78
|
-
"--#{boundary}\r\n#{all_entities}\r\n--#{boundary}--\r\n"
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
#
|
84
|
-
# The Alternative subtype indicates that each contained entity is an
|
85
|
-
# alternatively formatted version of the same content. The most complex
|
86
|
-
# version should be added to the message first, i.e. it will be sequentially
|
87
|
-
# last in the message.
|
88
|
-
#
|
89
|
-
class MultipartMedia::Alternative < MultipartMedia
|
90
|
-
|
91
|
-
#
|
92
|
-
# Returns a MultipartMedia::Alternative object with a content type of
|
93
|
-
# multipart/alternative.
|
94
|
-
#
|
95
|
-
def initialize
|
96
|
-
super("multipart/alternative; boundary=#{boundary}")
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
#
|
102
|
-
# The FormData subtype expresses values for HTML form data submissions.
|
103
|
-
# ---
|
104
|
-
# RFCs consulted during implementation:
|
105
|
-
#
|
106
|
-
# * RFC-1867 Form-based File Upload in HTML
|
107
|
-
# * RFC-2388 Returning Values from Forms: multipart/form-data
|
108
|
-
#
|
109
|
-
class MultipartMedia::FormData < MultipartMedia
|
110
|
-
|
111
|
-
#
|
112
|
-
# Returns a MultipartMedia::FormData object with a content type of
|
113
|
-
# multipart/form-data.
|
114
|
-
#
|
115
|
-
def initialize
|
116
|
-
super("multipart/form-data; boundary=#{boundary}")
|
117
|
-
end
|
118
|
-
|
119
|
-
#
|
120
|
-
# Add the MediaType object, +entity+, to the FormData object. +name+ is
|
121
|
-
# typically an HTML input tag variable name. If the input tag is of type
|
122
|
-
# _file_, then +filename+ must be specified to indicate a file upload.
|
123
|
-
#
|
124
|
-
def add_entity entity, name, filename = nil
|
125
|
-
entity.set_content_disposition('form-data', 'name' => name, 'filename' => filename)
|
126
|
-
super(entity)
|
127
|
-
end
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
#
|
132
|
-
# The Mixed subtype aggregates contextually independent entities.
|
133
|
-
#
|
134
|
-
class MultipartMedia::Mixed < MultipartMedia
|
135
|
-
|
136
|
-
#
|
137
|
-
# Returns a MultipartMedia::Mixed object with a content type of
|
138
|
-
# multipart/mixed.
|
139
|
-
#
|
140
|
-
def initialize
|
141
|
-
super("multipart/mixed; boundary=#{boundary}")
|
142
|
-
end
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
#
|
147
|
-
# The Related subtype aggregates multiple related entities. The message
|
148
|
-
# consists of a root (the first entity) which references subsequent inline
|
149
|
-
# entities. Message entities should be referenced by their Content-ID header.
|
150
|
-
# The syntax of a reference is unspecified and is instead dictated by the
|
151
|
-
# encoding or protocol used in the entity.
|
152
|
-
# ---
|
153
|
-
# RFC consulted during implementation:
|
154
|
-
#
|
155
|
-
# * RFC-2387 The MIME Multipart/Related Content-type
|
156
|
-
#
|
157
|
-
class MultipartMedia::Related < MultipartMedia
|
158
|
-
|
159
|
-
#
|
160
|
-
# Returns a MultipartMedia::Related object with a content type of
|
161
|
-
# multipart/related.
|
162
|
-
#
|
163
|
-
def initialize
|
164
|
-
super("multipart/related; boundary=#{boundary}")
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
module MIME
|
2
|
-
|
3
|
-
#
|
4
|
-
# Discrete media types must be handled by non-MIME mechanisms; they are
|
5
|
-
# opaque to MIME processors. Therefore, the body of a DiscreteMediaType
|
6
|
-
# object does not need further MIME processing.
|
7
|
-
#
|
8
|
-
# This class is abstract.
|
9
|
-
#
|
10
|
-
class DiscreteMediaType < MediaType
|
11
|
-
def initialize(body, media_subtype, content_params)
|
12
|
-
AbstractClassError.no_instantiation(self, DiscreteMediaType)
|
13
|
-
super(body, "#{@media_type}/#{media_subtype}", content_params)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
#
|
18
|
-
# ApplicationMedia is intended for discrete data that is to be processed by
|
19
|
-
# some type of application program. The body contains information which must
|
20
|
-
# be processed by an application before it is viewable or usable by a user.
|
21
|
-
#
|
22
|
-
# ApplicationMedia is the catch all class. If your content cannot be
|
23
|
-
# identified as another DiscreteMediaType, then it is application media.
|
24
|
-
#
|
25
|
-
class ApplicationMedia < DiscreteMediaType
|
26
|
-
def initialize(body, subtype = 'octet-stream', params = {})
|
27
|
-
@media_type = 'application'
|
28
|
-
super
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
#
|
33
|
-
# AudioMedia is intended for discrete audio content. The +subtype+ indicates
|
34
|
-
# the specific audio format, such as *mpeg* or *midi*.
|
35
|
-
#
|
36
|
-
class AudioMedia < DiscreteMediaType
|
37
|
-
def initialize(body, subtype = 'basic', params = {})
|
38
|
-
@media_type = 'audio'
|
39
|
-
super
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
#
|
44
|
-
# ImageMedia is intented for discrete image content. The +subtype+ indicates
|
45
|
-
# the specific image format, such as *jpeg* or *gif*.
|
46
|
-
#
|
47
|
-
class ImageMedia < DiscreteMediaType
|
48
|
-
def initialize(body, subtype = 'jpeg', params = {})
|
49
|
-
@media_type = 'image'
|
50
|
-
super
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
#
|
55
|
-
# TextMedia is intended for content which is principally textual in form. The
|
56
|
-
# +subtype+ indicates the specific text type, such as *plain* or *html*.
|
57
|
-
#
|
58
|
-
class TextMedia < DiscreteMediaType
|
59
|
-
def initialize(body, subtype = 'plain', params = {})
|
60
|
-
@media_type = 'text'
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
#
|
66
|
-
# VideoMedia is intended for discrete video content. The content +subtype+
|
67
|
-
# indicates the specific video format. The RFC describes video media as
|
68
|
-
# content that contains a time-varying-picture image, possibly with color and
|
69
|
-
# coordinated sound.
|
70
|
-
#
|
71
|
-
class VideoMedia < DiscreteMediaType
|
72
|
-
def initialize(body, subtype = 'mpeg', params = {})
|
73
|
-
@media_type = 'video'
|
74
|
-
super
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module MIME
|
2
|
-
|
3
|
-
#
|
4
|
-
# Storage for RFC 2822 headers.
|
5
|
-
#
|
6
|
-
class HeaderContainer
|
7
|
-
|
8
|
-
#
|
9
|
-
# Return a new header container object.
|
10
|
-
#
|
11
|
-
def initialize
|
12
|
-
@headers = Hash.new
|
13
|
-
end
|
14
|
-
|
15
|
-
#
|
16
|
-
# Convert all headers to their string equivalents and join them using the
|
17
|
-
# RFC 2822 CRLF line separator.
|
18
|
-
#
|
19
|
-
def to_s
|
20
|
-
@headers.to_a.map {|kv| kv.join(": ")}.join("\r\n")
|
21
|
-
end
|
22
|
-
|
23
|
-
#
|
24
|
-
# Add the +name+/+value+ pair to the header container.
|
25
|
-
#
|
26
|
-
def add name, value
|
27
|
-
@headers.store(name, value)
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
data/lib/mime/media_type.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'mime/error'
|
2
|
-
require 'mime/header_container'
|
3
|
-
require 'mime/headers/mime'
|
4
|
-
|
5
|
-
module MIME
|
6
|
-
|
7
|
-
#
|
8
|
-
# Abstract top-level media type class.
|
9
|
-
#
|
10
|
-
class MediaType
|
11
|
-
|
12
|
-
include Headers::MIME
|
13
|
-
|
14
|
-
attr_reader :headers
|
15
|
-
attr_accessor :body
|
16
|
-
protected :body, :body=
|
17
|
-
|
18
|
-
def initialize body, content_type, content_params = {}
|
19
|
-
AbstractClassError.no_instantiation(self, MediaType)
|
20
|
-
|
21
|
-
@headers = HeaderContainer.new
|
22
|
-
@body = body
|
23
|
-
self.content_id = unique_id
|
24
|
-
self.content_type =
|
25
|
-
if content_params.empty?
|
26
|
-
content_type
|
27
|
-
else
|
28
|
-
params = content_params.to_a.map {|kv| kv.join('=')}.join('; ')
|
29
|
-
"#{content_type}; #{params}"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
#
|
34
|
-
# Transform the the MediaType object into a MIME message.
|
35
|
-
#
|
36
|
-
def to_s
|
37
|
-
"#{headers}\r\n\r\n#{body}"
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
#
|
43
|
-
# Generate a globally unique identifier for use in boundaries and IDs.
|
44
|
-
#
|
45
|
-
def unique_id
|
46
|
-
"#{object_id.abs}#{rand}"
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
data/lib/mime/message.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'time'
|
2
|
-
require 'mime/headers/internet'
|
3
|
-
require 'mime/headers/mime'
|
4
|
-
|
5
|
-
|
6
|
-
module MIME
|
7
|
-
|
8
|
-
#
|
9
|
-
# Construct textual messages using the RFC 2822 Internet message format.
|
10
|
-
#
|
11
|
-
class Message
|
12
|
-
|
13
|
-
include Headers::Internet
|
14
|
-
include Headers::MIME
|
15
|
-
|
16
|
-
# HeaderContainer access
|
17
|
-
attr_reader :headers
|
18
|
-
|
19
|
-
attr_accessor :body
|
20
|
-
|
21
|
-
#
|
22
|
-
# Return a Message object with body optionally set to +body+.
|
23
|
-
#
|
24
|
-
def initialize body = nil
|
25
|
-
@body = body
|
26
|
-
@headers = HeaderContainer.new
|
27
|
-
self.date = Time.now.rfc2822
|
28
|
-
self.message_id = "#{rand(1E9)}@#{__id__.abs}"
|
29
|
-
self.mime_version = "1.0 (Ruby MIME v#{VERSION})"
|
30
|
-
end
|
31
|
-
|
32
|
-
#
|
33
|
-
# Return the Internet message formatted representation of the instance.
|
34
|
-
#
|
35
|
-
def to_s
|
36
|
-
#--
|
37
|
-
# In an RFC 2822 message, the header and body sections must be separated
|
38
|
-
# by two line breaks (CRLF). One line break is deliberately missing,
|
39
|
-
# allowing a body supplier to append headers to the top-level message
|
40
|
-
# header section. Consequently, the body supplier is responsible for
|
41
|
-
# handling the body-header separation. Furthermore, if the +body+ is
|
42
|
-
# empty, the header section will be properly terminated, creating a
|
43
|
-
# standards compliant message.
|
44
|
-
#++
|
45
|
-
|
46
|
-
# FIXME writing directly to body VS setting body with a MediaType gives an
|
47
|
-
# imbalance of "\r\n". Setting body directly with string requires a CRLF
|
48
|
-
# prepended, but not the case for TextMedia for instance. So where do we
|
49
|
-
# handle this? Perhaps HeaderContainer#to_s should append a CRLF, or
|
50
|
-
# self#body should detect String and automatically prepend CRLF??? Then we
|
51
|
-
# remove the CRLF here between the headers and body.
|
52
|
-
#
|
53
|
-
# Also, TextMedia adds the Content-ID and Content-Type headers, which we
|
54
|
-
# don't get if body is set directly with String. Does this matter?
|
55
|
-
|
56
|
-
"#{headers}\r\n#{body}\r\n"
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|