zypper-onlinesearch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,342 @@
1
+ require 'nokogiri'
2
+
3
+ module Zypper
4
+ module Onlinesearch
5
+
6
+ class PageData
7
+
8
+ FORMATS = {
9
+ 'aarch64': 'ARM v8.x 64-bit',
10
+ 'aarch64_ilp32': 'ARM v8.x 64-bit ilp32 mode',
11
+ 'all': 'All',
12
+ 'armv6l': 'ARM v6',
13
+ 'armv7l': 'ARM v7',
14
+ 'extra': 'Extra',
15
+ 'i586': 'Intel 32-bit',
16
+ 'i686': 'Intel Pentium 32-bit',
17
+ 'lang': 'Language',
18
+ 'lsrc': 'Language source',
19
+ 'noarch': 'No architecture',
20
+ 'ppc64le': 'PowerPC 64-bit little-endian',
21
+ 'ppc64': 'PowerPC 64-bit',
22
+ 'ppc': 'PowerPC',
23
+ 'repo': 'Repository',
24
+ 'riscv64': 'Risc v64',
25
+ 's390x': 'IBM System/390',
26
+ 'src': 'Source',
27
+ 'x86_64': 'Intel/AMD 64-bit',
28
+ 'ymp': '1 Click Install',
29
+ }
30
+
31
+ def initialize(page)
32
+ @page = Nokogiri::HTML(page)
33
+ end
34
+
35
+ def expand_link(link)
36
+ link = [self.class::URL, link].join('/') unless link =~ /:\/\//
37
+ URI(link).to_s.gsub(/([^:])\/\//, '\1/')
38
+ end
39
+ end
40
+
41
+
42
+ module Data
43
+
44
+ module Search
45
+ class Opensuse < PageData
46
+
47
+ URL = 'https://software.opensuse.org'
48
+ XPATH_CARDS = '//div[@id="search-result-list"]//div[@class="card-body"]'
49
+ XPATH_NAME = './/h4[@class="card-title"]'
50
+ XPATH_DESC = './/p[@class="card-text"]'
51
+ XPATH_URL = './/h4[@class="card-title"]/a/@href'
52
+
53
+ XPATH_ERROR = './/div[@id="search-result-error"]'
54
+
55
+ def data
56
+ res = []
57
+ cards = @page.xpath(XPATH_CARDS)
58
+
59
+ cards.each do |c|
60
+ url = expand_link(c.xpath(XPATH_URL).text)
61
+ name = c.xpath(XPATH_NAME).text
62
+ name = (File.basename(url) == name) ? name : (File.basename(url) + ' (' + name + ')')
63
+ res << { name: name, description: c.xpath(XPATH_DESC).text.strip.gsub(/\n|\ +/, ' '), url: url }
64
+ end
65
+
66
+ if res.empty?
67
+ if @page.xpath(XPATH_ERROR).text.empty?
68
+ name = @page.xpath(Page::Opensuse::XPATH_NAME).text
69
+
70
+ unless name.to_s.empty?
71
+ res << { name: name, description: @page.xpath(Page::Opensuse::XPATH_SHORTDESC).text.strip }
72
+ end
73
+ end
74
+ end
75
+
76
+ res
77
+ end
78
+ end
79
+
80
+ class Packman < PageData
81
+
82
+ URL = 'http://packman.links2linux.org'
83
+
84
+ XPATH_PACKAGE = '//table[@id="packagelist"]//tr'
85
+ XPATH_NAME = './/td[@class="package-name"]/a'
86
+ XPATH_DESC = './/td[@class="package-descr"]'
87
+ XPATH_URL = './/td[@class="package-name"]/a/@href'
88
+
89
+ def data
90
+ res = []
91
+
92
+ @page.xpath(XPATH_PACKAGE).each do |pack|
93
+ name = pack.xpath(XPATH_NAME).text
94
+ res << { name: name, description: pack.xpath(XPATH_DESC).text.strip.gsub(/\n|\ +/,' '), url: expand_link(pack.xpath(XPATH_URL).text) } unless name.empty?
95
+ end
96
+
97
+ if res.empty?
98
+ name = @page.xpath(Page::Packman::XPATH_NAME).text
99
+
100
+ unless name.to_s.empty?
101
+ res << { name: name, description: @page.xpath(Page::Packman::XPATH_DESC).text.strip }
102
+ end
103
+ end
104
+
105
+ res
106
+ end
107
+
108
+ end
109
+ end
110
+
111
+
112
+ module Page
113
+
114
+ class Opensuse < PageData
115
+
116
+ URL = 'https://software.opensuse.org'
117
+ XPATH_NAME = '//h1'
118
+ XPATH_SHORTDESC = '//h1/following::p/strong'
119
+ XPATH_DESC = '//*[@id="pkg-desc"]'
120
+
121
+ XPATH_SUPPORTED = '//div[@id="other-distributions-listing"]/h4'
122
+
123
+ XPATH_SUPPORTED_DISTRO = './h4'
124
+ XPATH_SUPPORTED_LABEL = './/following-sibling::div[@class="card mb-2"][1]//a'
125
+ XPATH_SUPPORTED_LINK = './/@href'
126
+ XPATH_SUPPORTED_VERSION = '../..//div[@class="col-md-2"]'
127
+
128
+ XPATH_COMMUNITY = './/following-sibling::div[contains(@id,"community-packages")][1]//div/div/a'
129
+ XPATH_COMMUNITY_LINK = './/@href'
130
+ XPATH_COMMUNITY_VERSION = '../..//div[@class="col-md-2"]'
131
+
132
+ XPATH_EXPERIMENTAL = './/following-sibling::div[contains(@id,"experimental-packages")][1]//div/div/a'
133
+ XPATH_EXPERIMENTAL_LINK = './/@href'
134
+ XPATH_EXPERIMENTAL_VERSION = '../..//div[@class="col-md-2"]'
135
+
136
+ def data
137
+ res = {}
138
+
139
+ res[:name] = @page.xpath(XPATH_NAME).text
140
+ res[:short_description] = @page.xpath(XPATH_SHORTDESC).text.strip
141
+ res[:description] = @page.xpath(XPATH_DESC).text.chomp
142
+ res[:versions] = []
143
+
144
+ @page.xpath(XPATH_SUPPORTED).each do |ver|
145
+ extract(ver, res, :supported, XPATH_SUPPORTED_LABEL, XPATH_SUPPORTED_VERSION, XPATH_SUPPORTED_LINK)
146
+ extract(ver, res, :community, XPATH_COMMUNITY, XPATH_COMMUNITY_VERSION, XPATH_COMMUNITY_LINK)
147
+ extract(ver, res, :experimental, XPATH_EXPERIMENTAL, XPATH_EXPERIMENTAL_VERSION, XPATH_EXPERIMENTAL_LINK)
148
+ end
149
+
150
+ res
151
+ end
152
+
153
+
154
+ private
155
+
156
+ def extract(ver, res, type, xpath_group, xpath_version, xpath_link)
157
+ repo = ''; format = ''; version = nil
158
+
159
+ ver.xpath(xpath_group).each do |pack|
160
+
161
+ version = pack.xpath(xpath_version).text.strip
162
+
163
+ if version.empty?
164
+ version = @old_version
165
+ else
166
+ @old_version = version
167
+ end
168
+
169
+ if format? pack.text.strip
170
+ format = PageData::FORMATS.key(pack.text.strip)
171
+ else
172
+ repo = pack.text.strip
173
+ if repo.empty?
174
+ repo = @old_repo
175
+ else
176
+ @old_repo = repo unless repo =~ /Expert Download/
177
+ end
178
+ end
179
+
180
+ #puts repo, link, format
181
+ link = expand_link(pack.xpath(xpath_link).text)
182
+
183
+ if repo =~ /Expert Download/
184
+ res[:versions] << { distro: ver.text, link: link, type: type, repo: @old_repo, format: :extra, version: version}
185
+ next
186
+ end
187
+
188
+ next if format.to_s.empty? || (link.include?('/package/show/'))
189
+
190
+ res[:versions] << { distro: ver.text, link: link, type: type, repo: repo, format: format, version: version }
191
+ end
192
+
193
+ end
194
+
195
+ def format?(str)
196
+ PageData::FORMATS.has_value? str
197
+ end
198
+
199
+ end
200
+
201
+
202
+ class Packman < PageData
203
+
204
+ URL = 'http://packman.links2linux.org'
205
+
206
+ XPATH_NAME = '//td[@id="package-details-header-name"]'
207
+ XPATH_DESC = '//div[@id="package-description"]'
208
+
209
+ XPATH_PACKAGES = '//td[@id="package-details-left"]//tbody/tr'
210
+ XPATH_VERSION = './/td[1]'
211
+ XPATH_DISTRO = './/td[2]'
212
+ XPATH_FORMAT = './/td[3]'
213
+ XPATH_LINK = './/a/@href'
214
+
215
+ def data
216
+ res = {}
217
+
218
+ res[:name] = @page.xpath(XPATH_NAME).text
219
+ res[:short_description] = ''
220
+ res[:description] = @page.xpath(XPATH_DESC).text
221
+ res[:versions] = []
222
+
223
+
224
+ @page.xpath(XPATH_PACKAGES).each do |pack|
225
+
226
+ version = pack.xpath(XPATH_VERSION).text.split('-')[0].to_s
227
+ distro = pack.xpath(XPATH_DISTRO).text.gsub(/_/, ' ')
228
+ format = pack.xpath(XPATH_FORMAT).text.strip.to_sym
229
+ link = pack.xpath(XPATH_LINK).text
230
+
231
+ res[:versions] << { format: format, version: version, distro: distro,
232
+ type: :supported, link: "http://packman.links2linux.org#{link}",
233
+ repo: 'Packman' }
234
+ end
235
+
236
+ res
237
+ end
238
+ end
239
+
240
+ end
241
+
242
+
243
+ module Links
244
+
245
+ class Opensuse < PageData
246
+
247
+ XPATH_REPO = '//*[@id="manualopenSUSE"]/h5'
248
+ XPATH_REPO_DISTRO = './strong[1]'
249
+ XPATH_REPO_LINK = 'following-sibling::pre[1]'
250
+
251
+ XPATH_PACKAGE_GROUP = '//*[@id="directopenSUSE"]/div/div'
252
+ XPATH_PACKAGE_DISTRO = './p/strong'
253
+ XPATH_PACKAGE_LINK = './/@href'
254
+
255
+ def data
256
+ res = { versions: [] }
257
+
258
+ extract(res, -1, XPATH_REPO, XPATH_REPO_DISTRO, XPATH_REPO_LINK)
259
+ extract(res, -2, XPATH_PACKAGE_GROUP, XPATH_PACKAGE_DISTRO, XPATH_PACKAGE_LINK)
260
+
261
+ res
262
+ end
263
+
264
+
265
+ private
266
+
267
+ def extract(res, format_idx, xpath_group, xpath_distro, xpath_link)
268
+ @page.xpath(xpath_group).each do |section|
269
+ distro = ''
270
+ section.xpath(xpath_distro).each do |subsection|
271
+ distro = subsection.text
272
+ distro = "openSUSE Leap #{distro}" if distro =~ /^\d\d.\d$/
273
+ end
274
+
275
+ #p distro
276
+ section.xpath(xpath_link).each do |subsection|
277
+ link = subsection.text
278
+ link = link.gsub("\n", ' ').scan(/(https:\/\/[^ \n]+)/).pop.pop
279
+ res[:versions] << {
280
+ distro: distro,
281
+ format: File.basename(link).split('.')[format_idx].to_sym,
282
+ link: link,
283
+ }
284
+ #p link
285
+ end
286
+ end
287
+
288
+ end
289
+ end
290
+
291
+ class Packman < PageData
292
+
293
+ XPATH_LINK_DISTRO = '//*[@id="selected-release"]/td[2]'
294
+ XPATH_LINK_BIN = '//*[@id="package-details-binfiles"]//a/@href'
295
+ XPATH_LINK_SRC = '//*[@id="package-details-srcfile-heading"]//a/@href'
296
+ XPATH_LINK_YMP = '//*[@class="ymp"]//a/@href'
297
+
298
+ URL = 'http://packman.links2linux.org'
299
+
300
+ def data
301
+ res = { versions: [] }
302
+
303
+ distro = @page.xpath(XPATH_LINK_DISTRO).text.gsub(/\_/, ' ')
304
+
305
+ @page.xpath(XPATH_LINK_BIN).each do |pack|
306
+ link = pack.text
307
+ res[:versions] << {
308
+ distro: distro,
309
+ format: File.basename(link).split('.')[-2].to_sym,
310
+ link: URL + link
311
+ }
312
+ end
313
+
314
+ link = res[:versions].last[:link]
315
+ is_lang = (File.basename(link) =~ /-lang/) && (res[:versions].last[:format] == :noarch)
316
+
317
+ link = @page.xpath(XPATH_LINK_SRC).text
318
+ res[:versions] << {
319
+ distro: distro,
320
+ format: is_lang ? :lsrc : File.basename(link).split('.')[-2].to_sym,
321
+ link: URL + link
322
+ }
323
+
324
+ unless is_lang
325
+ link = @page.xpath(XPATH_LINK_YMP).text
326
+ res[:versions] << {
327
+ distro: distro,
328
+ format: :ymp,
329
+ link: URL + link
330
+ }
331
+ end
332
+
333
+ res
334
+ end
335
+ end
336
+
337
+ end
338
+
339
+ end # Data module
340
+
341
+ end
342
+ end
@@ -0,0 +1,42 @@
1
+ require 'iniparse'
2
+
3
+ module Zypper
4
+ module Onlinesearch
5
+
6
+ class Release
7
+
8
+
9
+ def initialize()
10
+ @filename = File.exist?('/etc/SuSE-release') ? '/etc/SuSE-release' : '/etc/os-release'
11
+ @ini = IniParse.parse(File.read(@filename))
12
+ end
13
+
14
+ def name
15
+ ini['NAME'].delete('"')
16
+ end
17
+
18
+ def version
19
+ ini['VERSION'].delete('"')
20
+ end
21
+
22
+ def id
23
+ ini['ID'].delete('"')
24
+ end
25
+
26
+ def pretty_name
27
+ ini['PRETTY_NAME'].delete('"')
28
+ end
29
+
30
+ def arch
31
+ `uname -i`.strip.chomp.to_sym
32
+ end
33
+
34
+ private
35
+
36
+ def ini
37
+ @ini['__anonymous__']
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,229 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+
4
+ module Zypper
5
+ module Onlinesearch
6
+
7
+ class RequestList
8
+ attr_reader :engines, :refresh, :timeout, :query
9
+
10
+ def initialize(args)
11
+ @engines = {}
12
+ @refresh = args[:refresh]
13
+ @timeout = args[:timeout]
14
+ @query = args[:query]
15
+
16
+ if args[:engine] == :all
17
+ m = Request.const_get(args[:operation].to_s.capitalize)
18
+ m.constants.each do |k|
19
+ @engines[k.to_s.downcase] = m.const_get(k).new(args[:query], args[:refresh], args[:timeout])
20
+ end
21
+ else
22
+ m = Request.const_get(args[:operation].to_s.capitalize)
23
+ klass = args[:engine].to_s.capitalize.to_sym
24
+ raise InvalidEngine, args[:engine] unless m.constants.include? klass
25
+ @engines[args[:engine].to_s.downcase] = m.const_get(klass).new(args[:query], args[:refresh], args[:timeout])
26
+ end
27
+ end
28
+
29
+ def self.has_class?(operation, engine)
30
+ Request.const_get(operation.to_s.capitalize).constants.include?(engine.to_s.capitalize.to_sym)
31
+ end
32
+ end
33
+
34
+
35
+ class PageRequest
36
+
37
+ attr_reader :page
38
+
39
+ USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0'
40
+
41
+ def initialize(query, refresh, timeout = 60, cookies = [])
42
+ @query = query.strip
43
+ @cache = Cache.new(*(self.class.to_s.split('::')[-2..-1].map(&:downcase)))
44
+ @refresh = refresh
45
+ @cookies = cookies
46
+ @timeout = timeout
47
+ end
48
+
49
+ def available?
50
+ ping.is_a?(Net::HTTPSuccess)
51
+ end
52
+
53
+ def redirected?
54
+ ping.is_a?(Net::HTTPRedirection)
55
+ end
56
+
57
+ def redirected_to
58
+ ping['location']
59
+ end
60
+
61
+ def not_found?
62
+ ping.is_a?(Net::HTTPNotFound)
63
+ end
64
+
65
+ def forbidden?
66
+ ping.is_a?(Net::HTTPForbidden)
67
+ end
68
+
69
+ def timeout?
70
+ ping.is_a?(Net::HTTPRequestTimeOut)
71
+ end
72
+
73
+ def status
74
+ ping.class.to_s
75
+ end
76
+
77
+ def cache!
78
+ @page = nil
79
+ end
80
+
81
+ def to_data
82
+ klass = self.class.to_s.split('::')[-2..-1]
83
+ Data.const_get(klass[0].to_sym).const_get(klass[1].to_sym).new(@page.body).data
84
+ end
85
+
86
+ def cache_time
87
+ @cache.mtime(@query) if @query
88
+ end
89
+
90
+ private
91
+
92
+ def get_request(request_uri = nil, limit = 10)
93
+ if request_uri
94
+ request_uri = (request_uri =~ /:\/\//) ? URI(request_uri).request_uri : request_uri
95
+ else
96
+ request_uri = uri.request_uri
97
+ end
98
+
99
+ request = Net::HTTP::Get.new(request_uri)
100
+ request['User-Agent'] = USER_AGENT
101
+ request['Cookie'] = @cookies.join(';') unless @cookies.empty?
102
+
103
+ http = Net::HTTP.new(uri.host, uri.port)
104
+ http.use_ssl = (uri.scheme == 'https')
105
+ http.open_timeout = @timeout
106
+
107
+ res = http.request(request)
108
+
109
+ if res.is_a? Net::HTTPRedirection
110
+ raise TooManyRedirections, uri.to_s if limit < 0
111
+ res = get_request(res['location'], limit - 1)
112
+ end
113
+
114
+ res
115
+ end
116
+
117
+ def ping(force = false)
118
+ begin
119
+ @page = @cache.get(@query) unless @refresh
120
+ if @page.nil? || force
121
+ @page = get_request
122
+ @cache.set(@query, @page)
123
+ end
124
+ rescue SocketError
125
+ raise NoConnection
126
+ rescue Net::OpenTimeout
127
+ @page = Net::HTTPRequestTimeOut.new('1.1', '', '')
128
+ end
129
+ @page
130
+ end
131
+ end
132
+
133
+
134
+ module Request
135
+
136
+ module Search
137
+
138
+ class Opensuse < PageRequest
139
+
140
+ URL = 'https://software.opensuse.org/search'
141
+
142
+ def initialize(query, cache, timeout, cookies = [])
143
+ super query, cache, timeout, cookies << 'baseproject=ALL;search_devel=true;search_debug=false;search_lang=false'
144
+ end
145
+
146
+ def uri
147
+ u = URI(URL)
148
+ u.query = URI.encode_www_form(q: @query)
149
+ u
150
+ end
151
+ end
152
+
153
+
154
+ class Packman < PageRequest
155
+
156
+ URL = 'http://packman.links2linux.org/search'
157
+
158
+ def uri
159
+ u = URI(URL)
160
+ u.query = URI.encode_www_form({q: @query, scope: 'name'})
161
+ u
162
+ end
163
+ end
164
+
165
+ end
166
+
167
+
168
+ module Page
169
+
170
+ class Opensuse < PageRequest
171
+
172
+ URL = 'https://software.opensuse.org/package/'
173
+
174
+ def initialize(query, cache, timeout, cookies = [])
175
+ super query, cache, timeout, cookies << 'baseproject=ALL;search_devel=true;search_debug=false;search_lang=false'
176
+ end
177
+
178
+ def uri
179
+ URI(URL + URI.encode(@query))
180
+ end
181
+ end
182
+
183
+
184
+ class Packman < PageRequest
185
+
186
+ URL = 'http://packman.links2linux.org/package/'
187
+
188
+ def uri
189
+ URI(URL + URI.encode(@query))
190
+ end
191
+
192
+ end
193
+ end
194
+
195
+
196
+ module Links
197
+
198
+ class Opensuse < PageRequest
199
+
200
+ URL = 'https://software.opensuse.org/download/package'
201
+
202
+ def initialize(query, refresh, timeout = 60, cookies = [])
203
+ query = URI(query).query
204
+ super query, refresh, timeout, cookies
205
+ end
206
+
207
+ def uri
208
+ URI(@query =~ /:\/\// ? @query : URL + '?' + @query)
209
+ end
210
+ end
211
+
212
+ class Packman < PageRequest
213
+
214
+ URL = 'http://packman.links2linux.org/package/'
215
+
216
+ def initialize(query, refresh, timeout = 60, cookies = [])
217
+ query = query.split('/')[-2..-1].join('/') if query =~ /:\/\//
218
+ super query, refresh, timeout, cookies
219
+ end
220
+
221
+ def uri
222
+ URI(@query =~ /:\/\// ? @query : URL + URI.encode(@query))
223
+ end
224
+ end
225
+ end
226
+
227
+ end
228
+ end
229
+ end