mms2r 1.0.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/History.txt +4 -0
- data/Manifest.txt +54 -0
- data/README.txt +81 -0
- data/Rakefile +30 -0
- data/conf/mms2r_cingularmedia_transform.yml +6 -0
- data/conf/mms2r_sprintmedia_ignore.yml +10 -0
- data/conf/mms2r_tmobilemedia_ignore.yml +17 -0
- data/conf/mms2r_verizonmedia_ignore.yml +3 -0
- data/lib/mms2r.rb +3 -0
- data/lib/mms2r/cingular_media.rb +11 -0
- data/lib/mms2r/media.rb +345 -0
- data/lib/mms2r/mmode_media.rb +13 -0
- data/lib/mms2r/sprint_media.rb +50 -0
- data/lib/mms2r/tmobile_media.rb +11 -0
- data/lib/mms2r/verizon_media.rb +11 -0
- data/lib/mms2r/version.rb +12 -0
- data/lib/vendor/text/format.rb +1466 -0
- data/lib/vendor/tmail.rb +3 -0
- data/lib/vendor/tmail/address.rb +242 -0
- data/lib/vendor/tmail/attachments.rb +39 -0
- data/lib/vendor/tmail/base64.rb +71 -0
- data/lib/vendor/tmail/config.rb +69 -0
- data/lib/vendor/tmail/encode.rb +467 -0
- data/lib/vendor/tmail/facade.rb +552 -0
- data/lib/vendor/tmail/header.rb +914 -0
- data/lib/vendor/tmail/info.rb +35 -0
- data/lib/vendor/tmail/loader.rb +1 -0
- data/lib/vendor/tmail/mail.rb +447 -0
- data/lib/vendor/tmail/mailbox.rb +433 -0
- data/lib/vendor/tmail/mbox.rb +1 -0
- data/lib/vendor/tmail/net.rb +280 -0
- data/lib/vendor/tmail/obsolete.rb +135 -0
- data/lib/vendor/tmail/parser.rb +1522 -0
- data/lib/vendor/tmail/port.rb +377 -0
- data/lib/vendor/tmail/quoting.rb +131 -0
- data/lib/vendor/tmail/scanner.rb +41 -0
- data/lib/vendor/tmail/scanner_r.rb +263 -0
- data/lib/vendor/tmail/stringio.rb +277 -0
- data/lib/vendor/tmail/tmail.rb +1 -0
- data/lib/vendor/tmail/utils.rb +238 -0
- data/test/files/dot.jpg +0 -0
- data/test/files/sprint-image-01.mail +195 -0
- data/test/files/sprint-text-01.mail +8 -0
- data/test/files/sprint-video-01.mail +195 -0
- data/test/files/sprint.mov +0 -0
- data/test/files/verizon-image-01.mail +815 -0
- data/test/files/verizon-text-01.mail +11 -0
- data/test/files/verizon-video-01.mail +336 -0
- data/test/test_mms2r_cingular.rb +52 -0
- data/test/test_mms2r_media.rb +311 -0
- data/test/test_mms2r_mmode.rb +52 -0
- data/test/test_mms2r_sprint.rb +154 -0
- data/test/test_mms2r_tmobile.rb +169 -0
- data/test/test_mms2r_verizon.rb +74 -0
- metadata +130 -0
@@ -0,0 +1,377 @@
|
|
1
|
+
#
|
2
|
+
# port.rb
|
3
|
+
#
|
4
|
+
#--
|
5
|
+
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#
|
26
|
+
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
|
27
|
+
# with permission of Minero Aoki.
|
28
|
+
#++
|
29
|
+
|
30
|
+
require 'tmail/stringio'
|
31
|
+
|
32
|
+
|
33
|
+
module TMail
|
34
|
+
|
35
|
+
class Port
|
36
|
+
def reproducible?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
###
|
43
|
+
### FilePort
|
44
|
+
###
|
45
|
+
|
46
|
+
class FilePort < Port
|
47
|
+
|
48
|
+
def initialize( fname )
|
49
|
+
@filename = File.expand_path(fname)
|
50
|
+
super()
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :filename
|
54
|
+
|
55
|
+
alias ident filename
|
56
|
+
|
57
|
+
def ==( other )
|
58
|
+
other.respond_to?(:filename) and @filename == other.filename
|
59
|
+
end
|
60
|
+
|
61
|
+
alias eql? ==
|
62
|
+
|
63
|
+
def hash
|
64
|
+
@filename.hash
|
65
|
+
end
|
66
|
+
|
67
|
+
def inspect
|
68
|
+
"#<#{self.class}:#{@filename}>"
|
69
|
+
end
|
70
|
+
|
71
|
+
def reproducible?
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def size
|
76
|
+
File.size @filename
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def ropen( &block )
|
81
|
+
File.open(@filename, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def wopen( &block )
|
85
|
+
File.open(@filename, 'w', &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
def aopen( &block )
|
89
|
+
File.open(@filename, 'a', &block)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def read_all
|
94
|
+
ropen {|f|
|
95
|
+
return f.read
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def remove
|
101
|
+
File.unlink @filename
|
102
|
+
end
|
103
|
+
|
104
|
+
def move_to( port )
|
105
|
+
begin
|
106
|
+
File.link @filename, port.filename
|
107
|
+
rescue Errno::EXDEV
|
108
|
+
copy_to port
|
109
|
+
end
|
110
|
+
File.unlink @filename
|
111
|
+
end
|
112
|
+
|
113
|
+
alias mv move_to
|
114
|
+
|
115
|
+
def copy_to( port )
|
116
|
+
if FilePort === port
|
117
|
+
copy_file @filename, port.filename
|
118
|
+
else
|
119
|
+
File.open(@filename) {|r|
|
120
|
+
port.wopen {|w|
|
121
|
+
while s = r.sysread(4096)
|
122
|
+
w.write << s
|
123
|
+
end
|
124
|
+
} }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
alias cp copy_to
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
# from fileutils.rb
|
133
|
+
def copy_file( src, dest )
|
134
|
+
st = r = w = nil
|
135
|
+
|
136
|
+
File.open(src, 'rb') {|r|
|
137
|
+
File.open(dest, 'wb') {|w|
|
138
|
+
st = r.stat
|
139
|
+
begin
|
140
|
+
while true
|
141
|
+
w.write r.sysread(st.blksize)
|
142
|
+
end
|
143
|
+
rescue EOFError
|
144
|
+
end
|
145
|
+
} }
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
module MailFlags
|
152
|
+
|
153
|
+
def seen=( b )
|
154
|
+
set_status 'S', b
|
155
|
+
end
|
156
|
+
|
157
|
+
def seen?
|
158
|
+
get_status 'S'
|
159
|
+
end
|
160
|
+
|
161
|
+
def replied=( b )
|
162
|
+
set_status 'R', b
|
163
|
+
end
|
164
|
+
|
165
|
+
def replied?
|
166
|
+
get_status 'R'
|
167
|
+
end
|
168
|
+
|
169
|
+
def flagged=( b )
|
170
|
+
set_status 'F', b
|
171
|
+
end
|
172
|
+
|
173
|
+
def flagged?
|
174
|
+
get_status 'F'
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def procinfostr( str, tag, true_p )
|
180
|
+
a = str.upcase.split(//)
|
181
|
+
a.push true_p ? tag : nil
|
182
|
+
a.delete tag unless true_p
|
183
|
+
a.compact.sort.join('').squeeze
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
class MhPort < FilePort
|
190
|
+
|
191
|
+
include MailFlags
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def set_status( tag, flag )
|
196
|
+
begin
|
197
|
+
tmpfile = @filename + '.tmailtmp.' + $$.to_s
|
198
|
+
File.open(tmpfile, 'w') {|f|
|
199
|
+
write_status f, tag, flag
|
200
|
+
}
|
201
|
+
File.unlink @filename
|
202
|
+
File.link tmpfile, @filename
|
203
|
+
ensure
|
204
|
+
File.unlink tmpfile
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def write_status( f, tag, flag )
|
209
|
+
stat = ''
|
210
|
+
File.open(@filename) {|r|
|
211
|
+
while line = r.gets
|
212
|
+
if line.strip.empty?
|
213
|
+
break
|
214
|
+
elsif m = /\AX-TMail-Status:/i.match(line)
|
215
|
+
stat = m.post_match.strip
|
216
|
+
else
|
217
|
+
f.print line
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
s = procinfostr(stat, tag, flag)
|
222
|
+
f.puts 'X-TMail-Status: ' + s unless s.empty?
|
223
|
+
f.puts
|
224
|
+
|
225
|
+
while s = r.read(2048)
|
226
|
+
f.write s
|
227
|
+
end
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
def get_status( tag )
|
232
|
+
File.foreach(@filename) {|line|
|
233
|
+
return false if line.strip.empty?
|
234
|
+
if m = /\AX-TMail-Status:/i.match(line)
|
235
|
+
return m.post_match.strip.include?(tag[0])
|
236
|
+
end
|
237
|
+
}
|
238
|
+
false
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
class MaildirPort < FilePort
|
245
|
+
|
246
|
+
def move_to_new
|
247
|
+
new = replace_dir(@filename, 'new')
|
248
|
+
File.rename @filename, new
|
249
|
+
@filename = new
|
250
|
+
end
|
251
|
+
|
252
|
+
def move_to_cur
|
253
|
+
new = replace_dir(@filename, 'cur')
|
254
|
+
File.rename @filename, new
|
255
|
+
@filename = new
|
256
|
+
end
|
257
|
+
|
258
|
+
def replace_dir( path, dir )
|
259
|
+
"#{File.dirname File.dirname(path)}/#{dir}/#{File.basename path}"
|
260
|
+
end
|
261
|
+
private :replace_dir
|
262
|
+
|
263
|
+
|
264
|
+
include MailFlags
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
MAIL_FILE = /\A(\d+\.[\d_]+\.[^:]+)(?:\:(\d),(\w+)?)?\z/
|
269
|
+
|
270
|
+
def set_status( tag, flag )
|
271
|
+
if m = MAIL_FILE.match(File.basename(@filename))
|
272
|
+
s, uniq, type, info, = m.to_a
|
273
|
+
return if type and type != '2' # do not change anything
|
274
|
+
newname = File.dirname(@filename) + '/' +
|
275
|
+
uniq + ':2,' + procinfostr(info.to_s, tag, flag)
|
276
|
+
else
|
277
|
+
newname = @filename + ':2,' + tag
|
278
|
+
end
|
279
|
+
|
280
|
+
File.link @filename, newname
|
281
|
+
File.unlink @filename
|
282
|
+
@filename = newname
|
283
|
+
end
|
284
|
+
|
285
|
+
def get_status( tag )
|
286
|
+
m = MAIL_FILE.match(File.basename(@filename)) or return false
|
287
|
+
m[2] == '2' and m[3].to_s.include?(tag[0])
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
###
|
294
|
+
### StringPort
|
295
|
+
###
|
296
|
+
|
297
|
+
class StringPort < Port
|
298
|
+
|
299
|
+
def initialize( str = '' )
|
300
|
+
@buffer = str
|
301
|
+
super()
|
302
|
+
end
|
303
|
+
|
304
|
+
def string
|
305
|
+
@buffer
|
306
|
+
end
|
307
|
+
|
308
|
+
def to_s
|
309
|
+
@buffer.dup
|
310
|
+
end
|
311
|
+
|
312
|
+
alias read_all to_s
|
313
|
+
|
314
|
+
def size
|
315
|
+
@buffer.size
|
316
|
+
end
|
317
|
+
|
318
|
+
def ==( other )
|
319
|
+
StringPort === other and @buffer.equal? other.string
|
320
|
+
end
|
321
|
+
|
322
|
+
alias eql? ==
|
323
|
+
|
324
|
+
def hash
|
325
|
+
@buffer.object_id.hash
|
326
|
+
end
|
327
|
+
|
328
|
+
def inspect
|
329
|
+
"#<#{self.class}:id=#{sprintf '0x%x', @buffer.object_id}>"
|
330
|
+
end
|
331
|
+
|
332
|
+
def reproducible?
|
333
|
+
true
|
334
|
+
end
|
335
|
+
|
336
|
+
def ropen( &block )
|
337
|
+
@buffer or raise Errno::ENOENT, "#{inspect} is already removed"
|
338
|
+
StringInput.open(@buffer, &block)
|
339
|
+
end
|
340
|
+
|
341
|
+
def wopen( &block )
|
342
|
+
@buffer = ''
|
343
|
+
StringOutput.new(@buffer, &block)
|
344
|
+
end
|
345
|
+
|
346
|
+
def aopen( &block )
|
347
|
+
@buffer ||= ''
|
348
|
+
StringOutput.new(@buffer, &block)
|
349
|
+
end
|
350
|
+
|
351
|
+
def remove
|
352
|
+
@buffer = nil
|
353
|
+
end
|
354
|
+
|
355
|
+
alias rm remove
|
356
|
+
|
357
|
+
def copy_to( port )
|
358
|
+
port.wopen {|f|
|
359
|
+
f.write @buffer
|
360
|
+
}
|
361
|
+
end
|
362
|
+
|
363
|
+
alias cp copy_to
|
364
|
+
|
365
|
+
def move_to( port )
|
366
|
+
if StringPort === port
|
367
|
+
str = @buffer
|
368
|
+
port.instance_eval { @buffer = str }
|
369
|
+
else
|
370
|
+
copy_to port
|
371
|
+
end
|
372
|
+
remove
|
373
|
+
end
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
end # module TMail
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module TMail
|
2
|
+
class Mail
|
3
|
+
def subject(to_charset = 'utf-8')
|
4
|
+
Unquoter.unquote_and_convert_to(quoted_subject, to_charset)
|
5
|
+
end
|
6
|
+
|
7
|
+
def unquoted_body(to_charset = 'utf-8')
|
8
|
+
from_charset = sub_header("content-type", "charset")
|
9
|
+
case (content_transfer_encoding || "7bit").downcase
|
10
|
+
when "quoted-printable"
|
11
|
+
Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
|
12
|
+
to_charset, from_charset, true)
|
13
|
+
when "base64"
|
14
|
+
Unquoter.unquote_base64_and_convert_to(quoted_body, to_charset,
|
15
|
+
from_charset)
|
16
|
+
when "7bit", "8bit"
|
17
|
+
Unquoter.convert_to(quoted_body, to_charset, from_charset)
|
18
|
+
when "binary"
|
19
|
+
quoted_body
|
20
|
+
else
|
21
|
+
quoted_body
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def body(to_charset = 'utf-8', &block)
|
26
|
+
attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
|
27
|
+
|
28
|
+
if multipart?
|
29
|
+
parts.collect { |part|
|
30
|
+
header = part["content-type"]
|
31
|
+
|
32
|
+
if part.multipart?
|
33
|
+
part.body(to_charset, &attachment_presenter)
|
34
|
+
elsif header.nil?
|
35
|
+
""
|
36
|
+
elsif !attachment?(part)
|
37
|
+
part.unquoted_body(to_charset)
|
38
|
+
else
|
39
|
+
attachment_presenter.call(header["name"] || "(unnamed)")
|
40
|
+
end
|
41
|
+
}.join
|
42
|
+
else
|
43
|
+
unquoted_body(to_charset)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Unquoter
|
49
|
+
class << self
|
50
|
+
def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1", preserve_underscores=false)
|
51
|
+
return "" if text.nil?
|
52
|
+
text.gsub(/(.*?)(?:(?:=\?(.*?)\?(.)\?(.*?)\?=)|$)/) do
|
53
|
+
before = $1
|
54
|
+
from_charset = $2
|
55
|
+
quoting_method = $3
|
56
|
+
text = $4
|
57
|
+
|
58
|
+
before = convert_to(before, to_charset, from_charset) if before.length > 0
|
59
|
+
before + case quoting_method
|
60
|
+
when "q", "Q" then
|
61
|
+
unquote_quoted_printable_and_convert_to(text, to_charset, from_charset, preserve_underscores)
|
62
|
+
when "b", "B" then
|
63
|
+
unquote_base64_and_convert_to(text, to_charset, from_charset)
|
64
|
+
when nil then
|
65
|
+
# will be nil at the end of the string, due to the nature of
|
66
|
+
# the regex used.
|
67
|
+
""
|
68
|
+
else
|
69
|
+
raise "unknown quoting method #{quoting_method.inspect}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
|
75
|
+
text = text.gsub(/_/, " ") unless preserve_underscores
|
76
|
+
text = text.gsub(/\r\n|\r/, "\n") # normalize newlines
|
77
|
+
convert_to(text.unpack("M*").first, to, from)
|
78
|
+
end
|
79
|
+
|
80
|
+
def unquote_base64_and_convert_to(text, to, from)
|
81
|
+
convert_to(Base64.decode(text).first, to, from)
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
require 'iconv'
|
86
|
+
def convert_to(text, to, from)
|
87
|
+
return text unless to && from
|
88
|
+
text ? Iconv.iconv(to, from, text).first : ""
|
89
|
+
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
|
90
|
+
# the 'from' parameter specifies a charset other than what the text
|
91
|
+
# actually is...not much we can do in this case but just return the
|
92
|
+
# unconverted text.
|
93
|
+
#
|
94
|
+
# Ditto if either parameter represents an unknown charset, like
|
95
|
+
# X-UNKNOWN.
|
96
|
+
text
|
97
|
+
end
|
98
|
+
rescue LoadError
|
99
|
+
# Not providing quoting support
|
100
|
+
def convert_to(text, to, from)
|
101
|
+
warn "Action Mailer: iconv not loaded; ignoring conversion from #{from} to #{to} (#{__FILE__}:#{__LINE__})"
|
102
|
+
text
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
if __FILE__ == $0
|
110
|
+
require 'test/unit'
|
111
|
+
|
112
|
+
class TC_Unquoter < Test::Unit::TestCase
|
113
|
+
def test_unquote_quoted_printable
|
114
|
+
a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
|
115
|
+
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
116
|
+
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_unquote_base64
|
120
|
+
a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
|
121
|
+
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
122
|
+
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_unquote_without_charset
|
126
|
+
a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
|
127
|
+
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
|
128
|
+
assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|