mms2r 2.4.1 → 3.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 (61) hide show
  1. data/.gitignore +1 -0
  2. data/History.txt +5 -0
  3. data/Manifest.txt +1 -1
  4. data/README.txt +12 -9
  5. data/Rakefile +11 -10
  6. data/conf/mobile.indosat.net.id.yml +1 -1
  7. data/conf/waw.plspictures.com.yml +1 -1
  8. data/lib/mail_ext.rb +37 -0
  9. data/lib/mms2r.rb +7 -5
  10. data/lib/mms2r/media.rb +29 -35
  11. data/lib/mms2r/media/sprint.rb +19 -19
  12. data/mms2r.gemspec +23 -17
  13. data/test/fixtures/att-blackberry-02.mail +5 -6
  14. data/test/fixtures/att-iphone-01.mail +3 -3
  15. data/test/fixtures/dobson-image-01.mail +3 -3
  16. data/test/fixtures/maroctelecom-france-mms-01.mail +4 -4
  17. data/test/fixtures/mycingular-image-01.mail +3 -3
  18. data/test/fixtures/nextel-image-02.mail +2 -2
  19. data/test/fixtures/orangepoland-text-01.mail +2 -1
  20. data/test/fixtures/orangepoland-text-02.mail +2 -1
  21. data/test/fixtures/pics.cingularme.com-image-01.mail +0 -1
  22. data/test/fixtures/sasktel-image-01.mail +4 -4
  23. data/test/fixtures/sprint-broken-image-01.mail +1 -1
  24. data/test/fixtures/sprint-text-01.mail +1 -1
  25. data/test/fixtures/tmobile-blackberry-02.mail +4 -4
  26. data/test/test_1nbox_net.rb +16 -19
  27. data/test/test_bell_canada.rb +8 -13
  28. data/test/test_bellsouth_net.rb +5 -10
  29. data/test/test_helper.rb +16 -11
  30. data/test/test_mediamessaging_o2_co_uk.rb +21 -17
  31. data/test/test_messaging_nextel_com.rb +18 -12
  32. data/test/test_messaging_sprintpcs_com.rb +4 -8
  33. data/test/test_mms2r_media.rb +153 -143
  34. data/test/test_mms_3ireland_ie.rb +4 -9
  35. data/test/test_mms_ae.rb +12 -12
  36. data/test/test_mms_alltel_com.rb +14 -15
  37. data/test/test_mms_att_net.rb +35 -27
  38. data/test/test_mms_dobson_net.rb +9 -11
  39. data/test/test_mms_luxgsm_lu.rb +23 -17
  40. data/test/test_mms_mobileiam_ma.rb +4 -10
  41. data/test/test_mms_mtn_co_za.rb +4 -9
  42. data/test/test_mms_mycricket_com.rb +9 -9
  43. data/test/test_mms_myhelio_com.rb +18 -14
  44. data/test/test_mms_netcom_no.rb +19 -17
  45. data/test/test_mms_o2online_de.rb +20 -18
  46. data/test/test_mms_three_co_uk.rb +14 -14
  47. data/test/test_mms_vodacom4me_co_za.rb +24 -24
  48. data/test/test_mobile_indosat_net_id.rb +6 -9
  49. data/test/test_msg_telus_com.rb +5 -10
  50. data/test/test_orangemms_net.rb +25 -17
  51. data/test/test_pm_sprint_com.rb +37 -22
  52. data/test/test_pxt_vodafone_net_nz.rb +7 -11
  53. data/test/test_rci_rogers_com.rb +3 -8
  54. data/test/test_sms_sasktel_com.rb +2 -7
  55. data/test/test_tmomail_net.rb +25 -23
  56. data/test/test_unicel_com.rb +8 -12
  57. data/test/test_vmpix_com.rb +14 -13
  58. data/test/test_vzwpix_com.rb +24 -14
  59. data/test/test_waw_plspictures_com.rb +2 -7
  60. metadata +77 -26
  61. data/lib/tmail_ext.rb +0 -22
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ coverage/
3
3
  pkg/
4
4
  email.txt
5
5
  coverage.info
6
+ mkmf.log
6
7
  doc/
data/History.txt CHANGED
@@ -1,3 +1,8 @@
1
+ ### 3.0.0 / 2010-02-24 (General Crozier - Chairman of the Joint Chiefs of Staff)
2
+
3
+ * 1 new feature
4
+ * dependence upon Mail gem rather than TMail gem
5
+
1
6
  ### 2.4.1 / 2010-02-07 (Vater Orlaag - political and spiritual specialist)
2
7
 
3
8
  * 3 minor enhancements
data/Manifest.txt CHANGED
@@ -44,7 +44,7 @@ init.rb
44
44
  lib/mms2r.rb
45
45
  lib/mms2r/media.rb
46
46
  lib/mms2r/media/sprint.rb
47
- lib/tmail_ext.rb
47
+ lib/mail_ext.rb
48
48
  mms2r.gemspec
49
49
  test/fixtures/1nbox-2images-01.mail
50
50
  test/fixtures/1nbox-2images-02.mail
data/README.txt CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  http://mms2r.rubyforge.org/
4
4
  by Mike Mondragon
5
- http://rubyforge.org/tracker/?group_id=3065
6
5
  http://github.com/monde/mms2r
6
+ http://github.com/monde/mms2r/issues
7
7
  http://peepcode.com/products/mms2r-pdf
8
8
 
9
9
  == DESCRIPTION
@@ -60,6 +60,12 @@ Corpus of carriers currently processed by MMS2R:
60
60
  * Virgin Mobile of Canada: vmobile.ca
61
61
  * Vodacom: mms.vodacom4me.co.za
62
62
 
63
+ Corpus of smart phones known to MMS2R:
64
+ * Blackberry variants
65
+ * Droid variants
66
+ * HTC variants (T-Mobile Dash, Sprint HERO)
67
+ * Apple iPhone variants
68
+
63
69
  == FEATURES
64
70
 
65
71
  * #default_media and #default_text methods return a File that can be used in
@@ -67,8 +73,8 @@ Corpus of carriers currently processed by MMS2R:
67
73
  * #process supports blocks to for enumerating over the content of the MMS
68
74
  * #process can be made lazy when :process => :lazy is passed to new
69
75
  * logging is enabled when :logger => your_logger is passed to new
70
- * an mms instance acts like a tmail object, any methods not defined on the
71
- instance are delegated to its underlying tmail object
76
+ * an mms instance acts like a Mail object, any methods not defined on the
77
+ instance are delegated to its underlying Mail object
72
78
  * #device_type? returns a symbol representing a device or smartphone type
73
79
  Known smartphones thus far: iPhone, BlackBerry, T-Mobile Dash, Droid
74
80
 
@@ -83,12 +89,9 @@ http://peepcode.com/products/mms2r-pdf
83
89
  require 'mms2r'
84
90
 
85
91
  # required for the example
86
- require 'tmail'
87
92
  require 'fileutils'
88
93
 
89
- mail = MMS2R.parse mail
90
- # mail = MMS2R.parse File.read('some_saved_mail.file')
91
-
94
+ mail = MMS2R.new(Mail.read('some_saved_mail.file'))
92
95
  puts "mail has default carrier subject" if mail.subject.empty?
93
96
 
94
97
  # access the sender's phone number
@@ -119,7 +122,7 @@ http://peepcode.com/products/mms2r-pdf
119
122
 
120
123
  puts "does the mail have quicktime video? #{!mail.media['video/quicktime'].nil?}"
121
124
 
122
- puts "plus run anything that TMail provides, e.g. #{mail.to.inspect}"
125
+ puts "plus run anything that Mail provides, e.g. #{mail.to.inspect}"
123
126
 
124
127
  # check if the mail is from a mobile phone
125
128
  puts "mail is from a mobile phone #{mail.is_mobile?}"
@@ -149,7 +152,7 @@ http://peepcode.com/products/mms2r-pdf
149
152
 
150
153
  == REQUIREMENTS
151
154
 
152
- * TMail
155
+ * Mail
153
156
  * Nokogiri (for mms from Sprint)
154
157
  * UUIDTools
155
158
 
data/Rakefile CHANGED
@@ -11,17 +11,18 @@ end
11
11
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "lib")
12
12
  require 'mms2r'
13
13
 
14
- Hoe.new('mms2r', MMS2R::Media::VERSION) do |p|
14
+ Hoe.spec('mms2r') do |p|
15
+ p.version = MMS2R::Media::VERSION
15
16
  p.rubyforge_name = 'mms2r'
16
- p.author = ['Mike Mondragon']
17
- p.email = ['mikemondragon@gmail.com']
18
- p.summary = 'Extract user media from MMS (and not carrier cruft)'
19
- p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
20
- p.url = p.paragraphs_of('README.txt', 1).first.strip
21
- p.changes = p.paragraphs_of('History.txt', 0..2).join("\n\n")
22
- p.extra_deps << ['nokogiri']
23
- p.extra_deps << ['tmail']
24
- p.extra_deps << ['uuidtools']
17
+ p.author = ['Mike Mondragon']
18
+ p.email = ['mikemondragon@gmail.com']
19
+ p.summary = 'Extract user media from MMS (and not carrier cruft)'
20
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
21
+ p.url = p.paragraphs_of('README.txt', 1).first.strip
22
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
23
+ p.extra_deps << ['nokogiri', '>= 1.4.0']
24
+ p.extra_deps << ['mail', '>= 2.1.0']
25
+ p.extra_deps << ['uuidtools', '>= 2.1.0']
25
26
  p.clean_globs << 'coverage'
26
27
  end
27
28
 
@@ -9,6 +9,6 @@ ignore:
9
9
  - cp__newBanner.jpg
10
10
  number:
11
11
  - return-path
12
- - /^<([^@]+)@.*/
12
+ - /^([^@]+)@.*/
13
13
  - "\1"
14
14
 
@@ -5,7 +5,7 @@ transform:
5
5
  - ""
6
6
  number:
7
7
  - return-path
8
- - /^<([^@]+)@.*/
8
+ - /^([^@]+)@.*/
9
9
  - "\1"
10
10
  ignore:
11
11
  text/html:
data/lib/mail_ext.rb ADDED
@@ -0,0 +1,37 @@
1
+ #--
2
+ # Copyright (c) 2010 by Mike Mondragon (mikemondragon@gmail.com)
3
+ #
4
+ # Please see the LICENSE file for licensing information.
5
+ #++
6
+
7
+ class Mail::Message
8
+
9
+ ##
10
+ # Generically determines the mime-type of a message used in mms2r processing.
11
+ # Guarantees a type is returned.
12
+
13
+ def part_type?
14
+ if self.content_type
15
+ self.content_type.split(';').first.downcase
16
+ else
17
+ 'text/plain'
18
+ end
19
+ end
20
+
21
+ ##
22
+ # override #filename to account for the true filename in the content_type
23
+ # returns foo.jpg #content_type is 'image/jpeg; filename="foo.jpg"; name="foo.jpg"'
24
+ # returns foo.jpg #content_type is 'image/jpeg;Name=foo.jpg'
25
+
26
+ def filename
27
+ if self.content_type && names = Hash[self.content_type.split(';').map{|t| t.strip.split('=')}]
28
+ if name = names.detect{|key,val| key.downcase == 'filename'} || names.detect{|key,val| key.downcase == 'name'}
29
+ return (name.last.match(/^"?(.+?)"?$/))[1]
30
+ end
31
+ end
32
+
33
+ find_attachment
34
+ end
35
+
36
+
37
+ end
data/lib/mms2r.rb CHANGED
@@ -39,29 +39,31 @@ module MMS2R
39
39
  ##
40
40
  # MMS2R library version
41
41
 
42
- VERSION = '2.4.1'
42
+ VERSION = '3.0.0'
43
43
 
44
44
  end
45
45
 
46
46
  # Simple convenience function to make it a one-liner:
47
47
  # MMS2R.parse raw_mail or MMS2R.parse File.load(raw_mail)
48
- # Combined w/ the method_missing delegation, this should behave as an enhanced TMail object, more or less.
48
+ # Combined w/ the method_missing delegation, this should behave as an enhanced Mail object, more or less.
49
49
  def self.parse raw_mail
50
- mail = TMail::Mail.parse raw_mail
50
+ mail = Mail.new raw_mail
51
51
  MMS2R::Media.new(mail)
52
52
  end
53
53
 
54
54
  end
55
55
 
56
56
  require 'rubygems'
57
- require 'tmail/mail'
57
+ require 'mail'
58
+ gem 'mail', '>= 2.1.2'
58
59
  require 'fileutils'
59
60
  require 'pathname'
60
61
  require 'tmpdir'
61
62
  require 'yaml'
62
63
  require 'uuidtools'
64
+ require 'iconv'
63
65
 
64
- require File.join(File.dirname(__FILE__), 'tmail_ext')
66
+ require File.join(File.dirname(__FILE__), 'mail_ext')
65
67
  require File.join(File.dirname(__FILE__), 'mms2r', 'media')
66
68
  require File.join(File.dirname(__FILE__), 'mms2r', 'media', 'sprint')
67
69
  MMS2R.register('pm.sprint.com', MMS2R::Media::Sprint)
data/lib/mms2r/media.rb CHANGED
@@ -23,7 +23,7 @@
23
23
  #
24
24
  # require 'rubygems'
25
25
  # require 'mms2r'
26
- # mail = TMail::Mail.parse(IO.readlines("sample-MMS.file").join)
26
+ # mail = Mail.read("sample-MMS.file")
27
27
  # mms = MMS2R::Media.new(mail)
28
28
  # subject = mms.subject
29
29
  # number = mms.number
@@ -114,14 +114,14 @@ module MMS2R
114
114
  end
115
115
  end
116
116
 
117
- # Pass off everything we don't do to the TMail object
117
+ # Pass off everything we don't do to the Mail object
118
118
  # TODO: refactor to explicit addition a la http://blog.jayfields.com/2008/02/ruby-replace-methodmissing-with-dynamic.html
119
119
  def method_missing method, *args, &block
120
120
  mail.send method, *args, &block
121
121
  end
122
122
 
123
123
  ##
124
- # TMail object that the media files were derived from.
124
+ # Mail object that the media files were derived from.
125
125
 
126
126
  attr_reader :mail
127
127
 
@@ -143,14 +143,9 @@ module MMS2R
143
143
 
144
144
  attr_reader :media_dir
145
145
 
146
- ##
147
- # Various multi-parts that are bundled into mail
148
-
149
- MULTIPARTS_TO_SPLIT = [ 'multipart/related', 'multipart/alternative', 'multipart/mixed', 'multipart/appledouble' ]
150
-
151
146
  ##
152
147
  # Factory method that creates MMS2R::Media products based on the domain
153
- # name of the carrier from which the MMS originated. mail is a TMail
148
+ # name of the carrier from which the MMS originated. mail is a Mail
154
149
  # object.
155
150
 
156
151
  def self.create(mail)
@@ -171,9 +166,8 @@ module MMS2R
171
166
 
172
167
  def self.domain(mail)
173
168
  return_path = case
174
- when mail.header['return-path']
175
- matched = /^<.+@([^@]+)>$/.match(mail.header['return-path'].to_s.strip)
176
- matched ? matched[1] : ''
169
+ when mail.return_path
170
+ mail.return_path ? mail.return_path.split('@').last : ''
177
171
  else
178
172
  ''
179
173
  end
@@ -248,11 +242,15 @@ module MMS2R
248
242
  def number
249
243
  unless @number
250
244
  params = config['number']
251
- if params && (header = mail.header[params[0]])
245
+ if params && params.any? && (header = mail.header[params[0]])
252
246
  @number = header.to_s.gsub(eval(params[1]), params[2])
253
247
  end
248
+ if @number.nil? || @number.blank?
249
+ @number = mail.from.first.split(/@|\//).first rescue ""
250
+ end
254
251
  end
255
- @number ||= mail.from.first.split('@').first rescue ""
252
+
253
+ @number
256
254
  end
257
255
 
258
256
  ##
@@ -344,7 +342,7 @@ module MMS2R
344
342
  for i in 1..2
345
343
  flat = []
346
344
  parts.each do |p|
347
- if MULTIPARTS_TO_SPLIT.include?(p.part_type?)
345
+ if p.multipart?
348
346
  p.parts.each {|mp| flat << mp }
349
347
  else
350
348
  flat << p
@@ -380,12 +378,12 @@ module MMS2R
380
378
  # true for media such as images that are advertising, carrier logos, etc.
381
379
  # See the ignore section in the discussion of the built-in configuration.
382
380
 
383
- def ignore_media?(type,part)
381
+ def ignore_media?(type, part)
384
382
  ignores = config['ignore'][type] || []
385
- ignore = ignores.detect{|test| filename?(part) == test}
386
- ignore ||= ignores.detect{|test| filename?(part) =~ eval(test) if test.index('/') == 0 }
387
- ignore ||= ignores.detect{|test| part.body.strip =~ eval(test) if test.index('/') == 0 }
388
- ignore ||= (part.body.strip.size == 0 ? true : nil)
383
+ ignore = ignores.detect{ |test| filename?(part) == test}
384
+ ignore ||= ignores.detect{ |test| filename?(part) =~ eval(test) if test.index('/') == 0 }
385
+ ignore ||= ignores.detect{ |test| part.body.decoded.strip =~ eval(test) if test.index('/') == 0 }
386
+ ignore ||= (part.body.decoded.strip.size == 0 ? true : nil)
389
387
  ignore.nil? ? false : true
390
388
  end
391
389
 
@@ -402,20 +400,20 @@ module MMS2R
402
400
  # Returns a tuple of content type, file path
403
401
 
404
402
  def process_media(part)
405
- # TMail body auto-magically decodes quoted
403
+ # Mail body auto-magically decodes quoted
406
404
  # printable for text/html type.
407
405
  file = temp_file(part)
408
- if part.main_type('text') == 'text' ||
409
- part.content_type == 'application/smil'
406
+ if part.part_type? =~ /^text\// ||
407
+ part.part_type? == 'application/smil'
410
408
  type, content = transform_text_part(part)
411
409
  mode = 'w'
412
410
  else
413
- if part.content_type == 'application/octet-stream'
411
+ if part.part_type? == 'application/octet-stream'
414
412
  type = type_from_filename(filename?(part))
415
413
  else
416
- type = part.content_type
414
+ type = part.part_type?
417
415
  end
418
- content = part.body
416
+ content = part.body.decoded
419
417
  mode = 'wb' # open with binary bit for Windows for non text
420
418
  end
421
419
  return type, nil if content.nil? || content.empty?
@@ -456,7 +454,7 @@ module MMS2R
456
454
 
457
455
  def transform_text_part(part)
458
456
  type = part.part_type?
459
- text = part.body.strip
457
+ text = part.body.decoded.strip
460
458
  transform_text(type, text)
461
459
  end
462
460
 
@@ -506,11 +504,9 @@ module MMS2R
506
504
  # returns a filename declared for a part, or a default if its not defined
507
505
 
508
506
  def filename?(part)
509
- name = part.sub_header("content-type", "name") ||
510
- part.sub_header("content-disposition", "filename") ||
511
- (part['content-location'] && part['content-location'].to_s.strip)
507
+ name = part.filename
512
508
  if (name.nil? || name.empty?)
513
- if part['content-id'] && part['content-id'].real_body.strip =~ /^<(.+)>$/
509
+ if part.content_id && part.content_id.strip =~ /^<(.+)>$/
514
510
  name = $1
515
511
  else
516
512
  name = "#{Time.now.to_f}.#{self.default_ext(part.part_type?)}"
@@ -570,11 +566,9 @@ module MMS2R
570
566
  headers.keys.each do |header|
571
567
  if mail.header[header.downcase]
572
568
  # headers[header] refers to a hash of smart phone types with regex values
573
- # that if they match the header signals the type should be returned
569
+ # that if they match, the header signals the type should be returned
574
570
  headers[header].each do |type, regex|
575
- # HACK to get at the full value of the header before TMail parses according to spec
576
- # see @body in and ensure_parsed in lib/tmail/header.rb
577
- return type if mail.header[header.downcase].instance_variable_get(:@body) =~ regex
571
+ return type if mail.header[header.downcase].decoded =~ regex
578
572
  end
579
573
  end
580
574
  end
@@ -15,12 +15,12 @@ module MMS2R
15
15
  ##
16
16
  # Sprint version of MMS2R::Media
17
17
  #
18
- # Sprint is an annoying carrier because they don't actually transmit user
19
- # generated content (like images or videos) directly in the MMS message.
20
- # Instead, they hijack the media that is sent from the cellular subscriber
21
- # and place that content on a content server. In place of the media
22
- # the recipient receives a HTML message with unsolicited Sprint
23
- # advertising and links back to their content server. The recipient has
18
+ # Sprint is an annoying carrier because they don't actually transmit user
19
+ # generated content (like images or videos) directly in the MMS message.
20
+ # Instead, they hijack the media that is sent from the cellular subscriber
21
+ # and place that content on a content server. In place of the media
22
+ # the recipient receives a HTML message with unsolicited Sprint
23
+ # advertising and links back to their content server. The recipient has
24
24
  # to click through Sprint more pages to view the content.
25
25
  #
26
26
  # The default subject on these messages from the
@@ -29,11 +29,11 @@ module MMS2R
29
29
  class Sprint < MMS2R::Media
30
30
 
31
31
  ##
32
- # Override process() because Sprint doesn't attach media (images, video,
33
- # etc.) to its MMS. Media such as images and videos are hosted on a
34
- # Sprint content server. MMS2R::Media::Sprint has to pick apart an
35
- # HTML attachment to find the URL to the media on Sprint's content
36
- # server and download each piece of content. Any text message part of
32
+ # Override process() because Sprint doesn't attach media (images, video,
33
+ # etc.) to its MMS. Media such as images and videos are hosted on a
34
+ # Sprint content server. MMS2R::Media::Sprint has to pick apart an
35
+ # HTML attachment to find the URL to the media on Sprint's content
36
+ # server and download each piece of content. Any text message part of
37
37
  # the MMS if it exists is embedded in the html.
38
38
 
39
39
  def process
@@ -46,21 +46,21 @@ module MMS2R
46
46
  doc = nil
47
47
  parts.each do |p|
48
48
  next unless p.part_type? == 'text/html'
49
- d = Nokogiri(p.body)
49
+ d = Nokogiri(p.body.decoded)
50
50
  title = d.at('title').inner_html
51
51
  if title =~ /You have new Picture Mail!/
52
52
  doc = d
53
- @is_video = (p.body =~ /type=&quot;VIDEO&quot;&gt;/m ? true : false)
53
+ @is_video = (p.body.decoded =~ /type=&quot;VIDEO&quot;&gt;/m ? true : false)
54
54
  end
55
55
  end
56
56
  return if doc.nil? # it was a dud
57
57
  @is_video ||= false
58
-
58
+
59
59
  # break it down
60
60
  sprint_phone_number(doc)
61
61
  sprint_process_text(doc)
62
62
  sprint_process_media(doc)
63
-
63
+
64
64
  @was_processed = true
65
65
  end
66
66
 
@@ -90,8 +90,8 @@ module MMS2R
90
90
 
91
91
  def sprint_process_text(doc)
92
92
  # there is at least one <pre> with MMS text if text has been included by
93
- # the user. (note) we'll have to verify that if they attach multiple texts
94
- # to the MMS then Sprint stacks it up in multiple <pre>'s. The only <pre>
93
+ # the user. (note) we'll have to verify that if they attach multiple texts
94
+ # to the MMS then Sprint stacks it up in multiple <pre>'s. The only <pre>
95
95
  # tag in the document is for text from the user.
96
96
  doc.search("/html/body//pre").each do |pre|
97
97
  type = 'text/plain'
@@ -110,7 +110,7 @@ module MMS2R
110
110
  srcs = Array.new
111
111
  # collect all the images in the document, even though
112
112
  # they are <img> tag some might actually refer to video.
113
- # To know the link refers to vide one must look at the
113
+ # To know the link refers to vide one must look at the
114
114
  # content type on the http GET response.
115
115
  imgs = doc.search("/html/body//img")
116
116
  imgs.each do |i|
@@ -130,7 +130,7 @@ module MMS2R
130
130
  cnt = 0
131
131
  srcs.each do |src|
132
132
  begin
133
-
133
+
134
134
  url = URI.parse(CGI.unescapeHTML(src))
135
135
  unless @is_video
136
136
  query={}