brendanlim-mms2r 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/History.txt +292 -0
- data/Manifest.txt +169 -0
- data/README.txt +202 -0
- data/Rakefile +36 -0
- data/conf/1nbox.net.yml +13 -0
- data/conf/aliases.yml +17 -0
- data/conf/bellsouth.net.yml +2 -0
- data/conf/from.yml +2 -0
- data/conf/mediamessaging.o2.co.uk.yml +4 -0
- data/conf/messaging.nextel.com.yml +10 -0
- data/conf/mms.3ireland.ie.yml +17 -0
- data/conf/mms.ae.yml +5 -0
- data/conf/mms.alltel.com.yml +15 -0
- data/conf/mms.att.net.yml +11 -0
- data/conf/mms.dobson.net.yml +4 -0
- data/conf/mms.luxgsm.lu.yml +13 -0
- data/conf/mms.mobileiam.ma.yml +16 -0
- data/conf/mms.mtn.co.za.yml +13 -0
- data/conf/mms.mycricket.com.yml +4 -0
- data/conf/mms.myhelio.com.yml +12 -0
- data/conf/mms.netcom.no.yml +4 -0
- data/conf/mms.o2online.de.yml +8 -0
- data/conf/mms.three.co.uk.yml +15 -0
- data/conf/mms.vodacom4me.co.za.yml +13 -0
- data/conf/mms2r_media.yml +24 -0
- data/conf/mobile.indosat.net.id.yml +14 -0
- data/conf/msg.telus.com.yml +6 -0
- data/conf/orangemms.net.yml +13 -0
- data/conf/pm.sprint.com.yml +12 -0
- data/conf/pxt.vodafone.net.nz.yml +5 -0
- data/conf/rci.rogers.com.yml +18 -0
- data/conf/sms.sasktel.com.yml +12 -0
- data/conf/tmomail.net.yml +28 -0
- data/conf/txt.bell.ca.yml +4 -0
- data/conf/unicel.com.yml +6 -0
- data/conf/vmpix.com.yml +11 -0
- data/conf/vzwpix.com.yml +8 -0
- data/conf/waw.plspictures.com.yml +15 -0
- data/dev_tools/anonymizer.rb +20 -0
- data/dev_tools/debug_sprint_hpricot_parsing.rb +82 -0
- data/dev_tools/github.rb +8 -0
- data/lib/mms2r.rb +58 -0
- data/lib/mms2r/media.rb +746 -0
- data/lib/mms2r/media/sprint.rb +189 -0
- data/lib/tmail_ext.rb +17 -0
- data/mms2r.gemspec +41 -0
- data/test/fixtures/1nbox-2images-01.mail +738 -0
- data/test/fixtures/1nbox-2images-02.mail +736 -0
- data/test/fixtures/1nbox-2images-03.mail +736 -0
- data/test/fixtures/1nbox-2images-04.mail +732 -0
- data/test/fixtures/3ireland-mms-01.mail +671 -0
- data/test/fixtures/alltel-image-01.mail +319 -0
- data/test/fixtures/alltel-mms-01.mail +270 -0
- data/test/fixtures/alltel-mms-03.mail +270 -0
- data/test/fixtures/apple-double-image-01.mail +53 -0
- data/test/fixtures/att-blackberry-02.mail +54 -0
- data/test/fixtures/att-blackberry.mail +60 -0
- data/test/fixtures/att-image-01.mail +37 -0
- data/test/fixtures/att-image-02.mail +37 -0
- data/test/fixtures/att-iphone-01.mail +73 -0
- data/test/fixtures/att-iphone-02.mail +61 -0
- data/test/fixtures/att-iphone-03.mail +61 -0
- data/test/fixtures/att-text-01.mail +27 -0
- data/test/fixtures/bell-canada-image-01.mail +2273 -0
- data/test/fixtures/cingularme-text-01.mail +13 -0
- data/test/fixtures/cingularme-text-02.mail +14 -0
- data/test/fixtures/dobson-image-01.mail +64 -0
- data/test/fixtures/dot.jpg +0 -0
- data/test/fixtures/generic.mail +50 -0
- data/test/fixtures/helio-image-01.mail +348 -0
- data/test/fixtures/helio-message-01.mail +327 -0
- data/test/fixtures/iconv-fr-text-01.mail +50 -0
- data/test/fixtures/indosat-image-01.mail +751 -0
- data/test/fixtures/indosat-image-02.mail +751 -0
- data/test/fixtures/info2go-image-01.mail +43 -0
- data/test/fixtures/luxgsm-image-01.mail +388 -0
- data/test/fixtures/maroctelecom-france-mms-01.mail +511 -0
- data/test/fixtures/mediamessaging_o2_co_uk-image-01.mail +61 -0
- data/test/fixtures/mmode-image-01.mail +36 -0
- data/test/fixtures/mms.ae-image-01.mail +45 -0
- data/test/fixtures/mms.mycricket.com-pic-and-text.mail +87 -0
- data/test/fixtures/mms.mycricket.com-pic.mail +78 -0
- data/test/fixtures/mmsreply.t-mobile.co.uk-text-image-01.mail +431 -0
- data/test/fixtures/mobile.mycingular.com-text-01.mail +25 -0
- data/test/fixtures/mtn-southafrica-mms.mail +127 -0
- data/test/fixtures/mycingular-image-01.mail +66 -0
- data/test/fixtures/netcom-image-01.mail +63 -0
- data/test/fixtures/nextel-image-01.mail +46 -0
- data/test/fixtures/nextel-image-02.mail +43 -0
- data/test/fixtures/nextel-image-03.mail +44 -0
- data/test/fixtures/nextel-image-04.mail +43 -0
- data/test/fixtures/o2-de-image-01.mail +134 -0
- data/test/fixtures/orange-uk-image-01.mail +71 -0
- data/test/fixtures/orangefrance-text-and-image.mail +68 -0
- data/test/fixtures/orangepoland-text-01.mail +7 -0
- data/test/fixtures/orangepoland-text-02.mail +7 -0
- data/test/fixtures/pics.cingularme.com-image-01.mail +44 -0
- data/test/fixtures/pxt-image-01.mail +118 -0
- data/test/fixtures/rogers-canada-mms-01.mail +1029 -0
- data/test/fixtures/sasktel-image-01.mail +1791 -0
- data/test/fixtures/sprint-broken-image-01.mail +195 -0
- data/test/fixtures/sprint-image-01.mail +211 -0
- data/test/fixtures/sprint-pcs-text-01.mail +8 -0
- data/test/fixtures/sprint-purged-image-01.mail +195 -0
- data/test/fixtures/sprint-text-01.mail +195 -0
- data/test/fixtures/sprint-two-images-01.mail +231 -0
- data/test/fixtures/sprint-video-01.mail +210 -0
- data/test/fixtures/sprint.mov +0 -0
- data/test/fixtures/suncom-blackberry.mail +43 -0
- data/test/fixtures/telus-image-01.mail +89 -0
- data/test/fixtures/three-uk-image-01.mail +684 -0
- data/test/fixtures/tmo.blackberry.net-image-01.mail +48 -0
- data/test/fixtures/tmobile-blackberry-02.mail +50 -0
- data/test/fixtures/tmobile-blackberry.mail +44 -0
- data/test/fixtures/tmobile-image-01.mail +145 -0
- data/test/fixtures/tmobile-image-02.mail +145 -0
- data/test/fixtures/unicel-image-01.mail +60 -0
- data/test/fixtures/verizon-blackberry.mail +53 -0
- data/test/fixtures/verizon-image-01.mail +44 -0
- data/test/fixtures/verizon-image-02.mail +50 -0
- data/test/fixtures/verizon-image-03.mail +53 -0
- data/test/fixtures/verizon-text-01.mail +10 -0
- data/test/fixtures/verizon-video-01.mail +336 -0
- data/test/fixtures/virgin-mobile-image-01.mail +73 -0
- data/test/fixtures/virgin.ca-text-01.mail +21 -0
- data/test/fixtures/vodacom4me-co-za-01.mail +104 -0
- data/test/fixtures/vodacom4me-co-za-02.mail +108 -0
- data/test/fixtures/vodacom4me-southafrica-mms-01.mail +152 -0
- data/test/fixtures/vodacom4me-southafrica-mms-04.mail +655 -0
- data/test/fixtures/vtext-text-01.mail +10 -0
- data/test/fixtures/waw.plspictures.com-image-01.mail +1611 -0
- data/test/test_1nbox_net.rb +78 -0
- data/test/test_bell_canada.rb +44 -0
- data/test/test_bellsouth_net.rb +22 -0
- data/test/test_github.rb +12 -0
- data/test/test_helper.rb +58 -0
- data/test/test_mediamessaging_o2_co_uk.rb +52 -0
- data/test/test_messaging_nextel_com.rb +93 -0
- data/test/test_messaging_sprintpcs_com.rb +26 -0
- data/test/test_mms2r_media.rb +760 -0
- data/test/test_mms_3ireland_ie.rb +25 -0
- data/test/test_mms_ae.rb +46 -0
- data/test/test_mms_alltel_com.rb +53 -0
- data/test/test_mms_att_net.rb +142 -0
- data/test/test_mms_dobson_net.rb +41 -0
- data/test/test_mms_luxgsm_lu.rb +58 -0
- data/test/test_mms_mobileiam_ma.rb +26 -0
- data/test/test_mms_mtn_co_za.rb +25 -0
- data/test/test_mms_mycricket_com.rb +60 -0
- data/test/test_mms_myhelio_com.rb +62 -0
- data/test/test_mms_netcom_no.rb +52 -0
- data/test/test_mms_o2online_de.rb +53 -0
- data/test/test_mms_three_co_uk.rb +42 -0
- data/test/test_mms_vodacom4me_co_za.rb +107 -0
- data/test/test_mobile_indosat_net_id.rb +52 -0
- data/test/test_msg_telus_com.rb +28 -0
- data/test/test_orangemms_net.rb +100 -0
- data/test/test_pm_sprint_com.rb +202 -0
- data/test/test_pxt_vodafone_net_nz.rb +37 -0
- data/test/test_rci_rogers_com.rb +28 -0
- data/test/test_sms_sasktel_com.rb +28 -0
- data/test/test_tmomail_net.rb +108 -0
- data/test/test_unicel_com.rb +49 -0
- data/test/test_vmpix_com.rb +56 -0
- data/test/test_vzwpix_com.rb +131 -0
- data/test/test_waw_plspictures_com.rb +27 -0
- data/vendor/plugins/mms2r/lib/autotest/discover.rb +3 -0
- data/vendor/plugins/mms2r/lib/autotest/mms2r.rb +38 -0
- metadata +288 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
transform:
|
3
|
+
text/plain:
|
4
|
+
- - /^You have received a new message$/i
|
5
|
+
- ""
|
6
|
+
ignore:
|
7
|
+
image/jpeg:
|
8
|
+
- images/reb.jpg
|
9
|
+
- images/tpm.jpg
|
10
|
+
image/gif:
|
11
|
+
- images/vf9.gif
|
12
|
+
text/html:
|
13
|
+
- /<html><head>.*<title>MMS Message<\/title>/im
|
14
|
+
text/plain:
|
15
|
+
- /You have received an MMS message. To see the message correctly, you will need to view this message\s+in a graphical email application./im
|
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
text/html:
|
4
|
+
- /.*You have an <b>MMS<\/b> message!.*/m
|
5
|
+
transform:
|
6
|
+
text/plain:
|
7
|
+
- - /\s*This is an MMS message brought to you by Vodacom\. Click here to view online presentation http:\/\/mms.vodacom4me.co.za\/.*$/
|
8
|
+
- ""
|
9
|
+
number:
|
10
|
+
- from
|
11
|
+
- /^([^\s]+)\s.*/
|
12
|
+
- "\1"
|
13
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
text/plain:
|
4
|
+
- /^\(no subject\)$/i
|
5
|
+
multipart/mixed:
|
6
|
+
- "/^Attachment: /i"
|
7
|
+
transform:
|
8
|
+
text/plain:
|
9
|
+
- - /\A(.*?)Sent via BlackBerry (from|by) .*$/im
|
10
|
+
- "\1"
|
11
|
+
- - /\A(.*?)Sent from my Verizon Wireless BlackBerry$/im
|
12
|
+
- "\1"
|
13
|
+
- - /\A(.*?)Sent from (my|your) iPhone.?$/im
|
14
|
+
- "\1"
|
15
|
+
- - /\A(.*?)\s+image\/jpeg$/im
|
16
|
+
- "\1"
|
17
|
+
device_types:
|
18
|
+
headers:
|
19
|
+
X-Mailer:
|
20
|
+
- !ruby/regexp /^iPhone Mail/i
|
21
|
+
- :iphone
|
22
|
+
X-rim-org-msg-ref-id:
|
23
|
+
- !ruby/regexp /.+/
|
24
|
+
- :blackberry
|
@@ -0,0 +1,13 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
application/smil:
|
4
|
+
- /\A<smil><head><layout><root-layout height=/m
|
5
|
+
text/plain:
|
6
|
+
- banniere.txt
|
7
|
+
- /\A--\s+This Orange Multi Media Message was sent wirefree from an Orange MMS.*/mi
|
8
|
+
transform:
|
9
|
+
text/plain:
|
10
|
+
- - /^Orange UK MMS$/i
|
11
|
+
- ""
|
12
|
+
- - /^Wiadomo\303\205\302\233\303\204\302\207 multimedialna\.$/i
|
13
|
+
- ""
|
@@ -0,0 +1,12 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
text/html:
|
4
|
+
- /We're sorry, this page is not available. We apologize for the inconvenience./mi
|
5
|
+
text/plain:
|
6
|
+
- /You have new [Picture|Video] Mail!\s+Click Go\/View to see now./mi
|
7
|
+
transform:
|
8
|
+
text/plain:
|
9
|
+
- - /^You have new Picture Mail!$/i
|
10
|
+
- ""
|
11
|
+
- - /^PictureMail$/i
|
12
|
+
- ""
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
image/gif:
|
4
|
+
- /^.*$/i
|
5
|
+
text/plain:
|
6
|
+
- /^Uve received a video or picture msg from .*/im
|
7
|
+
text/html:
|
8
|
+
- /Picture from your Rogers Wireless friend/im
|
9
|
+
|
10
|
+
transform:
|
11
|
+
text/plain:
|
12
|
+
- - /^You have received a Picture Message from your Rogers Wireless.*/i
|
13
|
+
- ""
|
14
|
+
|
15
|
+
number:
|
16
|
+
- from
|
17
|
+
- /^([^\/|^@]+).*/
|
18
|
+
- "\1"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
image/jpeg:
|
4
|
+
- /^masthead.jpg$/i
|
5
|
+
- /^logo.jpg$/i
|
6
|
+
image/gif:
|
7
|
+
- /^dottedline350.gif$/i
|
8
|
+
- /^dottedline600.gif$/i
|
9
|
+
- /^dottedLine_350.gif$/i
|
10
|
+
- /^dottedLine_600.gif$/i
|
11
|
+
- /^spacer.gif$/i
|
12
|
+
- /^video.gif$/i
|
13
|
+
- /^audio.gif$/i
|
14
|
+
- /^tmobilelogo.gif$/i
|
15
|
+
- /^tmobilespace.gif$/i
|
16
|
+
text/html:
|
17
|
+
- /<html>\s+<head>\s+<title>T-Mobile<\/title>/m
|
18
|
+
- /<title>MMS E-Mail<\/title>/m
|
19
|
+
|
20
|
+
number:
|
21
|
+
- from
|
22
|
+
- /^"?([^\/|^@]+).*/
|
23
|
+
- "\1"
|
24
|
+
|
25
|
+
transform:
|
26
|
+
text/plain:
|
27
|
+
- - /.*\s+Text:\s+(.*?)\s+-+\s.*/im
|
28
|
+
- "\1"
|
data/conf/unicel.com.yml
ADDED
data/conf/vmpix.com.yml
ADDED
data/conf/vzwpix.com.yml
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
---
|
2
|
+
ignore:
|
3
|
+
text/plain:
|
4
|
+
- /\AThis message was sent using (the Picture and Video|PIX-FLIX) Messaging service from Verizon Wireless!.*/m
|
5
|
+
transform:
|
6
|
+
text/plain:
|
7
|
+
- - /\A(.+?)\s+This message was sent using (the Picture and Video|PIX-FLIX) Messaging service from Verizon Wireless!.*/m
|
8
|
+
- "\1"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
transform:
|
3
|
+
text/plain:
|
4
|
+
- - /^You have received a Multimedia Message$/i
|
5
|
+
- ""
|
6
|
+
number:
|
7
|
+
- return-path
|
8
|
+
- /^<([^@]+)@.*/
|
9
|
+
- "\1"
|
10
|
+
ignore:
|
11
|
+
text/html:
|
12
|
+
- /.*/m
|
13
|
+
text/plain:
|
14
|
+
- /\AA New Photo\/Video Message.\s+Click Go\/View to see now http/im
|
15
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
if ARGV.size != 3
|
4
|
+
puts "Usage: anonymizer.rb message_file phone_number email_address"
|
5
|
+
exit(1)
|
6
|
+
end
|
7
|
+
|
8
|
+
message_file = ARGV[0]
|
9
|
+
phone_number = ARGV[1]
|
10
|
+
email_address = ARGV[2]
|
11
|
+
|
12
|
+
message = File.read(message_file)
|
13
|
+
message.gsub!(phone_number, '2068675309')
|
14
|
+
message.gsub!(email_address, 'tommy.tutone@example.com')
|
15
|
+
|
16
|
+
out = File.open(message_file, "w")
|
17
|
+
out.puts message
|
18
|
+
out.close
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'net/http'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'hpricot'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
if ARGV[0].nil?
|
9
|
+
puts "please execute with a file path to a Sprint HTML file that was extracted from a MMS"
|
10
|
+
puts "ruby #{$0} MYFILE"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
doc = open(ARGV[0]) { |f| Hpricot(f) }
|
15
|
+
|
16
|
+
puts "TITLE: #{doc.at('title').inner_html}"
|
17
|
+
|
18
|
+
#phone number is tucked away in the comment in the head
|
19
|
+
c = doc.search("/html/head/comment//").last
|
20
|
+
t = c.content.gsub(/\s+/m," ").strip
|
21
|
+
number = / name="MDN">(\d+)</.match(t)[1]
|
22
|
+
puts "NUMBER: #{number}"
|
23
|
+
|
24
|
+
#if there is a text message with the MMS its in the
|
25
|
+
#inner html of the only pre on the page
|
26
|
+
text = doc.search("/html/body//pre").first.inner_html
|
27
|
+
puts "TEXT: #{text}"
|
28
|
+
|
29
|
+
# just see what they say this MMS is it really doesn't
|
30
|
+
# mean anything, the content is in paux image with
|
31
|
+
# a RECIPIENT in its URI path
|
32
|
+
trs = doc.search("/html/body//tr")
|
33
|
+
text = trs[3].search("/td/p/font/b/")
|
34
|
+
case text.text
|
35
|
+
when /You have a Video Mail from/
|
36
|
+
puts "it claims to be a video"
|
37
|
+
when /You have a Picture Mail from /
|
38
|
+
puts "it claims to be an image"
|
39
|
+
else
|
40
|
+
puts "what is it? #{text.text}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# group all the images together
|
44
|
+
srcs = Array.new
|
45
|
+
imgs = doc.search("/html/body//img")
|
46
|
+
imgs.each do |i|
|
47
|
+
src = i.attributes['src']
|
48
|
+
#next unless /pictures.sprintpcs.com\/+mmps\/RECIPIENT\//.match(src)
|
49
|
+
#we don't want to double fetch content and we only
|
50
|
+
#want to fetch media from the content server, you get
|
51
|
+
#a clue about that as there is a RECIPIENT in the URI path
|
52
|
+
next unless /mmps\/RECIPIENT\//.match(src)
|
53
|
+
next if srcs.detect{|s| s.eql?(src)}
|
54
|
+
srcs << src
|
55
|
+
end
|
56
|
+
|
57
|
+
# now fetch the media
|
58
|
+
puts "there are #{srcs.size} sources to fetch"
|
59
|
+
cnt = 0
|
60
|
+
srcs.each do |src|
|
61
|
+
puts "--"
|
62
|
+
puts "FETCHING:\n #{src}"
|
63
|
+
|
64
|
+
url = URI.parse(src)
|
65
|
+
#res = Net::HTTP.get_response(url)
|
66
|
+
agent = "Mozilla/5.0 (X11; U; Minix3 i686 (x86_64); en-US; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1"
|
67
|
+
res = Net::HTTP.start(url.host, url.port) { |http|
|
68
|
+
req = Net::HTTP::Get.new(url.request_uri, {'User-Agent' => agent})
|
69
|
+
http.request(req)
|
70
|
+
}
|
71
|
+
|
72
|
+
# prep and write a file
|
73
|
+
base = /\/RECIPIENT\/([^\/]+)\//.match(src)[1]
|
74
|
+
ext = /^[^\/]+\/(.+)/.match(res.content_type)[1]
|
75
|
+
file_name ="#{base}.#{cnt}.#{ext}"
|
76
|
+
puts "writing file: #{file_name}"
|
77
|
+
File.open(file_name,'w'){ |f| f.write(res.body) }
|
78
|
+
puts "file is sized #{File.size(file_name)}"
|
79
|
+
cnt = cnt + 1
|
80
|
+
end
|
81
|
+
|
82
|
+
puts "no images or video" if srcs.size == 0
|
data/dev_tools/github.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
# based on Trouble shooting section in http://gems.github.com/
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
data = File.read(File.join(File.dirname(__FILE__), "..", "mms2r.gemspec"))
|
5
|
+
spec = nil
|
6
|
+
result = Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
7
|
+
puts spec
|
8
|
+
(!!result) ? exit(0) : exit(1)
|
data/lib/mms2r.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 by Mike Mondragon (mikemondragon@gmail.com)
|
3
|
+
#
|
4
|
+
# Please see the LICENSE file for licensing information.
|
5
|
+
#++
|
6
|
+
|
7
|
+
module MMS2R
|
8
|
+
|
9
|
+
##
|
10
|
+
# A hash of MMS2R processors keyed by MMS carrier domain.
|
11
|
+
|
12
|
+
CARRIERS = {}
|
13
|
+
|
14
|
+
##
|
15
|
+
# Registers a class as a processor for a MMS domain. Should only be
|
16
|
+
# used in special cases such as MMS2R::Media::Sprint for 'pm.sprint.com'
|
17
|
+
|
18
|
+
def self.register(domain, processor_class)
|
19
|
+
MMS2R::CARRIERS[domain] = processor_class
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# A hash of file extensions for common mime-types
|
24
|
+
|
25
|
+
EXT = {
|
26
|
+
'text/plain' => 'text',
|
27
|
+
'text/plain' => 'txt',
|
28
|
+
'text/html' => 'html',
|
29
|
+
'image/png' => 'png',
|
30
|
+
'image/gif' => 'gif',
|
31
|
+
'image/jpeg' => 'jpeg',
|
32
|
+
'image/jpeg' => 'jpg',
|
33
|
+
'video/quicktime' => 'mov',
|
34
|
+
'video/3gpp2' => '3g2'
|
35
|
+
}
|
36
|
+
|
37
|
+
class MMS2R::Media
|
38
|
+
|
39
|
+
##
|
40
|
+
# MMS2R library version
|
41
|
+
|
42
|
+
VERSION = '2.2.0'
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'rubygems'
|
49
|
+
require 'tmail/mail'
|
50
|
+
require 'fileutils'
|
51
|
+
require 'pathname'
|
52
|
+
require 'tmpdir'
|
53
|
+
require 'yaml'
|
54
|
+
|
55
|
+
require File.join(File.dirname(__FILE__), 'tmail_ext')
|
56
|
+
require File.join(File.dirname(__FILE__), 'mms2r', 'media')
|
57
|
+
require File.join(File.dirname(__FILE__), 'mms2r', 'media', 'sprint')
|
58
|
+
MMS2R.register('pm.sprint.com', MMS2R::Media::Sprint)
|
data/lib/mms2r/media.rb
ADDED
@@ -0,0 +1,746 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 by Mike Mondragon (mikemondragon@gmail.com)
|
3
|
+
#
|
4
|
+
# Please see the LICENSE file for licensing information
|
5
|
+
#++
|
6
|
+
|
7
|
+
##
|
8
|
+
# = Synopsis
|
9
|
+
#
|
10
|
+
# MMS2R is a library to collect media files from MMS messages. MMS messages
|
11
|
+
# are multipart emails and mobile carriers often inject branding into these
|
12
|
+
# messages. MMS2R strips the advertising from an MMS leaving the actual user
|
13
|
+
# generated media.
|
14
|
+
#
|
15
|
+
# The Tracker for MMS2R is located at
|
16
|
+
# http://rubyforge.org/tracker/?group_id=3065
|
17
|
+
# Please submit bugs and feature requests using the Tracker.
|
18
|
+
#
|
19
|
+
# If MMS from a carrier not known by MMS2R is encountered please submit a
|
20
|
+
# sample to the author for inclusion in this project.
|
21
|
+
#
|
22
|
+
# == Stand Alone Example
|
23
|
+
#
|
24
|
+
# require 'rubygems'
|
25
|
+
# require 'mms2r'
|
26
|
+
# mail = TMail::Mail.parse(IO.readlines("sample-MMS.file").join)
|
27
|
+
# mms = MMS2R::Media.new(mail)
|
28
|
+
# subject = mms.subject
|
29
|
+
# number = mms.number
|
30
|
+
# file = mms.default_media
|
31
|
+
# mms.purge
|
32
|
+
#
|
33
|
+
# == Rails ActionMailer#receive w/ AttachmentFu Example
|
34
|
+
#
|
35
|
+
# def receive(mail)
|
36
|
+
# mms = MMS2R::Media.new(mail)
|
37
|
+
# picture = Picture.new # picture is an attachemnt_fu model
|
38
|
+
# picture.title = mms.subject
|
39
|
+
# picture.uploaded_data = mms.default_media
|
40
|
+
# picture.save!
|
41
|
+
# mms.purge
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# == More Examples
|
45
|
+
#
|
46
|
+
# See the README.txt file for more examples
|
47
|
+
#
|
48
|
+
# == Built In Configuration
|
49
|
+
#
|
50
|
+
# A custom configuration can be created for processing the MMS from carriers
|
51
|
+
# that are not currently known by MMS2R. In the conf/ directory create a
|
52
|
+
# YAML file named by combining the domain name of the MMS sender plus a .yml
|
53
|
+
# extension. For instance the configuration of senders from AT&T's cellular
|
54
|
+
# service with a Sender pattern of 2065551212@mms.att.net have a configuration
|
55
|
+
# named conf/mms.att.net.yml
|
56
|
+
#
|
57
|
+
# The YAML configuration contains a Hash with instructions for determining what
|
58
|
+
# is content generated by the user and what is content inserted by the carrier.
|
59
|
+
#
|
60
|
+
# The root hash itself has two hashes under the keys 'ignore' and 'transform',
|
61
|
+
# and an array under the 'number' key.
|
62
|
+
# Each hash is itself keyed by mime-type. The value pointed to by the mime-type
|
63
|
+
# key is an array. The ignore arrays are first evaluated as a regular expressions
|
64
|
+
# and if the evaluation fails as a equality for a string filename. Ignores
|
65
|
+
# work by filename for the multi-part of the MMS that is being inspected. The
|
66
|
+
# array pointed to by the 'number' key represents an alternate mail header where
|
67
|
+
# the sender's number can be found with a regular expression and replacement
|
68
|
+
# value for a gsub eval.
|
69
|
+
#
|
70
|
+
# The transform arrays are themselves an array of two element arrays. The elements
|
71
|
+
# are parameters for gsub and will be evaluated from within the ruby code.
|
72
|
+
#
|
73
|
+
# Ignore instructions are honored first then transform instructions. In the sample,
|
74
|
+
# masthead.jpg is ignored as a regular expression, and spacer.gif is ignored as a
|
75
|
+
# filename comparison. The transform has a match and a replacement, see the gsub
|
76
|
+
# documentation for more information about match and replace.
|
77
|
+
#
|
78
|
+
# --
|
79
|
+
# ignore:
|
80
|
+
# image/jpeg:
|
81
|
+
# - /^masthead.jpg$/i
|
82
|
+
# image/gif:
|
83
|
+
# - spacer.gif
|
84
|
+
# text/plain:
|
85
|
+
# - /\AThis message was sent using PIX-FLIX Messaging service from .*/m
|
86
|
+
# transform:
|
87
|
+
# text/plain:
|
88
|
+
# - - /\A(.+?)\s+This message was sent using PIX-FLIX Messaging .*/m
|
89
|
+
# - "\1"
|
90
|
+
# number:
|
91
|
+
# - from
|
92
|
+
# - /^([^\s]+)\s.*/
|
93
|
+
# - "\1"
|
94
|
+
#
|
95
|
+
# Carriers often provide their services under many different domain names.
|
96
|
+
# The conf/aliases.yml is a YAML file with a hash that maps alternative or
|
97
|
+
# legacy carrier names to the most common name of their service. For example
|
98
|
+
# in terms of MMS2R txt.att.net is an alias for mms.att.net. Therefore when
|
99
|
+
# an MMS with a Sender of txt.att.net is processed MMS2R will use the
|
100
|
+
# mms.att.net configuration to process the message.
|
101
|
+
|
102
|
+
module MMS2R
|
103
|
+
|
104
|
+
class MMS2R::Media
|
105
|
+
|
106
|
+
class << self #:nodoc:
|
107
|
+
# alias new so that we can use ::create to select the media processor and
|
108
|
+
# then initialize the new object
|
109
|
+
alias orig_new new
|
110
|
+
def new(mail, opts = {})
|
111
|
+
klass, carrier = MMS2R::Media.create(mail)
|
112
|
+
opts[:domain] = carrier
|
113
|
+
klass.orig_new(mail, opts)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# TMail object that the media files were derived from.
|
119
|
+
|
120
|
+
attr_reader :mail
|
121
|
+
|
122
|
+
##
|
123
|
+
# media returns the hash of media. The media hash is keyed by mime-type
|
124
|
+
# such as 'text/plain' and the value mapped to the key is an array of
|
125
|
+
# media that are of that type.
|
126
|
+
|
127
|
+
attr_reader :media
|
128
|
+
|
129
|
+
##
|
130
|
+
# Carrier is the domain name of the carrier. If the carrier is not known
|
131
|
+
# the carrier will be set to 'mms2r.media'
|
132
|
+
|
133
|
+
attr_reader :carrier
|
134
|
+
|
135
|
+
##
|
136
|
+
# Base working dir where media for a unique mms message are dropped
|
137
|
+
|
138
|
+
attr_reader :media_dir
|
139
|
+
|
140
|
+
##
|
141
|
+
# Various multi-parts that are bundled into mail
|
142
|
+
|
143
|
+
MULTIPARTS_TO_SPLIT = [ 'multipart/related', 'multipart/alternative', 'multipart/mixed', 'multipart/appledouble' ]
|
144
|
+
|
145
|
+
##
|
146
|
+
# Factory method that creates MMS2R::Media products based on the domain
|
147
|
+
# name of the carrier from which the MMS originated. mail is a TMail
|
148
|
+
# object.
|
149
|
+
|
150
|
+
def self.create(mail)
|
151
|
+
d = lambda{ ['mms2r.media', MMS2R::Media] } #sets a default to detect
|
152
|
+
from_domain = self.domain(mail)
|
153
|
+
processor = MMS2R::CARRIERS.detect(d) do |domain, klass|
|
154
|
+
return klass, domain if from_domain == domain
|
155
|
+
end
|
156
|
+
[MMS2R::Media, from_domain]
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Determine if return-path or from is going to be used to desiginate the
|
161
|
+
# origin carrier. If the domain in the From header is listed in
|
162
|
+
# conf/from.yaml then that is the carrier domain. Else if there is a
|
163
|
+
# Return-Path header its address's domain is the carrier doamin, else
|
164
|
+
# use From header's address domain.
|
165
|
+
|
166
|
+
def self.domain(mail)
|
167
|
+
return_path = case
|
168
|
+
when mail.header['return-path']
|
169
|
+
matched = /^<.+@([^@]+)>$/.match(mail.header['return-path'].to_s.strip)
|
170
|
+
matched ? matched[1] : ''
|
171
|
+
else
|
172
|
+
''
|
173
|
+
end
|
174
|
+
|
175
|
+
from_domain = case
|
176
|
+
when mail.from && mail.from.first
|
177
|
+
mail.from.first.split('@').last
|
178
|
+
else
|
179
|
+
''
|
180
|
+
end
|
181
|
+
|
182
|
+
f = File.join(self.conf_dir(), "from.yml")
|
183
|
+
from = YAML::load_file(f) rescue {}
|
184
|
+
|
185
|
+
ret = case
|
186
|
+
when from.include?(from_domain)
|
187
|
+
from_domain
|
188
|
+
when return_path.any?
|
189
|
+
return_path
|
190
|
+
else
|
191
|
+
from_domain
|
192
|
+
end
|
193
|
+
ret
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
##
|
198
|
+
# Initialize a new MMS2R::Media comprised of a mail.
|
199
|
+
#
|
200
|
+
# Specify options to initialize with:
|
201
|
+
# :logger => some_logger for logging
|
202
|
+
# :process => :lazy, for non-greedy processing upon initialization
|
203
|
+
#
|
204
|
+
# #process will have to be called explicitly if the lazy process option
|
205
|
+
# is chosen.
|
206
|
+
|
207
|
+
def initialize(mail, opts={})
|
208
|
+
|
209
|
+
@mail = mail
|
210
|
+
@logger = opts[:logger]
|
211
|
+
log("#{self.class} created", :info)
|
212
|
+
@carrier = opts[:domain]
|
213
|
+
@dir_count = 0
|
214
|
+
@media_dir = File.join(self.tmp_dir(),
|
215
|
+
self.safe_message_id(@mail.message_id))
|
216
|
+
@media = {}
|
217
|
+
@was_processed = false
|
218
|
+
@number = nil
|
219
|
+
@subject = nil
|
220
|
+
@body = nil
|
221
|
+
@default_media = nil
|
222
|
+
@default_text = nil
|
223
|
+
|
224
|
+
f = File.join(self.conf_dir(), "aliases.yml")
|
225
|
+
@aliases = YAML::load_file(f) rescue {}
|
226
|
+
|
227
|
+
conf = "#{@aliases[@carrier] || @carrier}.yml"
|
228
|
+
f = File.join(self.conf_dir(), conf)
|
229
|
+
c = YAML::load_file(f) rescue {}
|
230
|
+
@config = self.class.initialize_config(c)
|
231
|
+
|
232
|
+
lazy = (opts[:process] == :lazy) rescue false
|
233
|
+
self.process() unless lazy
|
234
|
+
end
|
235
|
+
|
236
|
+
##
|
237
|
+
# Get the phone number associated with this MMS if it exists. The value
|
238
|
+
# returned is simplistic, it is just the user name of the from address
|
239
|
+
# before the @ symbol. Validation of the number is left to you. Most
|
240
|
+
# carriers are using the real phone number as the username.
|
241
|
+
|
242
|
+
def number
|
243
|
+
unless @number
|
244
|
+
params = config['number']
|
245
|
+
if params && (header = mail.header[params[0]])
|
246
|
+
@number = header.to_s.gsub(eval(params[1]), params[2])
|
247
|
+
end
|
248
|
+
end
|
249
|
+
@number ||= mail.from.first.split('@').first rescue ""
|
250
|
+
end
|
251
|
+
|
252
|
+
##
|
253
|
+
# Return the Subject for this message, returns "" for default carrier
|
254
|
+
# subject such as 'Multimedia message' for ATT&T carrier.
|
255
|
+
|
256
|
+
def subject
|
257
|
+
|
258
|
+
unless @subject
|
259
|
+
subject = mail.subject.strip rescue ""
|
260
|
+
ignores = config['ignore']['text/plain']
|
261
|
+
if ignores && ignores.detect{|s| s == subject}
|
262
|
+
@subject = ""
|
263
|
+
else
|
264
|
+
@subject = transform_text('text/plain', subject).last
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
@subject
|
269
|
+
end
|
270
|
+
|
271
|
+
# Convenience method that returns a string including all the text of the
|
272
|
+
# first text/plain file found. Returns empty string if no body text
|
273
|
+
# is found.
|
274
|
+
|
275
|
+
def body
|
276
|
+
text_file = default_text
|
277
|
+
@body = text_file ? IO.readlines(text_file.path).join.strip : ""
|
278
|
+
@body
|
279
|
+
end
|
280
|
+
|
281
|
+
# Returns a File with the most likely candidate for the user-submitted
|
282
|
+
# media. Given that most MMS messages only have one file attached, this
|
283
|
+
# method will try to return that file. Singleton methods are added to
|
284
|
+
# the File object so it can be used in place of a CGI upload (local_path,
|
285
|
+
# original_filename, size, and content_type) such as in conjunction with
|
286
|
+
# AttachementFu. The largest file found in terms of bytes is returned.
|
287
|
+
#
|
288
|
+
# Returns nil if there are not any video or image Files found.
|
289
|
+
|
290
|
+
def default_media
|
291
|
+
@default_media ||= attachment(['video', 'image', 'application', 'text'])
|
292
|
+
end
|
293
|
+
|
294
|
+
# Returns a File with the most likely candidate that is text, or nil
|
295
|
+
# otherwise. It also adds singleton methods to the File object so it can be
|
296
|
+
# used in place of a CGI upload (local_path, original_filename, size, and
|
297
|
+
# content_type) such as in conjunction with AttachmentFu. The largest file
|
298
|
+
# found in terms of bytes is returned.
|
299
|
+
#
|
300
|
+
# Returns nil if there are not any text Files found
|
301
|
+
|
302
|
+
def default_text
|
303
|
+
@default_text ||= attachment(['text/plain'])
|
304
|
+
end
|
305
|
+
|
306
|
+
##
|
307
|
+
# process is a template method and collects all the media in a MMS.
|
308
|
+
# Override helper methods to this template to clean out advertising and/or
|
309
|
+
# ignore media that are advertising. This method should not be overridden
|
310
|
+
# unless there is an extreme special case in processing the media of a MMS
|
311
|
+
# (like Sprint)
|
312
|
+
#
|
313
|
+
# Helper methods for the process template:
|
314
|
+
# * ignore_media? -- true if the media contained in a part should be ignored.
|
315
|
+
# * process_media -- retrieves media to temporary file, returns path to file.
|
316
|
+
# * transform_text -- called by process_media, strips out advertising.
|
317
|
+
# * temp_file -- creates a temporary filepath based on information from the part.
|
318
|
+
#
|
319
|
+
# Block support:
|
320
|
+
# Call process() with a block to automatically iterate through media.
|
321
|
+
# For example, to process and receive only media of video type:
|
322
|
+
# mms.process do |media_type, file|
|
323
|
+
# results << file if media_type =~ /video/
|
324
|
+
# end
|
325
|
+
#
|
326
|
+
# note: purge must be explicitly called to remove the media files
|
327
|
+
# mms2r extracts from an mms message.
|
328
|
+
|
329
|
+
def process() # :yields: media_type, file
|
330
|
+
unless @was_processed
|
331
|
+
log("#{self.class} processing", :info)
|
332
|
+
|
333
|
+
parts = mail.multipart? ? mail.parts : [mail]
|
334
|
+
|
335
|
+
# Double check for multipart/related, if it exists replace it with its
|
336
|
+
# children parts. Do this twice as multipart/alternative can have
|
337
|
+
# children and we want to fold everything down
|
338
|
+
for i in 1..2
|
339
|
+
flat = []
|
340
|
+
parts.each do |p|
|
341
|
+
if MULTIPARTS_TO_SPLIT.include?(p.part_type?)
|
342
|
+
p.parts.each {|mp| flat << mp }
|
343
|
+
else
|
344
|
+
flat << p
|
345
|
+
end
|
346
|
+
end
|
347
|
+
parts = flat.dup
|
348
|
+
end
|
349
|
+
|
350
|
+
# get to work
|
351
|
+
parts.each do |p|
|
352
|
+
t = p.part_type?
|
353
|
+
unless ignore_media?(t,p)
|
354
|
+
t,f = process_media(p)
|
355
|
+
add_file(t,f) unless t.nil? || f.nil?
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
@was_processed = true
|
360
|
+
end
|
361
|
+
|
362
|
+
# when process acts upon a block
|
363
|
+
if block_given?
|
364
|
+
media.each do |k, v|
|
365
|
+
yield(k, v)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
370
|
+
|
371
|
+
##
|
372
|
+
# Helper for process template method to determine if media contained in a
|
373
|
+
# part should be ignored. Producers should override this method to return
|
374
|
+
# true for media such as images that are advertising, carrier logos, etc.
|
375
|
+
# See the ignore section in the discussion of the built-in configuration.
|
376
|
+
|
377
|
+
def ignore_media?(type,part)
|
378
|
+
ignores = config['ignore'][type] || []
|
379
|
+
ignore = ignores.detect{|test| filename?(part) == test}
|
380
|
+
ignore ||= ignores.detect{|test| filename?(part) =~ eval(test) if test.index('/') == 0 }
|
381
|
+
ignore ||= ignores.detect{|test| part.body.strip =~ eval(test) if test.index('/') == 0 }
|
382
|
+
ignore ||= (part.body.strip.size == 0 ? true : nil)
|
383
|
+
ignore.nil? ? false : true
|
384
|
+
end
|
385
|
+
|
386
|
+
##
|
387
|
+
# Helper for process template method to decode the part based on its type
|
388
|
+
# and write its content to a temporary file. Returns path to temporary
|
389
|
+
# file that holds the content. Parts with a main type of text will have
|
390
|
+
# their contents transformed with a call to transform_text
|
391
|
+
#
|
392
|
+
# Producers should only override this method if the parts of the MMS need
|
393
|
+
# special treatment besides what is expected for a normal mime part (like
|
394
|
+
# Sprint).
|
395
|
+
#
|
396
|
+
# Returns a tuple of content type, file path
|
397
|
+
|
398
|
+
def process_media(part)
|
399
|
+
# TMail body auto-magically decodes quoted
|
400
|
+
# printable for text/html type.
|
401
|
+
file = temp_file(part)
|
402
|
+
if part.main_type('text') == 'text' ||
|
403
|
+
part.content_type == 'application/smil'
|
404
|
+
type, content = transform_text_part(part)
|
405
|
+
mode = 'w'
|
406
|
+
else
|
407
|
+
if part.content_type == 'application/octet-stream'
|
408
|
+
type = type_from_filename(filename?(part))
|
409
|
+
else
|
410
|
+
type = part.content_type
|
411
|
+
end
|
412
|
+
content = part.body
|
413
|
+
mode = 'wb' # open with binary bit for Windows for non text
|
414
|
+
end
|
415
|
+
return type, nil if content.nil? || content.empty?
|
416
|
+
|
417
|
+
log("#{self.class} writing file #{file}", :info)
|
418
|
+
File.open(file, mode){ |f| f.write(content) }
|
419
|
+
return type, file
|
420
|
+
end
|
421
|
+
|
422
|
+
##
|
423
|
+
# Helper for process_media template method to transform text.
|
424
|
+
# See the transform section in the discussion of the built-in
|
425
|
+
# configuration.
|
426
|
+
|
427
|
+
def transform_text(type, text, original_nencoding = 'ISO-8859-1')
|
428
|
+
return type, text unless transforms = config['transform'][type]
|
429
|
+
|
430
|
+
#convert to UTF-8
|
431
|
+
begin
|
432
|
+
c = Iconv.new('UTF-8', original_nencoding )
|
433
|
+
utf_t = c.iconv(text)
|
434
|
+
rescue Exception => e
|
435
|
+
utf_t = text
|
436
|
+
end
|
437
|
+
|
438
|
+
transforms.each do |transform|
|
439
|
+
next unless transform.size == 2
|
440
|
+
p = transform.first
|
441
|
+
r = transform.last
|
442
|
+
utf_t = utf_t.gsub(eval(p), r) rescue utf_t
|
443
|
+
end
|
444
|
+
|
445
|
+
return type, utf_t
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Helper for process_media template method to transform text.
|
450
|
+
|
451
|
+
def transform_text_part(part)
|
452
|
+
type = part.part_type?
|
453
|
+
text = part.body.strip
|
454
|
+
transform_text(type, text)
|
455
|
+
end
|
456
|
+
|
457
|
+
##
|
458
|
+
# Helper for process template method to name a temporary filepath based on
|
459
|
+
# information in the part. This version attempts to honor the name of the
|
460
|
+
# media as labeled in the part header and creates a unique temporary
|
461
|
+
# directory for writing the file so filename collision does not occur.
|
462
|
+
# Consumers of this method expect the directory structure to the file
|
463
|
+
# exists, if the method is overridden it is mandatory that this behavior is
|
464
|
+
# retained.
|
465
|
+
|
466
|
+
def temp_file(part)
|
467
|
+
file_name = filename?(part)
|
468
|
+
File.join(msg_tmp_dir(),File.basename(file_name))
|
469
|
+
end
|
470
|
+
|
471
|
+
##
|
472
|
+
# Purges the unique MMS2R::Media.media_dir directory created
|
473
|
+
# for this producer and all of the media that it contains.
|
474
|
+
|
475
|
+
def purge()
|
476
|
+
log("#{self.class} purging #{@media_dir} and all its contents", :info)
|
477
|
+
FileUtils.rm_rf(@media_dir)
|
478
|
+
end
|
479
|
+
|
480
|
+
##
|
481
|
+
# Helper to add a file to the media hash.
|
482
|
+
|
483
|
+
def add_file(type, file)
|
484
|
+
media[type] = [] unless media[type]
|
485
|
+
media[type] << file
|
486
|
+
end
|
487
|
+
|
488
|
+
##
|
489
|
+
# Helper to temp_file to create a unique temporary directory that is a
|
490
|
+
# child of tmp_dir This version is based on the message_id of the mail.
|
491
|
+
|
492
|
+
def msg_tmp_dir()
|
493
|
+
@dir_count += 1
|
494
|
+
dir = File.join(@media_dir, "#{@dir_count}")
|
495
|
+
FileUtils.mkdir_p(dir)
|
496
|
+
dir
|
497
|
+
end
|
498
|
+
|
499
|
+
##
|
500
|
+
# returns a filename declared for a part, or a default if its not defined
|
501
|
+
|
502
|
+
def filename?(part)
|
503
|
+
name = part.sub_header("content-type", "name") ||
|
504
|
+
part.sub_header("content-disposition", "filename") ||
|
505
|
+
(part['content-location'] && part['content-location'].to_s.strip)
|
506
|
+
if (name.nil? || name.empty?)
|
507
|
+
if part['content-id'] && part['content-id'].real_body.strip =~ /^<(.+)>$/
|
508
|
+
name = $1
|
509
|
+
else
|
510
|
+
name = "#{Time.now.to_f}.#{self.default_ext(part.part_type?)}"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
# XXX fwiw, janky look for dot extension 1 to 4 chars long
|
514
|
+
name = (name =~ /\..{1,4}$/ ? name : "#{name}.#{self.default_ext(part.part_type?)}")
|
515
|
+
|
516
|
+
# handle excessively large filenames
|
517
|
+
if name.size > 255
|
518
|
+
ext = File.extname(name)
|
519
|
+
base = File.basename(name, ext)
|
520
|
+
name = "#{base[0, 255 - ext.size]}#{ext}"
|
521
|
+
end
|
522
|
+
|
523
|
+
name
|
524
|
+
end
|
525
|
+
|
526
|
+
def aliases
|
527
|
+
@aliases
|
528
|
+
end
|
529
|
+
|
530
|
+
##
|
531
|
+
# Best guess of the mobile device type. Simple heuristics thus far for
|
532
|
+
# :iphone :blackberry :handset :unknown . Could be expanded for exif
|
533
|
+
# probing or shifting mail header schemes
|
534
|
+
|
535
|
+
def device_type?
|
536
|
+
headers = config['device_types']['headers'] rescue {}
|
537
|
+
headers.keys.each do |header|
|
538
|
+
if mail.header[header.downcase]
|
539
|
+
regex, type = headers[header]
|
540
|
+
return type if mail.header[header.downcase].to_s =~ regex
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
return :handset if File.exist?(File.join(self.conf_dir,
|
545
|
+
"#{self.aliases[self.carrier] || self.carrier}.yml"))
|
546
|
+
|
547
|
+
:unknown
|
548
|
+
end
|
549
|
+
|
550
|
+
##
|
551
|
+
# The source of the MMS was some sort of mobile or smart phone
|
552
|
+
|
553
|
+
def is_mobile?
|
554
|
+
self.device_type? != :unknown
|
555
|
+
end
|
556
|
+
|
557
|
+
##
|
558
|
+
# Get the temporary directory where media files are written to.
|
559
|
+
|
560
|
+
def self.tmp_dir
|
561
|
+
@@tmp_dir ||= File.join(Dir.tmpdir, (ENV['USER'].nil? ? '':ENV['USER']), 'mms2r')
|
562
|
+
end
|
563
|
+
|
564
|
+
##
|
565
|
+
# Set the temporary directory where media files are written to.
|
566
|
+
def self.tmp_dir=(d)
|
567
|
+
@@tmp_dir=d
|
568
|
+
end
|
569
|
+
|
570
|
+
##
|
571
|
+
# Get the directory where conf files are stored.
|
572
|
+
|
573
|
+
def self.conf_dir
|
574
|
+
@@conf_dir ||= File.join(File.dirname(__FILE__), '..', '..', 'conf')
|
575
|
+
end
|
576
|
+
|
577
|
+
##
|
578
|
+
# Set the directory where conf files are stored.
|
579
|
+
|
580
|
+
def self.conf_dir=(d)
|
581
|
+
@@conf_dir=d
|
582
|
+
end
|
583
|
+
|
584
|
+
##
|
585
|
+
# Helper to create a safe directory path element based on the mail message
|
586
|
+
# id.
|
587
|
+
|
588
|
+
def self.safe_message_id(mid)
|
589
|
+
mid.nil? ? "#{Time.now.to_i}" : mid.gsub(/\$|<|>|@|\./, "")
|
590
|
+
end
|
591
|
+
|
592
|
+
##
|
593
|
+
# Returns a default file extension based on a content type
|
594
|
+
|
595
|
+
def self.default_ext(content_type)
|
596
|
+
if MMS2R::EXT[content_type]
|
597
|
+
MMS2R::EXT[content_type]
|
598
|
+
elsif content_type
|
599
|
+
content_type.split('/').last
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
##
|
604
|
+
# Joins the generic mms2r configuration with the carrier specific
|
605
|
+
# configuration.
|
606
|
+
|
607
|
+
def self.initialize_config(c)
|
608
|
+
f = File.join(self.conf_dir(), "mms2r_media.yml")
|
609
|
+
conf = YAML::load_file(f) rescue {}
|
610
|
+
conf['ignore'] ||= {} unless conf['ignore']
|
611
|
+
conf['transform'] = {} unless conf['transform']
|
612
|
+
conf['number'] = [] unless conf['number']
|
613
|
+
return conf unless c
|
614
|
+
|
615
|
+
kinds = ['ignore', 'transform']
|
616
|
+
|
617
|
+
kinds.each do |kind|
|
618
|
+
if c[kind]
|
619
|
+
c[kind].each do |type,array|
|
620
|
+
conf[kind][type] = [] unless conf[kind][type]
|
621
|
+
conf[kind][type] += array
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
conf['number'] = c['number'] if c['number']
|
626
|
+
|
627
|
+
conf
|
628
|
+
end
|
629
|
+
|
630
|
+
def log(message, level = :info)
|
631
|
+
@logger.send(level, message) unless @logger.nil?
|
632
|
+
end
|
633
|
+
|
634
|
+
##
|
635
|
+
# convenience accessor for self.class.conf_dir
|
636
|
+
|
637
|
+
def conf_dir
|
638
|
+
self.class.conf_dir
|
639
|
+
end
|
640
|
+
|
641
|
+
##
|
642
|
+
# convenience accessor for self.class.conf_dir
|
643
|
+
|
644
|
+
def tmp_dir
|
645
|
+
self.class.tmp_dir
|
646
|
+
end
|
647
|
+
|
648
|
+
##
|
649
|
+
# convenience accessor for self.class.default_ext
|
650
|
+
|
651
|
+
def default_ext(type)
|
652
|
+
self.class.default_ext(type)
|
653
|
+
end
|
654
|
+
|
655
|
+
##
|
656
|
+
# convenience accessor for self.class.safe_message_id
|
657
|
+
|
658
|
+
def safe_message_id(message_id)
|
659
|
+
self.class.safe_message_id(message_id)
|
660
|
+
end
|
661
|
+
|
662
|
+
##
|
663
|
+
# convenience accessor for self.class.initialize_confg
|
664
|
+
|
665
|
+
def initialize_config(config)
|
666
|
+
self.class.initialize_config(config)
|
667
|
+
end
|
668
|
+
|
669
|
+
private
|
670
|
+
|
671
|
+
##
|
672
|
+
# accessor for the config
|
673
|
+
|
674
|
+
def config
|
675
|
+
@config
|
676
|
+
end
|
677
|
+
|
678
|
+
##
|
679
|
+
# guess content type from filename
|
680
|
+
|
681
|
+
def type_from_filename(filename)
|
682
|
+
ext = filename.split('.').last
|
683
|
+
ent = MMS2R::EXT.detect{|k,v| v == ext}
|
684
|
+
ent.nil? ? nil : ent.first
|
685
|
+
end
|
686
|
+
|
687
|
+
##
|
688
|
+
# used by #default_media and #text to return the biggest attachment type
|
689
|
+
# listed in the types array
|
690
|
+
|
691
|
+
def attachment(types)
|
692
|
+
|
693
|
+
# get all the files that are of the major types passed in
|
694
|
+
files = []
|
695
|
+
|
696
|
+
types.each do |type|
|
697
|
+
media.keys.find_all{|k| type.include?("/") ? k == type : k.index(type) == 0 }.each do |key|
|
698
|
+
files += media[key]
|
699
|
+
end
|
700
|
+
end
|
701
|
+
return nil if files.empty?
|
702
|
+
|
703
|
+
# set temp holders
|
704
|
+
file = nil # explicitly declare the file and size
|
705
|
+
size = 0
|
706
|
+
mime_type = nil
|
707
|
+
|
708
|
+
#get the largest file
|
709
|
+
files.each do |f|
|
710
|
+
if File.size(f) > size
|
711
|
+
size = File.size(f)
|
712
|
+
file = File.new(f)
|
713
|
+
mime_type = media.detect{|type,files| files.detect{|fl| fl == f}}[0]
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
return nil if file.nil?
|
718
|
+
|
719
|
+
# These singleton methods implement the interface necessary to be used
|
720
|
+
# as a drop-in replacement for files uploaded with CGI.rb.
|
721
|
+
# This helps if you want to use the files with, for example,
|
722
|
+
# attachment_fu.
|
723
|
+
|
724
|
+
def file.local_path
|
725
|
+
self.path
|
726
|
+
end
|
727
|
+
|
728
|
+
def file.original_filename
|
729
|
+
File.basename(self.path)
|
730
|
+
end
|
731
|
+
|
732
|
+
def file.size
|
733
|
+
File.size(self.path)
|
734
|
+
end
|
735
|
+
|
736
|
+
# this one is kind of confusing because it needs a closure.
|
737
|
+
class << file
|
738
|
+
self
|
739
|
+
end.send(:define_method, :content_type) { mime_type }
|
740
|
+
|
741
|
+
file
|
742
|
+
end
|
743
|
+
|
744
|
+
end
|
745
|
+
|
746
|
+
end
|