mms2r 1.0.0

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