instances.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. ########################################################################
  2. # Searx-qt - Lightweight desktop application for SearX.
  3. # Copyright (C) 2020 CYBERDEViL
  4. #
  5. # This file is part of Searx-qt.
  6. #
  7. # Searx-qt is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # Searx-qt is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ########################################################################
  21. import time
  22. from searxqt.core.handler import HandlerProto, NetworkTypes
  23. from searxqt.core.instanceVersions import parseVersionString
  24. from searxqt.core.requests import JsonResult
  25. from searxqt.core import jsonVerify
  26. class SearchEngine:
  27. def __init__(self, name, data):
  28. """Model for a search engine data.
  29. @param name: Name of the search engine
  30. @type name: str
  31. @param data: Data of the search engine
  32. @type data: dict
  33. """
  34. self._name = name
  35. self._data = data
  36. def __repr__(self): return self._name
  37. @property
  38. def name(self):
  39. """
  40. @return: returns the name of this search engine.
  41. @rtype: str
  42. """
  43. return self._name
  44. @property
  45. def stats(self):
  46. """ TODO this is unused.
  47. @return:
  48. @rtype: bool
  49. """
  50. return self._data.get('stats', False)
  51. class TLS:
  52. def __init__(self, data):
  53. """Model for a instance it's TLS data.
  54. @param data: dict with the instance it's TLS data.
  55. @type data: dict
  56. """
  57. self._data = data
  58. @property
  59. def data(self): return self._data
  60. @property
  61. def version(self):
  62. """Returns the TLS version used.
  63. @return:
  64. @rtype: str
  65. """
  66. return self.data.get('version', "")
  67. @property
  68. def certificate(self):
  69. """Returns a TLSCertificate object.
  70. @return:
  71. @rtype: TLSCertificate
  72. """
  73. return TLSCertificate(self.data.get('certificate', {}))
  74. class TLSCertificate:
  75. def __init__(self, data):
  76. """Model for a instance it's TLS certificate-data.
  77. @param data: dict with the instance it's TLS certificate-data.
  78. @type data: dict
  79. """
  80. self._data = data
  81. @property
  82. def data(self): return self._data
  83. @property
  84. def version(self):
  85. """
  86. @return:
  87. @rtype: int
  88. """
  89. return self.data.get('version', 0)
  90. @property
  91. def issuer(self):
  92. """
  93. @return:
  94. @rtype: TLSCertificateIssuer
  95. """
  96. return TLSCertificateIssuer(self.data.get('issuer', {}))
  97. class TLSCertificateIssuer:
  98. def __init__(self, data):
  99. """Model for a instance it's TLS certificate-issuer-data.
  100. @param data: dict with the instance it's
  101. TLS certificate-issuer-data.
  102. @type data: dict
  103. """
  104. self._data = data
  105. @property
  106. def data(self): return self._data
  107. @property
  108. def commonName(self):
  109. """
  110. @rtype: str
  111. """
  112. return self.data.get('commonName', "")
  113. @property
  114. def countryName(self):
  115. """
  116. @rtype: str
  117. """
  118. return self.data.get('countryName', "")
  119. @property
  120. def organizationName(self):
  121. """
  122. @rtype: str
  123. """
  124. return self.data.get('organizationName', "")
  125. class Network:
  126. def __init__(self, data):
  127. """Model for a instance it's network data.
  128. @param data: dict with the instance it's network data.
  129. @type data: dict
  130. """
  131. self._data = data
  132. @property
  133. def data(self): return self._data
  134. @property
  135. def ipv6(self):
  136. """
  137. @return: If ipv6 is enabled on this instance.
  138. @rtype: bool
  139. """
  140. return self.data.get('ipv6', False)
  141. @property
  142. def ips(self):
  143. """
  144. @return: A list with NetworkIP objects
  145. @rtype: list
  146. """
  147. return [NetworkIP(ip, data)
  148. for ip, data in self.data.get('ips', {}).items()]
  149. @property
  150. def asnPrivacy(self):
  151. """
  152. @return: -1 no asn privacy, 0 asn privacy, -2 unknown.
  153. @rtype: int
  154. """
  155. return self.data.get('asn_privacy', -2)
  156. class NetworkIP:
  157. def __init__(self, ip, data):
  158. """Model for a network ip data.
  159. @param ip: ip address
  160. @type ip: str
  161. @param data: dict with a network ip data.
  162. @type data: dict
  163. """
  164. self._ip = ip
  165. self._data = data
  166. def __repr__(self):
  167. return (
  168. "IP: {0} Reverse: {1} FieldType: {2} asnCidr: {3}"
  169. .format(self.ip, self.reverse, self.fieldType, self.asnCidr)
  170. )
  171. def __str__(self): return repr(self)
  172. @property
  173. def ip(self):
  174. """
  175. @return: ip address
  176. @rtype: str
  177. """
  178. return self._ip
  179. @property
  180. def data(self): return self._data
  181. @property
  182. def reverse(self):
  183. """
  184. @return:
  185. @rtype: str
  186. """
  187. return self.data.get('reverse', None)
  188. @property
  189. def fieldType(self):
  190. """
  191. @return: Record type
  192. @rtype: str
  193. """
  194. return self.data.get('field_type', None)
  195. @property
  196. def asnCidr(self):
  197. """
  198. @return:
  199. @rtype: str
  200. """
  201. return self.data.get('asn_cidr', None)
  202. @property
  203. def httpsPort(self):
  204. """
  205. @return:
  206. @rtype: bool
  207. """
  208. return self.data.get('https_port', None)
  209. class Instance:
  210. def __init__(self, url, data):
  211. """Model for a SearX instance.
  212. @param url: Url of the instance
  213. @type url: str
  214. @param data: Data of the instance
  215. @type data: dict
  216. """
  217. self._url = url
  218. self._data = data
  219. def __str__(self): return self.url
  220. def __repr__(self): return str(self)
  221. @property
  222. def data(self): return self._data
  223. @property
  224. def url(self):
  225. """
  226. @return: returns the url of this instance.
  227. @rtype: str
  228. """
  229. return self._url
  230. @property
  231. def main(self):
  232. """Returns False when not set.
  233. @return: ?
  234. @rtype: bool
  235. """
  236. return self._data.get('main', False)
  237. @property
  238. def networkType(self):
  239. """ Network type; see core/handler.py:NetworkTypes
  240. @return: Network protocol used (Web, Tor, I2P from NetworkTypes)
  241. @rtype: int
  242. """
  243. return self._data.get('network_type', 0)
  244. @property
  245. def version(self):
  246. """Returns a empty string when none found.
  247. @return: Returns the instance it's version.
  248. @rtype: InstanceVersion
  249. """
  250. return parseVersionString(self._data.get('version', ''))
  251. @property
  252. def engines(self):
  253. """
  254. Returns a empty string when none found.
  255. @return: Returns a list with SearchEngine objects
  256. @rtype: list
  257. """
  258. return [SearchEngine(name, data) for name, data in self._data.get(
  259. 'engines', {}).items()]
  260. @property
  261. def tls(self):
  262. """
  263. @rtype: TLS
  264. """
  265. return TLS(self._data.get('tls', {}))
  266. @property
  267. def network(self):
  268. """
  269. @rtype: Network
  270. """
  271. return Network(self._data.get('network', {}))
  272. class Stats2Result(JsonResult):
  273. v_str = jsonVerify.Value(str)
  274. v_int = jsonVerify.Value(int)
  275. v_float = jsonVerify.Value(float)
  276. v_bool = jsonVerify.Value(bool)
  277. v_noneStr = jsonVerify.MultiValue((jsonVerify.NoneType, str))
  278. v_noneInt = jsonVerify.MultiValue((jsonVerify.NoneType, int))
  279. v_noneFloat = jsonVerify.MultiValue((jsonVerify.NoneType, float))
  280. v_noneBoolStr = jsonVerify.MultiValue((jsonVerify.NoneType, bool, str))
  281. TimingStructure = {
  282. "success_percentage": v_float,
  283. "all": {
  284. "median": v_float,
  285. "stdev": v_float,
  286. "value": v_float,
  287. "mean": v_float
  288. },
  289. "server": {
  290. "median": v_float,
  291. "stdev": v_float,
  292. "value": v_float,
  293. "mean": v_float
  294. },
  295. "load": {
  296. "median": v_float,
  297. "stdev": v_float,
  298. "value": v_float,
  299. "mean": v_float,
  300. "mean": v_float
  301. },
  302. "error": v_str
  303. }
  304. ExpectedStructure = {
  305. "metadata": {
  306. "timestamp": v_int,
  307. "ips": {
  308. "": {
  309. "reverse": v_noneStr,
  310. "field_type": v_str,
  311. "asn_cidr": v_str
  312. }
  313. },
  314. "ipv6": v_bool
  315. },
  316. "instances": {
  317. "": {
  318. "comments": [v_str],
  319. "alternativeUrls": {},
  320. "docs_url": v_noneStr,
  321. "contact_url": v_noneBoolStr,
  322. "main": v_bool,
  323. "network_type": v_str,
  324. "http": {
  325. "status_code": v_noneInt,
  326. "error": v_noneStr,
  327. "grade": v_str,
  328. "gradeUrl": v_str
  329. },
  330. "version": v_noneStr,
  331. "git_url": v_noneStr,
  332. "error": v_str,
  333. "timing": {
  334. "initial": TimingStructure,
  335. "search": TimingStructure,
  336. "search_wp": TimingStructure,
  337. "search_go": TimingStructure
  338. },
  339. "tls": {
  340. "version": v_str,
  341. "certificate": {
  342. "issuer": {
  343. "commonName": v_str,
  344. "countryName": v_str,
  345. "organizationName": v_str
  346. },
  347. "subject": {
  348. "commonName": v_noneStr,
  349. "countryName": v_noneStr,
  350. "organizationName": v_noneStr,
  351. "altName": v_str
  352. },
  353. "serialNumber": v_str,
  354. "notBefore": v_str,
  355. "notAfter": v_str,
  356. "OCSP": [v_str],
  357. "caIssuers": [v_str],
  358. "sha256": v_str,
  359. "signatureAlgorithm": v_str,
  360. "crlDistributionPoints": [v_str],
  361. "version": v_int
  362. },
  363. "grade": v_str,
  364. "gradeUrl": v_str
  365. },
  366. "html": {},
  367. "network": {
  368. "dnssec": v_int,
  369. "asn_privacy": v_int,
  370. "ips": {
  371. "": {
  372. "https_port": v_bool,
  373. "field_type": v_str,
  374. "reverse": v_noneStr,
  375. "asn_cidr": v_str,
  376. "whois_error": v_str,
  377. "https_port_error": v_str
  378. }
  379. },
  380. "ipv6": v_bool
  381. },
  382. "engines": {
  383. "": {
  384. "error_rate": v_noneInt,
  385. "errors": [v_int],
  386. "checker": {},
  387. "categories": [v_str]
  388. }
  389. }
  390. }
  391. },
  392. "engines": {
  393. "": {
  394. "categories": [v_str],
  395. "language_support": v_bool,
  396. "paging": v_bool,
  397. "safesearch": v_bool,
  398. "time_range_support": v_bool,
  399. "shortcut": v_str,
  400. "stats": {
  401. "instance_count": v_int,
  402. "stats_count": v_int,
  403. "error_rate": v_noneFloat
  404. }
  405. }
  406. },
  407. "engine_errors": [v_str],
  408. "categories": [v_str],
  409. "hashes": [{
  410. "count": v_int,
  411. "hash": v_str,
  412. "unknown": v_bool,
  413. "forks": [v_int]
  414. }],
  415. "cidrs": {
  416. "": {
  417. "asn": v_str,
  418. "asn_country_code": v_str,
  419. "network_country": v_str,
  420. "asn_privacy": v_int,
  421. "asn_description": v_str
  422. }
  423. },
  424. "forks": [v_str]
  425. }
  426. def __init__(self, url, response, err="", errType=None):
  427. JsonResult.__init__(self, url, response, err=err, errType=errType)
  428. class Stats2(HandlerProto):
  429. """ This class holds the instances.json data and will be passed
  430. to other classes (Instances, Engines)
  431. """
  432. URL = "https://searx.space/"
  433. def __init__(self, requestsHandler):
  434. HandlerProto.__init__(self, requestsHandler)
  435. def updateInstances(self):
  436. """Fetches instances.json from a searx-stats2 instance.
  437. @param requestKwargs: Kwargs to pass to requests.get
  438. @type requestKwargs: dict
  439. @return: A tuple (bool Success/Failed, str Message)
  440. @rtype: tuple
  441. """
  442. url = Stats2.URL.rstrip('/') + '/data/instances.json'
  443. result = self.requestsHandler.get(url, ResultType=Stats2Result)
  444. if result:
  445. self.setData(result.json())
  446. self._lastUpdated = time.time()
  447. # Processing (use our own definition of network types
  448. for url, data in self.instances.items():
  449. data.update({"network_type": NetworkTypes.netTypeFromUrl(url)})
  450. return True
  451. return False