| # -*- coding:utf-8 -*- |
| |
| from __future__ import print_function, unicode_literals |
| |
| import errno |
| import logging |
| import sys |
| import tempfile |
| import time |
| |
| try: |
| from urllib.parse import urlparse |
| except ImportError: |
| from urlparse import urlparse |
| |
| |
| # import our initialized portage instance |
| from repoman._portage import portage |
| |
| from portage import exception |
| from portage import os |
| from portage.output import green |
| |
| if sys.hexversion >= 0x3000000: |
| basestring = str |
| |
| if sys.hexversion >= 0x3000000: |
| basestring = str |
| |
| metadata_xml_encoding = 'UTF-8' |
| metadata_xml_declaration = '<?xml version="1.0" encoding="%s"?>' \ |
| % (metadata_xml_encoding,) |
| metadata_doctype_name = 'pkgmetadata' |
| metadata_dtd_uri = 'http://www.gentoo.org/dtd/metadata.dtd' |
| # force refetch if the local copy creation time is older than this |
| metadata_dtd_ctime_interval = 60 * 60 * 24 * 7 # 7 days |
| |
| |
| def parse_metadata_use(xml_tree): |
| """ |
| Records are wrapped in XML as per GLEP 56 |
| returns a dict with keys constisting of USE flag names and values |
| containing their respective descriptions |
| """ |
| uselist = {} |
| |
| usetags = xml_tree.findall("use") |
| if not usetags: |
| return uselist |
| |
| # It's possible to have multiple 'use' elements. |
| for usetag in usetags: |
| flags = usetag.findall("flag") |
| if not flags: |
| # DTD allows use elements containing no flag elements. |
| continue |
| |
| for flag in flags: |
| pkg_flag = flag.get("name") |
| if pkg_flag is None: |
| raise exception.ParseError("missing 'name' attribute for 'flag' tag") |
| flag_restrict = flag.get("restrict") |
| |
| # emulate the Element.itertext() method from python-2.7 |
| inner_text = [] |
| stack = [] |
| stack.append(flag) |
| while stack: |
| obj = stack.pop() |
| if isinstance(obj, basestring): |
| inner_text.append(obj) |
| continue |
| if isinstance(obj.text, basestring): |
| inner_text.append(obj.text) |
| if isinstance(obj.tail, basestring): |
| stack.append(obj.tail) |
| stack.extend(reversed(obj)) |
| |
| if pkg_flag not in uselist: |
| uselist[pkg_flag] = {} |
| |
| # (flag_restrict can be None) |
| uselist[pkg_flag][flag_restrict] = " ".join("".join(inner_text).split()) |
| |
| return uselist |
| |
| |
| def fetch_metadata_dtd(metadata_dtd, repoman_settings): |
| """ |
| Fetch metadata.dtd if it doesn't exist or the ctime is older than |
| metadata_dtd_ctime_interval. |
| @rtype: bool |
| @return: True if successful, otherwise False |
| """ |
| |
| must_fetch = True |
| metadata_dtd_st = None |
| current_time = int(time.time()) |
| try: |
| metadata_dtd_st = os.stat(metadata_dtd) |
| except EnvironmentError as e: |
| if e.errno not in (errno.ENOENT, errno.ESTALE): |
| raise |
| del e |
| else: |
| # Trigger fetch if metadata.dtd mtime is old or clock is wrong. |
| if abs(current_time - metadata_dtd_st.st_ctime) \ |
| < metadata_dtd_ctime_interval: |
| must_fetch = False |
| |
| if must_fetch: |
| print() |
| print( |
| "%s the local copy of metadata.dtd " |
| "needs to be refetched, doing that now" % green("***")) |
| print() |
| parsed_url = urlparse(metadata_dtd_uri) |
| setting = 'FETCHCOMMAND_' + parsed_url.scheme.upper() |
| fcmd = repoman_settings.get(setting) |
| if not fcmd: |
| fcmd = repoman_settings.get('FETCHCOMMAND') |
| if not fcmd: |
| logging.error("FETCHCOMMAND is unset") |
| return False |
| |
| destdir = repoman_settings["DISTDIR"] |
| fd, metadata_dtd_tmp = tempfile.mkstemp( |
| prefix='metadata.dtd.', dir=destdir) |
| os.close(fd) |
| |
| try: |
| if not portage.getbinpkg.file_get( |
| metadata_dtd_uri, destdir, fcmd=fcmd, |
| filename=os.path.basename(metadata_dtd_tmp)): |
| logging.error( |
| "failed to fetch metadata.dtd from '%s'" % metadata_dtd_uri) |
| return False |
| |
| try: |
| portage.util.apply_secpass_permissions( |
| metadata_dtd_tmp, |
| gid=portage.data.portage_gid, mode=0o664, mask=0o2) |
| except portage.exception.PortageException: |
| pass |
| |
| os.rename(metadata_dtd_tmp, metadata_dtd) |
| finally: |
| try: |
| os.unlink(metadata_dtd_tmp) |
| except OSError: |
| pass |
| |
| return True |