Ejemplo n.º 1
0
    def __init__(self, datadir=None, options=None, rootdir="/"):
        """ Provides the core functionality to configure the used software 
        repositories, the corresponding authentication keys and 
        update automation """
        self.popconfile = rootdir + "/etc/popularity-contest.conf"

        self.rootdir = rootdir
        if rootdir != "/":
            apt_pkg.config.set("Dir", rootdir)

        # FIXME: some saner way is needed here
        if datadir == None:
            datadir = "/usr/share/software-properties/"
        self.options = options
        self.datadir = datadir

        self.sourceslist = SourcesList()
        self.distro = aptsources.distro.get_distro()

        self.seen_server = []
        self.modified_sourceslist = False

        self.reload_sourceslist()
        self.backup_sourceslist()

        self.backup_apt_conf()

        # FIXME: we need to store this value in a config option
        #self.custom_mirrors = ["http://adasdwww.de/ubuntu"]
        self.custom_mirrors = []

        # apt-key stuff
        self.apt_key = AptAuth(rootdir=rootdir)

        atexit.register(self.wait_for_threads)
Ejemplo n.º 2
0
 def get_unique_hosts_from_apt_list(self):
     sl = SourcesList()
     sl.refresh()
     hosts = [
         urlparse(e.uri).netloc for e in sl if e.uri.startswith('http')
     ]
     unique_hosts = list(dict.fromkeys(hosts))
     self.logger.debug("Parsed unique hosts from apt lists: %s" %
                       json.dumps(unique_hosts))
     return unique_hosts
Ejemplo n.º 3
0
def remove(src: SourcesList, opts: Namespace, deletion_queue: List[str]) -> int:
    entries = find_entries(src.list, opts)
    if len(entries) == 0:
        return EXIT_NOMATCH
    for e in entries:
        if all([ x is not e and x.file != e.file for x in src.list ]):
            deletion_queue.append(e.file)
            src.remove(e)
        else:
            print("Entry has other neighbours, disabling instead of removing: <{}>".format(e),
                    file=sys.stderr)
            e.set_enabled(False)
    return EXIT_SUCCESS
  def __init__(self, datadir=None, options=None, rootdir="/"):
    """ Provides the core functionality to configure the used software 
        repositories, the corresponding authentication keys and 
        update automation """
    self.popconfile = rootdir+"/etc/popularity-contest.conf"

    self.rootdir = rootdir
    if rootdir != "/":
      apt_pkg.config.set("Dir", rootdir)

    # FIXME: some saner way is needed here
    if datadir == None:
      datadir = "/usr/share/software-properties/"
    self.options = options
    self.datadir = datadir

    self.sourceslist = SourcesList()
    self.distro = aptsources.distro.get_distro()
    
    self.seen_server = []
    self.modified_sourceslist = False

    self.reload_sourceslist()
    self.backup_sourceslist()

    self.backup_apt_conf()

    # FIXME: we need to store this value in a config option
    #self.custom_mirrors = ["http://adasdwww.de/ubuntu"]
    self.custom_mirrors= []

    # apt-key stuff
    self.apt_key = AptAuth(rootdir=rootdir)

    atexit.register(self.wait_for_threads)
Ejemplo n.º 5
0
def apply_template(src: SourcesList, opts: Namespace, deletion_queue: List[str]) -> int:
    def match_distro(v):
        if opts.regex:
            return not not re.search(opts.distribution, v, flags=re.IGNORECASE)
        else:
            return v == opts.distribution
    di = distinfo.DistInfo(dist=opts.template)
    for t in filter(lambda t: match_distro(t.name), di.templates):
        base_uri = t.base_uri if t.base_uri is not None else t.parents[0].base_uri
        base_components = [ c.name for c in (t.components if
            len(t.components)>0 else t.parents[0].components) ]
        target_file = opts.file or "{}/{}.list".format(APT_SOURCE_PARTSDIR,
                opts.template.lower())
        deletion_queue.append(target_file)
        if opts.component is not None:
            if opts.regex:
                r = re.compile(opts.component[0], flags=re.IGNORECASE)
                base_components = list(filter(r.search, base_components))
            else:
                base_components = list(filter(lambda c: c in opts.component,
                    base_components))
            if len(base_components) == 0:
                print("Error: No components left for template after filtering.", file=sys.stderr)
                return EXIT_NOMATCH
        print(src.add(t.type, base_uri, t.name, base_components, file=target_file))
    return EXIT_SUCCESS
Ejemplo n.º 6
0
 def _feed_in_private_sources_list_entry(self, source_entry):
     """
     this feeds in a private sources.list entry that is
     available to the user (like a private PPA) that may or
     may not be active 
     """
     # FIXME: strip out password and use apt/auth.conf
     potential_new_entry = SourceEntry(source_entry)
     # look if we have it
     sources = SourcesList()
     for source in sources.list:
         if source == potential_new_entry:
             return False
     # need to add it as a not yet enabled channel
     name = human_readable_name_from_ppa_uri(potential_new_entry.uri)
     # FIXME: use something better than uri as name
     private_channel = SoftwareChannel(name,
                                       None,
                                       None,
                                       source_entry=source_entry)
     private_channel.needs_adding = True
     if private_channel in self.extra_channels:
         return False
     # add it
     self.extra_channels.append(private_channel)
     return True
    def mirror_from_sources_list(self, uri, default_uri):
      """
      try to figure what the mirror is from current sources.list

      do this by looing for matching DEFAULT_COMPONENT, current dist
      in sources.list and then doing a http HEAD/ftp size request
      to see if the uri is available on this server
      """
      self._debug("mirror_from_sources_list: %s" % self.current_dist_name)
      sources = SourcesList(withMatcher=False)
      seen = set()
      for e in sources.list:
        if e.disabled or e.invalid or not e.type == "deb":
          continue
        # check if we probed this mirror already
        if e.uri in seen:
          continue
        # we are using the main mirror already, so we are fine
        if (e.uri.startswith(default_uri) and 
            e.dist == self.current_dist_name and
            self.DEFAULT_COMPONENT in e.comps):
          return uri
        elif (e.dist == self.current_dist_name and
              "main" in e.comps):
          mirror_uri = e.uri+uri[len(default_uri):]
          if url_downloadable(mirror_uri, self._debug):
            return mirror_uri
          seen.add(e.uri)
      self._debug("no mirror found")
      return ""
Ejemplo n.º 8
0
class Daemon(PolicyKitService):
    #TODO use signal
    liststate = None
    list = SourcesList()
    #    cache = apt.Cache()
    stable_url = 'http://ppa.launchpad.net/tualatrix/ppa/ubuntu'
    ppa_list = []
    p = None
    SOURCES_LIST = '/etc/apt/sources.list'

    def __init__(self, bus, mainloop):
        bus_name = dbus.service.BusName(INTERFACE, bus=bus)
        PolicyKitService.__init__(self, bus_name, PATH)
        self.mainloop = mainloop

    @dbus.service.method(INTERFACE, in_signature='b', out_signature='bv')
    def check_sources_is_valid(self, convert_source):
        try:
            if not os.path.exists(self.SOURCES_LIST):
                os.system('touch %s' % self.SOURCES_LIST)
        except Exception, e:
            log.error(e)

        self.list.refresh()
        to_add_entry = []
        to_rm_entry = []
        disabled_list = ['']

        for entry in self.list:
            entry_line = entry.str().strip()
            if entry.invalid and not entry.disabled and entry_line and not entry_line.startswith(
                    '#'):
                try:
                    entry.set_enabled(False)
                except Exception, e:
                    log.error(e)
                if entry.file not in disabled_list:
                    disabled_list.append(entry.file)
                continue

            if convert_source:
                if os.path.basename(entry.file) == 'sources.list':
                    continue
                log.debug("Check for url: %s, type: %s, filename: %s" %
                          (entry.uri, entry.type, entry.file))
                if ppa.is_ppa(entry.uri):
                    filename = '%s-%s.list' % (ppa.get_source_file_name(
                        entry.uri), entry.dist)
                    if filename != os.path.basename(entry.file):
                        log.debug("[%s] %s need rename to %s" %
                                  (entry.type, entry.file, filename))
                        to_rm_entry.append(entry)
                        if os.path.exists(entry.file):
                            log.debug("%s is exists, so remove it" %
                                      entry.file)
                            os.remove(entry.file)
                        entry.file = os.path.join(os.path.dirname(entry.file),
                                                  filename)
                        to_add_entry.append(entry)
Ejemplo n.º 9
0
    def go_install_drbl(self, widget):
	if self.linux_dist == "":
	    self.linux_dist = self.get_linux_dist()

	if self.linux_dist == "not support":
	    return -1
	
	if self.linux_dist == "Fedora":
	    os.system('yum -y install perl-Digest-SHA1')
	    os.system('yum -y install wget')
	    os.system('rm -f GPG-KEY-DRBL')
	    os.system('wget http://drbl.nchc.org.tw/GPG-KEY-DRBL')
	    os.system('rpm --import GPG-KEY-DRBL')
	    os.system('wget http://drbl.nchc.org.tw/one4all/desktop/download/stable/RPMS/drbl-current.i386.rpm')
	    os.system('yum -y install drbl-current.i386.rpm')

	elif self.linux_dist == "Ubuntu":
	    sourceslist = SourcesList ()
	    distro = aptsources.distro.get_distro ()
	    try:
		distro.get_sources (sourceslist)
	    except:
		print "your distribution is remix release!"
		return -1
	    has_drbl_repo = 0
	    has_drbl_comps = 0
	    for source in sourceslist:
		if source.disabled == False:
		    if source.uri == "http://free.nchc.org.tw/drbl-core":
			has_drbl_repo = 1
			if source.comps == self.comps:
			    has_drbl_comps = 1
			else:
			    print "remove drbl repo"
			    sourceslist.remove(source)

	    if has_drbl_repo == 0 or has_drbl_comps == 0:
		drbl_uri = "http://free.nchc.org.tw/drbl-core"
		drbl_dist = "drbl"
		drbl_comps = self.comps
		distro.add_source (type="deb", uri=drbl_uri, dist=drbl_dist, comps=drbl_comps, comment="DRBL Repository (Add by drbl_assistant)")
		sourceslist.backup ()
		sourceslist.save ()

	    add_key_st = os.popen("wget -q http://drbl.nchc.org.tw/GPG-KEY-DRBL -O- | apt-key add -").readlines()[0][:-1]

	    try:
		cache = apt.Cache()
		pkg = cache['drbl'] # Access the Package object for python-apt
		pkg.mark_install()
		# Now, really install it
		cache.commit()
	    except:
		print "apt-get install drbl"
		os.system("apt-get update")
		os.system("apt-get install drbl")
	else:
	    print "Unknown linux"
	print "install finish"
Ejemplo n.º 10
0
    def check_source_upgradable(self):
        log.debug("The check source string is: \"%s\"" %
                  self.__get_disable_string())
        for source in SourcesList():
            if self.__get_disable_string() in source.str() and \
                    source.uri in UPGRADE_DICT and \
                    source.disabled:
                return True

        return False
Ejemplo n.º 11
0
    def _rewrite_sources_list(self, targetdir, new_distro):
        from aptsources.sourceslist import SourcesList, SourceEntry
        apt_pkg.config.set(
            "Dir::Etc::sourcelist",
            os.path.abspath(
                os.path.join(targetdir, "etc", "apt", "sources.list")))
        apt_pkg.config.set(
            "Dir::Etc::sourceparts",
            os.path.abspath(
                os.path.join(targetdir, "etc", "apt", "sources.list.d")))
        sources = SourcesList()

        for entry in sources.list[:]:
            if entry.invalid or entry.disabled:
                continue
            replacement = ''
            for pocket in ('updates', 'security', 'backports'):
                if entry.dist.endswith('-%s' % pocket):
                    replacement = '%s-%s' % (new_distro, pocket)
                    break
            if replacement:
                entry.dist = replacement
            else:
                entry.dist = new_distro

        existing = os.path.join(targetdir, "etc", "apt",
                                "sources.list.apt-clone")
        sourcelist = apt_pkg.config.find_file("Dir::Etc::sourcelist")
        if os.path.exists(existing):
            with open(existing, 'r') as fp:
                for line in fp:
                    src = SourceEntry(line, sourcelist)
                    if (src.invalid or src.disabled) or src not in sources:
                        sources.list.append(src)
            os.remove(existing)

        for entry in sources.list:
            if entry.uri.startswith('cdrom:'):
                # Make sure CD entries come first.
                sources.list.remove(entry)
                sources.list.insert(0, entry)
                entry.disabled = True
        sources.save()
Ejemplo n.º 12
0
    def get_channels(self):
        """Return a list of channels configured.

        A channel is a deb line in sources.list or sources.list.d. It's
        represented by a dict with baseurl, distribution, components,
        and type keys.
        """
        sources_list = SourcesList()
        return [{"baseurl": entry.uri, "distribution": entry.dist,
                 "components": " ".join(entry.comps), "type": entry.type}
                for entry in sources_list if not entry.disabled]
Ejemplo n.º 13
0
    def _rewrite_sources_list(self, targetdir, new_distro):
        from aptsources.sourceslist import SourcesList, SourceEntry
        apt_pkg.config.set(
            "Dir::Etc::sourcelist",
            os.path.abspath(os.path.join(targetdir, "etc", "apt", "sources.list")))
        apt_pkg.config.set(
            "Dir::Etc::sourceparts",
            os.path.abspath(os.path.join(targetdir, "etc", "apt", "sources.list.d")))
        sources = SourcesList()

        for entry in sources.list[:]:
            if entry.invalid or entry.disabled:
                continue
            replacement = ''
            for pocket in ('updates', 'security', 'backports'):
                if entry.dist.endswith('-%s' % pocket):
                    replacement = '%s-%s' % (new_distro, pocket)
                    break
            if replacement:
                entry.dist = replacement
            else:
                entry.dist = new_distro

        existing = os.path.join(targetdir, "etc", "apt",
                                "sources.list.apt-clone")
        sourcelist = apt_pkg.config.find_file("Dir::Etc::sourcelist")
        if os.path.exists(existing):
            with open(existing, 'r') as fp:
                for line in fp:
                    src = SourceEntry(line, sourcelist)
                    if (src.invalid or src.disabled) or src not in sources:
                        sources.list.append(src)
            os.remove(existing)

        for entry in sources.list:
            if entry.uri.startswith('cdrom:'):
                # Make sure CD entries come first.
                sources.list.remove(entry)
                sources.list.insert(0, entry)
                entry.disabled = True
        sources.save()
Ejemplo n.º 14
0
def add(src: SourcesList, opts: Namespace, deletion_queue: List[str]) -> int:
    kwargs = dict()

    if opts.file is not None:
        kwargs["file"] = opts.file

    if not all([opts.type, opts.uri, opts.distribution,
        opts.component]):
        raise BaseException("Not enough arguments: add")

    print(src.add(opts.type, opts.uri, opts.distribution, opts.component, **kwargs))
    return EXIT_SUCCESS
def main ():
    uri = sys.argv[1].strip ()
    dist = sys.argv[2].strip ()
    comps = sys.argv[3].split (' ')
    comment = sys.argv[4].strip ()

    distro = aptsources.distro.get_distro ()
    sourceslist = SourcesList ()
    
    for source in sourceslist:
      if source.disabled == False:
        if source.uri == uri and source.comps == comps:
	  print "套件庫已存在。"
        else:
          distro.get_sources (sourceslist)
          distro.add_source (uri=uri, dist=dist, comps=comps, comment=comment)

    sourceslist.backup ()
    sourceslist.save ()

    return 0
Ejemplo n.º 16
0
 def _restore_sources_list(self, statefile, targetdir, mirror=None):
     with tarfile.open(statefile) as tar:
         existing = os.path.join(targetdir, "etc", "apt", "sources.list")
         if os.path.exists(existing):
             shutil.copy(existing, '%s.apt-clone' % existing)
         tar.extract(self.TARPREFIX+"etc/apt/sources.list", targetdir)
         td_sources = os.path.join(targetdir, "etc", "apt", "sources.list")
         os.chmod(td_sources, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP |
                  stat.S_IROTH)
         if mirror:
             from aptsources.sourceslist import SourcesList
             apt_pkg.config.set("Dir::Etc::sourcelist", td_sources)
             sources = SourcesList()
             for entry in sources.list[:]:
                 if entry.uri != mirror:
                    entry.uri = mirror
             sources.save()
         try:
             tar.extract(self.TARPREFIX+"etc/apt/sources.list.d", targetdir)
         except KeyError:
             pass
Ejemplo n.º 17
0
 def _remove_no_longer_needed_extra_channels(self, backend, res):
     """ go over the extra channels and remove no longer needed ones"""
     removed = False
     for channel in self.extra_channels:
         if not channel._source_entry:
             continue
         sources = SourcesList()
         for source in sources.list:
             if source == SourceEntry(channel._source_entry):
                 self.extra_channels.remove(channel)
                 removed = True
     if removed:
         self.backend.emit("channels-changed", True)
Ejemplo n.º 18
0
 def _restore_sources_list(self, statefile, targetdir, mirror=None):
     with tarfile.open(statefile) as tar:
         existing = os.path.join(targetdir, "etc", "apt", "sources.list")
         if os.path.exists(existing):
             shutil.copy(existing, '%s.apt-clone' % existing)
         tar.extract(self.TARPREFIX + "etc/apt/sources.list", targetdir)
         td_sources = os.path.join(targetdir, "etc", "apt", "sources.list")
         os.chmod(td_sources,
                  stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
         if mirror:
             from aptsources.sourceslist import SourcesList
             apt_pkg.config.set("Dir::Etc::sourcelist", td_sources)
             sources = SourcesList()
             for entry in sources.list[:]:
                 if entry.uri != mirror:
                     entry.uri = mirror
             sources.save()
         try:
             tar.extract(self.TARPREFIX + "etc/apt/sources.list.d",
                         targetdir)
         except KeyError:
             pass
Ejemplo n.º 19
0
    def update_sources(self):
        #First hide the item not supported by current distribution
        self.execute_script('''
                var system_codename = "%s";
                var ubuntu_codenames = %s;
                console.log("Updating source for system: " + system_codename + ', codenames: ' + ubuntu_codenames);
                var appController = Utapp.get('router.appController');
                appController.currentApp.sources.forEach(function(source) {
                    var distro_value = '';
                    console.log(source.name + " is filtering codename for: ");
                    source.distros.forEach(function(distro) {
                        var codename = distro.codename;
                        console.log('\t' + codename);
                        if (ubuntu_codenames.contains(codename)){
                            console.log('\t\tThis is ubuntu codename');
                            if (system_codename == codename) {
                                console.log('\t\tCodename match!');
                                distro_value = codename;
                                return false;
                            }
                        } else {
                            console.log("\t\tThis isn't Ubuntu codename!");
                            distro_value = codename;
                            return false;
                        };
                    });
                    if (distro_value == '') {
                        console.log('Set source: ' + source.name + ' to hide');
                        source.set('is_available', false);
                    } else {
                        source.set('distro_value', distro_value);
                    }
                });
                ''' % (system.CODENAME, list(system.UBUNTU_CODENAMES)))

        enabled_list = []

        for source in SourcesList().list:
            if source.type == 'deb' and not source.disabled:
                enabled_list.append(source.uri)

        self.execute_script('''
                var enabled_list = %s;
                $(".source-view").each(function(index) {
                    if (enabled_list.contains($(this).attr('urldata')))
                    {
                        this.checked = true;
                    }
                    console.log(index + ': ' + $(this).attr('urldata'));
                });
                ''' % enabled_list)
Ejemplo n.º 20
0
 def _getSourcesList(self):
     try:
         import apt_pkg
         from aptsources.sourceslist import SourcesList
         apt_pkg.init()
     except (SystemError, ImportError):
         return []
     sources_list = set([])
     # The matcher parses info files from
     # /usr/share/python-apt/templates/
     # But we don't use the calculated data, skip it
     for source in SourcesList(withMatcher=False):
         if not source.disabled and not source.invalid:
             for component in source.comps:
                 sources_list.add(component)
     return sources_list
Ejemplo n.º 21
0
def main(allowed_protocols, allowed_domains):
    check_debianos()
    sourceslist = SourcesList()
    external = ''
    protocols = '|'.join(allowed_protocols)
    domains = '|'.join(allowed_domains)

    sources = set(e.uri for e in sourceslist
                  if e.uri and not str(e).startswith('#'))
    regex = re.compile(r'^({0})://([^:]+:[^@]+@)?({1})(/)?'.format(
        protocols, domains))
    for source in sources:
        if not regex.match(source):
            external += '{}, '.format(source)
    if external:
        print('WARNING: external sources found: {0}'.format(external))
        sys.exit(1)

    print('OK: no external repos found')
Ejemplo n.º 22
0
class Daemon(PolicyKitService):
    #TODO use signal
    liststate = None
    list = SourcesList()
    cache = None
    stable_url = 'http://ppa.launchpad.net/tualatrix/ppa/ubuntu'
    ppa_list = []
    p = None
    SOURCES_LIST = '/etc/apt/sources.list'

    def __init__(self, bus, mainloop):
        bus_name = dbus.service.BusName(INTERFACE, bus=bus)
        PolicyKitService.__init__(self, bus_name, PATH)
        self.mainloop = mainloop

    def get_cache(self):
        try:
            self.update_apt_cache()
        except Exception, e:
            log.error("Error happened when get_cache(): %s" % str(e))
        finally:
Ejemplo n.º 23
0
    def enableSection(self, apturl):
        # parse sources.list
        sourceslist = SourcesList()
        distro = aptsources.distro.get_distro()
        distro.get_sources(sourceslist)

        # check if we actually need to enable anything
        requested_components = []
        for component in apturl.section:
            if not component in distro.enabled_comps:
                requested_components.append(component)
        # if not, we are fine
        if not requested_components:
            return RESULT_OK
        # otherwise ask the user if the really wants to anble them
        if not self.ui.askEnableSections(apturl.section):
            return RESULT_CANCELT
        if not self.ui.doEnableSection(apturl.section):
            self.ui.error(_("Enabling '%s' failed") %
                          ", ".join(apturl.section))
            return RESULT_ERROR
        self.ui.doUpdate()
        self.openCache()
        return RESULT_OK
def main():
    uri = sys.argv[1].strip()
    dist = sys.argv[2].strip()
    comps = sys.argv[3].split(' ')
    comment = sys.argv[4].strip()

    distro = aptsources.distro.get_distro()
    sourceslist = SourcesList()

    for source in sourceslist:
        if source.disabled == False:
            if source.uri == uri and source.comps == comps:
                print "套件庫已存在。"
            else:
                distro.get_sources(sourceslist)
                distro.add_source(uri=uri,
                                  dist=dist,
                                  comps=comps,
                                  comment=comment)

    sourceslist.backup()
    sourceslist.save()

    return 0
Ejemplo n.º 25
0
class SoftwareProperties(object):

    # known (whitelisted) channels
    CHANNEL_PATH = "/usr/share/app-install/channels/"

    # release upgrades policy
    RELEASE_UPGRADES_CONF = "/etc/update-manager/release-upgrades"
    #RELEASE_UPGRADES_CONF = "/tmp/release-upgrades"
    (RELEASE_UPGRADES_NORMAL, RELEASE_UPGRADES_LTS,
     RELEASE_UPGRADES_NEVER) = list(range(3))
    release_upgrades_policy_map = {
        RELEASE_UPGRADES_NORMAL: 'normal',
        RELEASE_UPGRADES_LTS: 'lts',
        RELEASE_UPGRADES_NEVER: 'never',
    }

    def __init__(self, datadir=None, options=None, rootdir="/"):
        """ Provides the core functionality to configure the used software 
        repositories, the corresponding authentication keys and 
        update automation """
        self.popconfile = rootdir + "/etc/popularity-contest.conf"

        self.rootdir = rootdir
        if rootdir != "/":
            apt_pkg.config.set("Dir", rootdir)

        # FIXME: some saner way is needed here
        if datadir == None:
            datadir = "/usr/share/software-properties/"
        self.options = options
        self.datadir = datadir

        self.sourceslist = SourcesList()
        self.distro = aptsources.distro.get_distro()

        self.seen_server = []
        self.modified_sourceslist = False

        self.reload_sourceslist()
        self.backup_sourceslist()

        self.backup_apt_conf()

        # FIXME: we need to store this value in a config option
        #self.custom_mirrors = ["http://adasdwww.de/ubuntu"]
        self.custom_mirrors = []

        # Queue to push/pop results from threads
        self.myqueue = queue.Queue()

        # apt-key stuff
        self.apt_key = AptAuth(rootdir=rootdir)

        self.cancellable = Gio.Cancellable()

        atexit.register(self.wait_for_threads)

    def wait_for_threads(self):
        " wait for all running threads (PPA key fetchers) to exit "
        for t in threading.enumerate():
            if t.ident != threading.current_thread().ident:
                t.join()

    def backup_apt_conf(self):
        """Backup all apt configuration options"""
        self.apt_conf_backup = {}
        for option in softwareproperties.CONF_MAP.keys():
            value = apt_pkg.config.find_i(softwareproperties.CONF_MAP[option])
            self.apt_conf_backup[option] = value

    def restore_apt_conf(self):
        """Restore the stored apt configuration"""
        for option in self.apt_conf_backup.keys():
            apt_pkg.config.set(softwareproperties.CONF_MAP[option],
                               str(self.apt_conf_backup[option]))
        self.write_config()

    def get_update_automation_level(self):
        """ Parse the apt cron configuration. Try to fit a predefined use case 
        and return it. Special case: if the user made a custom 
        configurtation, that we cannot represent it will return None """
        if apt_pkg.config.find_i(
                softwareproperties.CONF_MAP["autoupdate"]) > 0:
            # Autodownload
            if apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 1\
               and os.path.exists("/usr/bin/unattended-upgrade"):
                return softwareproperties.UPDATE_INST_SEC
            elif apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 1 and  \
                 apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 0:
                return softwareproperties.UPDATE_DOWNLOAD
            elif apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 0 and \
                 apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 0:
                return softwareproperties.UPDATE_NOTIFY
            else:
                return None
        elif apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 0 and \
             apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 0:
            return softwareproperties.UPDATE_MANUAL
        else:
            return None

    def set_update_automation_level(self, state):
        """ Set the apt periodic configurtation to the selected 
        update automation level. To synchronize the cache update and the 
        actual upgrading function, the upgrade function, e.g. unattended, 
        will run every day, if enabled. """
        if state == softwareproperties.UPDATE_INST_SEC:
            apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"],
                               str(1))
            apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"],
                               str(1))
        elif state == softwareproperties.UPDATE_DOWNLOAD:
            apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"],
                               str(1))
            apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"],
                               str(0))
        elif state == softwareproperties.UPDATE_NOTIFY:
            apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"],
                               str(0))
            apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"],
                               str(0))
        else:
            apt_pkg.config.set(softwareproperties.CONF_MAP["autoupdate"],
                               str(0))
            apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"],
                               str(0))
            apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"],
                               str(0))
        self.set_modified_config()

    def set_update_interval(self, days):
        """Set the interval in which we check for available updates"""
        # Only write the key if it has changed
        if not days == apt_pkg.config.find_i(
                softwareproperties.CONF_MAP["autoupdate"]):
            apt_pkg.config.set(softwareproperties.CONF_MAP["autoupdate"],
                               str(days))
            self.set_modified_config()

    def get_update_interval(self):
        """ Returns the interval of the apt periodic cron job """
        return apt_pkg.config.find_i(softwareproperties.CONF_MAP["autoupdate"])

    def get_release_upgrades_policy(self):
        """
    return the release upgrade policy:
     RELEASE_UPGRADES_NORMAL,
     RELEASE_UPGRADES_LTS,
     RELEASE_UPGRADES_NEVER
    """
        # default (if no option is set) is NORMAL
        if not os.path.exists(self.RELEASE_UPGRADES_CONF):
            return self.RELEASE_UPGRADES_NORMAL
        parser = ConfigParser()
        parser.read(self.RELEASE_UPGRADES_CONF)
        if parser.has_option("DEFAULT", "Prompt"):
            type = parser.get("DEFAULT", "Prompt").lower()
            for k, v in self.release_upgrades_policy_map.items():
                if v == type:
                    return k
        return self.RELEASE_UPGRADES_NORMAL

    def set_release_upgrades_policy(self, i):
        """
    set the release upgrade policy:
     RELEASE_UPGRADES_NORMAL,
     RELEASE_UPGRADES_LTS,
     RELEASE_UPGRADES_NEVER
     """
        # we are note using ConfigParser.write() as it removes comments
        if not os.path.exists(self.RELEASE_UPGRADES_CONF):
            f = open(self.RELEASE_UPGRADES_CONF, "w")
            f.write("[DEFAULT]\nPrompt=%s\n" %
                    self.release_upgrades_policy_map[i])
            return True
        f = open(self.RELEASE_UPGRADES_CONF, "r")
        out = NamedTemporaryFile(mode="w+")
        for line in f:
            line = line.strip()
            if line.lower().startswith("prompt"):
                out.write("Prompt=%s\n" % self.release_upgrades_policy_map[i])
            else:
                out.write(line + "\n")
        out.flush()
        shutil.copymode(self.RELEASE_UPGRADES_CONF, out.name)
        shutil.copy(out.name, self.RELEASE_UPGRADES_CONF)
        return True

    def get_popcon_participation(self):
        """ Will return True if the user wants to participate in the popularity 
        contest. Otherwise it will return False. Special case: if no 
        popcon is installed it will return False """
        if os.path.exists(self.popconfile):
            lines = open(self.popconfile).read().split("\n")
            active = False
            for line in lines:
                try:
                    (key, value) = line.split("=")
                    if key == "PARTICIPATE" and value.strip(
                            '"').lower() == "yes":
                        active = True
                except ValueError:
                    continue
            return active
        else:
            return False

    def set_popcon_pariticipation(self, is_helpful):
        """ Enable or disable the participation in the popularity contest """
        if is_helpful == True:
            value = "yes"
        else:
            value = "no"
        if os.path.exists(self.popconfile):
            # read the current config and replace the corresponding settings
            # FIXME: should we check the other values, too?
            with open(self.popconfile, "r") as popconfile:
                lines = [
                    re.sub(r'^(PARTICIPATE=)(".+?")', '\\1"%s"' % value, line)
                    for line in popconfile
                ]
        else:
            # create a new popcon config file
            m = md5()
            m.update(open("/dev/urandom", "rb").read(1024))
            id = m.hexdigest()
            lines = []
            lines.append("MY_HOSTID=\"%s\"\n" % id)
            lines.append("PARTICIPATE=\"%s\"\n" % str(value))
            lines.append("USE_HTTP=\"yes\"\n")
        open(self.popconfile, "w").writelines(lines)

    def get_source_code_state(self):
        """Return True if all distro componets are also available as 
       source code. Otherwise return Flase. Special case: If the
       configuration cannot be represented return None"""

        if len(self.distro.source_code_sources) < 1:
            # we don't have any source code sources, so
            # uncheck the button
            self.distro.get_source_code = False
            return False
        else:
            # there are source code sources, so we check the button
            self.distro.get_source_code = True
            # check if there is a corresponding source code source for
            # every binary source. if not set the checkbutton to inconsistent
            templates = {}
            sources = []
            sources.extend(self.distro.main_sources)
            sources.extend(self.distro.child_sources)
            for source in sources:
                if source.template in templates:
                    for comp in source.comps:
                        templates[source.template].add(comp)
                else:
                    templates[source.template] = set(source.comps)
            # add fake http sources for the cdrom, since the sources
            # for the cdrom are only available in the internet
            if len(self.distro.cdrom_sources) > 0:
                templates[
                    self.distro.source_template] = self.distro.cdrom_comps
            for source in self.distro.source_code_sources:
                if source.template not in templates or \
                   (source.template in templates and not \
                    (len(set(templates[source.template]) ^ set(source.comps)) == 0\
                     or (len(set(source.comps) ^ self.distro.enabled_comps) == 0))):
                    self.distro.get_source_code = False
                    return None
                    break
        return True

    def print_source_entry(self, source):
        """Print the data of a source entry to the command line"""
        for (label, value) in [("URI:", source.uri), ("Comps:", source.comps),
                               ("Enabled:", not source.disabled),
                               ("Valid:", not source.invalid)]:
            print(" %s %s" % (label, value))
        if source.template:
            for (label, value) in [("MatchURI:", source.template.match_uri),
                                   ("BaseURI:", source.template.base_uri)]:
                print(" %s %s" % (label, value))
        print("\n")

    def massive_debug_output(self):
        """Print the complete sources.list"""
        print("START SOURCES.LIST:")
        for source in self.sourceslist:
            print(source.str())
        print("END SOURCES.LIST\n")

    def change_main_download_server(self, server):
        """ change the main download server """
        self.distro.default_server = server
        res = self.distro.change_server(server)
        self.set_modified_sourceslist()
        return res

    def enable_component(self, comp):
        """Enable a component of the distro"""
        self.distro.enable_component(comp)
        self.set_modified_sourceslist()

    def disable_component(self, comp):
        """Disable a component of the distro"""
        self.distro.disable_component(comp)
        self.set_modified_sourceslist()

    def _find_template_from_string(self, name):
        for template in self.distro.source_template.children:
            if template.name == name:
                return template

    def disable_child_source(self, template):
        """Enable a child repo of the distribution main repository"""
        if isinstance(template, str):
            template = self._find_template_from_string(template)

        for source in self.distro.child_sources:
            if source.template == template:
                self.sourceslist.remove(source)
        for source in self.distro.source_code_sources:
            if source.template == template:
                self.sourceslist.remove(source)
        self.set_modified_sourceslist()

    def enable_child_source(self, template):
        """Enable a child repo of the distribution main repository"""
        if isinstance(template, str):
            template = self._find_template_from_string(template)

        # Use the currently selected mirror only if the child source
        # did not override the server
        if template.base_uri == None:
            child_uri = self.distro.default_server
        else:
            child_uri = template.base_uri
        self.distro.add_source(uri=child_uri, dist=template.name)
        self.set_modified_sourceslist()

    def disable_source_code_sources(self):
        """Remove all distro source code sources"""
        sources = []
        sources.extend(self.distro.main_sources)
        sources.extend(self.distro.child_sources)
        # remove all exisiting sources
        for source in self.distro.source_code_sources:
            self.sourceslist.remove(source)
        self.set_modified_sourceslist()

    def enable_source_code_sources(self):
        """Enable source code source for all distro sources"""
        sources = []
        sources.extend(self.distro.main_sources)
        sources.extend(self.distro.child_sources)

        # remove all exisiting sources
        for source in self.distro.source_code_sources:
            self.sourceslist.remove(source)

        for source in sources:
            self.sourceslist.add("deb-src", source.uri, source.dist,
                                 source.comps, "Added by software-properties",
                                 self.sourceslist.list.index(source) + 1,
                                 source.file)
        for source in self.distro.cdrom_sources:
            self.sourceslist.add("deb-src",
                                 self.distro.source_template.base_uri,
                                 self.distro.source_template.name,
                                 source.comps, "Added by software-properties",
                                 self.sourceslist.list.index(source) + 1,
                                 source.file)
        self.set_modified_sourceslist()

    def backup_sourceslist(self):
        """Store a backup of the source.list in memory"""
        self.sourceslist_backup = []
        for source in self.sourceslist.list:
            source_bkp = SourceEntry(line=source.line, file=source.file)
            self.sourceslist_backup.append(source_bkp)

    def _find_source_from_string(self, line):
        # ensure that we have a current list, it might have been changed underneath
        # us
        self.reload_sourceslist()

        for source in self.sourceslist.list:
            if str(source) == line:
                return source
        return None

    def toggle_source_use(self, source):
        """Enable or disable the selected channel"""
        #FIXME cdroms need to disable the comps in the childs and sources
        if isinstance(source, str):
            source = self._find_source_from_string(source)
        source.disabled = not source.disabled
        self.set_modified_sourceslist()

    def replace_source_entry(self, old_entry, new_entry):
        # find and replace, then write
        for (index, entry) in enumerate(self.sourceslist.list):
            if str(entry) == old_entry:
                file = self.sourceslist.list[index].file
                self.sourceslist.list[index] = SourceEntry(new_entry, file)
                self.set_modified_sourceslist()
                return True
        return False

    def revert(self):
        """Revert all settings to the state when software-properties 
       was launched"""
        #FIXME: GPG keys are still missing
        self.restore_apt_conf()
        self.revert_sourceslist()

    def revert_sourceslist(self):
        """Restore the source list from the startup of the dialog"""
        self.sourceslist.list = []
        for source in self.sourceslist_backup:
            source_reset = SourceEntry(line=source.line, file=source.file)
            self.sourceslist.list.append(source_reset)
        self.save_sourceslist()
        self.reload_sourceslist()

    def set_modified_sourceslist(self):
        """The sources list was changed and now needs to be saved and reloaded"""
        self.modified_sourceslist = True
        if self.options and self.options.massive_debug:
            self.massive_debug_output()
        self.save_sourceslist()
        self.reload_sourceslist()

    def set_modified_config(self):
        """Write the changed apt configuration to file"""
        self.write_config()

    def render_source(self, source):
        """Render a nice output to show the source in a treeview"""
        if source.template == None:
            if source.comment:
                contents = "<b>%s</b> %s" % (escape(
                    source.comment).strip(), source.dist)
                # Only show the components if there are more than one
                if len(source.comps) > 1:
                    for c in source.comps:
                        contents += " %s" % c
                if source.type in ("deb-src", "rpm-src"):
                    contents += " %s" % _("(Source Code)")
                contents += "\n%s" % source.uri
            else:
                contents = "<b>%s %s</b>" % (source.uri, source.dist)
                for c in source.comps:
                    contents += " %s" % c
                if source.type in ("deb-src", "rpm-src"):
                    contents += " %s" % _("(Source Code)")
            return contents
        else:
            # try to make use of a corresponding template
            contents = "<b>%s</b>" % source.template.description
            if source.type in ("deb-src", "rpm-src"):
                contents += " (%s)" % _("Source Code")
            if source.comment:
                contents += " %s" % source.comment
            if source.template.child == False:
                for comp in source.comps:
                    if source.template.has_component(comp):
                        # fixme: move something like this into distinfo.Template
                        #        (why not use a dictionary again?)
                        for c in source.template.components:
                            if c.name == comp:
                                contents += "\n%s" % c.description
                    else:
                        contents += "\n%s" % comp
            return contents

    def get_comparable(self, source):
        """extract attributes to sort the sources"""
        cur_sys = 1
        has_template = 1
        has_comment = 1
        is_source = 1
        revert_numbers = maketrans("0123456789", "9876543210")
        if source.template:
            has_template = 0
            desc = source.template.description
            if source.template.distribution == self.distro:
                cur_sys = 0
        else:
            desc = "%s %s %s" % (source.uri, source.dist, source.comps)
            if source.comment:
                has_comment = 0
        if source.type.find("src"):
            is_source = 0
        return (cur_sys, has_template, has_comment, is_source,
                desc.translate(revert_numbers))

    def get_isv_sources(self):
        """Return a list of sources that are not part of the distribution"""
        isv_sources = []
        for source in self.sourceslist.list:
            if not source.invalid and\
               (source not in self.distro.main_sources and\
                source not in self.distro.cdrom_sources and\
                source not in self.distro.child_sources and\
                source not in self.distro.disabled_sources) and\
               source not in self.distro.source_code_sources:
                isv_sources.append(source)
        return isv_sources

    def get_cdrom_sources(self):
        """Return the list of CDROM based distro sources"""
        return self.distro.cdrom_sources

    def get_comp_download_state(self, comp):
        """Return a tuple: the first value describes if a component is enabled
       in the Internet repositories. The second value describes if the
       first value is inconsistent."""
        #FIXME: also return a correct inconsistent value
        return (comp.name in self.distro.download_comps, False)

    def get_comp_child_state(self, template):
        """Return a tuple: the first value describes if a component is enabled
       in one of the child source that matcth the given template. 
       The second value describes if the first value is inconsistent."""
        comps = []
        for child in self.distro.child_sources:
            if child.template == template:
                comps.extend(child.comps)
        if len(comps) > 0 and \
            len(self.distro.enabled_comps ^ set(comps)) == 0:
            # All enabled distro components are also enabled for the child source
            return (True, False)
        elif len(comps) > 0 and\
            len(self.distro.enabled_comps ^ set(comps)) != 0:
            # A matching child source does exist but doesn't include all
            # enabled distro components
            return (False, True)
        else:
            # There is no corresponding child source at all
            return (False, False)

    def reload_sourceslist(self):
        self.sourceslist.refresh()
        self.sourceslist_visible = []
        self.distro.get_sources(self.sourceslist)

    def write_config(self):
        """Write the current apt configuration to file"""
        # update the adept file as well if it is there
        conffiles = [
            self.rootdir + "/etc/apt/apt.conf.d/10periodic",
            self.rootdir + "/etc/apt/apt.conf.d/20auto-upgrades",
            self.rootdir + "/etc/apt/apt.conf.d/15adept-periodic-update"
        ]

        # check (beforehand) if one exists, if not create one
        for f in conffiles:
            if os.path.isfile(f):
                break
        else:
            print("No config found, creating one")
            open(conffiles[0], "w")

        # ensure /etc/cron.daily/apt is executable
        ac = "/etc/cron.daily/apt"
        if os.path.exists(ac):
            perm = os.stat(ac)[stat.ST_MODE]
            if not (perm & stat.S_IXUSR):
                print("file '%s' not executable, fixing" % ac)
                os.chmod(ac, 0o755)

        # now update them
        for periodic in conffiles:
            # read the old content first
            content = []
            if os.path.isfile(periodic):
                content = open(periodic, "r").readlines()
                cnf = apt_pkg.config.subtree("APT::Periodic")

                # then write a new file without the updated keys
                f = open(periodic, "w")
                for line in content:
                    for key in cnf.list():
                        if line.find("APT::Periodic::%s" % (key)) >= 0:
                            break
                    else:
                        f.write(line)

                # and append the updated keys
                for i in cnf.list():
                    f.write("APT::Periodic::%s \"%s\";\n" % (i, cnf.find_i(i)))
                f.close()

    def save_sourceslist(self):
        """Backup the existing sources.list files and write the current 
       configuration"""
        self.sourceslist.backup(".save")
        self.sourceslist.save()

    def _is_line_in_whitelisted_channel(self, srcline):
        """
    helper that checks if a given line is in the source list
    return the channel name or None if not found
    """
        srcentry = SourceEntry(srcline)
        if os.path.exists(self.CHANNEL_PATH):
            for f in glob.glob("%s/*.list" % self.CHANNEL_PATH):
                for line in open(f):
                    if line.strip().startswith("#"):
                        continue
                    if srcentry == SourceEntry(line):
                        return os.path.splitext(os.path.basename(f))[0]
        return None

    def check_and_add_key_for_whitelisted_channels(self, srcline):
        # This is maintained for any legacy callers
        return self.check_and_add_key_for_whitelisted_shortcut(
            shortcut_handler(srcline))

    def check_and_add_key_for_whitelisted_shortcut(self, shortcut):
        """
    helper that adds the gpg key of the channel to the apt
    keyring *if* the channel is in the whitelist
    /usr/share/app-install/channels or it is a public Launchpad PPA.
    """
        (srcline, _fname) = shortcut.expand(codename=self.distro.codename,
                                            distro=self.distro.id.lower())
        channel = self._is_line_in_whitelisted_channel(srcline)
        if channel:
            keyp = "%s/%s.key" % (self.CHANNEL_PATH, channel)
            self.add_key(keyp)

        cdata = (shortcut.add_key, {'keyserver': (self.options)})

        def addkey_func():
            func, kwargs = cdata
            msg = "Added key."
            try:
                ret = func(**kwargs)
                if not ret:
                    msg = "Failed to add key."
            except Exception as e:
                ret = False
                msg = str(e)
            self.myqueue.put([ret, msg])

        worker = threading.Thread(target=addkey_func)
        worker.start()
        return worker

    def update_interface(self):
        " abstract interface to keep the UI alive "

    def expand_http_line(self, line):
        """
    short cut - this:
      apt-add-repository http://packages.medibuntu.org free non-free
    same as
      apt-add-repository 'deb http://packages.medibuntu.org/ '$(lsb_release -cs)' free non-free'
    """
        if not line.startswith("http"):
            return line
        repo = line.split()[0]
        try:
            areas = line.split(" ", 1)[1]
        except IndexError:
            areas = "main"
        line = "deb %s %s %s" % (repo, self.distro.codename, areas)
        return line

    def add_source_from_line(self, line, enable_source_code=False):
        """
    Add a source for the given line.
    """
        return self.add_source_from_shortcut(
            shortcut=shortcut_handler(line.strip()),
            enable_source_code=enable_source_code)

    def add_source_from_shortcut(self, shortcut, enable_source_code=False):
        """
    Add a source with the given shortcut and add the signing key if the
    site is in whitelist or the shortcut implementer adds it.
    """

        (deb_line, file) = shortcut.expand(codename=self.distro.codename,
                                           distro=self.distro.id.lower())
        deb_line = self.expand_http_line(deb_line)
        debsrc_entry_type = 'deb-src' if enable_source_code else '# deb-src'
        debsrc_line = debsrc_entry_type + deb_line[3:]
        new_deb_entry = SourceEntry(deb_line, file)
        new_debsrc_entry = SourceEntry(debsrc_line, file)
        if new_deb_entry.invalid or new_debsrc_entry.invalid:
            return False
        worker = self.check_and_add_key_for_whitelisted_shortcut(shortcut)
        self.sourceslist.add(new_deb_entry.type,
                             new_deb_entry.uri,
                             new_deb_entry.dist,
                             new_deb_entry.comps,
                             comment=new_deb_entry.comment,
                             file=new_deb_entry.file,
                             architectures=new_deb_entry.architectures)
        self.sourceslist.add(debsrc_entry_type,
                             new_debsrc_entry.uri,
                             new_debsrc_entry.dist,
                             new_debsrc_entry.comps,
                             comment=new_debsrc_entry.comment,
                             file=new_debsrc_entry.file,
                             architectures=new_debsrc_entry.architectures)
        self.set_modified_sourceslist()
        if worker:
            # wait for GPG key to be downloaded
            worker.join(30)
            if worker.isAlive():
                # thread timed out.
                raise shortcuts.ShortcutException(
                    "Error: retrieving gpg key timed out.")
            result, msg = self.myqueue.get()
            if not result:
                raise shortcuts.ShortcutException(msg)

        if self.options and self.options.update:
            import apt
            cache = apt.Cache()
            cache.update(sources_list=new_debsrc_entry.file)
        return True

    def remove_source(self, source, remove_source_code=True):
        """Remove the given source"""
        if remove_source_code:
            if isinstance(source, str):
                # recall this method giving a SourceEntry as an argument
                source = self._find_source_from_string(source)
                self.remove_source(source, True)
            elif source is not None:
                self.remove_source(source, False)
                # remove the deb-src lines (enabled or not) associated to
                # this source entry
                source = copy.copy(source)
                source.type = 'deb-src'
                source.disabled = True
                self.remove_source(source, False)
                source.disabled = False
                self.remove_source(source, False)
            return

        # first find the source object if we got a string
        if isinstance(source, str):
            source = self._find_source_from_string(source)
        if source is None:
            return
        # if its a sources.list.d file and it contains only a single line
        # (the line that we just remove), then all references to that
        # file are gone and the file is not saved. we work around that
        # here
        if source.file != apt_pkg.config.find_file("Dir::Etc::sourcelist"):
            self.sourceslist.list.append(SourceEntry("", file=source.file))
        try:
            self.sourceslist.remove(source)
        except ValueError:
            # this exception is raised if trying to remove an entry that does
            # not exist. in this case we suppress the error because there's no
            # need to propagate it (the aim of this method is to ensure that
            # the given entries are not listed in the sourceslist)
            pass
        self.set_modified_sourceslist()

    def add_key(self, path):
        """Add a gnupg key to the list of trusted software vendors"""
        if not os.path.exists(path):
            return False
        try:
            res = self.apt_key.add(path)
            return res
        except:
            return False

    def add_key_from_data(self, keydata):
        "Add a gnupg key from a utf-8 data string (e.g. copy-n-paste)"
        tmp = tempfile.NamedTemporaryFile()
        tmp.write(keydata.encode("utf-8"))
        tmp.flush()
        return self.add_key(tmp.name)

    def remove_key(self, keyid):
        """Remove a gnupg key from the list of trusted software vendors"""
        try:
            self.apt_key.rm(keyid)
            return True
        except:
            return False

    def update_keys(self):
        """ Run apt-key update """
        try:
            self.apt_key.update()
            return True
        except:
            return False

    def get_package_id(self, ver):
        """ Return the PackageKit package id """
        assert isinstance(ver, apt.package.Version)
        return "%s;%s;%s;" % (ver.package.shortname, ver.version,
                              ver.package.architecture())
Ejemplo n.º 26
0
 def reset_channels(self):
     """Remove all the configured channels."""
     sources_list = SourcesList()
     for entry in sources_list:
         entry.set_enabled(False)
     sources_list.save()
Ejemplo n.º 27
0
def main ():
    comps = []
    sourceslist = SourcesList ()
    distro = aptsources.distro.get_distro ()
    security_uri = ""
    security_dist = ""

    if distro.id == "Debian":
        comps = ["main", "contrib", "non-free"]
        security_uri = "http://security.debian.org/"
        security_dist = "%s/updates" % distro.codename
    elif distro.id == "Ubuntu":
        comps = ["main", "universe", "restricted", "multiverse"]
        security_uri = "http://security.ubuntu.com/ubuntu/"
        security_dist = "%s-security" % distro.codename
    else:
        print "can't identify distribution"
        return -1

    try:
        distro.get_sources (sourceslist)
    except:
        print "your distribution is remix release!"
        return -1

    has_medibuntu_source = 0
    has_ubuntu_tweak_source = 0
    has_swiftfox_source = 0
    has_winehq_source = 0
    for source in sourceslist:
     if source.disabled == False:
      if source.uri == "http://packages.medibuntu.org/":
       has_medibuntu_source = 1
      if source.uri == "http://ppa.launchpad.net/tualatrix/ubuntu":
       has_ubuntu_tweak_source = 1
      if source.uri == "http://getswiftfox.com/builds/debian":
       has_swiftfox_source = 1
      if source.uri == "http://wine.budgetdedicated.com/apt":
       has_winehq_source = 1

    [entry.set_enabled (False) for entry in sourceslist if entry.invalid == False]

    if distro.country_code == "tw" and distro.id == "Ubuntu":
        twaren_uri = "http://ftp.twaren.net/ubuntu"
        distro.change_server (uri=twaren_uri)
        distro.add_source (uri=twaren_uri, comps=comps, comment="國網中心伺服器 (Lazybuntu 新增)")
        distro.add_source (type="deb-src", uri=twaren_uri, comps=comps, comment="國網中心伺服器 (Lazybuntu 新增)")
    else:
        distro.add_source (uri=distro.nearest_server, comps=comps, comment="地區性伺服器 (Lazybuntu 新增)")
        distro.add_source (type="deb-src", uri=distro.nearest_server, comps=comps, comment="地區性伺服器 (Lazybuntu 新增)")

    distro.add_source (uri=security_uri, dist=security_dist, comps=comps, comment="安全性更新伺服器 (Lazybuntu 新增)")
    distro.add_source (type="deb-src", uri=security_uri, dist=security_dist, comps=comps, comment="安全性更新伺服器 (Lazybuntu 新增)")

    if has_medibuntu_source:
       medibuntu_uri = "http://packages.medibuntu.org/"
       medibuntu_comps = ["free non-free"]
       distro.add_source (uri=medibuntu_uri, comps=medibuntu_comps, comment="Medibuntu Install Source (Lazybuntu 新增)")
    if has_ubuntu_tweak_source:
       ubuntu_tweak_uri = "http://ppa.launchpad.net/tualatrix/ubuntu"
       ubuntu_tweak_comps = ["main"]
       if distro.codename == 'intrepid':
          ubuntu_tweak_dist = distro.codename
       else:
          ubuntu_tweak_dist = 'hardy'
       distro.add_source (uri=ubuntu_tweak_uri, dist=ubuntu_tweak_dist, comps=ubuntu_tweak_comps, comment="Ubuntu Tweak Install Source (Lazybuntu 新增)")
       distro.add_source (type="deb-src", uri=ubuntu_tweak_uri, dist=ubuntu_tweak_dist, comps=ubuntu_tweak_comps, comment="Ubuntu Tweak Install Source (Lazybuntu 新增)")
    if has_swiftfox_source:
       swiftfox_uri = "http://getswiftfox.com/builds/debian"
       swiftfox_comps = ["non-free"]
       swiftfox_dist = "unstable"
       distro.add_source (uri=swiftfox_uri, dist=swiftfox_dist, comps=swiftfox_comps, comment="Swiftfox Install Source (Lazybuntu 新增)")
    if has_winehq_source:
       winehq_uri = "http://wine.budgetdedicated.com/apt"
       winehq_comps = ["main"]
       distro.add_source (uri=winehq_uri, comps=winehq_comps, comment="WineHQ Install Source (Lazybuntu 新增)")


    sourceslist.backup ()
    sourceslist.save ()

    return 0
class SoftwareProperties(object):

  # known (whitelisted) channels
  CHANNEL_PATH="/usr/share/app-install/channels/"

  # release upgrades policy
  RELEASE_UPGRADES_CONF = "/etc/update-manager/release-upgrades"
  #RELEASE_UPGRADES_CONF = "/tmp/release-upgrades"
  (
    RELEASE_UPGRADES_NORMAL,
    RELEASE_UPGRADES_LTS,
    RELEASE_UPGRADES_NEVER
  ) = list(range(3))
  release_upgrades_policy_map = {
    RELEASE_UPGRADES_NORMAL : 'normal',
    RELEASE_UPGRADES_LTS    : 'lts',
    RELEASE_UPGRADES_NEVER  : 'never',
  }
  
  def __init__(self, datadir=None, options=None, rootdir="/"):
    """ Provides the core functionality to configure the used software 
        repositories, the corresponding authentication keys and 
        update automation """
    self.popconfile = rootdir+"/etc/popularity-contest.conf"

    self.rootdir = rootdir
    if rootdir != "/":
      apt_pkg.config.set("Dir", rootdir)

    # FIXME: some saner way is needed here
    if datadir == None:
      datadir = "/usr/share/software-properties/"
    self.options = options
    self.datadir = datadir

    self.sourceslist = SourcesList()
    self.distro = aptsources.distro.get_distro()
    
    self.seen_server = []
    self.modified_sourceslist = False

    self.reload_sourceslist()
    self.backup_sourceslist()

    self.backup_apt_conf()

    # FIXME: we need to store this value in a config option
    #self.custom_mirrors = ["http://adasdwww.de/ubuntu"]
    self.custom_mirrors= []

    # apt-key stuff
    self.apt_key = AptAuth(rootdir=rootdir)

    atexit.register(self.wait_for_threads)

  def wait_for_threads(self):
    " wait for all running threads (PPA key fetchers) to exit "
    for t in threading.enumerate():
      if t.ident != threading.current_thread().ident:
        t.join()

  def backup_apt_conf(self):
    """Backup all apt configuration options"""
    self.apt_conf_backup = {}
    for option in softwareproperties.CONF_MAP.keys():
        value = apt_pkg.config.find_i(softwareproperties.CONF_MAP[option])
        self.apt_conf_backup[option] = value

  def restore_apt_conf(self):
    """Restore the stored apt configuration"""
    for option in self.apt_conf_backup.keys():
        apt_pkg.config.set(softwareproperties.CONF_MAP[option],
                           str(self.apt_conf_backup[option]))
    self.write_config()

  def get_update_automation_level(self):
    """ Parse the apt cron configuration. Try to fit a predefined use case 
        and return it. Special case: if the user made a custom 
        configurtation, that we cannot represent it will return None """
    if apt_pkg.config.find_i(softwareproperties.CONF_MAP["autoupdate"]) > 0:
        # Autodownload
        if apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 1\
           and apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 1 :
            return softwareproperties.UPDATE_INST_SEC
        elif apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 1 and  \
             apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 0:
            return softwareproperties.UPDATE_DOWNLOAD
        elif apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 0 and \
             apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 0:
            return softwareproperties.UPDATE_NOTIFY
        else:
            return None
    elif apt_pkg.config.find_i(softwareproperties.CONF_MAP["unattended"]) == 0 and \
         apt_pkg.config.find_i(softwareproperties.CONF_MAP["autodownload"]) == 0:
        return softwareproperties.UPDATE_MANUAL
    else:
        return None

  def set_update_automation_level(self, state):
    """ Set the apt periodic configurtation to the selected 
        update automation level. To synchronize the cache update and the 
        actual upgrading function, the upgrade function, e.g. unattended, 
        will run every day, if enabled. """
    if state == softwareproperties.UPDATE_INST_SEC:
        apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"], str(1))
        apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"], str(1))
    elif state == softwareproperties.UPDATE_DOWNLOAD:
        apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"], str(1))
        apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"], str(0))
    elif state == softwareproperties.UPDATE_NOTIFY:
        apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"], str(0))
        apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"], str(0))
    else:
        apt_pkg.config.set(softwareproperties.CONF_MAP["autoupdate"], str(0))
        apt_pkg.config.set(softwareproperties.CONF_MAP["unattended"], str(0))
        apt_pkg.config.set(softwareproperties.CONF_MAP["autodownload"], str(0))
    self.set_modified_config()

  def set_update_interval(self, days):
      """Set the interval in which we check for available updates"""
      # Only write the key if it has changed
      if not days == apt_pkg.config.find_i(softwareproperties.CONF_MAP["autoupdate"]):
          apt_pkg.config.set(softwareproperties.CONF_MAP["autoupdate"], str(days))
          self.set_modified_config()

  def get_update_interval(self):
    """ Returns the interval of the apt periodic cron job """
    return apt_pkg.config.find_i(softwareproperties.CONF_MAP["autoupdate"])

  def get_release_upgrades_policy(self):
    """
    return the release upgrade policy:
     RELEASE_UPGRADES_NORMAL,
     RELEASE_UPGRADES_LTS,
     RELEASE_UPGRADES_NEVER
    """
    # default (if no option is set) is NORMAL
    if not os.path.exists(self.RELEASE_UPGRADES_CONF):
      return self.RELEASE_UPGRADES_NORMAL
    parser = ConfigParser()
    parser.read(self.RELEASE_UPGRADES_CONF)
    if parser.has_option("DEFAULT","Prompt"):
      type = parser.get("DEFAULT","Prompt").lower()
      for k, v in self.release_upgrades_policy_map.items():
        if v == type:
          return k
    return self.RELEASE_UPGRADES_NORMAL

  def set_release_upgrades_policy(self, i):
    """
    set the release upgrade policy:
     RELEASE_UPGRADES_NORMAL,
     RELEASE_UPGRADES_LTS,
     RELEASE_UPGRADES_NEVER
     """
    # we are note using ConfigParser.write() as it removes comments
    if not os.path.exists(self.RELEASE_UPGRADES_CONF):
      f = open(self.RELEASE_UPGRADES_CONF,"w")
      f.write("[DEFAULT]\nPrompt=%s\n"% self.release_upgrades_policy_map[i])
      return True
    f = open(self.RELEASE_UPGRADES_CONF,"r")
    out = NamedTemporaryFile(mode="w+")
    for line in f:
      line = line.strip()
      if line.lower().startswith("prompt"):
        out.write("Prompt=%s\n" % self.release_upgrades_policy_map[i])
      else:
        out.write(line+"\n")
    out.flush()
    shutil.copymode(self.RELEASE_UPGRADES_CONF, out.name)
    shutil.copy(out.name, self.RELEASE_UPGRADES_CONF)
    return True

  def get_popcon_participation(self):
    """ Will return True if the user wants to participate in the popularity 
        contest. Otherwise it will return False. Special case: if no 
        popcon is installed it will return False """
    if os.path.exists(self.popconfile):
        lines = open(self.popconfile).read().split("\n")
        active = False
        for line in lines:
            try:
                (key,value) = line.split("=")
                if key == "PARTICIPATE" and value.strip('"').lower() == "yes":
                    active = True
            except ValueError:
                continue
        return active
    else:
        return False

  def set_popcon_pariticipation(self, is_helpful):
    """ Enable or disable the participation in the popularity contest """
    if is_helpful == True:
        value = "yes"
    else:
        value = "no"
    if os.path.exists(self.popconfile):
        # read the current config and replace the corresponding settings
        # FIXME: should we check the other values, too?
        with open(self.popconfile, "r") as popconfile:
            lines = [re.sub(r'^(PARTICIPATE=)(".+?")', '\\1"%s"' % value, line)
                     for line in popconfile]
    else:
        # create a new popcon config file
        m = md5()
        m.update(open("/dev/urandom", "rb").read(1024))
        id = m.hexdigest()
        lines = []
        lines.append("MY_HOSTID=\"%s\"\n" % id)
        lines.append("PARTICIPATE=\"%s\"\n" % str(value))
        lines.append("USE_HTTP=\"yes\"\n")
    open(self.popconfile, "w").writelines(lines)

  def get_source_code_state(self):
    """Return True if all distro componets are also available as 
       source code. Otherwise return Flase. Special case: If the
       configuration cannot be represented return None"""
  
    if len(self.distro.source_code_sources) < 1:
        # we don't have any source code sources, so
        # uncheck the button
        self.distro.get_source_code = False
        return False
    else:
        # there are source code sources, so we check the button
        self.distro.get_source_code = True
        # check if there is a corresponding source code source for
        # every binary source. if not set the checkbutton to inconsistent
        templates = {}
        sources = []
        sources.extend(self.distro.main_sources)
        sources.extend(self.distro.child_sources)
        for source in sources:
            if source.template in templates:
                for comp in source.comps:
                    templates[source.template].add(comp)
            else:
                templates[source.template] = set(source.comps)
        # add fake http sources for the cdrom, since the sources
        # for the cdrom are only available in the internet
        if len(self.distro.cdrom_sources) > 0:
            templates[self.distro.source_template] = self.distro.cdrom_comps
        for source in self.distro.source_code_sources:
            if source.template not in templates or \
               (source.template in templates and not \
                (len(set(templates[source.template]) ^ set(source.comps)) == 0\
                 or (len(set(source.comps) ^ self.distro.enabled_comps) == 0))):
                self.distro.get_source_code = False
                return None
                break
    return True

  def print_source_entry(self, source):
    """Print the data of a source entry to the command line"""
    for (label, value) in [("URI:", source.uri),
                           ("Comps:", source.comps),
                           ("Enabled:", not source.disabled),
                           ("Valid:", not source.invalid)]:
        print(" %s %s" % (label, value))
    if source.template:
        for (label, value) in [("MatchURI:", source.template.match_uri),
                               ("BaseURI:", source.template.base_uri)]:
            print(" %s %s" % (label, value))
    print("\n")

  def massive_debug_output(self):
    """Print the complete sources.list""" 
    print("START SOURCES.LIST:")
    for source in self.sourceslist:
        print(source.str())
    print("END SOURCES.LIST\n")

  def change_main_download_server(self, server):
    """ change the main download server """
    self.distro.default_server = server
    res = self.distro.change_server(server)
    self.set_modified_sourceslist()
    return res

  def enable_component(self, comp):
    """Enable a component of the distro"""
    self.distro.enable_component(comp) 
    self.set_modified_sourceslist()

  def disable_component(self, comp):
    """Disable a component of the distro"""
    self.distro.disable_component(comp) 
    self.set_modified_sourceslist()

  def _find_template_from_string(self, name):
    for template in self.distro.source_template.children:
      if template.name == name:
        return template

  def disable_child_source(self, template):
    """Enable a child repo of the distribution main repository"""
    if isinstance(template, str):
      template = self._find_template_from_string(template)

    for source in self.distro.child_sources:
        if source.template == template:
            self.sourceslist.remove(source)
    for source in self.distro.source_code_sources:
        if source.template == template:
            self.sourceslist.remove(source)
    self.set_modified_sourceslist()

  def enable_child_source(self, template):
    """Enable a child repo of the distribution main repository"""
    if isinstance(template, str):
      template = self._find_template_from_string(template)

    # Use the currently selected mirror only if the child source
    # did not override the server
    if template.base_uri == None:
        child_uri = self.distro.default_server
    else:
        child_uri = template.base_uri
    self.distro.add_source(uri=child_uri, dist=template.name)
    self.set_modified_sourceslist()

  def disable_source_code_sources(self):
    """Remove all distro source code sources"""
    sources = []
    sources.extend(self.distro.main_sources)
    sources.extend(self.distro.child_sources)
    # remove all exisiting sources
    for source in self.distro.source_code_sources:
        self.sourceslist.remove(source)
    self.set_modified_sourceslist()
  
  def enable_source_code_sources(self):
    """Enable source code source for all distro sources"""
    sources = []
    sources.extend(self.distro.main_sources)
    sources.extend(self.distro.child_sources)

    # remove all exisiting sources
    for source in self.distro.source_code_sources:
        self.sourceslist.remove(source)

    for source in sources:
        self.sourceslist.add("deb-src",
                             source.uri,
                             source.dist,
                             source.comps,
                             "Added by software-properties",
                             self.sourceslist.list.index(source)+1,
                             source.file)
    for source in self.distro.cdrom_sources:
        self.sourceslist.add("deb-src",
                             self.distro.source_template.base_uri,
                             self.distro.source_template.name,
                             source.comps,
                             "Added by software-properties",
                             self.sourceslist.list.index(source)+1,
                             source.file)
    self.set_modified_sourceslist()

  def backup_sourceslist(self):
    """Store a backup of the source.list in memory"""
    self.sourceslist_backup = []
    for source in self.sourceslist.list:
        source_bkp = SourceEntry(line=source.line,file=source.file)
        self.sourceslist_backup.append(source_bkp)
  
  def _find_source_from_string(self, line):
    # ensure that we have a current list, it might have been changed underneath
    # us
    self.reload_sourceslist()

    for source in self.sourceslist.list:
      if str(source) == line:
        return source
    return None

  def toggle_source_use(self, source):
    """Enable or disable the selected channel"""
    #FIXME cdroms need to disable the comps in the childs and sources
    if isinstance(source, str):
      source = self._find_source_from_string(source)
    source.disabled = not source.disabled
    self.set_modified_sourceslist()

  def replace_source_entry(self, old_entry, new_entry):
    # find and replace, then write
    for (index, entry) in enumerate(self.sourceslist.list):
      if str(entry) == old_entry:
        file = self.sourceslist.list[index].file
        self.sourceslist.list[index] = SourceEntry(new_entry, file)
        self.set_modified_sourceslist()
        return True
    return False

  def revert(self):
    """Revert all settings to the state when software-properties 
       was launched"""
    #FIXME: GPG keys are still missing
    self.restore_apt_conf()
    self.revert_sourceslist()

  def revert_sourceslist(self):
    """Restore the source list from the startup of the dialog"""
    self.sourceslist.list = []
    for source in self.sourceslist_backup:
        source_reset = SourceEntry(line=source.line,file=source.file)
        self.sourceslist.list.append(source_reset)
    self.save_sourceslist()
    self.reload_sourceslist()

  def set_modified_sourceslist(self):
    """The sources list was changed and now needs to be saved and reloaded"""
    self.modified_sourceslist = True
    if self.options and self.options.massive_debug:
        self.massive_debug_output()
    self.save_sourceslist()
    self.reload_sourceslist()

  def set_modified_config(self):
    """Write the changed apt configuration to file"""
    self.write_config()

  def render_source(self, source):
    """Render a nice output to show the source in a treeview"""
    if source.template == None:
        if source.comment:
            contents = "<b>%s</b> %s" % (escape(source.comment).strip(),
                                         source.dist)
            # Only show the components if there are more than one
            if len(source.comps) > 1:
                for c in source.comps:
                    contents += " %s" % c
            if source.type in ("deb-src", "rpm-src"):
                contents += " %s" % _("(Source Code)")
            contents += "\n%s" % source.uri
        else:
            contents = "<b>%s %s</b>" % (source.uri, source.dist)
            for c in source.comps:
                contents += " %s" % c
            if source.type in ("deb-src", "rpm-src"):
                contents += " %s" % _("(Source Code)")
        return contents
    else:
        # try to make use of a corresponding template
        contents = "<b>%s</b>" % source.template.description
        if source.type in ("deb-src", "rpm-src"):
            contents += " (%s)" % _("Source Code")
        if source.comment:
            contents +=" %s" % source.comment
        if source.template.child == False:
            for comp in source.comps:
                if source.template.has_component(comp):
                    # fixme: move something like this into distinfo.Template
                    #        (why not use a dictionary again?)
                    for c in source.template.components:
                        if c.name == comp:
                            contents += "\n%s" % c.description
                else:
                    contents += "\n%s" % comp
        return contents

  def get_comparable(self, source):
      """extract attributes to sort the sources"""
      cur_sys = 1
      has_template = 1
      has_comment = 1
      is_source = 1
      revert_numbers = maketrans("0123456789", "9876543210")
      if source.template:
        has_template = 0
        desc = source.template.description
        if source.template.distribution == self.distro:
            cur_sys = 0
      else:
          desc = "%s %s %s" % (source.uri, source.dist, source.comps)
          if source.comment:
              has_comment = 0
      if source.type.find("src"):
          is_source = 0
      return (cur_sys, has_template, has_comment, is_source,
              desc.translate(revert_numbers))

  def get_isv_sources(self):
    """Return a list of sources that are not part of the distribution"""
    isv_sources = []
    for source in self.sourceslist.list:
        if not source.invalid and\
           (source not in self.distro.main_sources and\
            source not in self.distro.cdrom_sources and\
            source not in self.distro.child_sources and\
            source not in self.distro.disabled_sources) and\
           source not in self.distro.source_code_sources:
            isv_sources.append(source)
    return isv_sources

  def get_cdrom_sources(self):
    """Return the list of CDROM based distro sources"""
    return self.distro.cdrom_sources
      
  def get_comp_download_state(self, comp):
    """Return a tuple: the first value describes if a component is enabled
       in the Internet repositories. The second value describes if the
       first value is inconsistent."""
    #FIXME: also return a correct inconsistent value
    return (comp.name in self.distro.download_comps, False)

  def get_comp_child_state(self, template):
    """Return a tuple: the first value describes if a component is enabled
       in one of the child source that matcth the given template. 
       The second value describes if the first value is inconsistent."""
    comps = []
    for child in self.distro.child_sources:
        if child.template == template:
            comps.extend(child.comps)
    if len(comps) > 0 and \
        len(self.distro.enabled_comps ^ set(comps)) == 0:
        # All enabled distro components are also enabled for the child source
        return (True, False)
    elif len(comps) > 0 and\
        len(self.distro.enabled_comps ^ set(comps)) != 0:
        # A matching child source does exist but doesn't include all 
        # enabled distro components
        return(False, True)
    else:
        # There is no corresponding child source at all
        return (False, False)
  
  def reload_sourceslist(self):
    self.sourceslist.refresh()
    self.sourceslist_visible=[]
    self.distro.get_sources(self.sourceslist)    

  def write_config(self):
    """Write the current apt configuration to file"""
    # update the adept file as well if it is there
    conffiles = [self.rootdir+"/etc/apt/apt.conf.d/10periodic",
                 self.rootdir+"/etc/apt/apt.conf.d/20auto-upgrades",
                 self.rootdir+"/etc/apt/apt.conf.d/15adept-periodic-update"]

    # check (beforehand) if one exists, if not create one
    for f in conffiles:
      if os.path.isfile(f):
        break
    else:
      print("No config found, creating one")
      open(conffiles[0], "w")

    # ensure /etc/cron.daily/apt is executable
    ac = "/etc/cron.daily/apt"
    if os.path.exists(ac):
      perm = os.stat(ac)[stat.ST_MODE]
      if not (perm & stat.S_IXUSR):
        print("file '%s' not executable, fixing" % ac)
        os.chmod(ac, 0o755)

    # now update them
    for periodic in conffiles:
      # read the old content first
      content = []
      if os.path.isfile(periodic):
        content = open(periodic, "r").readlines()
        cnf = apt_pkg.config.subtree("APT::Periodic")

        # then write a new file without the updated keys
        f = open(periodic, "w")
        for line in content:
          for key in cnf.list():
            if line.find("APT::Periodic::%s" % (key)) >= 0:
              break
          else:
            f.write(line)

        # and append the updated keys
        for i in cnf.list():
          f.write("APT::Periodic::%s \"%s\";\n" % (i, cnf.find_i(i)))
        f.close()    

  def save_sourceslist(self):
    """Backup the existing sources.list files and write the current 
       configuration"""
    self.sourceslist.backup(".save")
    self.sourceslist.save()

  def _is_line_in_whitelisted_channel(self, srcline):
    """
    helper that checks if a given line is in the source list
    return the channel name or None if not found
    """
    srcentry = SourceEntry(srcline)
    if os.path.exists(self.CHANNEL_PATH):
      for f in glob.glob("%s/*.list" % self.CHANNEL_PATH):
        for line in open(f):
          if line.strip().startswith("#"):
            continue
          if srcentry == SourceEntry(line):
            return os.path.splitext(os.path.basename(f))[0]
    return None

  def check_and_add_key_for_whitelisted_channels(self, srcline):
    # This is maintained for any legacy callers
    return self.check_and_add_key_for_whitelisted_shortcut(shortcut_handler(srcline))

  def check_and_add_key_for_whitelisted_shortcut(self, shortcut):
    """
    helper that adds the gpg key of the channel to the apt
    keyring *if* the channel is in the whitelist
    /usr/share/app-install/channels or it is a public Launchpad PPA.
    """
    (srcline, _fname) = shortcut.expand(codename=self.distro.codename)
    channel = self._is_line_in_whitelisted_channel(srcline)
    if channel:
      keyp = "%s/%s.key" % (self.CHANNEL_PATH, channel)
      self.add_key(keyp)

    cdata = (shortcut.add_key, {'keyserver': (self.options and
                                              self.options.keyserver)})
    def addkey_func():
        func, kwargs = cdata
        func(**kwargs)

    worker = threading.Thread(target=addkey_func)
    worker.start()
    return worker

  def update_interface(self):
    " abstract interface to keep the UI alive "

  def expand_http_line(self, line):
    """
    short cut - this:
      apt-add-repository http://packages.medibuntu.org free non-free
    same as
      apt-add-repository 'deb http://packages.medibuntu.org/ '$(lsb_release -cs)' free non-free'
    """
    if not line.startswith("http"):
      return line
    repo = line.split()[0]
    try:
        areas = line.split(" ",1)[1]
    except IndexError:
        areas = "main"
    line = "deb %s %s %s" % ( repo, self.distro.codename, areas )
    return line

  def add_source_from_line(self, line, enable_source_code=False):
    """
    Add a source for the given line.
    """
    return self.add_source_from_shortcut(
        shortcut=shortcut_handler(line.strip()),
        enable_source_code=enable_source_code)

  def add_source_from_shortcut(self, shortcut, enable_source_code=False):
    """
    Add a source with the given shortcut and add the signing key if the
    site is in whitelist or the shortcut implementer adds it.
    """

    (deb_line, file) = shortcut.expand(codename=self.distro.codename)
    deb_line = self.expand_http_line(deb_line)
    debsrc_entry_type = 'deb-src' if enable_source_code else '# deb-src'
    debsrc_line = debsrc_entry_type + deb_line[3:]
    new_deb_entry = SourceEntry(deb_line, file)
    new_debsrc_entry = SourceEntry(debsrc_line, file)
    if new_deb_entry.invalid or new_debsrc_entry.invalid:
      return False
    worker = self.check_and_add_key_for_whitelisted_shortcut(shortcut)
    self.sourceslist.add(new_deb_entry.type,
                         new_deb_entry.uri,
                         new_deb_entry.dist,
                         new_deb_entry.comps,
                         comment=new_deb_entry.comment,
                         file=new_deb_entry.file,
                         architectures=new_deb_entry.architectures)
    self.sourceslist.add(debsrc_entry_type,
                         new_debsrc_entry.uri,
                         new_debsrc_entry.dist,
                         new_debsrc_entry.comps,
                         comment=new_debsrc_entry.comment,
                         file=new_debsrc_entry.file,
                         architectures=new_debsrc_entry.architectures)
    self.set_modified_sourceslist()
    if worker:
        # wait for GPG key to be downloaded
        worker.join(30)
    return True

  def remove_source(self, source, remove_source_code=True):
    """Remove the given source"""
    if remove_source_code:
      if isinstance(source, str):
        # recall this method giving a SourceEntry as an argument
        source = self._find_source_from_string(source)
        self.remove_source(source, True)
      elif source is not None:
        self.remove_source(source, False)
        # remove the deb-src lines (enabled or not) associated to
        # this source entry
        source = copy.copy(source)
        source.type = 'deb-src'
        source.disabled = True
        self.remove_source(source, False)
        source.disabled = False
        self.remove_source(source, False)
      return

    # first find the source object if we got a string
    if isinstance(source, str):
      source = self._find_source_from_string(source)
    if source is None:
      return
    # if its a sources.list.d file and it contains only a single line
    # (the line that we just remove), then all references to that
    # file are gone and the file is not saved. we work around that
    # here
    if source.file != apt_pkg.config.find_file("Dir::Etc::sourcelist"):
      self.sourceslist.list.append(SourceEntry("", file=source.file))
    try:
      self.sourceslist.remove(source)
    except ValueError:
      # this exception is raised if trying to remove an entry that does
      # not exist. in this case we suppress the error because there's no
      # need to propagate it (the aim of this method is to ensure that
      # the given entries are not listed in the sourceslist)
      pass
    self.set_modified_sourceslist()

  def add_key(self, path):
    """Add a gnupg key to the list of trusted software vendors"""
    if not isinstance(path, str):
      path = str(path) # allows non-ascii filenames
    if not os.path.exists(path):
        return False
    try:
        res = self.apt_key.add(path)
        self.KeysModified()
        return res
    except:
        return False

  def add_key_from_data(self, keydata):
    "Add a gnupg key from a data string (e.g. copy-n-paste)"
    tmp = tempfile.NamedTemporaryFile()
    tmp.write(keydata.encode())
    tmp.flush()
    return self.add_key(tmp.name)

  def remove_key(self, keyid):
    """Remove a gnupg key from the list of trusted software vendors"""
    try:
        self.apt_key.rm(keyid)
        self.KeysModified()
        return True
    except:
        return False

  def update_keys(self):
    """ Run apt-key update """
    try:
      self.apt_key.update()
      self.KeysModified()
      return True
    except:
      return False
Ejemplo n.º 29
0
#! /usr/bin/python

import aptsources.distro
from aptsources.sourceslist import SourcesList, SourceEntry

list=SourcesList()
distro=aptsources.distro.get_distro()
distro.get_sources(list)
distro.enable_component("universe")
distro.enable_component("multiverse")
list.save()
class SoftwareProperties:
    def __init__(self, datadir=None, options=None):
        """ Provides the core functionality to configure the used software 
        repositories, the corresponding authentication keys and 
        update automation """
        self.popconfile = "/etc/popularity-contest.conf"

        # FIXME: some saner way is needed here
        if datadir == None:
            datadir = "/usr/share/software-properties/"
        self.datadir = datadir

        self.sourceslist = SourcesList()
        self.distro = aptsources.distro.get_distro()

        self.seen_server = []
        self.modified_sourceslist = False

        self.reload_sourceslist()
        self.backup_sourceslist()

        self.backup_apt_conf()

        # FIXME: we need to store this value in a config option
        #self.custom_mirrors = ["http://adasdwww.de/ubuntu"]
        self.custom_mirrors = []

        # apt-key stuff
        self.apt_key = AptAuth()

    def backup_apt_conf(self):
        """Backup all apt configuration options"""
        self.apt_conf_backup = {}
        for option in softwareproperties.CONF_MAP.keys():
            value = apt_pkg.Config.FindI(softwareproperties.CONF_MAP[option])
            self.apt_conf_backup[option] = value

    def restore_apt_conf(self):
        """Restore the stored apt configuration"""
        for option in self.apt_conf_backup.keys():
            apt_pkg.Config.Set(softwareproperties.CONF_MAP[option],
                               str(self.apt_conf_backup[option]))
        self.write_config()

    def get_update_automation_level(self):
        """ Parse the apt cron configuration. Try to fit a predefined use case 
        and return it. Special case: if the user made a custom 
        configurtation, that we cannot represent it will return None """
        if apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autoupdate"]) > 0:
            # Autodownload
            if apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 1\
               and apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 1 :
                return softwareproperties.UPDATE_INST_SEC
            elif apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 1 and  \
                 apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 0:
                return softwareproperties.UPDATE_DOWNLOAD
            elif apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 0 and \
                 apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 0:
                return softwareproperties.UPDATE_NOTIFY
            else:
                return None
        elif apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 0 and \
             apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 0:
            return softwareproperties.UPDATE_MANUAL
        else:
            return None

    def set_update_automation_level(self, state):
        """ Set the apt periodic configurtation to the selected 
        update automation level. To synchronize the cache update and the 
        actual upgrading function, the upgrade function, e.g. unattended, 
        will run every day, if enabled. """
        if state == softwareproperties.UPDATE_INST_SEC:
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"],
                               str(1))
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"],
                               str(1))
        elif state == softwareproperties.UPDATE_DOWNLOAD:
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"],
                               str(1))
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"],
                               str(0))
        elif state == softwareproperties.UPDATE_NOTIFY:
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"],
                               str(0))
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"],
                               str(0))
        else:
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["autoupdate"],
                               str(0))
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"],
                               str(0))
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"],
                               str(0))
        self.set_modified_config()

    def set_update_interval(self, days):
        """Set the interval in which we check for available updates"""
        # Only write the key if it has changed
        if not days == apt_pkg.Config.FindI(
                softwareproperties.CONF_MAP["autoupdate"]):
            apt_pkg.Config.Set(softwareproperties.CONF_MAP["autoupdate"],
                               str(days))
            self.set_modified_config()

    def get_update_interval(self):
        """ Returns the interval of the apt periodic cron job """
        return apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autoupdate"])

    def get_popcon_participation(self):
        """ Will return True if the user wants to participate in the popularity 
        contest. Otherwise it will return False. Special case: if no 
        popcon is installed it will return False """
        if os.path.exists(self.popconfile):
            lines = open(self.popconfile).read().split("\n")
            active = False
            for line in lines:
                try:
                    (key, value) = line.split("=")
                    if key == "PARTICIPATE" and value.strip(
                            '"').lower() == "yes":
                        active = True
                except ValueError:
                    continue
            return active
        else:
            return False

    def set_popcon_pariticipation(self, is_helpful):
        """ Enable or disable the participation in the popularity contest """
        if is_helpful == True:
            value = "yes"
        else:
            value = "no"
        if os.path.exists(self.popconfile):
            # read the current config and replace the corresponding settings
            # FIXME: should we check the other values, too?
            lines = map(
                lambda line: re.sub(r'^(PARTICIPATE=)(".+?")', '\\1"%s"' %
                                    value, line),
                open(self.popconfile, "r").readlines())
        else:
            # create a new popcon config file
            m = md5.new()
            m.update(open("/dev/urandom", "r").read(1024))
            id = m.hexdigest()
            lines = []
            lines.append("MY_HOSTID=\"%s\"\n" % id)
            lines.append("PARTICIPATE=\"%s\"\n" % str(value))
            lines.append("USE_HTTP=\"yes\"\n")
        open(self.popconfile, "w").writelines(lines)

    def get_source_code_state(self):
        """Return True if all distro componets are also available as 
       source code. Otherwise return Flase. Special case: If the
       configuration cannot be represented return None"""

        if len(self.distro.source_code_sources) < 1:
            # we don't have any source code sources, so
            # uncheck the button
            self.distro.get_source_code = False
            return False
        else:
            # there are source code sources, so we check the button
            self.distro.get_source_code = True
            # check if there is a corresponding source code source for
            # every binary source. if not set the checkbutton to inconsistent
            templates = {}
            sources = []
            sources.extend(self.distro.main_sources)
            sources.extend(self.distro.child_sources)
            for source in sources:
                if templates.has_key(source.template):
                    for comp in source.comps:
                        templates[source.template].add(comp)
                else:
                    templates[source.template] = set(source.comps)
            # add fake http sources for the cdrom, since the sources
            # for the cdrom are only available in the internet
            if len(self.distro.cdrom_sources) > 0:
                templates[
                    self.distro.source_template] = self.distro.cdrom_comps
            for source in self.distro.source_code_sources:
                if not templates.has_key(source.template) or \
                   (templates.has_key(source.template) and not \
                    (len(set(templates[source.template]) ^ set(source.comps)) == 0\
                     or (len(set(source.comps) ^ self.distro.enabled_comps) == 0))):
                    self.distro.get_source_code = False
                    return None
                    break
        return True

    def print_source_entry(self, source):
        """Print the data of a source entry to the command line"""
        for (label, value) in [("URI:", source.uri), ("Comps:", source.comps),
                               ("Enabled:", not source.disabled),
                               ("Valid:", not source.invalid)]:
            print " %s %s" % (label, value)
        if source.template:
            for (label, value) in [("MatchURI:", source.template.match_uri),
                                   ("BaseURI:", source.template.base_uri)]:
                print " %s %s" % (label, value)
        print "\n"

    def massive_debug_output(self):
        """Print the complete sources.list"""
        print "START SOURCES.LIST:"
        for source in self.sourceslist:
            print source.str()
        print "END SOURCES.LIST\n"

    def enable_component(self, comp):
        """Enable a component of the distro"""
        self.distro.enable_component(comp)
        self.set_modified_sourceslist()

    def disable_component(self, comp):
        """Disable a component of the distro"""
        self.distro.disable_component(comp)
        self.set_modified_sourceslist()

    def disable_child_source(self, template):
        """Enable a child repo of the distribution main repository"""
        for source in self.distro.child_sources:
            if source.template == template:
                self.sourceslist.remove(source)
        for source in self.distro.source_code_sources:
            if source.template == template:
                self.sourceslist.remove(source)
        self.set_modified_sourceslist()

    def enable_child_source(self, template):
        """Enable a child repo of the distribution main repository"""
        # Use the currently selected mirror only if the child source
        # did not override the server
        if template.base_uri == None:
            child_uri = self.distro.default_server
        else:
            child_uri = template.base_uri
        self.distro.add_source(uri=child_uri, dist=template.name)
        self.set_modified_sourceslist()

    def disable_source_code_sources(self):
        """Remove all distro source code sources"""
        sources = []
        sources.extend(self.distro.main_sources)
        sources.extend(self.distro.child_sources)
        # remove all exisiting sources
        for source in self.distro.source_code_sources:
            self.sourceslist.remove(source)
        self.set_modified_sourceslist()

    def enable_source_code_sources(self):
        """Enable source code source for all distro sources"""
        sources = []
        sources.extend(self.distro.main_sources)
        sources.extend(self.distro.child_sources)

        # remove all exisiting sources
        for source in self.distro.source_code_sources:
            self.sourceslist.remove(source)

        for source in sources:
            self.sourceslist.add("deb-src", source.uri, source.dist,
                                 source.comps, "Added by software-properties",
                                 self.sourceslist.list.index(source) + 1,
                                 source.file)
        for source in self.distro.cdrom_sources:
            self.sourceslist.add("deb-src",
                                 self.distro.source_template.base_uri,
                                 self.distro.source_template.name,
                                 source.comps, "Added by software-properties",
                                 self.sourceslist.list.index(source) + 1,
                                 source.file)
        self.set_modified_sourceslist()

    def backup_sourceslist(self):
        """Store a backup of the source.list in memory"""
        self.sourceslist_backup = []
        for source in self.sourceslist.list:
            source_bkp = SourceEntry(line=source.line, file=source.file)
            self.sourceslist_backup.append(source_bkp)

    def toggle_source_use(self, source):
        """Enable or disable the selected channel"""
        #FIXME cdroms need to disable the comps in the childs and sources
        source.disabled = not source.disabled
        self.set_modified_sourceslist()

    def revert(self):
        """Revert all settings to the state when software-properties 
       was launched"""
        #FIXME: GPG keys are still missing
        self.restore_apt_conf()
        self.revert_sourceslist()

    def revert_sourceslist(self):
        """Restore the source list from the startup of the dialog"""
        self.sourceslist.list = []
        for source in self.sourceslist_backup:
            source_reset = SourceEntry(line=source.line, file=source.file)
            self.sourceslist.list.append(source_reset)
        self.save_sourceslist()
        self.reload_sourceslist()

    def set_modified_sourceslist(self):
        """The sources list was changed and now needs to be saved and reloaded"""
        self.modified_sourceslist = True
        if self.options.massive_debug == True:
            self.massive_debug_output()
        self.save_sourceslist()
        self.reload_sourceslist()

    def set_modified_config(self):
        """Write the changed apt configuration to file"""
        self.write_config()

    def render_source(self, source):
        """Render a nice output to show the source in a treeview"""
        if source.template == None:
            if source.comment:
                contents = "<b>%s</b>" % escape(source.comment)
                # Only show the components if there are more than one
                if len(source.comps) > 1:
                    for c in source.comps:
                        contents += " %s" % c
            else:
                contents = "<b>%s %s</b>" % (source.uri, source.dist)
                for c in source.comps:
                    contents += " %s" % c
            if source.type in ("deb-src", "rpm-src"):
                contents += " %s" % _("(Source Code)")
            return contents
        else:
            # try to make use of a corresponding template
            contents = "<b>%s</b>" % source.template.description
            if source.type in ("deb-src", "rpm-src"):
                contents += " (%s)" % _("Source Code")
            if source.comment:
                contents += " %s" % source.comment
            if source.template.child == False:
                for comp in source.comps:
                    if source.template.has_component(comp):
                        # fixme: move something like this into distinfo.Template
                        #        (why not use a dictionary again?)
                        for c in source.template.components:
                            if c.name == comp:
                                contents += "\n%s" % c.description
                    else:
                        contents += "\n%s" % comp
            return contents

    def get_comparable(self, source):
        """extract attributes to sort the sources"""
        cur_sys = 1
        has_template = 1
        has_comment = 1
        is_source = 1
        revert_numbers = string.maketrans("0123456789", "9876543210")
        if source.template:
            has_template = 0
            desc = source.template.description
            if source.template.distribution == self.distro:
                cur_sys = 0
        else:
            desc = "%s %s %s" % (source.uri, source.dist, source.comps)
            if source.comment:
                has_comment = 0
        if source.type.find("src"):
            is_source = 0
        return (cur_sys, has_template, has_comment, is_source,
                desc.translate(revert_numbers))

    def get_isv_sources(self):
        """Return a list of sources that are not part of the distribution"""
        isv_sources = []
        for source in self.sourceslist.list:
            if not source.invalid and\
               (source not in self.distro.main_sources and\
                source not in self.distro.cdrom_sources and\
                source not in self.distro.child_sources and\
                source not in self.distro.disabled_sources) and\
               source not in self.distro.source_code_sources:
                isv_sources.append(source)
        return isv_sources

    def get_cdrom_sources(self):
        """Return the list of CDROM based distro sources"""
        return self.distro.cdrom_sources

    def get_comp_download_state(self, comp):
        """Return a tuple: the first value describes if a component is enabled
       in the Internet repositories. The second value describes if the
       first value is inconsistent."""
        #FIXME: also return a correct inconsistent value
        return (comp.name in self.distro.download_comps, False)

    def get_comp_child_state(self, template):
        """Return a tuple: the first value describes if a component is enabled
       in one of the child source that matcth the given template. 
       The second value describes if the first value is inconsistent."""
        comps = []
        for child in self.distro.child_sources:
            if child.template == template:
                comps.extend(child.comps)
        if len(comps) > 0 and \
            len(self.distro.enabled_comps ^ set(comps)) == 0:
            # All enabled distro components are also enabled for the child source
            return (True, False)
        elif len(comps) > 0 and\
            len(self.distro.enabled_comps ^ set(comps)) != 0:
            # A matching child source does exist but doesn't include all
            # enabled distro components
            return (False, True)
        else:
            # There is no corresponding child source at all
            return (False, False)

    def reload_sourceslist(self):
        self.sourceslist.refresh()
        self.sourceslist_visible = []
        self.distro.get_sources(self.sourceslist)

    def write_config(self):
        """Write the current apt configuration to file"""
        # update the adept file as well if it is there
        conffiles = [
            "/etc/apt/apt.conf.d/10periodic",
            "/etc/apt/apt.conf.d/15adept-periodic-update"
        ]

        # check (beforehand) if one exists, if not create one
        for f in conffiles:
            if os.path.isfile(f):
                break
        else:
            print "No config found, creating one"
            open(conffiles[0], "w")

        # now update them
        for periodic in conffiles:
            # read the old content first
            content = []
            if os.path.isfile(periodic):
                content = open(periodic, "r").readlines()
                cnf = apt_pkg.Config.SubTree("APT::Periodic")

                # then write a new file without the updated keys
                f = open(periodic, "w")
                for line in content:
                    for key in cnf.List():
                        if line.find("APT::Periodic::%s" % (key)) >= 0:
                            break
                    else:
                        f.write(line)

                # and append the updated keys
                for i in cnf.List():
                    f.write("APT::Periodic::%s \"%s\";\n" % (i, cnf.FindI(i)))
                f.close()

    def save_sourceslist(self):
        """Backup the existing sources.list files and write the current 
       configuration"""
        self.sourceslist.backup(".save")
        self.sourceslist.save()

    def add_source_from_line(self, line):
        """Add a source with the given apt line"""
        self.sourceslist.list.append(SourceEntry(line))
        self.set_modified_sourceslist()

    def remove_source(self, source):
        """Remove the given source"""
        self.sourceslist.remove(source)
        self.set_modified_sourceslist()

    def add_key(self, path):
        """Add a gnupg key to the list of trusted software vendors"""
        if not os.path.exists(path):
            return False
        try:
            self.apt_key.add(path)
            return True
        except:
            return False

    def remove_key(self, fingerprint):
        """Remove a gnupg key from the list of trusted software vendors"""
        try:
            self.apt_key.rm(fingerprint)
            return True
        except:
            return False
Ejemplo n.º 31
0
 def get_sourceslist(self):
     return SourcesList()
Ejemplo n.º 32
0
#!/usr/bin/python

import sys

try:
    import aptsources
    import aptsources.distro
    from aptsources.sourceslist import SourcesList
except:
    sys.exit(2)

sourceslist = SourcesList()
sourceslist.refresh()
distro = aptsources.distro.get_distro()
distro.get_sources(sourceslist)
sys.exit(0 if sys.argv[1] in distro.download_comps else 1)