示例#1
0
    def get_changelog(self, pkg_name:str, no_local:bool=False):
        self.refresh_cache()
        self.candidate = self.parse_package_metadata(pkg_name)

        # parse the package's origin
        if not self.candidate.downloadable:
            origin = "local_package"
        elif self.candidate.origin == "linuxmint":
            origin = "linuxmint"
        elif self.candidate.origin.startswith("LP-PPA-"):
            origin = "LP-PPA"
        elif self.apt_origins and self.candidate.origin in self.apt_origins.list():
            origin = "APT"
        else:
            origin = "unsupported"

        # Check for changelog of installed package first
        has_local_changelog = False
        uri = None
        if not no_local and self.candidate.is_installed:
            if _DEBUG: print("Package is installed...")
            uri = self.get_changelog_from_filelist(
                self.candidate.installed_files, local=True)
            # Ubuntu kernel workarounds
            if self.candidate.origin == "Ubuntu":
                if self.candidate.source_name == "linux-signed":
                    uri = uri.replace("linux-image","linux-modules")
                if self.candidate.source_name == "linux-meta":
                    uri = None
            if uri and not os.path.isfile(uri):
                uri = None

        # Do nothing if local changelog exists
        if uri:
            has_local_changelog = True
        # all origins that APT supports
        elif origin == 'APT':
            uri = self.get_apt_changelog_uri(
                self.apt_origins.get(self.candidate.origin))
            r = self.check_url(uri)
            if not r:
                self.exit_on_fail(2)
        # Linux Mint repo
        elif origin == 'linuxmint':
            # Mint repos don't have .debian.tar.xz files, only full packages, so
            # check the package cache first
            base_uri, _ = os.path.split(self.candidate.uri)
            r, uri = self.get_changelog_uri(base_uri)
            if not r:
                # fall back to last change info for the source package
                # Mint's naming scheme seems to be using amd64 unless source
                # is i386 only, we always check amd64 first
                base_uri = "http://packages.linuxmint.com/dev/%s_%s_%s.changes"
                uri = base_uri % (self.candidate.source_name,
                    self.candidate.source_version, "amd64")
                r = self.check_url(uri, False)
                if not r:
                    uri = base_uri % (self.candidate.source_name,
                        self.candidate.source_version, "i386")
                    r = self.check_url(uri, False)
                    if not r:
                        self.exit_on_fail(3)

        # Launchpad PPA
        elif origin == 'LP-PPA':
            ppa_owner, ppa_name, _ = \
                self.candidate.uri.split("ppa.launchpad.net/")[1].split("/", 2)
            base_uri = "http://ppa.launchpad.net/%s/%s/ubuntu/pool/main/{self.source_prefix()}/%s" % (ppa_owner, ppa_name, self.candidate.source_name)
            r, uri = self.get_changelog_uri(base_uri)
            if not r:
                # fall back to last change info only
                uri = "https://launchpad.net/~%s/+archive/ubuntu/%s/+files/%s_%s_source.changes" % (ppa_owner, ppa_name, self.candidate.source_name, self.candidate.source_version)
                r = self.check_url(uri, False)
                if not r:
                    self.exit_on_fail(4)
        # Not supported origin
        elif origin == 'unsupported':
            if _DEBUG: print("Unsupported Package")
            base_uri, _ = os.path.split(self.candidate.uri)
            r, uri = self.get_changelog_uri(base_uri)
            if not r:
                self.exit_on_fail(5)
        # Locally installed package without local changelog or remote
        # source, hope it's cached and contains a changelog
        elif origin == 'local_package':
            uri = self.apt_cache_path + self.candidate.filename
            if not os.path.isfile(uri):
                self.exit_on_fail(6)

        # Changelog downloading, extracting and processing:
        changelog = ""
        # local changelog
        if has_local_changelog and not no_local:
            if _DEBUG: print("Using local changelog:",uri)
            try:
                filename = os.path.basename(uri)
                # determine file type by name/extension
                # as per debian policy 4.4 the encoding must be UTF-8
                # as per policy 12.7 the name must be changelog.Debian.gz or
                # changelog.gz (deprecated)
                if filename.lower().endswith('.gz'):
                    changelog = gzip.open(uri,'r').read().decode('utf-8')
                elif filename.lower().endswith('.xz'):
                    # just in case / future proofing
                    changelog = lzma.open(uri,'r').read().decode('utf-8')
                elif filename.lower() == 'changelog':
                    changelog = open(uri, 'r').read().encode().decode('utf-8')
                else:
                    raise ValueError('Unknown changelog format')
            except Exception as e:
                _generic_exception_handler(e)
                self.exit_on_fail(1)
        # APT-format changelog, download directly
        # - unfortunately this is slow since the servers support no compression
        elif origin == "APT":
            if _DEBUG: print("Downloading: %s (%.2f MB)" % (uri, r.length / self.MB))
            changelog = r.text
            r.close()
        # last change changelog, download directly
        elif uri.endswith('.changes'):
            if _DEBUG: print("Downloading: %s (%.2f MB)" % (uri, r.length / self.MB))
            changes = r.text.split("Changes:")[1].split("Checksums")[0].split("\n")
            r.close()
            for change in changes:
                change = change.strip()
                if change:
                    if change == ".":
                        change = ""
                    changelog += change + "\n"
        # compressed binary source, download and extract changelog
        else:
            source_is_cache = uri.startswith(self.apt_cache_path)
            if _DEBUG: print("Using cached package:" if source_is_cache else
                "Downloading: %s (%.2f MB)" % (uri, r.length / self.MB))
            try:
                if not source_is_cache:
                    # download stream to temporary file
                    tmpFile = tempfile.NamedTemporaryFile(prefix="apt-changelog-")
                    if self.interactive and r.length:
                        # download chunks with progress indicator
                        recv_length = 0
                        blocks = 60
                        for data in r.iter_content(chunk_size=16384):
                            recv_length += len(data)
                            tmpFile.write(data)
                            recv_pct = recv_length / r.length
                            recv_blocks = int(blocks * recv_pct)
                            print("\r[%(progress)s%(spacer)s] %(percentage).1f%%" %
                                {
                                    "progress": "=" * recv_blocks,
                                    "spacer":  " " * (blocks - recv_blocks),
                                    "percentage": recv_pct * 100
                                }, end="", flush=True)
                        # clear progress bar when done
                        print("\r" + " " * (blocks + 10), end="\r", flush=True)
                    else:
                        # no content-length or non-interactive, download in one go
                        # up to the configured max_download_size, ask only when
                        # exceeded
                        r.raw.decode_content = True
                        size = 0
                        size_exceeded = False
                        while True:
                            buf = r.raw.read(16*1024)
                            if not size_exceeded:
                                size += len(buf)
                                if size > self.max_download_size:
                                    if not self.user_confirm(self.max_download_size_msg_unknown):
                                        r.close()
                                        tmpFile.close()
                                        return ""
                                    else:
                                        size_exceeded = True
                            if not buf:
                                break
                            tmpFile.write(buf)
                    r.close()
                    tmpFile.seek(0)
                if uri.endswith(".deb"):
                    # process .deb file
                    if source_is_cache:
                        f = uri
                    else:
                        f = tmpFile.name
                        # We could copy the downloaded .deb files to the apt
                        # cache here but then we'd need to run the script elevated:
                        # shutil.copy(f, self.apt_cache_path + os.path.basename(uri))
                    deb = DebPackage(f)
                    changelog_file = self.get_changelog_from_filelist(deb.filelist)
                    if changelog_file:
                        changelog = deb.data_content(changelog_file)
                        if changelog.startswith('Automatically decompressed:'):
                            changelog = changelog[29:]
                    else:
                        raise ValueError('Malformed Debian package')
                elif uri.endswith(".diff.gz"):
                    # Ubuntu partner repo has .diff.gz files,
                    # we can extract a changelog from that
                    data = gzip.open(tmpFile.name, "r").read().decode('utf-8')
                    additions = data.split("+++")
                    for addition in additions:
                        lines = addition.split("\n")
                        if "/debian/changelog" in lines[0]:
                            for line in lines[2:]:
                                if line.startswith("+"):
                                    changelog += "%s\n" % line[1:]
                                else:
                                    break
                    if not changelog:
                        raise ValueError('No changelog in .diff.gz')
                else:
                    # process .tar.xz file
                    with tarfile.open(fileobj=tmpFile, mode="r:xz") as tar:
                        changelog_file = self.get_changelog_from_filelist(
                            [s.name for s in tar.getmembers() if s.type in (b"0", b"2")])
                        if changelog_file:
                            changelog = tar.extractfile(changelog_file).read().decode()
                        else:
                            raise ValueError('No changelog in source package')
            except Exception as e:
                _generic_exception_handler(e)
                self.exit_on_fail(520)
            if 'tmpFile' in vars():
                try:
                    tmpFile.close()
                except Exception as e:
                    _generic_exception_handler(e)

        # ALL DONE
        return changelog
示例#2
0
    def get_changelog(self, pkg_name:str, no_local:bool=False):
        self.refresh_cache()
        self.candidate = self.parse_package_metadata(pkg_name)

        # parse the package's origin
        if not self.candidate.downloadable:
            origin = "local_package"
        elif self.candidate.origin == "linuxmint":
            origin = "linuxmint"
        elif self.candidate.origin.startswith("LP-PPA-"):
            origin = "LP-PPA"
        elif self.apt_origins and self.candidate.origin in self.apt_origins.list():
            origin = "APT"
        else:
            origin = "unsupported"

        # Check for changelog of installed package first
        has_local_changelog = False
        uri = None
        if not no_local and self.candidate.is_installed:
            if _DEBUG: print("Package is installed...")
            uri = self.get_changelog_from_filelist(
                self.candidate.installed_files, local=True)
            # Ubuntu kernel workarounds
            if self.candidate.origin == "Ubuntu":
                if self.candidate.source_name == "linux-signed":
                    uri = uri.replace("linux-image","linux-modules")
                if self.candidate.source_name == "linux-meta":
                    uri = None
            if uri and not os.path.isfile(uri):
                uri = None

        # Do nothing if local changelog exists
        if uri:
            has_local_changelog = True
        # all origins that APT supports
        elif origin == 'APT':
            uri = self.get_apt_changelog_uri(
                self.apt_origins.get(self.candidate.origin))
            r = self.check_url(uri)
            if not r:
                self.exit_on_fail(2)
        # Linux Mint repo
        elif origin == 'linuxmint':
            # Mint repos don't have .debian.tar.xz files, only full packages, so
            # check the package cache first
            base_uri, _ = os.path.split(self.candidate.uri)
            r, uri = self.get_changelog_uri(base_uri)
            if not r:
                # fall back to last change info for the source package
                # Mint's naming scheme seems to be using amd64 unless source
                # is i386 only, we always check amd64 first
                base_uri = "http://packages.linuxmint.com/dev/%s_%s_%s.changes"
                uri = base_uri % (self.candidate.source_name,
                    self.candidate.source_version, "amd64")
                r = self.check_url(uri, False)
                if not r:
                    uri = base_uri % (self.candidate.source_name,
                        self.candidate.source_version, "i386")
                    r = self.check_url(uri, False)
                    if not r:
                        self.exit_on_fail(3)

        # Launchpad PPA
        elif origin == 'LP-PPA':
            ppa_owner, ppa_name, _ = \
                self.candidate.uri.split("ppa.launchpad.net/")[1].split("/", 2)
            base_uri = "http://ppa.launchpad.net/%(owner)s/%(name)s/ubuntu/pool/main/%(source_prefix)s/%(source_name)s" % \
                {
                    "owner": ppa_owner,
                    "name": ppa_name,
                    "source_prefix": self.source_prefix(),
                    "source_name": self.candidate.source_name
                }

            r, uri = self.get_changelog_uri(base_uri)
            if not r:
                # fall back to last change info only
                uri = "https://launchpad.net/~%(owner)s/+archive/ubuntu/%(name)s/+files/%(source_name)s_%(source_version)s_source.changes" % \
                    {
                        "owner" : ppa_owner,
                        "name": ppa_name,
                        "source_name": self.candidate.source_name,
                        "source_version": self.candidate.source_version
                    }
                r = self.check_url(uri, False)
                if not r:
                    self.exit_on_fail(4)
        # Not supported origin
        elif origin == 'unsupported':
            if _DEBUG: print("Unsupported Package")
            base_uri, _ = os.path.split(self.candidate.uri)
            r, uri = self.get_changelog_uri(base_uri)
            if not r:
                self.exit_on_fail(5)
        # Locally installed package without local changelog or remote
        # source, hope it's cached and contains a changelog
        elif origin == 'local_package':
            uri = self.apt_cache_path + self.candidate.filename
            if not os.path.isfile(uri):
                self.exit_on_fail(6)

        # Changelog downloading, extracting and processing:
        changelog = ""
        # local changelog
        if has_local_changelog and not no_local:
            if _DEBUG: print("Using local changelog:",uri)
            try:
                filename = os.path.basename(uri)
                # determine file type by name/extension
                # as per debian policy 4.4 the encoding must be UTF-8
                # as per policy 12.7 the name must be changelog.Debian.gz or
                # changelog.gz (deprecated)
                if filename.lower().endswith('.gz'):
                    changelog = gzip.open(uri,'r').read().decode('utf-8')
                elif filename.lower().endswith('.xz'):
                    # just in case / future proofing
                    changelog = lzma.open(uri,'r').read().decode('utf-8')
                elif filename.lower() == 'changelog':
                    changelog = open(uri, 'r').read().encode().decode('utf-8')
                else:
                    raise ValueError('Unknown changelog format')
            except Exception as e:
                _generic_exception_handler(e)
                self.exit_on_fail(1)
        # APT-format changelog, download directly
        # - unfortunately this is slow since the servers support no compression
        elif origin == "APT":
            if _DEBUG: print("Downloading: %s (%.2f MB)" % (uri, r.length / self.MB))
            changelog = r.text
            r.close()
        # last change changelog, download directly
        elif uri.endswith('.changes'):
            if _DEBUG: print("Downloading: %s (%.2f MB)" % (uri, r.length / self.MB))
            changes = r.text.split("Changes:")[1].split("Checksums")[0].split("\n")
            r.close()
            for change in changes:
                change = change.strip()
                if change:
                    if change == ".":
                        change = ""
                    changelog += change + "\n"
        # compressed binary source, download and extract changelog
        else:
            source_is_cache = uri.startswith(self.apt_cache_path)
            if _DEBUG: print("Using cached package:" if source_is_cache else
                "Downloading: %s (%.2f MB)" % (uri, r.length / self.MB))
            try:
                if not source_is_cache:
                    # download stream to temporary file
                    tmpFile = tempfile.NamedTemporaryFile(prefix="apt-changelog-")
                    if self.interactive and r.length:
                        # download chunks with progress indicator
                        recv_length = 0
                        blocks = 60
                        for data in r.iter_content(chunk_size=16384):
                            recv_length += len(data)
                            tmpFile.write(data)
                            recv_pct = recv_length / r.length
                            recv_blocks = int(blocks * recv_pct)
                            print("\r[%(progress)s%(spacer)s] %(percentage).1f%%" %
                                {
                                    "progress": "=" * recv_blocks,
                                    "spacer":  " " * (blocks - recv_blocks),
                                    "percentage": recv_pct * 100
                                }, end="", flush=True)
                        # clear progress bar when done
                        print("\r" + " " * (blocks + 10), end="\r", flush=True)
                    else:
                        # no content-length or non-interactive, download in one go
                        # up to the configured max_download_size, ask only when
                        # exceeded
                        r.raw.decode_content = True
                        size = 0
                        size_exceeded = False
                        while True:
                            buf = r.raw.read(16*1024)
                            if not size_exceeded:
                                size += len(buf)
                                if size > self.max_download_size:
                                    if not self.user_confirm(self.max_download_size_msg_unknown):
                                        r.close()
                                        tmpFile.close()
                                        return ""
                                    else:
                                        size_exceeded = True
                            if not buf:
                                break
                            tmpFile.write(buf)
                    r.close()
                    tmpFile.seek(0)
                if uri.endswith(".deb"):
                    # process .deb file
                    if source_is_cache:
                        f = uri
                    else:
                        f = tmpFile.name
                        # We could copy the downloaded .deb files to the apt
                        # cache here but then we'd need to run the script elevated:
                        # shutil.copy(f, self.apt_cache_path + os.path.basename(uri))
                    deb = DebPackage(f)
                    changelog_file = self.get_changelog_from_filelist(deb.filelist)
                    if changelog_file:
                        changelog = deb.data_content(changelog_file)
                        if changelog.startswith('Automatically decompressed:'):
                            changelog = changelog[29:]
                    else:
                        raise ValueError('Malformed Debian package')
                elif uri.endswith(".diff.gz"):
                    # Ubuntu partner repo has .diff.gz files,
                    # we can extract a changelog from that
                    data = gzip.open(tmpFile.name, "r").read().decode('utf-8')
                    additions = data.split("+++")
                    for addition in additions:
                        lines = addition.split("\n")
                        if "/debian/changelog" in lines[0]:
                            for line in lines[2:]:
                                if line.startswith("+"):
                                    changelog += "%s\n" % line[1:]
                                else:
                                    break
                    if not changelog:
                        raise ValueError('No changelog in .diff.gz')
                else:
                    # process .tar.xz file
                    with tarfile.open(fileobj=tmpFile, mode="r:xz") as tar:
                        changelog_file = self.get_changelog_from_filelist(
                            [s.name for s in tar.getmembers() if s.type in (b"0", b"2")])
                        if changelog_file:
                            changelog = tar.extractfile(changelog_file).read().decode()
                        else:
                            raise ValueError('No changelog in source package')
            except Exception as e:
                _generic_exception_handler(e)
                self.exit_on_fail(520)
            if 'tmpFile' in vars():
                try:
                    tmpFile.close()
                except Exception as e:
                    _generic_exception_handler(e)

        # ALL DONE
        return changelog