mwmitchell-rsolr 0.6.9 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +8 -0
- data/README.rdoc +21 -27
- data/examples/direct.rb +2 -1
- data/examples/http.rb +1 -1
- data/lib/core_ext.rb +8 -0
- data/lib/rsolr/connection/adapter/common_methods.rb +30 -25
- data/lib/rsolr/connection/adapter/direct.rb +1 -1
- data/lib/rsolr/connection/adapter/http.rb +1 -1
- data/lib/rsolr/connection/base.rb +39 -89
- data/lib/rsolr/connection.rb +0 -1
- data/lib/rsolr.rb +4 -1
- data/test/connection/test_methods.rb +0 -31
- metadata +2 -5
- data/lib/rsolr/connection/param_mapping/dismax.rb +0 -41
- data/lib/rsolr/connection/param_mapping/standard.rb +0 -127
- data/lib/rsolr/connection/param_mapping.rb +0 -39
- data/test/connection/param_mapping_test.rb +0 -61
- data/test/ruby-lang.org.rss.xml +0 -391
data/CHANGES.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
0.7.0 - February 20, 2009
|
2
|
+
Removed all param mapping behavior, code and tests
|
3
|
+
- this stuff just gunks up rsolr and should be in an extension of some sort
|
4
|
+
Can now specify the request handler in all RSolr::Connection::Base methods as the first argument:
|
5
|
+
- solr.query 'select', :q=>'ipod'
|
6
|
+
- solr.query 'catalog', :q=>'humphry'
|
7
|
+
- solr.query :q=>'big' # defaults to the /select handler
|
8
|
+
|
1
9
|
0.6.9 - January 29, 2009
|
2
10
|
Simplified facet response methods
|
3
11
|
Main facet method is called #facets
|
data/README.rdoc
CHANGED
@@ -10,8 +10,9 @@ Simple usage:
|
|
10
10
|
require 'rubygems'
|
11
11
|
require 'rsolr'
|
12
12
|
rsolr = RSolr.connect
|
13
|
-
response = rsolr.query(:q=>'*:*')
|
14
|
-
|
13
|
+
response = rsolr.query(:q=>'*:*') # becomes /solr/select?q=*:*
|
14
|
+
# can also set the request handler path like:
|
15
|
+
response = rsolr.query('catalog', :q=>'*:*') # becomes /solr/catalog?q=*:*
|
15
16
|
|
16
17
|
To run tests:
|
17
18
|
|
@@ -28,41 +29,35 @@ To get a direct connection (no http) in jRuby using DirectSolrConnection:
|
|
28
29
|
|
29
30
|
solr = RSolr.connect(:adapter=>:direct, :home_dir=>'/path/to/solr/home', :dist_dir=>'/path/to/solr/distribution')
|
30
31
|
|
31
|
-
You can set
|
32
|
+
You can set the request handler paths for every request:
|
32
33
|
|
33
|
-
solr = RSolr.connect(:
|
34
|
+
solr = RSolr.connect(:select_path=>'select', :update_path=>'update', :luke_path=>'admin/luke')
|
34
35
|
|
35
36
|
|
36
37
|
== Requests
|
37
38
|
Once you have a connection, you can execute queries, updates etc..
|
38
39
|
|
40
|
+
You can optionally specify the request handler path by sending it in as the first argument:
|
41
|
+
solr.query 'catalog', :q=>'object_type:"book"'
|
42
|
+
solr.update 'my/update', '<xml/>'
|
43
|
+
|
44
|
+
The default request handler path value for each of the different methods are as follows:
|
45
|
+
find_by_id, query == 'select'
|
46
|
+
add, update, commit, optimize, rollback, delete_by_id, delete_by_query == 'update'
|
47
|
+
index_info == 'admin/luke'
|
48
|
+
|
49
|
+
Please note that the path you specify should be relative.
|
50
|
+
|
39
51
|
|
40
52
|
=== Querying
|
41
|
-
Use the #query method to send requests to
|
42
|
-
|
43
|
-
response = solr.query(:q=>'washington', :facet=>true, 'facet.limit'=>-1, 'facet.field'=>'cat', 'facet.field'=>'inStock')
|
53
|
+
Use the #query method to send requests to the /select handler:
|
54
|
+
response = solr.query(:q=>'washington', :facet=>true, 'facet.limit'=>-1, 'facet.field'=>'cat', 'facet.field'=>'inStock', :start=>0, :rows=>10)
|
44
55
|
response = solr.find_by_id(1)
|
45
56
|
|
46
|
-
==== Search Params
|
47
|
-
The #search method can accept the following params:
|
48
|
-
===== When :qt is :standard
|
49
|
-
:page
|
50
|
-
:per_page
|
51
|
-
:queries
|
52
|
-
:filters
|
53
|
-
:phrase_queries
|
54
|
-
:phrase_filters
|
55
|
-
:facets
|
56
|
-
===== When :qt is :dismax (also includes the :standard params)
|
57
|
-
:alternate_query
|
58
|
-
:query_fields
|
59
|
-
:phrase_fields
|
60
|
-
:boost_query
|
61
|
-
|
62
57
|
==== Pagination
|
63
|
-
Pagination is simplified
|
64
|
-
|
65
|
-
response = solr.
|
58
|
+
Pagination is simplified from having a few helpful response methods:
|
59
|
+
|
60
|
+
response = solr.query(:start=>0, :rows=>10, :q=>'*:*')
|
66
61
|
response.per_page
|
67
62
|
response.total_pages
|
68
63
|
response.current_page
|
@@ -73,7 +68,6 @@ If you use WillPaginate, just pass-in the response to the #will_paginate view he
|
|
73
68
|
|
74
69
|
<%= will_paginate(@response) %>
|
75
70
|
|
76
|
-
The #search method automatically figures out the :start and :rows values, based on the values of :page and :per_page. The will_paginate view helper uses the methods: #current_page, #previous_page, #next_page and #total_pages to create the pagination view widget.
|
77
71
|
|
78
72
|
=== Updating Solr
|
79
73
|
Updating is done using native Ruby structures. Hashes are used for single documents and arrays are used for a collection of documents (hashes). These structures get turned into simple XML "messages".
|
data/examples/direct.rb
CHANGED
@@ -9,7 +9,8 @@ solr = RSolr.connect(:adapter=>:direct, :home_dir=>home, :dist_dir=>dist)
|
|
9
9
|
|
10
10
|
`cd ../apache-solr/example/exampledocs && ./post.sh ./*.xml`
|
11
11
|
|
12
|
-
|
12
|
+
# the 'select' here is optional
|
13
|
+
response = solr.query 'select', :q=>'ipod', :fq=>'price:[0 TO 50]', :rows=>2, :start=>0
|
13
14
|
|
14
15
|
solr.delete_by_query('*:*')
|
15
16
|
|
data/examples/http.rb
CHANGED
@@ -5,7 +5,7 @@ solr = RSolr.connect
|
|
5
5
|
|
6
6
|
`cd ../apache-solr/example/exampledocs && ./post.sh ./*.xml`
|
7
7
|
|
8
|
-
response = solr.
|
8
|
+
response = solr.query :q=>'ipod', :fq=>'price:[0 TO 50]', :rows=>2, :start=>0
|
9
9
|
|
10
10
|
solr.delete_by_query('*:*')
|
11
11
|
|
data/lib/core_ext.rb
CHANGED
@@ -4,42 +4,47 @@
|
|
4
4
|
# The classes that include this module only need to provide a request method like:
|
5
5
|
# send_request(request_path, params, data)
|
6
6
|
# where:
|
7
|
-
# request_path is a string to a handler (/select)
|
7
|
+
# request_path is a string to a handler (/select etc.)
|
8
8
|
# params is a hash for query string params
|
9
9
|
# data is optional string of xml
|
10
10
|
module RSolr::Connection::Adapter::CommonMethods
|
11
11
|
|
12
12
|
# send a request to the "select" handler
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# string (valid solr update xml)
|
20
|
-
# object with respond_to?(:to_xml)
|
21
|
-
# params is a hash with valid solr update params
|
22
|
-
def update(data, params={})
|
23
|
-
send_request @opts[:update_path], params, data
|
13
|
+
# the first argument is the select handler path
|
14
|
+
# the last argument is a hash of params
|
15
|
+
def query(*args)
|
16
|
+
params = args.extract_options!
|
17
|
+
path = args.first || @opts[:select_path]
|
18
|
+
self.send_request "/#{path}", params
|
24
19
|
end
|
25
20
|
|
26
21
|
# sends a request to the admin luke handler to get info on the index
|
27
|
-
|
22
|
+
# the first argument is the admin/luke request handler path
|
23
|
+
# the last argument is a hash of params
|
24
|
+
def index_info(*args)
|
25
|
+
params = args.extract_options!
|
26
|
+
path = args.first || @opts[:luke_path]
|
28
27
|
params[:numTerms]||=0
|
29
|
-
send_request
|
28
|
+
self.send_request "/#{path}", params
|
30
29
|
end
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
|
42
|
-
|
31
|
+
# sends data to the update handler
|
32
|
+
# If 2 arguments are passed in:
|
33
|
+
# - the first should be the POST data string
|
34
|
+
# - the second can be an optional url params hash
|
35
|
+
# - the path is defaulted to '/update'
|
36
|
+
# If 3 arguments are passed in:
|
37
|
+
# - the first argument should be the url path ('/my-update-handler' etc.)
|
38
|
+
# - the second should be the POST data string
|
39
|
+
# - the last/third should be an optional url params hash
|
40
|
+
# data can be:
|
41
|
+
# string (valid solr update xml)
|
42
|
+
# object with respond_to?(:to_xml)
|
43
|
+
def update(*args)
|
44
|
+
params = args.extract_options!
|
45
|
+
data = args.last
|
46
|
+
path = args.size == 2 ? args.first : @opts[:update_path]
|
47
|
+
self.send_request "/#{path}", params, data
|
43
48
|
end
|
44
49
|
|
45
50
|
end
|
@@ -27,7 +27,7 @@ class RSolr::Connection::Adapter::Direct
|
|
27
27
|
# add the standard lib and dist directories to the :jar_paths
|
28
28
|
opts[:jar_paths] = [File.join(opts[:dist_dir], 'lib'), File.join(opts[:dist_dir], 'dist')]
|
29
29
|
end
|
30
|
-
@opts =
|
30
|
+
@opts = opts
|
31
31
|
end
|
32
32
|
|
33
33
|
# loads/imports the java dependencies
|
@@ -21,7 +21,7 @@ class RSolr::Connection::Adapter::HTTP
|
|
21
21
|
#
|
22
22
|
def initialize(opts={}, &block)
|
23
23
|
opts[:url]||='http://127.0.0.1:8983/solr'
|
24
|
-
@opts =
|
24
|
+
@opts = opts
|
25
25
|
end
|
26
26
|
|
27
27
|
def connection
|
@@ -5,33 +5,14 @@ class RSolr::Connection::Base
|
|
5
5
|
|
6
6
|
attr_reader :adapter, :opts
|
7
7
|
|
8
|
-
attr_accessor :param_mappers
|
9
|
-
|
10
8
|
# "adapter" is instance of:
|
11
9
|
# RSolr::Adapter::HTTP
|
12
10
|
# RSolr::Adapter::Direct (jRuby only)
|
13
11
|
def initialize(adapter, opts={})
|
14
12
|
@adapter = adapter
|
15
|
-
@param_mappers = {
|
16
|
-
:standard=>RSolr::Connection::ParamMapping::Standard,
|
17
|
-
:dismax=>RSolr::Connection::ParamMapping::Dismax
|
18
|
-
}
|
19
|
-
opts[:global_params]||={}
|
20
|
-
default_global_params = {
|
21
|
-
:wt=>:ruby,
|
22
|
-
:echoParams=>'EXPLICIT',
|
23
|
-
:debugQuery=>true
|
24
|
-
}
|
25
|
-
opts[:global_params] = default_global_params.merge(opts[:global_params])
|
26
13
|
@opts = opts
|
27
14
|
end
|
28
15
|
|
29
|
-
# sets default params etc.. - could be used as a mapping hook
|
30
|
-
# type of request should be passed in here? -> map_params(:query, {})
|
31
|
-
def map_params(params)
|
32
|
-
{}.merge(@opts[:global_params]).merge(params)
|
33
|
-
end
|
34
|
-
|
35
16
|
# send request (no param mapping) to the select handler
|
36
17
|
# params is hash with valid solr request params (:q, :fl, :qf etc..)
|
37
18
|
# if params[:wt] is not set, the default is :ruby (see opts[:global_params])
|
@@ -39,106 +20,68 @@ class RSolr::Connection::Base
|
|
39
20
|
# otherwise, an instance of RSolr::Response::Query is returned
|
40
21
|
# NOTE: to get raw ruby, use :wt=>'ruby'
|
41
22
|
# There is NO param mapping here, what you put it is what gets sent to Solr
|
42
|
-
def query(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# The #search method uses a param mapper to prepare the request for solr.
|
49
|
-
# For example, instead of doing your fq params by hand,
|
50
|
-
# you can use the simplified :filters param instead.
|
51
|
-
# The 2 built in mappers are for dismax and standard: RSolr::Connection::ParamMapping::*
|
52
|
-
# The default is :dismax
|
53
|
-
# If you create your own request handler in solrconfig.xml,
|
54
|
-
# you can use it by setting the :qt=>:my_handler
|
55
|
-
# You'll need to set the correct param mapper class (when using the search method)
|
56
|
-
# To take advantage of the param mapping
|
57
|
-
# If your request handler uses the solr dismax class, then do nothing
|
58
|
-
# if it uses the standard, you'll need to set it like:
|
59
|
-
# solr.param_mappers[:my_search_handler] = :standard
|
60
|
-
# The value can also be a custom class constant that must have a #map method
|
61
|
-
# The initialize method must accept a hash of input params
|
62
|
-
# The #map method must handle a block being passed in and return a new hash of raw solr params
|
63
|
-
def search(params,&blk)
|
64
|
-
qt = params[:qt] ? params[:qt].to_sym : :dismax
|
65
|
-
mapper_class = @param_mappers[qt]
|
66
|
-
mapper_class = RSolr::Connection::ParamMapping::Dismax if mapper_class==:dismax
|
67
|
-
mapper_class = RSolr::Connection::ParamMapping::Standard if mapper_class==:standard
|
68
|
-
mapper = mapper_class.new(params)
|
69
|
-
query(mapper.map(&blk))
|
70
|
-
end
|
71
|
-
|
72
|
-
# "facet_field" -- the name of a facet field: language_facet
|
73
|
-
# "params" -- the standard #search method params
|
74
|
-
# Returns an instance of RSolr::Response::Query::Base
|
75
|
-
def search_facet_by_name(facet_field, params, &blk)
|
76
|
-
params[:per_page] = 0
|
77
|
-
params[:rows] = 0
|
78
|
-
params[:facets] ||= {}
|
79
|
-
params[:facets][:fields] = [facet_field]
|
80
|
-
params[:facets][:mincount] ||= 1
|
81
|
-
params[:facets][:prefix] ||= nil
|
82
|
-
params[:facets][:missing] ||= false
|
83
|
-
params[:facets][:sort] ||= :count
|
84
|
-
params[:facets][:offset] ||= 0
|
85
|
-
self.search(params, &blk)
|
23
|
+
def query(*args)
|
24
|
+
params = map_params(args.extract_options!)
|
25
|
+
args << params
|
26
|
+
response = @adapter.query(*args)
|
27
|
+
params[:wt] == :ruby ? RSolr::Response::Query::Base.new(response) : response
|
86
28
|
end
|
87
29
|
|
88
30
|
# Finds a document by its id
|
89
|
-
def find_by_id(
|
90
|
-
params = map_params(
|
31
|
+
def find_by_id(*args)
|
32
|
+
params = map_params(args.extract_options!)
|
91
33
|
params[:q] = 'id:"#{id}"'
|
92
|
-
|
34
|
+
args << params
|
35
|
+
self.query(*args)
|
93
36
|
end
|
94
37
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
38
|
+
#
|
39
|
+
def update(*args)
|
40
|
+
params = map_params(args.extract_options!)
|
41
|
+
args << params
|
42
|
+
response = @adapter.update(*args)
|
43
|
+
params[:wt] == :ruby ? RSolr::Response::Update.new(response) : response
|
99
44
|
end
|
100
45
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
params
|
106
|
-
response = @adapter.update(data, params)
|
107
|
-
params[:wt]==:ruby ? RSolr::Response::Update.new(response) : response
|
46
|
+
def index_info(*args)
|
47
|
+
params = map_params(args.extract_options!)
|
48
|
+
args << params
|
49
|
+
response = @adapter.index_info(*args)
|
50
|
+
params[:wt] == :ruby ? RSolr::Response::IndexInfo.new(response) : response
|
108
51
|
end
|
109
52
|
|
110
|
-
def add(
|
111
|
-
update message.add(
|
53
|
+
def add(*args, &block)
|
54
|
+
update message.add(*args, &block)
|
112
55
|
end
|
113
56
|
|
114
57
|
# send </commit>
|
115
|
-
def commit(
|
116
|
-
update message.commit,
|
58
|
+
def commit(*args)
|
59
|
+
update message.commit, *args
|
117
60
|
end
|
118
61
|
|
119
62
|
# send </optimize>
|
120
|
-
def optimize(
|
121
|
-
update message.optimize,
|
63
|
+
def optimize(*args)
|
64
|
+
update message.optimize, *args
|
122
65
|
end
|
123
66
|
|
124
67
|
# send </rollback>
|
125
68
|
# NOTE: solr 1.4 only
|
126
|
-
def rollback(
|
127
|
-
update message.rollback,
|
69
|
+
def rollback(*args)
|
70
|
+
update message.rollback, *args
|
128
71
|
end
|
129
72
|
|
130
73
|
# Delete one or many documents by id
|
131
74
|
# solr.delete_by_id 10
|
132
75
|
# solr.delete_by_id([12, 41, 199])
|
133
|
-
def delete_by_id(
|
134
|
-
update message.delete_by_id(
|
76
|
+
def delete_by_id(*args)
|
77
|
+
update message.delete_by_id(args.shift), *args
|
135
78
|
end
|
136
79
|
|
137
80
|
# delete one or many documents by query
|
138
81
|
# solr.delete_by_query 'available:0'
|
139
82
|
# solr.delete_by_query ['quantity:0', 'manu:"FQ"']
|
140
|
-
def delete_by_query(
|
141
|
-
update message.delete_by_query(
|
83
|
+
def delete_by_query(*args)
|
84
|
+
update message.delete_by_query(args.shift), *args
|
142
85
|
end
|
143
86
|
|
144
87
|
protected
|
@@ -148,4 +91,11 @@ class RSolr::Connection::Base
|
|
148
91
|
RSolr::Message
|
149
92
|
end
|
150
93
|
|
94
|
+
# sets default params etc.. - could be used as a mapping hook
|
95
|
+
# type of request should be passed in here? -> map_params(:query, {})
|
96
|
+
def map_params(params)
|
97
|
+
params||={}
|
98
|
+
{:wt=>:ruby}.merge(params)
|
99
|
+
end
|
100
|
+
|
151
101
|
end
|
data/lib/rsolr/connection.rb
CHANGED
data/lib/rsolr.rb
CHANGED
@@ -7,7 +7,7 @@ proc {|base, files|
|
|
7
7
|
|
8
8
|
module RSolr
|
9
9
|
|
10
|
-
VERSION = '0.
|
10
|
+
VERSION = '0.7.0'
|
11
11
|
|
12
12
|
autoload :Message, 'rsolr/message'
|
13
13
|
autoload :Response, 'rsolr/response'
|
@@ -25,6 +25,9 @@ module RSolr
|
|
25
25
|
:http=>'HTTP',
|
26
26
|
:direct=>'Direct'
|
27
27
|
}
|
28
|
+
opts[:select_path] ||= 'select'
|
29
|
+
opts[:update_path] ||= 'update'
|
30
|
+
opts[:luke_path] ||= 'admin/luke'
|
28
31
|
adapter_class = RSolr::Connection::Adapter.const_get(types[adapter_name])
|
29
32
|
RSolr::Connection::Base.new(adapter_class.new(opts), opts)
|
30
33
|
end
|
@@ -12,18 +12,6 @@ module ConnectionTestMethods
|
|
12
12
|
# assert_equal 0, @solr.query(:q=>'*:*').docs.size
|
13
13
|
#end
|
14
14
|
|
15
|
-
def test_default_options
|
16
|
-
assert_equal '/select', @solr.adapter.default_options[:select_path]
|
17
|
-
assert_equal '/update', @solr.adapter.default_options[:update_path]
|
18
|
-
assert_equal '/admin/luke', @solr.adapter.default_options[:luke_path]
|
19
|
-
end
|
20
|
-
|
21
|
-
# setting adapter options in Solr.connect method should set them in the adapter
|
22
|
-
def test_set_adapter_options
|
23
|
-
solr = RSolr.connect(:select_path=>'/select2')
|
24
|
-
assert_equal '/select2', solr.adapter.opts[:select_path]
|
25
|
-
end
|
26
|
-
|
27
15
|
# setting connection options in Solr.connect method should set them in the connection
|
28
16
|
def test_set_connection_options
|
29
17
|
solr = RSolr.connect(:default_wt=>:json)
|
@@ -116,23 +104,4 @@ module ConnectionTestMethods
|
|
116
104
|
assert [true, false].include?(response.has_deletions?)
|
117
105
|
end
|
118
106
|
|
119
|
-
def test_search_facet_by_name
|
120
|
-
@solr.add([{:id=>1, :cat=>'eletronics'}, {:id=>2, :cat=>'software'}]) and @solr.commit
|
121
|
-
response = @solr.search_facet_by_name('cat', {:q=>'*:*'})
|
122
|
-
|
123
|
-
response.facets.each do |facet|
|
124
|
-
puts facet.field
|
125
|
-
puts facet.values.inspect
|
126
|
-
facet.values.each do |value|
|
127
|
-
puts value.value
|
128
|
-
puts value.hits
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
assert_equal 2, response.facet_by_field_name(:cat).values.size
|
133
|
-
#
|
134
|
-
response = @solr.search_facet_by_name('cat', {:q=>'*:*'})
|
135
|
-
assert_equal 0, response.docs.size
|
136
|
-
end
|
137
|
-
|
138
107
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mwmitchell-rsolr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Mitchell
|
@@ -14,6 +14,7 @@ default_executable:
|
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: builder
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
@@ -43,9 +44,6 @@ files:
|
|
43
44
|
- lib/rsolr/connection/adapter/http.rb
|
44
45
|
- lib/rsolr/connection/adapter.rb
|
45
46
|
- lib/rsolr/connection/base.rb
|
46
|
-
- lib/rsolr/connection/param_mapping.rb
|
47
|
-
- lib/rsolr/connection/param_mapping/dismax.rb
|
48
|
-
- lib/rsolr/connection/param_mapping/standard.rb
|
49
47
|
- lib/rsolr/connection.rb
|
50
48
|
- lib/rsolr/http_client/adapter/curb.rb
|
51
49
|
- lib/rsolr/http_client/adapter/net_http.rb
|
@@ -92,7 +90,6 @@ summary: A Ruby client for Apache Solr
|
|
92
90
|
test_files:
|
93
91
|
- test/connection/direct_test.rb
|
94
92
|
- test/connection/http_test.rb
|
95
|
-
- test/connection/param_mapping_test.rb
|
96
93
|
- test/connection/test_methods.rb
|
97
94
|
- test/http_client/curb_test.rb
|
98
95
|
- test/http_client/net_http_test.rb
|
@@ -1,41 +0,0 @@
|
|
1
|
-
class RSolr::Connection::ParamMapping::Dismax < RSolr::Connection::ParamMapping::Standard
|
2
|
-
|
3
|
-
def setup_mappings
|
4
|
-
super
|
5
|
-
|
6
|
-
mapping_for :alternate_query, 'q.alt' do |val|
|
7
|
-
format_query(val).join(' ')
|
8
|
-
end
|
9
|
-
|
10
|
-
mapping_for :query_fields, :qf do |val|
|
11
|
-
create_boost_query(val)
|
12
|
-
end
|
13
|
-
|
14
|
-
mapping_for :phrase_fields, :pf do |val|
|
15
|
-
create_boost_query(val)
|
16
|
-
end
|
17
|
-
|
18
|
-
mapping_for :boost_query, :bq do |val|
|
19
|
-
format_query(val).join(' ')
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
|
26
|
-
def create_boost_query(input)
|
27
|
-
case input
|
28
|
-
when Hash
|
29
|
-
qf = []
|
30
|
-
input.each_pair do |k,v|
|
31
|
-
qf << (v.to_s.empty? ? k : "#{k}^#{v}")
|
32
|
-
end
|
33
|
-
qf.join(' ')
|
34
|
-
when Array
|
35
|
-
input.join(' ')
|
36
|
-
when String
|
37
|
-
input
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
class RSolr::Connection::ParamMapping::Standard
|
2
|
-
|
3
|
-
include RSolr::Connection::ParamMapping::MappingMethods
|
4
|
-
|
5
|
-
attr_reader :input, :output
|
6
|
-
|
7
|
-
def initialize(input)
|
8
|
-
@output = {}
|
9
|
-
@input = input
|
10
|
-
setup_mappings
|
11
|
-
end
|
12
|
-
|
13
|
-
def setup_mappings
|
14
|
-
|
15
|
-
mapping_for :per_page, :rows do |val|
|
16
|
-
val = val.to_s.to_i
|
17
|
-
val < 0 ? 0 : val
|
18
|
-
end
|
19
|
-
|
20
|
-
mapping_for :page, :start do |val|
|
21
|
-
val = val.to_s.to_i
|
22
|
-
page = val > 0 ? val : 1
|
23
|
-
((page - 1) * (@output[:rows] || 0))
|
24
|
-
end
|
25
|
-
|
26
|
-
mapping_for :queries, :q do |val|
|
27
|
-
format_query(val)
|
28
|
-
end
|
29
|
-
|
30
|
-
mapping_for :phrase_queries, :q do |val|
|
31
|
-
values = [@output[:q], format_query(val, true)]
|
32
|
-
# remove blank items
|
33
|
-
values.reject!{|v|v.to_s.empty?}
|
34
|
-
# join all items on a space
|
35
|
-
values.join(' ')
|
36
|
-
end
|
37
|
-
|
38
|
-
mapping_for :filters, :fq do |val|
|
39
|
-
format_query(val)
|
40
|
-
end
|
41
|
-
|
42
|
-
# this must come after the :filter/:fq mapper
|
43
|
-
mapping_for :phrase_filters, :fq do |val|
|
44
|
-
# use the previously set fq queries and generate the new phrased based ones
|
45
|
-
values = [@output[:fq], format_query(val, true)]
|
46
|
-
# flatten (need to do this because the previous fq could have been an array)
|
47
|
-
values = values.flatten
|
48
|
-
# remove blank items
|
49
|
-
values.reject!{|v|v.to_s.empty?} # don't join -- instead create multiple fq params
|
50
|
-
# don't join... fq needs to be an array so multiple fq params are sent to solr
|
51
|
-
values
|
52
|
-
end
|
53
|
-
|
54
|
-
mapping_for :facets do |input|
|
55
|
-
next if input.to_s.empty?
|
56
|
-
@output[:facet] = true
|
57
|
-
@output['facet.field'] = []
|
58
|
-
if input[:queries]
|
59
|
-
# convert to an array if needed
|
60
|
-
input[:queries] = [input[:queries]] unless input[:queries].is_a?(Array)
|
61
|
-
@output[:facet.query] = input[:queries].map{|q|format_query(q)}
|
62
|
-
end
|
63
|
-
common_sub_fields = [:sort, :limit, :missing, :mincount, :prefix, :offset, :method, 'enum.cache.minDf']
|
64
|
-
(common_sub_fields).each do |subfield|
|
65
|
-
next unless input[subfield]
|
66
|
-
@output["facet.#{subfield}"] = input[subfield]
|
67
|
-
end
|
68
|
-
if input[:fields]
|
69
|
-
input[:fields].each do |f|
|
70
|
-
if f.kind_of? Hash
|
71
|
-
key = f.keys[0]
|
72
|
-
value = f[key]
|
73
|
-
@output[:facet.field] << key
|
74
|
-
common_sub_fields.each do |subfield|
|
75
|
-
next unless value[subfield]
|
76
|
-
@output["f.#{key}.facet.#{subfield}"] = input[subfield]
|
77
|
-
end
|
78
|
-
else
|
79
|
-
@output['facet.field'] << f
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# takes an input and returns a formatted value
|
87
|
-
def format_query(input, quote=false)
|
88
|
-
case input
|
89
|
-
when Array
|
90
|
-
format_array_query(input, quote)
|
91
|
-
when Hash
|
92
|
-
format_hash_query(input, quote)
|
93
|
-
else
|
94
|
-
prep_value(input, quote)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def format_array_query(input, quote)
|
99
|
-
input.collect do |v|
|
100
|
-
v.is_a?(Hash) ? format_hash_query(v, quote) : prep_value(v, quote)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
# groups values to a single field: title:(value1 value2) instead of title:value1 title:value2
|
105
|
-
# a value can be a range or a string
|
106
|
-
def format_hash_query(input, quote=false)
|
107
|
-
q = []
|
108
|
-
input.each_pair do |field,value|
|
109
|
-
next if value.to_s.empty? # skip blank values!
|
110
|
-
# create the field plus the delimiter if the field is not blank
|
111
|
-
value = [value] unless value.is_a?(Array)
|
112
|
-
fielded_queries = value.collect do |vv|
|
113
|
-
vv.is_a?(Range) ? "[#{vv.min} TO #{vv.max}]" : prep_value(vv, quote)
|
114
|
-
end
|
115
|
-
field = field.to_s.empty? ? '' : "#{field}:"
|
116
|
-
fielded_queries.each do |fq|
|
117
|
-
q << "#{field}(#{fq})"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
q
|
121
|
-
end
|
122
|
-
|
123
|
-
def prep_value(val, quote=false)
|
124
|
-
quote ? %(\"#{val}\") : val.to_s
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module RSolr::Connection::ParamMapping
|
2
|
-
|
3
|
-
autoload :Standard, 'rsolr/connection/param_mapping/standard'
|
4
|
-
autoload :Dismax, 'rsolr/connection/param_mapping/dismax'
|
5
|
-
|
6
|
-
module MappingMethods
|
7
|
-
|
8
|
-
def mappers
|
9
|
-
@mappers ||= []
|
10
|
-
end
|
11
|
-
|
12
|
-
def mapping_for(user_param_name, solr_param_name=nil, &block)
|
13
|
-
return unless @input[user_param_name]
|
14
|
-
if (m = self.mappers.detect{|m|m[:input_name] == user_param_name})
|
15
|
-
self.mappers.delete m
|
16
|
-
end
|
17
|
-
self.mappers << {:input_name=>user_param_name, :output_name=>solr_param_name, :block=>block}
|
18
|
-
end
|
19
|
-
|
20
|
-
def map(&blk)
|
21
|
-
input = @input.dup
|
22
|
-
mappers.each do |m|
|
23
|
-
input_value = input[m[:input_name]]
|
24
|
-
input.delete m[:input_name]
|
25
|
-
if m[:block]
|
26
|
-
value = m[:block].call(input_value)
|
27
|
-
else
|
28
|
-
value = input_value
|
29
|
-
end
|
30
|
-
if m[:output_name]
|
31
|
-
@output[m[:output_name]] = value
|
32
|
-
end
|
33
|
-
end
|
34
|
-
@output.merge(input)
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), '..', 'test_helpers')
|
2
|
-
|
3
|
-
class ParamMappingTest < RSolrBaseTest
|
4
|
-
|
5
|
-
include RSolr::Connection::ParamMapping
|
6
|
-
|
7
|
-
def test_standard_simple
|
8
|
-
input = {
|
9
|
-
:queries=>'a query',
|
10
|
-
:filters=>'a filter',
|
11
|
-
:page=>1,
|
12
|
-
:per_page=>10,
|
13
|
-
:phrase_queries=>'a phrase query',
|
14
|
-
:phrase_filters=>'a phrase filter',
|
15
|
-
:facets=>{
|
16
|
-
:fields=>[:one,:two]
|
17
|
-
}
|
18
|
-
}
|
19
|
-
mapper = Standard.new(input)
|
20
|
-
output = mapper.map
|
21
|
-
|
22
|
-
assert_equal "a query \"a phrase query\"", output[:q]
|
23
|
-
assert_equal ["a filter", "\"a phrase filter\""], output[:fq]
|
24
|
-
assert_equal 0, output[:start]
|
25
|
-
assert_equal 10, output[:rows]
|
26
|
-
# facet.field can be specified multiple times, so we need an array
|
27
|
-
# the url builder automatically adds multiple params for arrays
|
28
|
-
assert_equal [:one, :two], output['facet.field']
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_standard_complex
|
32
|
-
input = {
|
33
|
-
:queries=>['a query', {:field=>'value'}, 'blah'],
|
34
|
-
:filters=>['a filter', {:filter=>'field'}, 'blah'],
|
35
|
-
:phrase_queries=>['a phrase', {:phrase_field=>'phrase value'}],
|
36
|
-
:phrase_filters=>{:can_also_be_a=>'hash'}
|
37
|
-
}
|
38
|
-
mapper = Standard.new(input)
|
39
|
-
output = mapper.map
|
40
|
-
|
41
|
-
assert_equal "a query field:(value) blah \"a phrase\" phrase_field:(\"phrase value\")", output[:q]
|
42
|
-
assert_equal ["a filter", "filter:(field)", "blah", "can_also_be_a:(\"hash\")"], output[:fq]
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_dismax
|
46
|
-
input = {
|
47
|
-
:alternate_query=>{:can_be_a_string_hash_or_array=>'OK'},
|
48
|
-
:query_fields=>{:a_field_to_boost=>20, :another_field_to_boost=>200},
|
49
|
-
:phrase_fields=>{:phrase_field=>20},
|
50
|
-
:boost_query=>[{:field_to_use_for_boost_query=>'a'}, 'test']
|
51
|
-
}
|
52
|
-
mapper = Dismax.new(input)
|
53
|
-
output = mapper.map
|
54
|
-
assert_equal 'can_be_a_string_hash_or_array:(OK)', output['q.alt']
|
55
|
-
assert output[:qf]=~/another_field_to_boost\^200/
|
56
|
-
assert output[:qf]=~/a_field_to_boost\^20/
|
57
|
-
assert_equal 'phrase_field^20', output[:pf]
|
58
|
-
assert_equal 'field_to_use_for_boost_query:(a) test', output[:bq]
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
data/test/ruby-lang.org.rss.xml
DELETED
@@ -1,391 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
3
|
-
<channel>
|
4
|
-
<title>Ruby News</title>
|
5
|
-
<link>http://www.ruby-lang.org/en/feeds/news.rss/</link>
|
6
|
-
<language>en-us</language>
|
7
|
-
<ttl>40</ttl>
|
8
|
-
<description>The latest news from Ruby-Lang.org.</description>
|
9
|
-
|
10
|
-
|
11
|
-
<item>
|
12
|
-
<title>Scotland on Rails 2009</title>
|
13
|
-
<description><p><a href="http://scotlandonrails.com">Scotland on Rails</a> is pleased to announce that Conference2009 will be held March 26-28 in Edinburgh, Scotland.</p>
|
14
|
-
|
15
|
-
|
16
|
-
<p>We are now accepting submissions. The closing date for submissions is December 1st 2008, so there&#8217;s still time! Please mail your plaintext proposals for 45 minute sessions to <a href="mailto:submissions@scotlandonrails.com">submissions@scotlandonrails.com</a>.</p>
|
17
|
-
|
18
|
-
|
19
|
-
<p>Alternatively, if you are interested in sponsoring the conference, please mail <a href="mailto:sponsorship@scotlandonrails.com">sponsorship@scotlandonrails.com</a> for a prospectus.</p>
|
20
|
-
|
21
|
-
|
22
|
-
<p>Lastly, if you wish to be notified when we open for registration, you can sign up on the site.</p>
|
23
|
-
|
24
|
-
|
25
|
-
<p>Come and enjoy all that Edinburgh has to offer (whisky! castle! volcano! ruby! whisky!) in March. We hope to see you there.</p> </description>
|
26
|
-
<pubDate>Mon, 10 Nov 2008 14:55:53 GMT</pubDate>
|
27
|
-
<guid>http://www.ruby-lang.org/en/news/2008/11/10/scotland-on-rails-2009/</guid>
|
28
|
-
<link>http://www.ruby-lang.org/en/news/2008/11/10/scotland-on-rails-2009/</link>
|
29
|
-
</item>
|
30
|
-
|
31
|
-
<item>
|
32
|
-
<title>MountainWest RubyConf 2009 dates and CFP</title>
|
33
|
-
<description><p><a href="http://mtnwestrubyconf.org">MountainWest RubyConf 2009</a> will be held March 13-14, 2009, in Salt Lake City, Utah, <span class="caps">USA</span>.</p>
|
34
|
-
|
35
|
-
|
36
|
-
<p>Proposals to speak at this regional conference are now being accepted. Please send your proposal to proposals@mtnwestrubyconf.org.</p>
|
37
|
-
|
38
|
-
|
39
|
-
<p>The submission deadline is midnight (MST) on December 31st, 2008.</p>
|
40
|
-
|
41
|
-
|
42
|
-
<p>There are sponsorship opportunities available as well. Please contact sponsorship@mtnwestruby.org if you are interested.</p>
|
43
|
-
|
44
|
-
|
45
|
-
<p>Please see <a href="http://mtnwestrubyconf.org">mtnwestrubyconf.org/</a> for more details as they become available.</p> </description>
|
46
|
-
<pubDate>Sat, 08 Nov 2008 15:03:32 GMT</pubDate>
|
47
|
-
<guid>http://www.ruby-lang.org/en/news/2008/11/08/mountainwest-rubyconf-2009-dates-and-cfp/</guid>
|
48
|
-
<link>http://www.ruby-lang.org/en/news/2008/11/08/mountainwest-rubyconf-2009-dates-and-cfp/</link>
|
49
|
-
</item>
|
50
|
-
|
51
|
-
<item>
|
52
|
-
<title> Ruby 1.9.1-preview 1 released</title>
|
53
|
-
<description><p>Yugui (Yuki Sonoda) announced the release of Ruby 1.9.1-preview 1:</p>
|
54
|
-
|
55
|
-
|
56
|
-
<blockquote>
|
57
|
-
This is a preview release of Ruby 1.9.1, which will be the first stable version of the Ruby 1.9 series. Try it out now and get an early taste of a modern, faster, multilingualized, and much improved Ruby with clearer syntax.<br><br>
|
58
|
-
|
59
|
-
<p>If you encounter any bugs or problems, please let us know via the official issue tracking system:</p>
|
60
|
-
|
61
|
-
|
62
|
-
<p><a href="http://redmine.ruby-lang.org">http://redmine.ruby-lang.org</a></p>
|
63
|
-
|
64
|
-
|
65
|
-
</blockquote>
|
66
|
-
|
67
|
-
<p>You can download the release from;</p>
|
68
|
-
|
69
|
-
|
70
|
-
<ul>
|
71
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.bz2">ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.bz2</a>
|
72
|
-
|
73
|
-
<p><span class="caps">SIZE</span>: 6169022 bytes
|
74
|
-
<span class="caps">MD5</span>: 0d51dc949bb6b438ad4ebfabbb5f6754
|
75
|
-
<span class="caps">SHA256</span>: dc39000537d7c7528ef26af8e1c3a6215b30b6c579c615eaec7013513410456a</p></li>
|
76
|
-
</ul>
|
77
|
-
|
78
|
-
|
79
|
-
<ul>
|
80
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.gz">ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.tar.gz</a>
|
81
|
-
|
82
|
-
<p><span class="caps">SIZE</span>: 7409682 bytes
|
83
|
-
<span class="caps">MD5</span>: 738f701532452fd5d36f5c155f3ba692
|
84
|
-
<span class="caps">SHA256</span>: 99443bdae9f94ba7b08de187881f8cbee172379edf9c5fa85fc04c869150ff6d</p></li>
|
85
|
-
</ul>
|
86
|
-
|
87
|
-
|
88
|
-
<ul>
|
89
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.zip">ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-preview1.zip</a>
|
90
|
-
|
91
|
-
<p><span class="caps">SIZE</span>: 8569116 bytes
|
92
|
-
<span class="caps">MD5</span>: 5f68246246c4cd29d8a3b6b34b29b6ac
|
93
|
-
<span class="caps">SHA256</span>: a6c3a7bf7ea83b595024764926353e08596a78e40c57ac58c568662e5e88df95</p></li>
|
94
|
-
</ul> </description>
|
95
|
-
<pubDate>Tue, 28 Oct 2008 19:45:27 GMT</pubDate>
|
96
|
-
<guid>http://www.ruby-lang.org/en/news/2008/10/28/ruby-1-9-1-preview-1-released/</guid>
|
97
|
-
<link>http://www.ruby-lang.org/en/news/2008/10/28/ruby-1-9-1-preview-1-released/</link>
|
98
|
-
</item>
|
99
|
-
|
100
|
-
<item>
|
101
|
-
<title>RubyConf 2008 is Sold-out</title>
|
102
|
-
<description><p><a href="http://rubyconf.org/">RubyConf 2008</a> is sold out</p>
|
103
|
-
|
104
|
-
|
105
|
-
<p>However, there is a <a href="http://www.regonline.com/builder/site/Default.aspx?eventid=636797">waiting list</a> you can join in case of cancellations.</p> </description>
|
106
|
-
<pubDate>Thu, 02 Oct 2008 23:21:06 GMT</pubDate>
|
107
|
-
<guid>http://www.ruby-lang.org/en/news/2008/10/02/rubyconf-2008-is-sold-out/</guid>
|
108
|
-
<link>http://www.ruby-lang.org/en/news/2008/10/02/rubyconf-2008-is-sold-out/</link>
|
109
|
-
</item>
|
110
|
-
|
111
|
-
<item>
|
112
|
-
<title>Voices That Matter 2008</title>
|
113
|
-
<description><p>Pearson Education is running a <a href="http://www.voicesthatmatter.com/ruby2008/">Voices That Matter</a> Ruby conference this fall in Boston. The conference, from the same people who Addison-Wesley's Professional Ruby Series, will give you a chance to meet and learn from those very same authors. Don't miss a chance to interact with so many Ruby professionals.</p> </description>
|
114
|
-
<pubDate>Tue, 09 Sep 2008 02:49:37 GMT</pubDate>
|
115
|
-
<guid>http://www.ruby-lang.org/en/news/2008/09/09/voices-that-matter-2008/</guid>
|
116
|
-
<link>http://www.ruby-lang.org/en/news/2008/09/09/voices-that-matter-2008/</link>
|
117
|
-
</item>
|
118
|
-
|
119
|
-
<item>
|
120
|
-
<title>DoS vulnerability in REXML</title>
|
121
|
-
<description><p>There is a DoS vulnerability in the REXML library included in the Ruby
|
122
|
-
Standard Library. A so-called "XML entity explosion" attack technique
|
123
|
-
can be used for remotely bringing down (disabling) any application
|
124
|
-
which parses user-provided XML using REXML.</p><p>Most Rails applications will be vulnerable because Rails parses
|
125
|
-
user-provided XML using REXML by default. </p> <h2><a name="label-0" id="label-0">Impact</a></h2><!-- RDLabel: "Impact" --><p>An attacker can cause a denial of service by causing REXML to parse a
|
126
|
-
document containing recursively nested entities such as:</p><pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
|
127
|
-
&lt;!DOCTYPE member [
|
128
|
-
&lt;!ENTITY a "&amp;b;&amp;b;&amp;b;&amp;b;&amp;b;&amp;b;&amp;b;&amp;b;&amp;b;&amp;b;"&gt;
|
129
|
-
&lt;!ENTITY b "&amp;c;&amp;c;&amp;c;&amp;c;&amp;c;&amp;c;&amp;c;&amp;c;&amp;c;&amp;c;"&gt;
|
130
|
-
&lt;!ENTITY c "&amp;d;&amp;d;&amp;d;&amp;d;&amp;d;&amp;d;&amp;d;&amp;d;&amp;d;&amp;d;"&gt;
|
131
|
-
&lt;!ENTITY d "&amp;e;&amp;e;&amp;e;&amp;e;&amp;e;&amp;e;&amp;e;&amp;e;&amp;e;&amp;e;"&gt;
|
132
|
-
&lt;!ENTITY e "&amp;f;&amp;f;&amp;f;&amp;f;&amp;f;&amp;f;&amp;f;&amp;f;&amp;f;&amp;f;"&gt;
|
133
|
-
&lt;!ENTITY f "&amp;g;&amp;g;&amp;g;&amp;g;&amp;g;&amp;g;&amp;g;&amp;g;&amp;g;&amp;g;"&gt;
|
134
|
-
&lt;!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"&gt;
|
135
|
-
]&gt;
|
136
|
-
&lt;member&gt;
|
137
|
-
&amp;a;
|
138
|
-
&lt;/member&gt;</pre><h2><a name="label-1" id="label-1">Vulnerable versions</a></h2><!-- RDLabel: "Vulnerable versions" --><h3><a name="label-2" id="label-2">1.8 series</a></h3><!-- RDLabel: "1.8 series" --><ul>
|
139
|
-
<li>1.8.6-p287 and all prior versions</li>
|
140
|
-
<li>1.8.7-p72 and all prior versions</li>
|
141
|
-
</ul><h3><a name="label-3" id="label-3">1.9 series</a></h3><!-- RDLabel: "1.9 series" --><ul>
|
142
|
-
<li>all versions</li>
|
143
|
-
</ul><h2><a name="label-4" id="label-4">Solution</a></h2><!-- RDLabel: "Solution" --><p>Please download the following monkey patch to fix this problem.</p><ul>
|
144
|
-
<li><a href="http://www.ruby-lang.org/security/20080823rexml/rexml-expansion-fix2.rb">&lt;URL:http://www.ruby-lang.org/security/20080823rexml/rexml-expansion-fix2.rb&gt;</a></li>
|
145
|
-
</ul><p>Then fix your application to load rexml-expansion-fix2.rb before using
|
146
|
-
REXML.</p><pre>require "rexml-expansion-fix2"
|
147
|
-
...
|
148
|
-
doc = REXML::Document.new(str)
|
149
|
-
...</pre><p>If you have a Rails application, copy rexml-expansion-fix2.rb into a
|
150
|
-
directory on the load path (such as RAILS_ROOT/lib/), and put the
|
151
|
-
following line into config/environment.rb.</p><pre>require "rexml-expansion-fix2"</pre><p>If your application is Rails 2.1 or later, you can simply copy
|
152
|
-
rexml-expansion-fix2.rb to RAILS_ROOT/config/initializers and it will
|
153
|
-
be required automatically.</p><p>By default, XML entity expansion limit is 10000. You can change it by
|
154
|
-
changing REXML::Document.entity_expansion_limit. e.g.</p><pre>REXML::Document.entity_expansion_limit = 1000</pre><p>This fix will be made available as a gem and used by future versions of
|
155
|
-
rails, but users should take corrective action immediately.</p><h2><a name="label-5" id="label-5">Credit</a></h2><!-- RDLabel: "Credit" --><p>Credit to Luka Treiber and Mitja Kolsek of ACROS Security for
|
156
|
-
disclosing the problem to Ruby and Rails Security Teams.</p><p>Credit to Michael Koziarski of Rails Core Team for creating the monkey
|
157
|
-
patch to fix the vulnerability.</p><h2><a name="label-6" id="label-6">Changes</a></h2><!-- RDLabel: "Changes" --><ul>
|
158
|
-
<li>2008-08-29 18:46 +09:00 fixed the summary not to mislead that this vulnerability is Rails specific.</li>
|
159
|
-
<li>2008-11-09 12:40 +09:00 fixed <a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=502535">a bug of the monkey patch</a>.</li>
|
160
|
-
</ul></description>
|
161
|
-
<pubDate>Sat, 23 Aug 2008 07:56:11 GMT</pubDate>
|
162
|
-
<guid>http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/</guid>
|
163
|
-
<link>http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/</link>
|
164
|
-
</item>
|
165
|
-
|
166
|
-
<item>
|
167
|
-
<title>Ruby 1.8.7-p72 and 1.8.6-p287 released</title>
|
168
|
-
<description><p>Ruby 1.8.7-p72 and 1.8.6-p287 have been released.
|
169
|
-
The last releases were incomplete, and the new releases include fixes of <a href="http://www.ruby-lang.org/en/news/2008/08/08/multiple-vulnerabilities-in-ruby/#label-3">the previously announced vulnerability of dl</a>.</p><p>The released source archives are available at:</p><ul>
|
170
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.tar.gz&gt;</a></li>
|
171
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.tar.bz2">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.tar.bz2&gt;</a></li>
|
172
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.zip">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.zip&gt;</a></li>
|
173
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz&gt;</a></li>
|
174
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.bz2">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.bz2&gt;</a></li>
|
175
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.zip">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.zip&gt;</a></li>
|
176
|
-
</ul> <p>Checksums:</p><pre>MD5(ruby-1.8.6-p287.tar.gz)= f6cd51001534ced5375339707a757556
|
177
|
-
SHA256(ruby-1.8.6-p287.tar.gz)= 6463d1932c34ff72b79174ac7d2c28940d29d147928250928a00a0dbee43db57
|
178
|
-
SIZE(ruby-1.8.6-p287.tar.gz)= 4590393
|
179
|
-
|
180
|
-
MD5(ruby-1.8.6-p287.tar.bz2)= 80b5f3db12531d36e6c81fac6d05dda9
|
181
|
-
SHA256(ruby-1.8.6-p287.tar.bz2)= ac15a1cb78c50ec9cc7e831616a143586bdd566bc865c6b769a0c47b3b3936ce
|
182
|
-
SIZE(ruby-1.8.6-p287.tar.bz2)= 3956902
|
183
|
-
|
184
|
-
MD5(ruby-1.8.6-p287.zip)= e555d51f5b387fdd52ae53d9bafa13f5
|
185
|
-
SHA256(ruby-1.8.6-p287.zip)= 844c66c015565839531a34b83e0526cd4fa2a71cc0f5cc8ddb0d4c158403543a
|
186
|
-
SIZE(ruby-1.8.6-p287.zip)= 5606238
|
187
|
-
|
188
|
-
MD5(ruby-1.8.7-p72.tar.gz)= 5e5b7189674b3a7f69401284f6a7a36d
|
189
|
-
SHA256(ruby-1.8.7-p72.tar.gz)= e15ca005076f5d6f91fc856fdfbd071698a4cadac3c6e25855899dba1f6fc5ef
|
190
|
-
SIZE(ruby-1.8.7-p72.tar.gz)= 4805594
|
191
|
-
|
192
|
-
MD5(ruby-1.8.7-p72.tar.bz2)= 0b215c46b89b28d7ab8d56d96e72d5b9
|
193
|
-
SHA256(ruby-1.8.7-p72.tar.bz2)= a8f8a28e286dd76747d8e97ea5cfe7a315eb896906ab8c8606d687d9f6f6146e
|
194
|
-
SIZE(ruby-1.8.7-p72.tar.bz2)= 4127450
|
195
|
-
|
196
|
-
MD5(ruby-1.8.7-p72.zip)= b44fe5a12d4bf138ba0d3660e13a8216
|
197
|
-
SHA256(ruby-1.8.7-p72.zip)= 77e67be4aa8c3e041e1d20d24e5fcf2e33ad9bccb3da3332b6c0a5b648334903
|
198
|
-
SIZE(ruby-1.8.7-p72.zip)= 5855902</pre><p>For a full list of all changes, see the bundled files named ChangeLog, which are also available at the following locations:</p><ul>
|
199
|
-
<li><a href="http://svn.ruby-lang.org/repos/ruby/tags/v1_8_6_287/ChangeLog">&lt;URL:http://svn.ruby-lang.org/repos/ruby/tags/v1_8_6_287/ChangeLog&gt;</a></li>
|
200
|
-
<li><a href="http://svn.ruby-lang.org/repos/ruby/tags/v1_8_7_72/ChangeLog">&lt;URL:http://svn.ruby-lang.org/repos/ruby/tags/v1_8_7_72/ChangeLog&gt;</a></li>
|
201
|
-
</ul></description>
|
202
|
-
<pubDate>Mon, 11 Aug 2008 02:01:00 GMT</pubDate>
|
203
|
-
<guid>http://www.ruby-lang.org/en/news/2008/08/11/ruby-1-8-7-p72-and-1-8-6-p287-released/</guid>
|
204
|
-
<link>http://www.ruby-lang.org/en/news/2008/08/11/ruby-1-8-7-p72-and-1-8-6-p287-released/</link>
|
205
|
-
</item>
|
206
|
-
|
207
|
-
<item>
|
208
|
-
<title>Multiple vulnerabilities in Ruby</title>
|
209
|
-
<description><p>Multiple vulnerabilities have been discovered in Ruby. It's
|
210
|
-
recommended that you upgrade to the latest versions.</p> <h2><a name="label-0" id="label-0">Details</a></h2><!-- RDLabel: "Details" --><p>The following vulnerabilities have been discovered.</p><h3><a name="label-1" id="label-1">Several vulnerabilities in safe level</a></h3><!-- RDLabel: "Several vulnerabilities in safe level" --><p>Several vulnerabilities in safe level have been discovered.</p><ul>
|
211
|
-
<li><p>untrace_var is permitted at safe level 4.</p>
|
212
|
-
<pre>trace_var(:$VAR) {|val| puts "$VAR = #{val}" }
|
213
|
-
|
214
|
-
Thread.new do
|
215
|
-
$SAFE = 4
|
216
|
-
eval %q{
|
217
|
-
proc = untrace_var :$VAR
|
218
|
-
proc.first.call("aaa")
|
219
|
-
}
|
220
|
-
end.join</pre></li>
|
221
|
-
<li><p>$PROGRAM_NAME may be modified at safe level 4.</p>
|
222
|
-
<pre>Thread.new do
|
223
|
-
$SAFE = 4
|
224
|
-
eval %q{$PROGRAM_NAME.replace "Hello, World!"}
|
225
|
-
end.join
|
226
|
-
|
227
|
-
$PROGRAM_NAME #=&gt; "Hello, World!"</pre></li>
|
228
|
-
<li><p>Insecure methods may be called at safe level 1-3.</p>
|
229
|
-
<pre>class Hello
|
230
|
-
def world
|
231
|
-
Thread.new do
|
232
|
-
$SAFE = 4
|
233
|
-
msg = "Hello, World!"
|
234
|
-
def msg.size
|
235
|
-
self.replace self*10 # replace string
|
236
|
-
1 # return wrong size
|
237
|
-
end
|
238
|
-
msg
|
239
|
-
end.value
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
$SAFE = 1 # or 2, or 3
|
244
|
-
s = Hello.new.world
|
245
|
-
if s.kind_of?(String)
|
246
|
-
puts s if s.size &lt; 20 # print string which size is less than 20
|
247
|
-
end</pre></li>
|
248
|
-
<li><p>Syslog operations are permitted at safe level 4.</p>
|
249
|
-
<pre>require "syslog"
|
250
|
-
|
251
|
-
Syslog.open
|
252
|
-
|
253
|
-
Thread.new do
|
254
|
-
$SAFE = 4
|
255
|
-
eval %q{
|
256
|
-
Syslog.log(Syslog::LOG_WARNING, "Hello, World!")
|
257
|
-
Syslog.mask = Syslog::LOG_UPTO(Syslog::LOG_EMERG)
|
258
|
-
Syslog.info("masked")
|
259
|
-
Syslog.close
|
260
|
-
}
|
261
|
-
end.join</pre></li>
|
262
|
-
</ul><p>These vulnerabilities were reported by Keita Yamaguchi.</p><h3><a name="label-2" id="label-2">DoS vulnerability in WEBrick</a></h3><!-- RDLabel: "DoS vulnerability in WEBrick" --><p>WEBrick::HTTP::DefaultFileHandler is faulty of exponential time taking
|
263
|
-
requests due to a backtracking regular expression in
|
264
|
-
WEBrick::HTTPUtils.split_header_value.</p><p>Exploitable server:</p><pre>require 'webrick'
|
265
|
-
WEBrick::HTTPServer.new(:Port =&gt; 2000, :DocumentRoot =&gt; "/etc").start</pre><p>Attack:</p><pre>require 'net/http'
|
266
|
-
res = Net::HTTP.start("localhost", 2000) { |http|
|
267
|
-
req = Net::HTTP::Get.new("/passwd")
|
268
|
-
req['If-None-Match'] = %q{meh=""} + %q{foo="bar" } * 100
|
269
|
-
http.request(req)
|
270
|
-
}
|
271
|
-
p res</pre><p>The request likely won't finish in this universe.</p><p>This vulnerability was reported by Christian Neukirchen.</p><h3><a name="label-3" id="label-3">Lack of taintness check in dl</a></h3><!-- RDLabel: "Lack of taintness check in dl" --><p>dl doesn't check taintness, so it could allow attackers to call
|
272
|
-
dangerous functions.</p><pre>require 'dl'
|
273
|
-
$SAFE = 1
|
274
|
-
h = DL.dlopen(nil)
|
275
|
-
sys = h.sym('system', 'IP')
|
276
|
-
uname = 'uname -rs'.taint
|
277
|
-
sys[uname]</pre><p>This vulnerability was reported by sheepman.</p><h3><a name="label-4" id="label-4">DNS spoofing vulnerability in resolv.rb</a></h3><!-- RDLabel: "DNS spoofing vulnerability in resolv.rb" --><p>resolv.rb allow remote attackers to spoof DNS answers. This risk can be
|
278
|
-
reduced by randomness of DNS transaction IDs and source ports, so resolv.rb
|
279
|
-
is fixed to randomize them.</p><ul>
|
280
|
-
<li>see also: <a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-1447">CVE-2008-1447</a></li>
|
281
|
-
</ul><p>This vulnerability was reported by Tanaka Akira.</p><h2><a name="label-5" id="label-5">Vulnerable versions</a></h2><!-- RDLabel: "Vulnerable versions" --><dl>
|
282
|
-
<dt><a name="label-6" id="label-6">1.8 series</a></dt><!-- RDLabel: "1.8 series" -->
|
283
|
-
<dd>
|
284
|
-
<ul>
|
285
|
-
<li>1.8.5 and all prior versions</li>
|
286
|
-
<li>1.8.6-p286 and all prior versions</li>
|
287
|
-
<li>1.8.7-p71 and all prior versions</li>
|
288
|
-
</ul>
|
289
|
-
</dd>
|
290
|
-
<dt><a name="label-7" id="label-7">1.9 series</a></dt><!-- RDLabel: "1.9 series" -->
|
291
|
-
<dd>
|
292
|
-
<ul>
|
293
|
-
<li>r18423 and all prior revisions</li>
|
294
|
-
</ul>
|
295
|
-
</dd>
|
296
|
-
</dl><h2><a name="label-8" id="label-8">Solution</a></h2><!-- RDLabel: "Solution" --><dl>
|
297
|
-
<dt><a name="label-9" id="label-9">1.8 series</a></dt><!-- RDLabel: "1.8 series" -->
|
298
|
-
<dd>
|
299
|
-
Please upgrade to 1.8.6-p287, or 1.8.7-p72.
|
300
|
-
<ul>
|
301
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p287.tar.gz&gt;</a></li>
|
302
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz&gt;</a></li>
|
303
|
-
</ul>
|
304
|
-
</dd>
|
305
|
-
<dt><a name="label-10" id="label-10">1.9 series</a></dt><!-- RDLabel: "1.9 series" -->
|
306
|
-
<dd>
|
307
|
-
<p>Please check out the latest version using Subversion.</p>
|
308
|
-
<pre>$ svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby</pre>
|
309
|
-
</dd>
|
310
|
-
</dl><p>Please note that a package that corrects this weakness may already be
|
311
|
-
available through your package management software.</p><h2><a name="label-11" id="label-11">Credit</a></h2><!-- RDLabel: "Credit" --><p>Credit to Keita Yamaguchi, Christian Neukirchen, sheepman, and Tanaka
|
312
|
-
Akira for disclosing these problems to Ruby Security Team.</p><h2><a name="label-12" id="label-12">Changes</a></h2><!-- RDLabel: "Changes" --><ul>
|
313
|
-
<li>2008-08-08 12:21 +09:00 fixed the revision number of ruby 1.9.</li>
|
314
|
-
<li>2008-08-11 11:23 +09:00 fixed the patchlevel of ruby 1.8. see <a href="http://www.ruby-lang.org/en/news/2008/08/11/ruby-1-8-7-p72-and-1-8-6-p287-released/">the release announcement of Ruby 1.8.7-p72 and 1.8.6-p287</a></li>
|
315
|
-
</ul></description>
|
316
|
-
<pubDate>Fri, 08 Aug 2008 02:59:49 GMT</pubDate>
|
317
|
-
<guid>http://www.ruby-lang.org/en/news/2008/08/08/multiple-vulnerabilities-in-ruby/</guid>
|
318
|
-
<link>http://www.ruby-lang.org/en/news/2008/08/08/multiple-vulnerabilities-in-ruby/</link>
|
319
|
-
</item>
|
320
|
-
|
321
|
-
<item>
|
322
|
-
<title>RubyConf 2008 Proposals Now Being Accepted</title>
|
323
|
-
<description><p><a href="http://www.rubyconf.org">RubyConf 2008</a> will be held in Orlando, Florida, <span class="caps">USA</span>, from November 6 to November 8.</p>
|
324
|
-
|
325
|
-
|
326
|
-
<p><a href="http://www.rubyconf.org/proposals/new">Proposals for presentations</a> are now begin accepted. All proposals must be received by August 21.</p> </description>
|
327
|
-
<pubDate>Mon, 04 Aug 2008 20:26:29 GMT</pubDate>
|
328
|
-
<guid>http://www.ruby-lang.org/en/news/2008/08/04/rubyconf-2008-proposals-now-being-accepted/</guid>
|
329
|
-
<link>http://www.ruby-lang.org/en/news/2008/08/04/rubyconf-2008-proposals-now-being-accepted/</link>
|
330
|
-
</item>
|
331
|
-
|
332
|
-
<item>
|
333
|
-
<title>Arbitrary code execution vulnerabilities</title>
|
334
|
-
<description><p>Multiple vulnerabilities in Ruby may lead to a denial of service (DoS)
|
335
|
-
condition or allow execution of arbitrary code.</p> <h2><a name="label-0" id="label-0">Impact</a></h2><!-- RDLabel: "Impact" --><p>With the following vulnerabilities, an attacker can lead to denial of
|
336
|
-
service condition or execute arbitrary code.</p><ul>
|
337
|
-
<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2662">CVE-2008-2662</a></li>
|
338
|
-
<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2663">CVE-2008-2663</a></li>
|
339
|
-
<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2725">CVE-2008-2725</a></li>
|
340
|
-
<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2726">CVE-2008-2726</a></li>
|
341
|
-
<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2664">CVE-2008-2664</a></li>
|
342
|
-
</ul><h2><a name="label-1" id="label-1">Vulnerable versions</a></h2><!-- RDLabel: "Vulnerable versions" --><dl>
|
343
|
-
<dt><a name="label-2" id="label-2">1.8 series</a></dt><!-- RDLabel: "1.8 series" -->
|
344
|
-
<dd>
|
345
|
-
<ul>
|
346
|
-
<li>1.8.4 and all prior versions</li>
|
347
|
-
<li>1.8.5-p230 and all prior versions</li>
|
348
|
-
<li>1.8.6-p229 and all prior versions</li>
|
349
|
-
<li>1.8.7-p21 and all prior versions</li>
|
350
|
-
</ul>
|
351
|
-
</dd>
|
352
|
-
<dt><a name="label-3" id="label-3">1.9 series</a></dt><!-- RDLabel: "1.9 series" -->
|
353
|
-
<dd>
|
354
|
-
<ul>
|
355
|
-
<li>1.9.0-1 and all prior versions</li>
|
356
|
-
</ul>
|
357
|
-
</dd>
|
358
|
-
</dl><h2><a name="label-4" id="label-4">Solution</a></h2><!-- RDLabel: "Solution" --><dl>
|
359
|
-
<dt><a name="label-5" id="label-5">1.8 series</a></dt><!-- RDLabel: "1.8 series" -->
|
360
|
-
<dd>
|
361
|
-
Please upgrade to 1.8.5-p231, or 1.8.6-p230, or 1.8.7-p22.
|
362
|
-
<ul>
|
363
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.5-p231.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.5-p231.tar.gz&gt;</a>
|
364
|
-
(md5sum: e900cf225d55414bffe878f00a85807c)</li>
|
365
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p230.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p230.tar.gz&gt;</a>
|
366
|
-
(md5sum: 5e8247e39be2dc3c1a755579c340857f)</li>
|
367
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p22.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p22.tar.gz&gt;</a>
|
368
|
-
(md5sum: fc3ede83a98f48d8cb6de2145f680ef2)</li>
|
369
|
-
</ul>
|
370
|
-
</dd>
|
371
|
-
<dt><a name="label-6" id="label-6">1.9 series</a></dt><!-- RDLabel: "1.9 series" -->
|
372
|
-
<dd>
|
373
|
-
Please upgrade to 1.9.0-2.
|
374
|
-
<ul>
|
375
|
-
<li><a href="ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-2.tar.gz">&lt;URL:ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-2.tar.gz&gt;</a>
|
376
|
-
(md5sum: 2a848b81ed1d6393b88eec8aa6173b75)</li>
|
377
|
-
</ul>
|
378
|
-
</dd>
|
379
|
-
</dl><p>These versions also fix the vulnerability of WEBrick (<a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-1891">CVE-2008-1891</a>).</p><p>Please note that a package that corrects this weakness may already be
|
380
|
-
available through your package management software.</p><h2><a name="label-7" id="label-7">Credit</a></h2><!-- RDLabel: "Credit" --><p>Credit to Drew Yao of Apple Product Security for disclosing the problem to Ruby
|
381
|
-
Security Team.</p><h2><a name="label-8" id="label-8">Changes</a></h2><!-- RDLabel: "Changes" --><ul>
|
382
|
-
<li>2008-06-21 00:29 +09:00 removed wrong CVE IDs (CVE-2008-2727, CVE-2008-2728).</li>
|
383
|
-
</ul></description>
|
384
|
-
<pubDate>Fri, 20 Jun 2008 12:54:43 GMT</pubDate>
|
385
|
-
<guid>http://www.ruby-lang.org/en/news/2008/06/20/arbitrary-code-execution-vulnerabilities/</guid>
|
386
|
-
<link>http://www.ruby-lang.org/en/news/2008/06/20/arbitrary-code-execution-vulnerabilities/</link>
|
387
|
-
</item>
|
388
|
-
|
389
|
-
|
390
|
-
</channel>
|
391
|
-
</rss>
|