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.
Files changed (55) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +54 -0
  3. data/README.txt +81 -0
  4. data/Rakefile +30 -0
  5. data/conf/mms2r_cingularmedia_transform.yml +6 -0
  6. data/conf/mms2r_sprintmedia_ignore.yml +10 -0
  7. data/conf/mms2r_tmobilemedia_ignore.yml +17 -0
  8. data/conf/mms2r_verizonmedia_ignore.yml +3 -0
  9. data/lib/mms2r.rb +3 -0
  10. data/lib/mms2r/cingular_media.rb +11 -0
  11. data/lib/mms2r/media.rb +345 -0
  12. data/lib/mms2r/mmode_media.rb +13 -0
  13. data/lib/mms2r/sprint_media.rb +50 -0
  14. data/lib/mms2r/tmobile_media.rb +11 -0
  15. data/lib/mms2r/verizon_media.rb +11 -0
  16. data/lib/mms2r/version.rb +12 -0
  17. data/lib/vendor/text/format.rb +1466 -0
  18. data/lib/vendor/tmail.rb +3 -0
  19. data/lib/vendor/tmail/address.rb +242 -0
  20. data/lib/vendor/tmail/attachments.rb +39 -0
  21. data/lib/vendor/tmail/base64.rb +71 -0
  22. data/lib/vendor/tmail/config.rb +69 -0
  23. data/lib/vendor/tmail/encode.rb +467 -0
  24. data/lib/vendor/tmail/facade.rb +552 -0
  25. data/lib/vendor/tmail/header.rb +914 -0
  26. data/lib/vendor/tmail/info.rb +35 -0
  27. data/lib/vendor/tmail/loader.rb +1 -0
  28. data/lib/vendor/tmail/mail.rb +447 -0
  29. data/lib/vendor/tmail/mailbox.rb +433 -0
  30. data/lib/vendor/tmail/mbox.rb +1 -0
  31. data/lib/vendor/tmail/net.rb +280 -0
  32. data/lib/vendor/tmail/obsolete.rb +135 -0
  33. data/lib/vendor/tmail/parser.rb +1522 -0
  34. data/lib/vendor/tmail/port.rb +377 -0
  35. data/lib/vendor/tmail/quoting.rb +131 -0
  36. data/lib/vendor/tmail/scanner.rb +41 -0
  37. data/lib/vendor/tmail/scanner_r.rb +263 -0
  38. data/lib/vendor/tmail/stringio.rb +277 -0
  39. data/lib/vendor/tmail/tmail.rb +1 -0
  40. data/lib/vendor/tmail/utils.rb +238 -0
  41. data/test/files/dot.jpg +0 -0
  42. data/test/files/sprint-image-01.mail +195 -0
  43. data/test/files/sprint-text-01.mail +8 -0
  44. data/test/files/sprint-video-01.mail +195 -0
  45. data/test/files/sprint.mov +0 -0
  46. data/test/files/verizon-image-01.mail +815 -0
  47. data/test/files/verizon-text-01.mail +11 -0
  48. data/test/files/verizon-video-01.mail +336 -0
  49. data/test/test_mms2r_cingular.rb +52 -0
  50. data/test/test_mms2r_media.rb +311 -0
  51. data/test/test_mms2r_mmode.rb +52 -0
  52. data/test/test_mms2r_sprint.rb +154 -0
  53. data/test/test_mms2r_tmobile.rb +169 -0
  54. data/test/test_mms2r_verizon.rb +74 -0
  55. metadata +130 -0
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2007-03-06
2
+
3
+ * Birthday!
4
+
@@ -0,0 +1,54 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ conf/mms2r_cingularmedia_transform.yml
6
+ conf/mms2r_sprintmedia_ignore.yml
7
+ conf/mms2r_tmobilemedia_ignore.yml
8
+ conf/mms2r_verizonmedia_ignore.yml
9
+ lib/mms2r.rb
10
+ lib/mms2r/cingular_media.rb
11
+ lib/mms2r/media.rb
12
+ lib/mms2r/mmode_media.rb
13
+ lib/mms2r/sprint_media.rb
14
+ lib/mms2r/tmobile_media.rb
15
+ lib/mms2r/verizon_media.rb
16
+ lib/mms2r/version.rb
17
+ lib/vendor/text/format.rb
18
+ lib/vendor/tmail.rb
19
+ lib/vendor/tmail/address.rb
20
+ lib/vendor/tmail/attachments.rb
21
+ lib/vendor/tmail/base64.rb
22
+ lib/vendor/tmail/config.rb
23
+ lib/vendor/tmail/encode.rb
24
+ lib/vendor/tmail/facade.rb
25
+ lib/vendor/tmail/header.rb
26
+ lib/vendor/tmail/info.rb
27
+ lib/vendor/tmail/loader.rb
28
+ lib/vendor/tmail/mail.rb
29
+ lib/vendor/tmail/mailbox.rb
30
+ lib/vendor/tmail/mbox.rb
31
+ lib/vendor/tmail/net.rb
32
+ lib/vendor/tmail/obsolete.rb
33
+ lib/vendor/tmail/parser.rb
34
+ lib/vendor/tmail/port.rb
35
+ lib/vendor/tmail/quoting.rb
36
+ lib/vendor/tmail/scanner.rb
37
+ lib/vendor/tmail/scanner_r.rb
38
+ lib/vendor/tmail/stringio.rb
39
+ lib/vendor/tmail/tmail.rb
40
+ lib/vendor/tmail/utils.rb
41
+ test/files/dot.jpg
42
+ test/files/sprint-image-01.mail
43
+ test/files/sprint-text-01.mail
44
+ test/files/sprint-video-01.mail
45
+ test/files/sprint.mov
46
+ test/files/verizon-image-01.mail
47
+ test/files/verizon-text-01.mail
48
+ test/files/verizon-video-01.mail
49
+ test/test_mms2r_cingular.rb
50
+ test/test_mms2r_media.rb
51
+ test/test_mms2r_mmode.rb
52
+ test/test_mms2r_sprint.rb
53
+ test/test_mms2r_tmobile.rb
54
+ test/test_mms2r_verizon.rb
@@ -0,0 +1,81 @@
1
+ mms2r
2
+
3
+ by Mike Mondragon
4
+
5
+ http://mms2r.rubyforge.org/
6
+
7
+ == DESCRIPTION:
8
+
9
+ MMS2R is a library that decodes the parts of an MMS message to disk while
10
+ stripping out advertising injected by the cellphone carriers. MMS messages are
11
+ multipart email and the carriers often inject branding into these messages.
12
+
13
+ If MMS2R is not aware of a particular carrier no extra processing is done
14
+ to the MMS other than decoding and consolidating its media.
15
+
16
+ Contact the author to add additional carriers to be processed by the library.
17
+
18
+ == FEATURES/PROBLEMS:
19
+
20
+ Corpus of carriers currently processed by MMS2R:
21
+
22
+ * AT&T/Cingular => mmode.com
23
+ * Cingular => mms.mycingular.com
24
+ * Sprint => pm.sprint.com
25
+ * Sprint => messaging.sprintpcs.com
26
+ * T-Mobile => tmomail.net
27
+ * Verizon => vzwpix.com
28
+
29
+ == SYNOPSIS:
30
+
31
+ require 'rubygems'
32
+ require 'mms2r'
33
+ require 'tmail'
34
+ media = TMail::Media.parse(IO.readlines("mymail.file").join)
35
+ mms = MMS2R::Media.create(mail,Logger.new(STDOUT))
36
+ mms.process
37
+ # mms.media is a hash that is indexed by mime-type
38
+ # the mime-type key returns an array of filepaths
39
+ # to media in the MMS that was of that type
40
+ mms.media['image/jpeg'].each {|f| puts "${f}"}
41
+ mms.media['text/plain'].each {|f| puts "${f}"}
42
+ text = File.open(mms.media['text/plain'][0], 'rb') { |file|
43
+ file.read
44
+ }
45
+ puts text
46
+ #remove the media that was put to temporary disk
47
+ #mms.purge
48
+
49
+ == REQUIREMENTS:
50
+
51
+ * TMail
52
+ * Hpricot
53
+
54
+ == INSTALL:
55
+
56
+ * sudo gem install mms2r
57
+
58
+ == LICENSE:
59
+
60
+ (The MIT License)
61
+
62
+ Copyright (c) 2007 Mike Mondragon. All rights reserved.
63
+
64
+ Permission is hereby granted, free of charge, to any person obtaining
65
+ a copy of this software and associated documentation files (the
66
+ 'Software'), to deal in the Software without restriction, including
67
+ without limitation the rights to use, copy, modify, merge, publish,
68
+ distribute, sublicense, and/or sell copies of the Software, and to
69
+ permit persons to whom the Software is furnished to do so, subject to
70
+ the following conditions:
71
+
72
+ The above copyright notice and this permission notice shall be
73
+ included in all copies or substantial portions of the Software.
74
+
75
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
76
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
77
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
78
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
79
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
80
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
81
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'rcov/rcovtask'
6
+
7
+ $LOAD_PATH.unshift 'lib'
8
+ require 'mms2r'
9
+ require 'mms2r/media'
10
+
11
+ Hoe.new('mms2r', MMS2R::Media::VERSION) do |p|
12
+ p.rubyforge_name = 'mms2r'
13
+ p.author = 'Mike Mondragon'
14
+ p.email = 'mike@mondragon.cc'
15
+ p.summary = 'Extract media from MMS '
16
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
17
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
18
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
19
+ p.extra_deps << ['hpricot']
20
+ p.extra_deps << ['rcov']
21
+ p.clean_globs << 'coverage'
22
+ end
23
+
24
+ Rcov::RcovTask.new do |t|
25
+ t.test_files = FileList['test/test*.rb']
26
+ t.verbose = true
27
+ t.rcov_opts << "--exclude rcov.rb,hpricot.rb,hpricot/.*\.rb"
28
+ end
29
+
30
+ # vim: syntax=Ruby
@@ -0,0 +1,6 @@
1
+ ---
2
+ text/plain:
3
+ "--\n\
4
+ ===============================================\n\
5
+ Brought to you by, Cingular Wireless Messaging\n\
6
+ http://www.CingularMe.COM/\n ": ""
@@ -0,0 +1,10 @@
1
+ ---
2
+ text/plain:
3
+ - |-
4
+ You have new Picture Mail!
5
+
6
+ Click Go/View to see now.
7
+ - |-
8
+ You have new Video Mail!
9
+
10
+ Click Go/View to see now.
@@ -0,0 +1,17 @@
1
+ ---
2
+ text/html:
3
+ - |
4
+ <html>
5
+ <head>
6
+ <title>T-Mobile</title>
7
+ image/gif:
8
+ - dottedline350.gif
9
+ - dottedline600.gif
10
+ - dottedLine_350.gif
11
+ - dottedLine_600.gif
12
+ - masthead.jpg
13
+ - spacer.gif
14
+ - video.gif
15
+ - audio.gif
16
+ - tmobilelogo.gif
17
+ - tmobilespace.gif
@@ -0,0 +1,3 @@
1
+ ---
2
+ text/plain:
3
+ - This message was sent using PIX-FLIX Messaging service from Verizon Wireless!
@@ -0,0 +1,3 @@
1
+ $:.unshift(File.dirname(__FILE__) + "/vendor/")
2
+ module MMS2R # :nodoc:
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'mms2r'
2
+ require 'mms2r/media'
3
+
4
+ module MMS2R
5
+
6
+ ##
7
+ # Cingular version of MMS2R::Media
8
+
9
+ class MMS2R::CingularMedia < MMS2R::Media
10
+ end
11
+ end
@@ -0,0 +1,345 @@
1
+ # Copyright (c) 2007 by Mike Mondragon ()
2
+ #
3
+ # Please see the LICENSE file for licensing.
4
+
5
+ require 'fileutils'
6
+ require 'pathname'
7
+ require 'tmpdir'
8
+ require 'yaml'
9
+ require 'mms2r'
10
+ require 'mms2r/version'
11
+ require 'mms2r/cingular_media'
12
+ require 'mms2r/mmode_media'
13
+ require 'mms2r/sprint_media'
14
+ require 'mms2r/tmobile_media'
15
+ require 'mms2r/verizon_media'
16
+
17
+ ##
18
+ # MMS2R is a library to collect media files from MMS messages. MMS messages
19
+ # are multipart emails and cellphone carriers often inject branding into these
20
+ # messages. MMS2R strips the advertising from an MMS leaving the actual user
21
+ # generated media.
22
+ #
23
+ # If you encounter MMS from a carrier that contains advertising other non-
24
+ # standard media features submit a sample to the author for inclusion in this
25
+ # project.
26
+ #
27
+ # The create method is a factory method to create MMS2R::Media
28
+ # Custom media producers can be pushed into the factory via the
29
+ # MMS2R::CARRIER_CLASSES Hash, e.g.
30
+ #
31
+ # class MMS2R::FakeCarrier < MMS2R::Media; end
32
+ # MMS2R::CARRIER_CLASSES['mms.fakecarrier.com'] = MMS2R::FakeCarrier
33
+ # ...
34
+ # media = MMS2R::Media.create(some_tmail) #media will be a MMS2R::FakeCarrier
35
+
36
+ module MMS2R
37
+
38
+ ##
39
+ # A hash of file extentions for common mimetypes
40
+ EXT = {
41
+ 'text/plain' => 'txt',
42
+ 'text/html' => 'html',
43
+ 'image/png' => 'png',
44
+ 'image/gif' => 'gif',
45
+ 'image/jpeg' => 'jpg',
46
+ 'video/quicktime' => 'mov',
47
+ 'video/3gpp2' => '3g2'
48
+ }
49
+
50
+ ##
51
+ # A hash of carriers that MMS2r is currently aware of.
52
+ # The factory create method uses the hostname portion
53
+ # of an MMS's from to select the correct type of MMS2R::Media
54
+ # product. If a specific media product is not available
55
+ # MMS2R::Media should be used.
56
+
57
+ CARRIER_CLASSES = {
58
+ 'mms.mycingular.com' => MMS2R::CingularMedia,
59
+ 'mmode.com' => MMS2R::MModeMedia,
60
+ 'pm.sprint.com' => MMS2R::SprintMedia,
61
+ 'messaging.sprintpcs.com' => MMS2R::SprintMedia,
62
+ 'tmomail.net' => MMS2R::TMobileMedia,
63
+ 'vzwpix.com' => MMS2R::VerizonMedia
64
+ }
65
+
66
+ class MMS2R::Media
67
+
68
+ ##
69
+ # TMail object that the media files were derived from.
70
+ attr_reader :mail
71
+
72
+ ##
73
+ # media returns the hash of media. The media hash
74
+ # is keyed by mimetype such as 'text/plain' and the
75
+ # value mapped to the key is an array of media that
76
+ # are of that type.
77
+ attr_reader :media
78
+
79
+ ##
80
+ # Creates a new Media comprised of a mail
81
+ # a logger. Logger is an instance attribute allowing
82
+ # for a logging strategy per carrier type
83
+
84
+ def initialize(mail, logger=nil)
85
+ @mail = mail
86
+ @logger = logger
87
+ @logger.info("#{self.class} created") unless @logger.nil?
88
+ @media = Hash.new
89
+ @dir_count = 0
90
+ @media_dir = File.join(self.class.tmp_dir(),
91
+ self.class.safe_message_id(@mail.message_id))
92
+ end
93
+
94
+ ##
95
+ # Helper for process template method to decode the part based
96
+ # on its type and write its content to a temporary file. Returns
97
+ # path to temporary file that holds the content. Parts with a main
98
+ # type of text will have their contents transformed with a call to
99
+ # transform_text
100
+ #
101
+ # Producers should only override this method if the parts of
102
+ # the MMS need special treatment besides what is expected for
103
+ # a normal mime part.
104
+ #
105
+ # Returns a tupple of content type, file path
106
+
107
+ def process_media(part)
108
+ # TMail body auto-magically decodes quoted
109
+ # printable for text/html type.
110
+ # base64_decode is safe in TMail facade, no work is performed
111
+ # if the part is not base64 encoded.
112
+ part.base64_decode
113
+ file = temp_file(part)
114
+ content = part.body
115
+ content = transform_text(self.class.part_type?(part),content) if self.class.main_type?(part).eql?('text')
116
+ @logger.info("#{self.class} writing file #{file}") unless @logger.nil?
117
+ File.open(file,'w'){ |f|
118
+ f.write(content)
119
+ }
120
+ type = self.class.part_type?(part)
121
+ return type, file
122
+ end
123
+
124
+ ##
125
+ # Helper for process_media template method to transform text.
126
+
127
+ def transform_text(type,text)
128
+ f = "#{self.class.name.downcase.gsub(/::/,'_')}_transform.yml"
129
+ yf = File.join(self.class.conf_dir(), "#{f}")
130
+ return text unless File::exist?(yf)
131
+ h = YAML::load_file(yf)
132
+ a = h[type]
133
+ return text if a.nil?
134
+ a.each do |from,to|
135
+ text.gsub!(/#{from}/m,to)
136
+ end
137
+ text
138
+ end
139
+
140
+ ##
141
+ # Helper for process template method to determine if
142
+ # media contained in a part should be ignored. Producers
143
+ # should override this method to return true for media such
144
+ # as images that are advertising, carrier logos, etc.
145
+
146
+ def ignore_media?(type,part)
147
+ f = "#{self.class.name.downcase.gsub(/::/,'_')}_ignore.yml"
148
+ yf = File.join(self.class.conf_dir(), "#{f}")
149
+ return false unless File::exist?(yf)
150
+ h = YAML::load_file(yf)
151
+ a = h[type]
152
+ return false if a.nil?
153
+ m = /^([^\/]+)\//.match(type)[1]
154
+ a.each do |i|
155
+ if m.eql?('text')
156
+ return true if part.body =~ /#{Regexp.escape("#{i}")}/m
157
+ else
158
+ return true if filename?(part).eql?(i)
159
+ end
160
+ end
161
+ false
162
+ end
163
+
164
+ ##
165
+ # Helper for process template method to name a temporary
166
+ # filepath based on information in the part. This version
167
+ # attempts to honor the name of the media as labeled in the part
168
+ # header and creates a unique temporary directory for writing
169
+ # the file so filename collision does not occur.
170
+ # Consumers of this method expect the directory
171
+ # structure to the file exists, if the method is overriden it
172
+ # is mandatory that this behavior is retained.
173
+
174
+ def temp_file(part)
175
+ file_name = filename?(part)
176
+ File.join(msg_tmp_dir(),File.basename(file_name))
177
+ end
178
+
179
+ ##
180
+ # Purges the unique directory created for this producer and
181
+ # all of the media that it contains. This implementation is
182
+ # based on an instance variable called @media_dir
183
+
184
+ def purge()
185
+ @logger.info("#{self.class} purging #{@media_dir} and all its contents") unless @logger.nil?
186
+ FileUtils.rm_rf(@media_dir)
187
+ end
188
+
189
+ ##
190
+ # process is a template method and collects all the media in a MMS.
191
+ # Override helper methods to this template to clean out advertising
192
+ # and/or ignore media that are advertising. This method should not be
193
+ # overridden unless there is an extreme special case in processing the
194
+ # media of a MMS.
195
+ #
196
+ # Helpers methods for the process template:
197
+ # ignore_media? true if the media contained a is part should
198
+ # be ignored.
199
+ # process_media retrieves media to temporary file, returns path
200
+ # to file.
201
+ # transform_text called by process_media, strips out advertising.
202
+ # temp_file creates a temporary filepath based on information from
203
+ # the part.
204
+
205
+ def process()
206
+ @logger.info("#{self.class} processing") unless @logger.nil?
207
+
208
+ parts = @mail.parts
209
+ if !@mail.multipart?
210
+ parts = Array.new()
211
+ parts << @mail
212
+ end
213
+ parts.each do |p|
214
+ t = self.class.part_type?(p)
215
+ unless ignore_media?(t,p)
216
+ t,f = process_media(p)
217
+ add_file(t,f) unless f.nil?
218
+ end
219
+ end
220
+ end
221
+
222
+ ##
223
+ # Helper to add a file to the media hash.
224
+
225
+ def add_file(type, file)
226
+ if @media[type].nil?
227
+ @media[type] = Array.new
228
+ end
229
+ @media[type] << file
230
+ end
231
+
232
+ ##
233
+ # Helper to temp_file to create a unique temporary directory that is
234
+ # a child of tmp_dir This version is based on the message_id of the
235
+ # mail.
236
+
237
+ def msg_tmp_dir()
238
+ @dir_count += 1
239
+ dir = File.join(@media_dir, "#{@dir_count}")
240
+ FileUtils.mkdir_p(dir)
241
+ dir
242
+ end
243
+
244
+ ##
245
+ # Factory method that creates MMS2R::Media products.
246
+ #
247
+ # Returns a MMS2R::Media product based on the characteristics
248
+ # of the carrier from which the the MMS originated.
249
+ # mail is a TMail object, logger is a Logger and may be
250
+ # nil.
251
+
252
+ def self.create(mail, logger=nil)
253
+ d = lambda{['',MMS2R::Media]}
254
+ cc = MMS2R::CARRIER_CLASSES.detect(d) do |n, c|
255
+ /[^@]+@(.+)/.match(mail.from[0])[1] =~ /#{Regexp.escape("#{n}")}/
256
+ end
257
+ cls = cc[1]
258
+ cls.new(mail, logger)
259
+ end
260
+
261
+ ##
262
+ # returns a filename declared for a part, or a default if its not defined
263
+
264
+ def filename?(part)
265
+ part.sub_header("content-type", "name") ||
266
+ part.sub_header("content-disposition", "filename") ||
267
+ (part['content-location'] && part['content-location'].body) ||
268
+ "#{Time.now.to_i}.#{self.class.default_ext(self.class.part_type?(part))}"
269
+ end
270
+
271
+ @@tmp_dir = File.join(Dir.tmpdir, ENV['USER'],'mms2r')
272
+
273
+ ##
274
+ # Get the temporary directory where media files are written to.
275
+
276
+ def self.tmp_dir
277
+ @@tmp_dir
278
+ end
279
+
280
+ ##
281
+ # Set the temporary directory where media files are written to.
282
+ def self.tmp_dir=(d)
283
+ @@tmp_dir=d
284
+ end
285
+
286
+ @@conf_dir = File.join(File.dirname(__FILE__), '..', '..', 'conf')
287
+
288
+ ##
289
+ # Set the directory where conf files are stored.
290
+
291
+ def self.conf_dir
292
+ @@conf_dir
293
+ end
294
+
295
+ ##
296
+ # Set the directory where conf files are stored.
297
+ def self.conf_dir=(d)
298
+ @@conf_dir=d
299
+ end
300
+
301
+ ##
302
+ # Helper to created a safe directory path element based on the
303
+ # mail message id.
304
+
305
+ def self.safe_message_id(mid)
306
+ return "#{Time.now.to_i}" if mid.nil?
307
+ mid.gsub(/<|>|@|\./, "")
308
+ end
309
+
310
+ ##
311
+ # Returns a default file extension based on a content_type
312
+
313
+ def self.default_ext(content_type)
314
+ ext = MMS2R::EXT[content_type]
315
+ return /[^\/]+\/(.+)/.match(content_type)[1] if ext.nil?
316
+ ext
317
+ end
318
+
319
+ ##
320
+ # Determines the mimetype of a part. Gauruntees a type is returned.
321
+
322
+ def self.part_type?(part)
323
+ if part.content_type.nil?
324
+ return 'text/plain'
325
+ end
326
+ part.content_type
327
+ end
328
+
329
+ ##
330
+ # Determines the main type of the part's mimetype
331
+
332
+ def self.main_type?(part)
333
+ /^([^\/]+)\//.match(self.part_type?(part))[1]
334
+ end
335
+
336
+ ##
337
+ # Determines the sub type of the part's mimetype
338
+
339
+ def self.sub_type?(part)
340
+ /\/([^\/]+)$/.match(self.part_type?(part))[1]
341
+ end
342
+
343
+ end
344
+
345
+ end