%PDF- %PDF-
Direktori : /usr/lib/python3/dist-packages/softwareproperties/ |
Current File : //usr/lib/python3/dist-packages/softwareproperties/ppa.py |
# software-properties PPA support, using launchpadlib # # Copyright (c) 2019 Canonical Ltd. # # Original Author: Michael Vogt <mvo@debian.org> # Rewrite: Dan Streetman <ddstreet@canonical.com> # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA from gettext import gettext as _ from launchpadlib.launchpad import Launchpad from lazr.restfulclient.errors import (NotFound, BadRequest, Unauthorized) from softwareproperties.shortcuthandler import (ShortcutHandler, ShortcutException, InvalidShortcutException) from softwareproperties.sourceslist import SourcesListShortcutHandler from softwareproperties.uri import URIShortcutHandler from aptsources.sourceslist import Deb822SourceEntry from urllib.parse import urlparse PPA_URI_FORMAT = 'https://ppa.launchpadcontent.net/{team}/{ppa}/ubuntu/' PRIVATE_PPA_URI_FORMAT = 'https://private-ppa.launchpadcontent.net/{team}/{ppa}/ubuntu/' PPA_VALID_HOSTNAMES = [ urlparse(PPA_URI_FORMAT).hostname, urlparse(PRIVATE_PPA_URI_FORMAT).hostname, # Old hostnames. 'ppa.launchpad.net', 'private-ppa.launchpad.net', ] PPA_VALID_COMPS = ['main', 'main/debug'] class PPAShortcutHandler(ShortcutHandler): def __init__(self, shortcut, login=False, **kwargs): super(PPAShortcutHandler, self).__init__(shortcut, deb822=True, **kwargs) self._lp_anon = not login self._signing_key_data = None self._lp = None # LP object self._lpteam = None # Person/Team LP object self._lpppa = None # PPA Archive LP object self._is_sourceslist = False # one of these will set teamname and ppaname, and maybe _source_entry if not self._match_ppa(shortcut): # The input is either a sources.list line or a URI. Both cases lead # to the SourcesListShortcutHandler being used, so unset # self.deb822 (LP: #2037210). self.deb822 = False if not any((self._match_uri(shortcut), self._match_sourceslist(shortcut))): msg = (_("ERROR: '%s' is not a valid ppa format") % shortcut) raise InvalidShortcutException(msg) self._filebase = "%s-ubuntu-%s" % (self.teamname, self.ppaname) self._set_auth() # Make sure we can find/access the PPA, lp:#1965180 if self._is_sourceslist: try: self.lpppa except ShortcutException: raise InvalidShortcutException(_("ERROR: Can't find ppa")) if not self._source_entry: comps = self.components if not comps: comps = ['main'] if self.lpppa.publish_debug_symbols: print("PPA publishes dbgsym, you may need to include 'main/debug' component") # comps += ['main/debug'] uri_format = PRIVATE_PPA_URI_FORMAT if self.lpppa.private else PPA_URI_FORMAT uri = uri_format.format(team=self.teamname, ppa=self.ppaname) entry = Deb822SourceEntry(None, '') entry.types = [self.binary_type] entry.uris = [uri] entry.suites = [self.dist] entry.comps = comps self._set_source_entry(str(entry)) @property def lp(self): if not self._lp: if self._lp_anon: login_func = Launchpad.login_anonymously else: login_func = Launchpad.login_with self._lp = login_func("%s.%s" % (self.__module__, self.__class__.__name__), service_root='production', version='devel') return self._lp @property def lpteam(self): if not self._lpteam: try: self._lpteam = self.lp.people(self.teamname) except NotFound: msg = (_("ERROR: user/team '%s' not found (use --login if private)") % self.teamname) raise ShortcutException(msg) except Unauthorized: msg = (_("ERROR: invalid user/team name '%s'") % self.teamname) raise ShortcutException(msg) return self._lpteam @property def lpppa(self): if not self._lpppa: try: self._lpppa = self.lpteam.getPPAByName(name=self.ppaname) except NotFound: msg = (_("ERROR: ppa '%s/%s' not found (use --login if private)") % (self.teamname, self.ppaname)) raise ShortcutException(msg) except BadRequest: msg = (_("ERROR: invalid ppa name '%s'") % self.ppaname) raise ShortcutException(msg) return self._lpppa @property def description(self): return self.lpppa.description @property def web_link(self): return self.lpppa.web_link @property def trustedparts_content(self): if not self._signing_key_data: key = self.lpppa.getSigningKeyData() fingerprint = self.lpppa.signing_key_fingerprint if not fingerprint: print(_("Warning: could not get PPA signing_key_fingerprint from LP, using anyway")) elif 'redacted' in fingerprint: print(_("Private PPA fingerprint redacted, using key anyway (LP: #1879781)")) elif not fingerprint in self.fingerprints(key): msg = (_("Fingerprints do not match, not importing: '%s' != '%s'") % (fingerprint, ','.join(self.fingerprints(key)))) raise ShortcutException(msg) self._signing_key_data = key return self._signing_key_data def SourceEntry(self, pkgtype=None): entry = super(PPAShortcutHandler, self).SourceEntry(pkgtype=pkgtype) if pkgtype != self.source_type or self.components: return entry # 'main/debug' is needed to get dbgsyms from ppas, # but it's not a valid component for ppa deb-src lines. # Sigh. entry.comps = list(set(entry.comps) - set(['main/debug'])) return entry def _set_source_entry(self, line): super(PPAShortcutHandler, self)._set_source_entry(line) invalid_comps = set(self.SourceEntry().comps) - set(PPA_VALID_COMPS) if invalid_comps: print(_("Warning: components '%s' not valid for PPA") % ' '.join(invalid_comps)) def _match_ppa(self, shortcut): (prefix, _, ppa) = shortcut.rpartition(':') if not prefix.lower() == 'ppa': return False (teamname, _, ppaname) = ppa.partition('/') teamname = teamname.lstrip('~') if '/' in ppaname: (ubuntu, _, ppaname) = ppaname.partition('/') if ubuntu.lower() != 'ubuntu': # PPAs only support ubuntu return False if '/' in ppaname: # Path is too long for valid ppa return False self.teamname = teamname self.ppaname = ppaname or 'ppa' return True def _match_uri(self, shortcut): try: return self._match_handler(URIShortcutHandler(shortcut)) except InvalidShortcutException: return False def _match_sourceslist(self, shortcut): try: handler = self._match_handler(SourcesListShortcutHandler(shortcut)) except InvalidShortcutException: return False self._is_sourceslist = True return handler def _match_handler(self, handler): parsed = urlparse(handler.SourceEntry().uri) if not parsed.hostname in PPA_VALID_HOSTNAMES: return False path = parsed.path.strip().strip('/').split('/') if len(path) < 2: return False self.teamname = path[0] self.ppaname = path[1] self._username = handler.username self._password = handler.password self._set_source_entry(handler.SourceEntry().line) return True def _set_auth(self): if self._lp_anon or not self.lpppa.private: return if self._username and self._password: return try: # Named ops work against actual person, not the me alias object me = self.lp.people(self.lp.me.name) url = me.getArchiveSubscriptionURL(archive=self.lpppa) except Unauthorized: msg = (_("Could not find PPA subscription for ppa:%s/%s, you may need to request access") % (self.teamname, self.ppaname)) raise ShortcutException(msg) else: parsed = urlparse(url) self._username = parsed.username self._password = parsed.password