mime 0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|