zypper-onlinesearch 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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