brendanlim-mms2r 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. data/.gitignore +3 -0
  2. data/History.txt +292 -0
  3. data/Manifest.txt +169 -0
  4. data/README.txt +202 -0
  5. data/Rakefile +36 -0
  6. data/conf/1nbox.net.yml +13 -0
  7. data/conf/aliases.yml +17 -0
  8. data/conf/bellsouth.net.yml +2 -0
  9. data/conf/from.yml +2 -0
  10. data/conf/mediamessaging.o2.co.uk.yml +4 -0
  11. data/conf/messaging.nextel.com.yml +10 -0
  12. data/conf/mms.3ireland.ie.yml +17 -0
  13. data/conf/mms.ae.yml +5 -0
  14. data/conf/mms.alltel.com.yml +15 -0
  15. data/conf/mms.att.net.yml +11 -0
  16. data/conf/mms.dobson.net.yml +4 -0
  17. data/conf/mms.luxgsm.lu.yml +13 -0
  18. data/conf/mms.mobileiam.ma.yml +16 -0
  19. data/conf/mms.mtn.co.za.yml +13 -0
  20. data/conf/mms.mycricket.com.yml +4 -0
  21. data/conf/mms.myhelio.com.yml +12 -0
  22. data/conf/mms.netcom.no.yml +4 -0
  23. data/conf/mms.o2online.de.yml +8 -0
  24. data/conf/mms.three.co.uk.yml +15 -0
  25. data/conf/mms.vodacom4me.co.za.yml +13 -0
  26. data/conf/mms2r_media.yml +24 -0
  27. data/conf/mobile.indosat.net.id.yml +14 -0
  28. data/conf/msg.telus.com.yml +6 -0
  29. data/conf/orangemms.net.yml +13 -0
  30. data/conf/pm.sprint.com.yml +12 -0
  31. data/conf/pxt.vodafone.net.nz.yml +5 -0
  32. data/conf/rci.rogers.com.yml +18 -0
  33. data/conf/sms.sasktel.com.yml +12 -0
  34. data/conf/tmomail.net.yml +28 -0
  35. data/conf/txt.bell.ca.yml +4 -0
  36. data/conf/unicel.com.yml +6 -0
  37. data/conf/vmpix.com.yml +11 -0
  38. data/conf/vzwpix.com.yml +8 -0
  39. data/conf/waw.plspictures.com.yml +15 -0
  40. data/dev_tools/anonymizer.rb +20 -0
  41. data/dev_tools/debug_sprint_hpricot_parsing.rb +82 -0
  42. data/dev_tools/github.rb +8 -0
  43. data/lib/mms2r.rb +58 -0
  44. data/lib/mms2r/media.rb +746 -0
  45. data/lib/mms2r/media/sprint.rb +189 -0
  46. data/lib/tmail_ext.rb +17 -0
  47. data/mms2r.gemspec +41 -0
  48. data/test/fixtures/1nbox-2images-01.mail +738 -0
  49. data/test/fixtures/1nbox-2images-02.mail +736 -0
  50. data/test/fixtures/1nbox-2images-03.mail +736 -0
  51. data/test/fixtures/1nbox-2images-04.mail +732 -0
  52. data/test/fixtures/3ireland-mms-01.mail +671 -0
  53. data/test/fixtures/alltel-image-01.mail +319 -0
  54. data/test/fixtures/alltel-mms-01.mail +270 -0
  55. data/test/fixtures/alltel-mms-03.mail +270 -0
  56. data/test/fixtures/apple-double-image-01.mail +53 -0
  57. data/test/fixtures/att-blackberry-02.mail +54 -0
  58. data/test/fixtures/att-blackberry.mail +60 -0
  59. data/test/fixtures/att-image-01.mail +37 -0
  60. data/test/fixtures/att-image-02.mail +37 -0
  61. data/test/fixtures/att-iphone-01.mail +73 -0
  62. data/test/fixtures/att-iphone-02.mail +61 -0
  63. data/test/fixtures/att-iphone-03.mail +61 -0
  64. data/test/fixtures/att-text-01.mail +27 -0
  65. data/test/fixtures/bell-canada-image-01.mail +2273 -0
  66. data/test/fixtures/cingularme-text-01.mail +13 -0
  67. data/test/fixtures/cingularme-text-02.mail +14 -0
  68. data/test/fixtures/dobson-image-01.mail +64 -0
  69. data/test/fixtures/dot.jpg +0 -0
  70. data/test/fixtures/generic.mail +50 -0
  71. data/test/fixtures/helio-image-01.mail +348 -0
  72. data/test/fixtures/helio-message-01.mail +327 -0
  73. data/test/fixtures/iconv-fr-text-01.mail +50 -0
  74. data/test/fixtures/indosat-image-01.mail +751 -0
  75. data/test/fixtures/indosat-image-02.mail +751 -0
  76. data/test/fixtures/info2go-image-01.mail +43 -0
  77. data/test/fixtures/luxgsm-image-01.mail +388 -0
  78. data/test/fixtures/maroctelecom-france-mms-01.mail +511 -0
  79. data/test/fixtures/mediamessaging_o2_co_uk-image-01.mail +61 -0
  80. data/test/fixtures/mmode-image-01.mail +36 -0
  81. data/test/fixtures/mms.ae-image-01.mail +45 -0
  82. data/test/fixtures/mms.mycricket.com-pic-and-text.mail +87 -0
  83. data/test/fixtures/mms.mycricket.com-pic.mail +78 -0
  84. data/test/fixtures/mmsreply.t-mobile.co.uk-text-image-01.mail +431 -0
  85. data/test/fixtures/mobile.mycingular.com-text-01.mail +25 -0
  86. data/test/fixtures/mtn-southafrica-mms.mail +127 -0
  87. data/test/fixtures/mycingular-image-01.mail +66 -0
  88. data/test/fixtures/netcom-image-01.mail +63 -0
  89. data/test/fixtures/nextel-image-01.mail +46 -0
  90. data/test/fixtures/nextel-image-02.mail +43 -0
  91. data/test/fixtures/nextel-image-03.mail +44 -0
  92. data/test/fixtures/nextel-image-04.mail +43 -0
  93. data/test/fixtures/o2-de-image-01.mail +134 -0
  94. data/test/fixtures/orange-uk-image-01.mail +71 -0
  95. data/test/fixtures/orangefrance-text-and-image.mail +68 -0
  96. data/test/fixtures/orangepoland-text-01.mail +7 -0
  97. data/test/fixtures/orangepoland-text-02.mail +7 -0
  98. data/test/fixtures/pics.cingularme.com-image-01.mail +44 -0
  99. data/test/fixtures/pxt-image-01.mail +118 -0
  100. data/test/fixtures/rogers-canada-mms-01.mail +1029 -0
  101. data/test/fixtures/sasktel-image-01.mail +1791 -0
  102. data/test/fixtures/sprint-broken-image-01.mail +195 -0
  103. data/test/fixtures/sprint-image-01.mail +211 -0
  104. data/test/fixtures/sprint-pcs-text-01.mail +8 -0
  105. data/test/fixtures/sprint-purged-image-01.mail +195 -0
  106. data/test/fixtures/sprint-text-01.mail +195 -0
  107. data/test/fixtures/sprint-two-images-01.mail +231 -0
  108. data/test/fixtures/sprint-video-01.mail +210 -0
  109. data/test/fixtures/sprint.mov +0 -0
  110. data/test/fixtures/suncom-blackberry.mail +43 -0
  111. data/test/fixtures/telus-image-01.mail +89 -0
  112. data/test/fixtures/three-uk-image-01.mail +684 -0
  113. data/test/fixtures/tmo.blackberry.net-image-01.mail +48 -0
  114. data/test/fixtures/tmobile-blackberry-02.mail +50 -0
  115. data/test/fixtures/tmobile-blackberry.mail +44 -0
  116. data/test/fixtures/tmobile-image-01.mail +145 -0
  117. data/test/fixtures/tmobile-image-02.mail +145 -0
  118. data/test/fixtures/unicel-image-01.mail +60 -0
  119. data/test/fixtures/verizon-blackberry.mail +53 -0
  120. data/test/fixtures/verizon-image-01.mail +44 -0
  121. data/test/fixtures/verizon-image-02.mail +50 -0
  122. data/test/fixtures/verizon-image-03.mail +53 -0
  123. data/test/fixtures/verizon-text-01.mail +10 -0
  124. data/test/fixtures/verizon-video-01.mail +336 -0
  125. data/test/fixtures/virgin-mobile-image-01.mail +73 -0
  126. data/test/fixtures/virgin.ca-text-01.mail +21 -0
  127. data/test/fixtures/vodacom4me-co-za-01.mail +104 -0
  128. data/test/fixtures/vodacom4me-co-za-02.mail +108 -0
  129. data/test/fixtures/vodacom4me-southafrica-mms-01.mail +152 -0
  130. data/test/fixtures/vodacom4me-southafrica-mms-04.mail +655 -0
  131. data/test/fixtures/vtext-text-01.mail +10 -0
  132. data/test/fixtures/waw.plspictures.com-image-01.mail +1611 -0
  133. data/test/test_1nbox_net.rb +78 -0
  134. data/test/test_bell_canada.rb +44 -0
  135. data/test/test_bellsouth_net.rb +22 -0
  136. data/test/test_github.rb +12 -0
  137. data/test/test_helper.rb +58 -0
  138. data/test/test_mediamessaging_o2_co_uk.rb +52 -0
  139. data/test/test_messaging_nextel_com.rb +93 -0
  140. data/test/test_messaging_sprintpcs_com.rb +26 -0
  141. data/test/test_mms2r_media.rb +760 -0
  142. data/test/test_mms_3ireland_ie.rb +25 -0
  143. data/test/test_mms_ae.rb +46 -0
  144. data/test/test_mms_alltel_com.rb +53 -0
  145. data/test/test_mms_att_net.rb +142 -0
  146. data/test/test_mms_dobson_net.rb +41 -0
  147. data/test/test_mms_luxgsm_lu.rb +58 -0
  148. data/test/test_mms_mobileiam_ma.rb +26 -0
  149. data/test/test_mms_mtn_co_za.rb +25 -0
  150. data/test/test_mms_mycricket_com.rb +60 -0
  151. data/test/test_mms_myhelio_com.rb +62 -0
  152. data/test/test_mms_netcom_no.rb +52 -0
  153. data/test/test_mms_o2online_de.rb +53 -0
  154. data/test/test_mms_three_co_uk.rb +42 -0
  155. data/test/test_mms_vodacom4me_co_za.rb +107 -0
  156. data/test/test_mobile_indosat_net_id.rb +52 -0
  157. data/test/test_msg_telus_com.rb +28 -0
  158. data/test/test_orangemms_net.rb +100 -0
  159. data/test/test_pm_sprint_com.rb +202 -0
  160. data/test/test_pxt_vodafone_net_nz.rb +37 -0
  161. data/test/test_rci_rogers_com.rb +28 -0
  162. data/test/test_sms_sasktel_com.rb +28 -0
  163. data/test/test_tmomail_net.rb +108 -0
  164. data/test/test_unicel_com.rb +49 -0
  165. data/test/test_vmpix_com.rb +56 -0
  166. data/test/test_vzwpix_com.rb +131 -0
  167. data/test/test_waw_plspictures_com.rb +27 -0
  168. data/vendor/plugins/mms2r/lib/autotest/discover.rb +3 -0
  169. data/vendor/plugins/mms2r/lib/autotest/mms2r.rb +38 -0
  170. 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,14 @@
1
+ ---
2
+ ignore:
3
+ text/plain:
4
+ - /\A\.\z/im
5
+ - .
6
+ text/html:
7
+ - /I-Memova - Email Praktis. Cara Taktis. Powered by IM2./i
8
+ image/jpeg:
9
+ - cp__newBanner.jpg
10
+ number:
11
+ - return-path
12
+ - /^<([^@]+)@.*/
13
+ - "\1"
14
+
@@ -0,0 +1,6 @@
1
+ ---
2
+ ignore:
3
+ text/plain:
4
+ - /This is an MMS message. Please go to http/m
5
+ text/html:
6
+ - /You've received a Message from a TELUS phone./m
@@ -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,5 @@
1
+ ---
2
+ transform:
3
+ text/plain:
4
+ - - /^You have a PXT from \d+$/i
5
+ - ""
@@ -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,12 @@
1
+ ---
2
+ ignore:
3
+ text/html:
4
+ - /.*/im
5
+ text/plain:
6
+ - /^You have received a New Message/i
7
+ image/gif:
8
+ - /.*/im
9
+ transform:
10
+ text/plain:
11
+ - - /^A Picture\/Video Message!$/i
12
+ - ""
@@ -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"
@@ -0,0 +1,4 @@
1
+ transform:
2
+ text/html:
3
+ - [/.*(<html)/im, "\1"]
4
+
@@ -0,0 +1,6 @@
1
+ ---
2
+ transform:
3
+ text/plain:
4
+ - - /^Unicel$/i
5
+ - ""
6
+
@@ -0,0 +1,11 @@
1
+ ---
2
+ ignore:
3
+ text/html:
4
+ - /This message is from\s+a Virgin Mobile user. Enjoy./mi
5
+ text/plain:
6
+ - /^This is an MMS message\.$/i
7
+
8
+ transform:
9
+ text/plain:
10
+ - - /(.*?)-+\s+Sent by Text Messaging from a mobile device.*/im
11
+ - "\1"
@@ -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=&quot;MDN&quot;&gt;(\d+)&lt;/.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
@@ -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)
@@ -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