aboutsummaryrefslogtreecommitdiff
blob: d55dda5b342394b19cedb1f41047631ec30c63f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# -*- coding:utf-8 -*-

from __future__ import print_function, unicode_literals

import sys
import xml

# import our initialized portage instance
from repoman._portage import portage

from portage import os
from portage.output import red
from portage.process import find_binary

from repoman.metadata import fetch_metadata_dtd
from repoman._subprocess import repoman_getstatusoutput


class _XMLParser(xml.etree.ElementTree.XMLParser):

	def __init__(self, data, **kwargs):
		xml.etree.ElementTree.XMLParser.__init__(self, **kwargs)
		self._portage_data = data
		if hasattr(self, 'parser'):
			self._base_XmlDeclHandler = self.parser.XmlDeclHandler
			self.parser.XmlDeclHandler = self._portage_XmlDeclHandler
			self._base_StartDoctypeDeclHandler = \
				self.parser.StartDoctypeDeclHandler
			self.parser.StartDoctypeDeclHandler = \
				self._portage_StartDoctypeDeclHandler

	def _portage_XmlDeclHandler(self, version, encoding, standalone):
		if self._base_XmlDeclHandler is not None:
			self._base_XmlDeclHandler(version, encoding, standalone)
		self._portage_data["XML_DECLARATION"] = (version, encoding, standalone)

	def _portage_StartDoctypeDeclHandler(
		self, doctypeName, systemId, publicId, has_internal_subset):
		if self._base_StartDoctypeDeclHandler is not None:
			self._base_StartDoctypeDeclHandler(
				doctypeName, systemId, publicId, has_internal_subset)
		self._portage_data["DOCTYPE"] = (doctypeName, systemId, publicId)


class _MetadataTreeBuilder(xml.etree.ElementTree.TreeBuilder):
	"""
	Implements doctype() as required to avoid deprecation warnings with
	>=python-2.7.
	"""
	def doctype(self, name, pubid, system):
		pass


class XmlLint(object):

	def __init__(self, options, repoman_settings, metadata_dtd=None):
		self.metadata_dtd = (metadata_dtd or
			os.path.join(repoman_settings["DISTDIR"], 'metadata.dtd'))
		self.options = options
		self.repoman_settings = repoman_settings
		self._is_capable = metadata_dtd is not None
		self.binary = None
		self._check_capable()

	def _check_capable(self):
		if self.options.mode == "manifest":
			return
		self.binary = find_binary('xmllint')
		if not self.binary:
			print(red("!!! xmllint not found. Can't check metadata.xml.\n"))
		elif not self._is_capable:
			if not fetch_metadata_dtd(self.metadata_dtd, self.repoman_settings):
				sys.exit(1)
			# this can be problematic if xmllint changes their output
			self._is_capable = True

	@property
	def capable(self):
		return self._is_capable

	def check(self, checkdir, repolevel):
		'''Runs checks on the package metadata.xml file

		@param checkdir: string, path
		@param repolevel: integer
		@return boolean, False == bad metadata
		'''
		if not self.capable:
			if self.options.xml_parse or repolevel == 3:
				print("%s sorry, xmllint is needed.  failing\n" % red("!!!"))
				sys.exit(1)
			return True
		# xmlint can produce garbage output even on success, so only dump
		# the ouput when it fails.
		st, out = repoman_getstatusoutput(
			self.binary + " --nonet --noout --dtdvalid %s %s" % (
				portage._shell_quote(self.metadata_dtd),
				portage._shell_quote(
					os.path.join(checkdir, "metadata.xml"))))
		if st != os.EX_OK:
			print(red("!!!") + " metadata.xml is invalid:")
			for z in out.splitlines():
				print(red("!!! ") + z)
			return False
		return True