mime 0.1 → 0.2.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 +303 -0
- data/Rakefile +8 -24
- data/lib/mime.rb +1 -1
- data/lib/mime/discrete_media_factory.rb +2 -3
- data/lib/mime/headers/internet.rb +15 -13
- data/lib/mime/headers/mime.rb +18 -18
- data/lib/mime/message.rb +10 -0
- data/mime.gemspec +18 -0
- data/test/scaffold/application.msg +4 -4
- data/test/scaffold/audio.msg +3 -3
- data/test/scaffold/{index.html → data.htm} +0 -0
- data/test/scaffold/image.msg +0 -0
- data/test/scaffold/multipart_alternative.msg +10 -12
- 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 +31 -26
- 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/plain_text_email.msg +9 -7
- data/test/scaffold/text.msg +2 -2
- data/test/scaffold/video.msg +4 -4
- data/test/test_mime.rb +417 -0
- data/test/test_mime.rb-try +616 -0
- metadata +53 -51
- data/README +0 -256
- data/test/mime_test.rb +0 -386
- data/test/scaffold/multipart_form_data_file.msg +0 -0
data/lib/mime/message.rb
CHANGED
@@ -43,6 +43,16 @@ module MIME
|
|
43
43
|
# standards compliant message.
|
44
44
|
#++
|
45
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
|
+
|
46
56
|
"#{headers}\r\n#{body}\r\n"
|
47
57
|
end
|
48
58
|
|
data/mime.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
version = File.read('./lib/mime.rb')[/VERSION = '(.*)'/, 1]
|
2
|
+
|
3
|
+
Gem::Specification.new('mime', version) do |s|
|
4
|
+
s.author = 'Clint Pachl'
|
5
|
+
s.email = 'pachl@ecentryx.com'
|
6
|
+
s.homepage = 'http://ecentryx.com/gems/mime'
|
7
|
+
s.license = 'ISC'
|
8
|
+
s.summary = 'Multipurpose Internet Mail Extensions (MIME) Library'
|
9
|
+
s.description = <<-EOF
|
10
|
+
A library for building RFC compliant Multipurpose Internet Mail Extensions
|
11
|
+
(MIME) messages. It can be used to construct standardized MIME messages for use
|
12
|
+
in client/server communications, such as Internet mail or HTTP
|
13
|
+
multipart/form-data transactions.
|
14
|
+
EOF
|
15
|
+
s.files = Dir['README.rdoc', 'Rakefile', 'mime.gemspec',
|
16
|
+
'lib/**/*.rb', 'test/**/*']
|
17
|
+
s.test_files = Dir['test/*.rb']
|
18
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
Message-ID: <350015069@1033007998>
|
2
1
|
Date: Wed, 29 Oct 2008 22:02:00 -0700
|
3
|
-
|
2
|
+
Message-ID: <350015069@1033007998>
|
3
|
+
MIME-Version: 1.0 (Ruby MIME v0.2.0)
|
4
4
|
Content-ID: <10330079680.971971651113797>
|
5
|
-
Content-Transfer-Encoding: binary
|
6
5
|
Content-Type: application/octet-stream
|
6
|
+
Content-Transfer-Encoding: binary
|
7
7
|
|
8
|
-
|
8
|
+
0110000101110101011001000110100101101111
|
data/test/scaffold/audio.msg
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
Message-ID: <738362674@978907908>
|
2
1
|
Date: Wed, 29 Oct 2008 22:06:46 -0700
|
3
|
-
|
2
|
+
Message-ID: <738362674@978907908>
|
3
|
+
MIME-Version: 1.0 (Ruby MIME v0.2.0)
|
4
4
|
Content-ID: <9789078880.11546613041526>
|
5
|
-
Content-Transfer-Encoding: binary
|
6
5
|
Content-Type: audio/midi
|
6
|
+
Content-Transfer-Encoding: binary
|
7
7
|
|
8
8
|
0110000101110101011001000110100101101111
|
File without changes
|
data/test/scaffold/image.msg
CHANGED
Binary file
|
@@ -1,17 +1,15 @@
|
|
1
|
-
Content-ID: <
|
2
|
-
Content-Type: multipart/alternative; boundary=
|
1
|
+
Content-ID: <154873488180000.3045551269054866>
|
2
|
+
Content-Type: multipart/alternative; boundary=Boundary_154873488180000.5284747660488839
|
3
3
|
|
4
|
-
--
|
5
|
-
Content-ID: <
|
6
|
-
Content-Type: text/
|
4
|
+
--Boundary_154873488180000.5284747660488839
|
5
|
+
Content-ID: <154873488189000.6392244491224042>
|
6
|
+
Content-Type: text/enhanced; charset=us-ascii
|
7
7
|
|
8
8
|
*Header*
|
9
|
-
message
|
10
|
-
|
11
|
-
|
12
|
-
Content-ID: <9825630480.371191475053649>
|
9
|
+
message
|
10
|
+
--Boundary_154873488180000.5284747660488839
|
11
|
+
Content-ID: <154873488185800.3424043482513621>
|
13
12
|
Content-Type: text/html; charset=iso-8859-1
|
14
13
|
|
15
|
-
<html><body><h1>Header</h1><p>message</p></body></html>
|
16
|
-
|
17
|
-
--Boundary_9825632980.209951487532625--
|
14
|
+
<html><body><h1>Header</h1><p>message</p></body></html>
|
15
|
+
--Boundary_154873488180000.5284747660488839--
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,22 +1,33 @@
|
|
1
|
-
Content-ID: <
|
2
|
-
Content-Type: multipart/form-data; boundary=
|
1
|
+
Content-ID: <88135231713600.7894413004791365>
|
2
|
+
Content-Type: multipart/form-data; boundary=Boundary_88135231713600.3996846921776409
|
3
3
|
|
4
|
-
--
|
5
|
-
Content-ID: <
|
6
|
-
Content-
|
7
|
-
Content-
|
4
|
+
--Boundary_88135231713600.3996846921776409
|
5
|
+
Content-ID: <88135231717000.5720607746781624>
|
6
|
+
Content-Type: text/xml
|
7
|
+
Content-Disposition: form-data; name="xml"
|
8
8
|
|
9
|
-
<
|
10
|
-
<
|
11
|
-
<
|
12
|
-
<
|
13
|
-
|
14
|
-
|
9
|
+
<media_types abstract="true">
|
10
|
+
<discrete_media_types abstract="true">
|
11
|
+
<application_media></application_media>
|
12
|
+
<audio_media></audio_media>
|
13
|
+
<image_media></image_media>
|
14
|
+
<text_media></text_media>
|
15
|
+
<video_media></video_media>
|
16
|
+
</discrete_media_types>
|
17
|
+
<composite_media_types abstract="true">
|
18
|
+
<multipart_media abstract="true">
|
19
|
+
<form_data></form_data>
|
20
|
+
<alternative></alternative>
|
21
|
+
</multipart>
|
22
|
+
<message_media>
|
23
|
+
</message_media>
|
24
|
+
</composite>
|
25
|
+
</media_types>
|
15
26
|
|
16
|
-
--
|
17
|
-
Content-ID: <
|
18
|
-
Content-Disposition: form-data; name="txt3"
|
27
|
+
--Boundary_88135231713600.3996846921776409
|
28
|
+
Content-ID: <88135231721400.4415016053712104>
|
19
29
|
Content-Type: text/html
|
30
|
+
Content-Disposition: form-data; name="htm"
|
20
31
|
|
21
32
|
<html>
|
22
33
|
<body>
|
@@ -25,16 +36,10 @@ Content-Type: text/html
|
|
25
36
|
</body>
|
26
37
|
</html>
|
27
38
|
|
28
|
-
--
|
29
|
-
Content-ID: <
|
30
|
-
Content-Disposition: form-data; name="txt2"
|
31
|
-
Content-Type: text/enhanced
|
32
|
-
|
33
|
-
this is t2
|
34
|
-
--Boundary_10552388980.858426568801724
|
35
|
-
Content-ID: <10552385480.358570542235564>
|
36
|
-
Content-Disposition: form-data; name="txt1"
|
39
|
+
--Boundary_88135231713600.3996846921776409
|
40
|
+
Content-ID: <88135231725400.11623307734175536>
|
37
41
|
Content-Type: text/plain; charset=us-ascii
|
42
|
+
Content-Disposition: form-data; name="txt"
|
38
43
|
|
39
|
-
|
40
|
-
--
|
44
|
+
text body
|
45
|
+
--Boundary_88135231713600.3996846921776409--
|
Binary file
|
Binary file
|
Binary file
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
Date: Tue, 17 Dec 2013 04:54:56 -0700
|
2
|
+
Message-ID: <776432154@10297424291820>
|
3
|
+
MIME-Version: 1.0 (Ruby MIME v0.0.0)
|
4
|
+
To: John <john@example.com>, paul@example.com, Mary <mary@example.com>
|
5
|
+
Cc: boss@example.com <Head Honcho>
|
6
|
+
From: jane@example.com
|
2
7
|
Subject: This is an important email
|
3
|
-
|
4
|
-
|
5
|
-
To: Mary <person3@domain.com>, person2@domain.com, Harry <person1@domain.com>
|
6
|
-
Date: Mon, 27 Oct 2008 07:08:50 -0700
|
7
|
-
MIME-Version: 1.0 (Ruby MIME v0.1)
|
8
|
+
Content-ID: <102975158103800.7998663631497803>
|
9
|
+
Content-Type: text/plain; charset=us-ascii
|
8
10
|
|
9
|
-
|
11
|
+
Hello, world!
|
data/test/scaffold/text.msg
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
Message-ID: <357114433@1021851988>
|
2
1
|
Date: Wed, 29 Oct 2008 14:55:34 -0700
|
3
|
-
|
2
|
+
Message-ID: <357114433@1021851988>
|
3
|
+
MIME-Version: 1.0 (Ruby MIME v0.1.0)
|
4
4
|
Content-ID: <10218519780.0838656876757202>
|
5
5
|
Content-Type: text/plain; charset=us-ascii
|
6
6
|
|
data/test/scaffold/video.msg
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
Message-ID: <740518531@1061166128>
|
2
1
|
Date: Wed, 29 Oct 2008 21:56:48 -0700
|
3
|
-
|
2
|
+
Message-ID: <740518531@1061166128>
|
3
|
+
MIME-Version: 1.0 (Ruby MIME v0.1.0)
|
4
4
|
Content-ID: <10611659780.38729886896997>
|
5
|
-
Content-Transfer-Encoding: binary
|
6
5
|
Content-Type: video/mpeg
|
6
|
+
Content-Transfer-Encoding: binary
|
7
7
|
|
8
|
-
|
8
|
+
0110000101110101011001000110100101101111
|
data/test/test_mime.rb
ADDED
@@ -0,0 +1,417 @@
|
|
1
|
+
gem 'minitest' # minitest in 1.9 stdlib is crufty
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'mime'
|
4
|
+
|
5
|
+
Encoding.default_external = 'ASCII-8BIT' # may be able to remove in 2.0
|
6
|
+
|
7
|
+
|
8
|
+
class MIMETest < Minitest::Test
|
9
|
+
|
10
|
+
CRLF = "\r\n"
|
11
|
+
BINARY_DATA = '0110000101110101011001000110100101101111'
|
12
|
+
|
13
|
+
|
14
|
+
### DISCRETE MESSAGE CONSTRUCTION (SINGLE ENTITY) ###
|
15
|
+
|
16
|
+
def test_make_top_level_rfc2822_message
|
17
|
+
msg = MIME::Message.new
|
18
|
+
msg.body = "\r\nmessage body"
|
19
|
+
headers, body = msg.to_s.split(CRLF*2)
|
20
|
+
|
21
|
+
assert_match(/^Message-ID: <\d+@\d+>/, headers)
|
22
|
+
assert_match(/^Date: ..., \d{1,2} ... \d{4} \d\d:\d\d:\d\d -\d{4}/, headers)
|
23
|
+
assert_match(/^MIME-Version: 1.0 \(Ruby MIME v\d\.\d\.\d\)/, headers)
|
24
|
+
assert_equal 3, headers.split(CRLF).count
|
25
|
+
assert_equal "message body", body.chomp
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_make_audio_message
|
29
|
+
audio_media = MIME::AudioMedia.new(BINARY_DATA, 'audio/midi')
|
30
|
+
audio_media.content_transfer_encoding = 'binary'
|
31
|
+
assert_equal_mime_msg 'audio.msg', MIME::Message.new(audio_media)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_make_application_message
|
35
|
+
app_media = MIME::ApplicationMedia.new(BINARY_DATA)
|
36
|
+
app_media.content_transfer_encoding = 'binary'
|
37
|
+
assert_equal_mime_msg 'application.msg', MIME::Message.new(app_media)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_make_image_message
|
41
|
+
image = IO.read(sd('image.jpg'))
|
42
|
+
image_media = MIME::ImageMedia.new(image)
|
43
|
+
image_media.content_type = 'image/jpeg'
|
44
|
+
image_media.content_transfer_encoding = 'binary'
|
45
|
+
assert_equal_mime_msg 'image.msg', MIME::Message.new(image_media)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_make_text_message
|
49
|
+
body = MIME::TextMedia.new('a plain text message')
|
50
|
+
assert_equal_mime_msg 'text.msg', MIME::Message.new(body)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_make_video_message
|
54
|
+
video_media = MIME::VideoMedia.new(BINARY_DATA)
|
55
|
+
video_media.content_type = 'video/mpeg'
|
56
|
+
video_media.content_transfer_encoding = 'binary'
|
57
|
+
assert_equal_mime_msg 'video.msg', MIME::Message.new(video_media)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_construction_of_plain_text_email_message
|
61
|
+
email = MIME::Message.new
|
62
|
+
email.to = {
|
63
|
+
'john@example.com' => 'John',
|
64
|
+
'paul@example.com' => nil,
|
65
|
+
'mary@example.com' => 'Mary'
|
66
|
+
}
|
67
|
+
email.cc = {'Head Honcho' => 'boss@example.com'}
|
68
|
+
email.from = {'jane@example.com' => nil}
|
69
|
+
email.subject = 'This is an important email'
|
70
|
+
email.body = MIME::TextMedia.new('Hello, world!')
|
71
|
+
assert_equal_mime_msg 'plain_text_email.msg', email
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
### COMPOSITE MESSAGE CONSTRUCTION (MULTIPLE ENTITIES) ###
|
76
|
+
|
77
|
+
def test_multipart_form_data_with_text_entities
|
78
|
+
txt_data = 'text body'
|
79
|
+
htm_data = IO.read(sd('data.htm'))
|
80
|
+
xml_data = IO.read(sd('data.xml'))
|
81
|
+
|
82
|
+
txt = MIME::TextMedia.new(txt_data)
|
83
|
+
htm = MIME::TextMedia.new(htm_data, 'text/html')
|
84
|
+
xml = MIME::TextMedia.new(xml_data, 'text/xml')
|
85
|
+
|
86
|
+
form = MIME::MultipartMedia::FormData.new
|
87
|
+
form.add_entity txt, 'txt'
|
88
|
+
form.add_entity htm, 'htm'
|
89
|
+
form.add_entity xml, 'xml'
|
90
|
+
assert_equal_mime_msg 'multipart_form_data_text.msg', form
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_multipart_form_data_with_text_and_file_entities
|
94
|
+
img1_filename = 'image.jpg'
|
95
|
+
img2_filename = 'ruby.png'
|
96
|
+
img1_data = IO.read(sd(img1_filename))
|
97
|
+
img2_data = IO.read(sd(img2_filename))
|
98
|
+
img1 = MIME::ImageMedia.new(img1_data, 'image/jpeg')
|
99
|
+
img2 = MIME::ImageMedia.new(img2_data, 'image/png')
|
100
|
+
img1.content_transfer_encoding = '8bit'
|
101
|
+
img2.content_transfer_encoding = '8bit'
|
102
|
+
|
103
|
+
desc_data = 'This is plain text description of images.'
|
104
|
+
desc = MIME::TextMedia.new(desc_data)
|
105
|
+
|
106
|
+
form = MIME::MultipartMedia::FormData.new
|
107
|
+
form.add_entity desc, 'description'
|
108
|
+
form.add_entity img2, 'image_2', img2_filename
|
109
|
+
form.add_entity img1, 'image_1', img1_filename
|
110
|
+
assert_equal_mime_msg 'multipart_form_data_file_and_text.msg', form
|
111
|
+
end
|
112
|
+
|
113
|
+
# Similar to example 6 in RFC1867.
|
114
|
+
def test_multipart_form_data_with_mixed_entity
|
115
|
+
txt = MIME::TextMedia.new('Joe Blow')
|
116
|
+
img1 = MIME::DiscreteMediaFactory.create(sd('image.jpg'))
|
117
|
+
img2 = MIME::DiscreteMediaFactory.create(sd('ruby.png'))
|
118
|
+
|
119
|
+
mixed_msg = MIME::MultipartMedia::Mixed.new
|
120
|
+
mixed_msg.attach_entity(img2)
|
121
|
+
mixed_msg.attach_entity(img1)
|
122
|
+
|
123
|
+
form = MIME::MultipartMedia::FormData.new
|
124
|
+
form.add_entity(mixed_msg, 'pics')
|
125
|
+
form.add_entity(txt, 'field1')
|
126
|
+
|
127
|
+
assert_equal_mime_msg 'multipart_form_data_mixed.msg', form
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_multipart_alternative_message
|
131
|
+
txt_data = "*Header*\nmessage"
|
132
|
+
htm_data = "<html><body><h1>Header</h1><p>message</p></body></html>"
|
133
|
+
txt_msg = MIME::TextMedia.new(txt_data)
|
134
|
+
htm_msg = MIME::TextMedia.new(htm_data)
|
135
|
+
|
136
|
+
txt_msg.content_type = ('text/enhanced; charset=us-ascii')
|
137
|
+
htm_msg.content_type = ('text/html; charset=iso-8859-1')
|
138
|
+
|
139
|
+
msg = MIME::MultipartMedia::Alternative.new
|
140
|
+
msg.add_entity htm_msg
|
141
|
+
msg.add_entity txt_msg
|
142
|
+
assert_equal_mime_msg 'multipart_alternative.msg', msg
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_multipart_alternative_with_related_html_entity
|
146
|
+
img = MIME::DiscreteMediaFactory.create(sd('ruby.png'))
|
147
|
+
img.content_transfer_encoding = 'binary'
|
148
|
+
|
149
|
+
html_msg = MIME::TextMedia.new(<<EOF, 'text/html; charset=iso-8859-1')
|
150
|
+
<html>
|
151
|
+
<body>
|
152
|
+
<h1>HTML multipart/alternative message</h1>
|
153
|
+
<p>txt before pix</p>
|
154
|
+
<img alt="cool ruby" src="cid:#{img.content_id}"/>
|
155
|
+
<p>txt after pix</p>
|
156
|
+
</body>
|
157
|
+
</html>
|
158
|
+
EOF
|
159
|
+
html_msg.content_transfer_encoding = '7bit'
|
160
|
+
|
161
|
+
text_msg = MIME::TextMedia.new(<<EOF)
|
162
|
+
*HTML multipart/alternative message*
|
163
|
+
txt before pix
|
164
|
+
<cool ruby image>
|
165
|
+
txt after pix
|
166
|
+
EOF
|
167
|
+
text_msg.content_transfer_encoding = '7bit'
|
168
|
+
|
169
|
+
related_msg = MIME::MultipartMedia::Related.new
|
170
|
+
related_msg.inline_entity(img)
|
171
|
+
related_msg.add_entity(html_msg)
|
172
|
+
|
173
|
+
msg = MIME::MultipartMedia::Alternative.new
|
174
|
+
msg.add_entity(related_msg)
|
175
|
+
msg.add_entity(text_msg)
|
176
|
+
assert_equal_mime_msg 'multipart_alternative_related.msg', msg
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_multipart_mixed_with_inline_and_attachment
|
180
|
+
msg = MIME::MultipartMedia::Mixed.new
|
181
|
+
|
182
|
+
open(sd('image.jpg')) do |img_file|
|
183
|
+
img_data = img_file.read
|
184
|
+
img_msg = MIME::ImageMedia.new(img_data, 'image/jpeg')
|
185
|
+
msg.attach_entity(img_msg, 'filename' => img_file.path)
|
186
|
+
end
|
187
|
+
|
188
|
+
msg.inline_entity(MIME::TextMedia.new('Plain Text'))
|
189
|
+
assert_equal_mime_msg 'multipart_mixed_inline_and_attachment.msg', msg
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_multipart_mixed_message_using_media_factory
|
193
|
+
bot_img = MIME::DiscreteMediaFactory.create(sd('image.jpg'))
|
194
|
+
top_img = MIME::DiscreteMediaFactory.create(sd('ruby.png'))
|
195
|
+
top_txt = MIME::DiscreteMediaFactory.create(sd('data.htm'))
|
196
|
+
|
197
|
+
msg = MIME::MultipartMedia::Mixed.new
|
198
|
+
msg.attach_entity(bot_img)
|
199
|
+
msg.attach_entity(top_img)
|
200
|
+
msg.inline_entity(top_txt)
|
201
|
+
assert_equal_mime_msg 'multipart_mixed_inline_and_attachment2.msg', msg
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_multipart_related_html_message_with_embedded_image
|
205
|
+
img = MIME::DiscreteMediaFactory.create(sd('/ruby.png'))
|
206
|
+
img.content_transfer_encoding = 'binary'
|
207
|
+
|
208
|
+
html_msg = MIME::TextMedia.new(<<EOF, 'text/html; charset=iso-8859-1')
|
209
|
+
<html>
|
210
|
+
<body>
|
211
|
+
<h1>HTML multipart/related message</h1>
|
212
|
+
<p>
|
213
|
+
txt before pix
|
214
|
+
<img alt="cool ruby" src="cid:#{img.content_id}">
|
215
|
+
</p>
|
216
|
+
<p>txt after pix</p>
|
217
|
+
</body>
|
218
|
+
</html>
|
219
|
+
EOF
|
220
|
+
html_msg.content_transfer_encoding = '7bit'
|
221
|
+
|
222
|
+
msg = MIME::MultipartMedia::Related.new
|
223
|
+
msg.inline_entity(img)
|
224
|
+
msg.add_entity(html_msg)
|
225
|
+
assert_equal_mime_msg 'multipart_related.msg', msg
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
### GENERAL RFC ADHERENCE ###
|
230
|
+
|
231
|
+
def test_boundary_format
|
232
|
+
form = MIME::MultipartMedia::FormData.new
|
233
|
+
%w(one two three four).each do |ent|
|
234
|
+
form.add_entity(MIME::TextMedia.new(ent), ent)
|
235
|
+
end
|
236
|
+
|
237
|
+
boundary = form.to_s.scan(/--Boundary_.*\r\n/).each
|
238
|
+
first_boundary = boundary.next
|
239
|
+
assert_equal first_boundary, boundary.next
|
240
|
+
assert_equal first_boundary, boundary.next
|
241
|
+
assert_equal first_boundary, boundary.next
|
242
|
+
refute_equal first_boundary, (last_boundary = boundary.next)
|
243
|
+
assert_match(/^--Boundary_\d+\.\d+\r\n/, first_boundary)
|
244
|
+
assert_match(/^--Boundary_\d+\.\d+--\r\n/, last_boundary)
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_unique_content_ids_in_multipart_message
|
248
|
+
form = MIME::MultipartMedia::FormData.new
|
249
|
+
%w(one two three four).each do |ent|
|
250
|
+
form.add_entity(MIME::TextMedia.new(ent), ent)
|
251
|
+
end
|
252
|
+
|
253
|
+
# 5 IDs: main header ID + 4 entity IDs
|
254
|
+
content_ids = form.to_s.scan(/^Content-ID: <(\d+.\d+)>/)
|
255
|
+
assert_equal 5, content_ids.flatten.uniq.count # IDs must be unique
|
256
|
+
end
|
257
|
+
|
258
|
+
def test_content_disposition_filenames
|
259
|
+
filename1 = 'book.pdf'
|
260
|
+
filename2 = 'mini.mov'
|
261
|
+
filename3 = 'song.mp3'
|
262
|
+
filename4 = 'none.txt'
|
263
|
+
filename5 = 'none.htm'
|
264
|
+
file1 = MIME::DiscreteMediaFactory.create(sd filename1)
|
265
|
+
file2 = MIME::DiscreteMediaFactory.create(sd filename2)
|
266
|
+
file3 = MIME::DiscreteMediaFactory.create(sd filename3)
|
267
|
+
file4 = MIME::TextMedia.new('txt')
|
268
|
+
file5 = MIME::TextMedia.new('htm')
|
269
|
+
file6 = MIME::TextMedia.new('xml')
|
270
|
+
|
271
|
+
form = MIME::MultipartMedia::FormData.new
|
272
|
+
# file backed objects
|
273
|
+
form.add_entity file1, 'file_1' # none
|
274
|
+
form.add_entity file2, 'file_2', filename2 # relative
|
275
|
+
form.add_entity file3, 'file_3', "/tmp/#{filename3}" # absolute
|
276
|
+
# non-file backed objects
|
277
|
+
form.add_entity file4, 'file_4', "/tmp/#{filename4}" # absolute
|
278
|
+
form.add_entity file5, 'file_5', filename5 # relative
|
279
|
+
form.add_entity file6, 'file_6' # none
|
280
|
+
msg = form.to_s
|
281
|
+
hdr = 'Content-Disposition: form-data;'
|
282
|
+
|
283
|
+
# only the file basename should be assigned to filename, never a path
|
284
|
+
assert_match(/^#{hdr} name="file_1"; filename="#{filename1}"\r\n/, msg)
|
285
|
+
assert_match(/^#{hdr} name="file_2"; filename="#{filename2}"\r\n/, msg)
|
286
|
+
assert_match(/^#{hdr} name="file_3"; filename="#{filename3}"\r\n/, msg)
|
287
|
+
assert_match(/^#{hdr} name="file_4"; filename="#{filename4}"\r\n/, msg)
|
288
|
+
assert_match(/^#{hdr} name="file_5"; filename="#{filename5}"\r\n/, msg)
|
289
|
+
assert_match(/^#{hdr} name="file_6"\r\n/, msg)
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
### LIBRARY OPERATION ###
|
294
|
+
|
295
|
+
def test_no_instantiation_of_abstract_classes
|
296
|
+
e = MIME::AbstractClassError
|
297
|
+
assert_raises(e) {MIME::MediaType.new(nil, nil)}
|
298
|
+
assert_raises(e) {MIME::DiscreteMediaType.new(nil)}
|
299
|
+
assert_raises(e) {MIME::CompositeMediaType.new(nil)}
|
300
|
+
assert_raises(e) {MIME::MultipartMedia.new(nil)}
|
301
|
+
end
|
302
|
+
|
303
|
+
def test_content_type_detection
|
304
|
+
(o = Object.new).extend(MIME::ContentTypes)
|
305
|
+
|
306
|
+
# test file extension; file path is irrelevant here
|
307
|
+
assert_equal 'application/pdf', o.file_type('a.pdf')
|
308
|
+
assert_equal 'video/quicktime', o.file_type('b.mov')
|
309
|
+
assert_equal 'application/octet-stream', o.file_type('c.iso')
|
310
|
+
assert_equal 'audio/mpeg', o.file_type('/d/e.mp3')
|
311
|
+
assert_equal 'text/css', o.file_type('/f/g/h.css')
|
312
|
+
assert_equal nil, o.file_type('i.nil')
|
313
|
+
|
314
|
+
# test using file object
|
315
|
+
img_type = open(sd('ruby.png')) {|f| o.file_type(f)}
|
316
|
+
assert_equal 'image/png', img_type
|
317
|
+
refute_equal 'image/jpeg', img_type
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_object_instantiation_using_discrete_media_factory
|
321
|
+
app_file = sd('book.pdf')
|
322
|
+
audio_file = sd('song.mp3')
|
323
|
+
text_file = sd('data.xml')
|
324
|
+
video_file = sd('mini.mov')
|
325
|
+
image_file = sd('image.jpg')
|
326
|
+
unknown_file = sd('unknown.yyy')
|
327
|
+
|
328
|
+
dmf = MIME::DiscreteMediaFactory
|
329
|
+
|
330
|
+
# test using file path
|
331
|
+
assert_kind_of MIME::ApplicationMedia, dmf.create(app_file)
|
332
|
+
assert_kind_of MIME::AudioMedia, dmf.create(audio_file)
|
333
|
+
assert_kind_of MIME::TextMedia, dmf.create(text_file)
|
334
|
+
assert_kind_of MIME::VideoMedia, dmf.create(video_file)
|
335
|
+
assert_kind_of MIME::ImageMedia, dmf.create(image_file)
|
336
|
+
|
337
|
+
# test using file object
|
338
|
+
open(image_file) do |image_file_obj|
|
339
|
+
assert_kind_of MIME::ImageMedia, dmf.create(image_file_obj)
|
340
|
+
end
|
341
|
+
open(text_file) do |text_file_obj|
|
342
|
+
assert_kind_of MIME::TextMedia, dmf.create(text_file_obj)
|
343
|
+
end
|
344
|
+
|
345
|
+
# raise for unknown file path and File object
|
346
|
+
assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file)}
|
347
|
+
open(unknown_file) do |unknown_file_obj|
|
348
|
+
assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file_obj)}
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def test_discrete_media_factory_with_specified_invalid_conent_type
|
353
|
+
invalid_ctype1 = 'application-x/pdf'
|
354
|
+
invalid_ctype2 = 'application'
|
355
|
+
valid_ctype = 'application/pdf'
|
356
|
+
pdf = sd('book.pdf')
|
357
|
+
|
358
|
+
assert_raises(MIME::UnknownContentError) {
|
359
|
+
MIME::DiscreteMediaFactory.create(pdf, invalid_ctype1)
|
360
|
+
}
|
361
|
+
assert_raises(MIME::UnknownContentError) {
|
362
|
+
MIME::DiscreteMediaFactory.create(pdf, invalid_ctype2)
|
363
|
+
}
|
364
|
+
assert MIME::DiscreteMediaFactory.create(pdf, valid_ctype)
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_discrete_media_factory_creates_path_singleton_method
|
368
|
+
pdf_file_path = sd('book.pdf')
|
369
|
+
|
370
|
+
media_obj = MIME::DiscreteMediaFactory.create(pdf_file_path)
|
371
|
+
assert_equal pdf_file_path, media_obj.path
|
372
|
+
|
373
|
+
open(pdf_file_path) do |pdf_file_obj|
|
374
|
+
media_obj = MIME::DiscreteMediaFactory.create(pdf_file_obj)
|
375
|
+
assert_equal pdf_file_path, media_obj.path
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
private
|
381
|
+
|
382
|
+
#
|
383
|
+
# Test equality of the +expected+ and +actual+ MIME messages.
|
384
|
+
#
|
385
|
+
def assert_equal_mime_msg expected, actual
|
386
|
+
m1 = generalize_msg(IO.read(sd(expected)))
|
387
|
+
m2 = generalize_msg(actual.to_s)
|
388
|
+
assert_equal m1, m2
|
389
|
+
end
|
390
|
+
|
391
|
+
#
|
392
|
+
# Remove unique identifiers to make +message+ structurally comparable.
|
393
|
+
#
|
394
|
+
def generalize_msg message
|
395
|
+
# Very delicate REs that are inter-dependent, be careful!
|
396
|
+
cid = /cid:\d+\.\d+/
|
397
|
+
id = /-ID: <\d+[@.]\d+>\r\n/
|
398
|
+
boundary = /Boundary_\d+\.\d+/
|
399
|
+
date = /^Date: .* -\d{4}\r\n/
|
400
|
+
version = /Ruby MIME v\d\.\d\.\d/
|
401
|
+
|
402
|
+
message.
|
403
|
+
gsub(cid, "cid").
|
404
|
+
gsub(id, "-ID:\r\n").
|
405
|
+
gsub(boundary, "Boundary_").
|
406
|
+
sub(date, "Date:\r\n").
|
407
|
+
sub(version, "Ruby MIME")
|
408
|
+
end
|
409
|
+
|
410
|
+
#
|
411
|
+
# Return the path of the scaffold +file+.
|
412
|
+
#
|
413
|
+
def sd file
|
414
|
+
File.join(File.dirname(__FILE__), 'scaffold', file)
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|