home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / softwareproperties / SoftwareProperties.py < prev   
Encoding:
Python Source  |  2009-03-27  |  23.4 KB  |  630 lines

  1. #  software-properties backend
  2. #
  3. #  Copyright (c) 2004-2007 Canonical Ltd.
  4. #                2004-2005 Michiel Sikkes
  5. #
  6. #  Author: Michiel Sikkes <michiel@eyesopened.nl>
  7. #          Michael Vogt <mvo@debian.org>
  8. #          Sebastian Heinlein <glatzor@ubuntu.com>
  9. #
  10. #  This program is free software; you can redistribute it and/or
  11. #  modify it under the terms of the GNU General Public License as
  12. #  published by the Free Software Foundation; either version 2 of the
  13. #  License, or (at your option) any later version.
  14. #
  15. #  This program is distributed in the hope that it will be useful,
  16. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. #  GNU General Public License for more details.
  19. #
  20. #  You should have received a copy of the GNU General Public License
  21. #  along with this program; if not, write to the Free Software
  22. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  23. #  USA
  24.  
  25. import apt_pkg
  26. from hashlib import md5
  27. import re
  28. import os
  29. import glob
  30. import sys
  31. import shutil
  32. import subprocess
  33. from tempfile import NamedTemporaryFile
  34. import string
  35. from xml.sax.saxutils import escape
  36. from ConfigParser import ConfigParser
  37. from gettext import gettext as _
  38.  
  39. from AptAuth import AptAuth
  40. import softwareproperties
  41. import aptsources
  42. import aptsources.distro
  43. from aptsources.sourceslist import SourcesList, SourceEntry
  44.  
  45.  
  46.  
  47. class SoftwareProperties(object):
  48.  
  49.   # known (whitelisted) channels
  50.   CHANNEL_PATH="/usr/share/app-install/channels/"
  51.  
  52.   # release upgrades policy
  53.   RELEASE_UPGRADES_CONF = "/etc/update-manager/release-upgrades"
  54.   #RELEASE_UPGRADES_CONF = "/tmp/release-upgrades"
  55.   (
  56.     RELEASE_UPGRADES_NEVER,
  57.     RELEASE_UPGRADES_NORMAL,
  58.     RELEASE_UPGRADES_LTS
  59.   ) = range(3)
  60.   release_upgrades_policy_map = {
  61.     RELEASE_UPGRADES_NEVER  : 'never',
  62.     RELEASE_UPGRADES_NORMAL : 'normal',
  63.     RELEASE_UPGRADES_LTS    : 'lts',
  64.   }
  65.   
  66.   def __init__(self, datadir=None, options=None):
  67.     """ Provides the core functionality to configure the used software 
  68.         repositories, the corresponding authentication keys and 
  69.         update automation """
  70.     self.popconfile = "/etc/popularity-contest.conf"
  71.  
  72.     # FIXME: some saner way is needed here
  73.     if datadir == None:
  74.       datadir = "/usr/share/software-properties/"
  75.     self.datadir = datadir
  76.  
  77.     self.sourceslist = SourcesList()
  78.     self.distro = aptsources.distro.get_distro()
  79.  
  80.     self.seen_server = []
  81.     self.modified_sourceslist = False
  82.  
  83.     self.reload_sourceslist()
  84.     self.backup_sourceslist()
  85.  
  86.     self.backup_apt_conf()
  87.  
  88.     # FIXME: we need to store this value in a config option
  89.     #self.custom_mirrors = ["http://adasdwww.de/ubuntu"]
  90.     self.custom_mirrors= []
  91.  
  92.     # apt-key stuff
  93.     self.apt_key = AptAuth()
  94.  
  95.   def backup_apt_conf(self):
  96.     """Backup all apt configuration options"""
  97.     self.apt_conf_backup = {}
  98.     for option in softwareproperties.CONF_MAP.keys():
  99.         value = apt_pkg.Config.FindI(softwareproperties.CONF_MAP[option])
  100.         self.apt_conf_backup[option] = value
  101.  
  102.   def restore_apt_conf(self):
  103.     """Restore the stored apt configuration"""
  104.     for option in self.apt_conf_backup.keys():
  105.         apt_pkg.Config.Set(softwareproperties.CONF_MAP[option],
  106.                            str(self.apt_conf_backup[option]))
  107.     self.write_config()
  108.  
  109.   def get_update_automation_level(self):
  110.     """ Parse the apt cron configuration. Try to fit a predefined use case 
  111.         and return it. Special case: if the user made a custom 
  112.         configurtation, that we cannot represent it will return None """
  113.     if apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autoupdate"]) > 0:
  114.         # Autodownload
  115.         if apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 1\
  116.            and apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 1 :
  117.             return softwareproperties.UPDATE_INST_SEC
  118.         elif apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 1 and  \
  119.              apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 0:
  120.             return softwareproperties.UPDATE_DOWNLOAD
  121.         elif apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 0 and \
  122.              apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 0:
  123.             return softwareproperties.UPDATE_NOTIFY
  124.         else:
  125.             return None
  126.     elif apt_pkg.Config.FindI(softwareproperties.CONF_MAP["unattended"]) == 0 and \
  127.          apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autodownload"]) == 0:
  128.         return softwareproperties.UPDATE_MANUAL
  129.     else:
  130.         return None
  131.  
  132.   def set_update_automation_level(self, state):
  133.     """ Set the apt periodic configurtation to the selected 
  134.         update automation level. To synchronize the cache update and the 
  135.         actual upgrading function, the upgrade function, e.g. unattended, 
  136.         will run every day, if enabled. """
  137.     if state == softwareproperties.UPDATE_INST_SEC:
  138.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"], str(1))
  139.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"], str(1))
  140.     elif state == softwareproperties.UPDATE_DOWNLOAD:
  141.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"], str(1))
  142.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"], str(0))
  143.     elif state == softwareproperties.UPDATE_NOTIFY:
  144.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"], str(0))
  145.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"], str(0))
  146.     else:
  147.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["autoupdate"], str(0))
  148.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["unattended"], str(0))
  149.         apt_pkg.Config.Set(softwareproperties.CONF_MAP["autodownload"], str(0))
  150.     self.set_modified_config()
  151.  
  152.   def set_update_interval(self, days):
  153.       """Set the interval in which we check for available updates"""
  154.       # Only write the key if it has changed
  155.       if not days == apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autoupdate"]):
  156.           apt_pkg.Config.Set(softwareproperties.CONF_MAP["autoupdate"], str(days))
  157.           self.set_modified_config()
  158.  
  159.   def get_update_interval(self):
  160.     """ Returns the interval of the apt periodic cron job """
  161.     return apt_pkg.Config.FindI(softwareproperties.CONF_MAP["autoupdate"])
  162.  
  163.   def get_release_upgrades_policy(self):
  164.     """
  165.     return the release upgrade policy:
  166.      RELASE_UPGRADE_NEVER
  167.      RELASE_UPGRADE_NORMAL
  168.      RELASE_UPGRADE_LTS
  169.     """
  170.     # default (if no option is set) is NORMAL
  171.     if not os.path.exists(self.RELEASE_UPGRADES_CONF):
  172.       return self.RELEASE_UPGRADES_NORMAL
  173.     parser = ConfigParser()
  174.     parser.read(self.RELEASE_UPGRADES_CONF)
  175.     if parser.has_option("DEFAULT","Prompt"):
  176.       type = parser.get("DEFAULT","Prompt").lower()
  177.       for k, v in self.release_upgrades_policy_map.iteritems():
  178.         if v == type:
  179.           return k
  180.     return self.RELEASE_UPGRADES_NORMAL
  181.  
  182.   def set_release_upgrades_policy(self, i):
  183.     """
  184.     set the release upgrade policy:
  185.      RELASE_UPGRADE_NEVER
  186.      RELASE_UPGRADE_NORMAL
  187.      RELASE_UPGRADE_LTS
  188.      """
  189.     # we are note using ConfigParser.write() as it removes comments
  190.     if not os.path.exists(self.RELEASE_UPGRADES_CONF):
  191.       f = open(self.RELEASE_UPGRADES_CONF,"w")
  192.       f.write("[DEFAULT]\nprompt=%s\n"% self.release_upgrades_policy_map[i])
  193.       return True
  194.     f = open(self.RELEASE_UPGRADES_CONF,"r")
  195.     out = NamedTemporaryFile()
  196.     for line in map(string.strip, f.readlines()):
  197.       if line.lower().startswith("prompt"):
  198.         out.write("prompt=%s\n" % self.release_upgrades_policy_map[i])
  199.       else:
  200.         out.write(line+"\n")
  201.     out.flush()
  202.     shutil.copymode(self.RELEASE_UPGRADES_CONF, out.name)
  203.     shutil.copy(out.name, self.RELEASE_UPGRADES_CONF)
  204.     return True
  205.  
  206.   def get_popcon_participation(self):
  207.     """ Will return True if the user wants to participate in the popularity 
  208.         contest. Otherwise it will return False. Special case: if no 
  209.         popcon is installed it will return False """
  210.     if os.path.exists(self.popconfile):
  211.         lines = open(self.popconfile).read().split("\n")
  212.         active = False
  213.         for line in lines:
  214.             try:
  215.                 (key,value) = line.split("=")
  216.                 if key == "PARTICIPATE" and value.strip('"').lower() == "yes":
  217.                     active = True
  218.             except ValueError:
  219.                 continue
  220.         return active
  221.     else:
  222.         return False
  223.  
  224.   def set_popcon_pariticipation(self, is_helpful):
  225.     """ Enable or disable the participation in the popularity contest """
  226.     if is_helpful == True:
  227.         value = "yes"
  228.     else:
  229.         value = "no"
  230.     if os.path.exists(self.popconfile):
  231.         # read the current config and replace the corresponding settings
  232.         # FIXME: should we check the other values, too?
  233.         lines = map(lambda line: re.sub(r'^(PARTICIPATE=)(".+?")',
  234.                                         '\\1"%s"' % value,
  235.                                         line),
  236.                     open(self.popconfile, "r").readlines())
  237.     else:
  238.         # create a new popcon config file
  239.         m = md5()
  240.         m.update(open("/dev/urandom", "r").read(1024))
  241.         id = m.hexdigest()
  242.         lines = []
  243.         lines.append("MY_HOSTID=\"%s\"\n" % id)
  244.         lines.append("PARTICIPATE=\"%s\"\n" % str(value))
  245.         lines.append("USE_HTTP=\"yes\"\n")
  246.     open(self.popconfile, "w").writelines(lines)
  247.  
  248.   def get_source_code_state(self):
  249.     """Return True if all distro componets are also available as 
  250.        source code. Otherwise return Flase. Special case: If the
  251.        configuration cannot be represented return None"""
  252.   
  253.     if len(self.distro.source_code_sources) < 1:
  254.         # we don't have any source code sources, so
  255.         # uncheck the button
  256.         self.distro.get_source_code = False
  257.         return False
  258.     else:
  259.         # there are source code sources, so we check the button
  260.         self.distro.get_source_code = True
  261.         # check if there is a corresponding source code source for
  262.         # every binary source. if not set the checkbutton to inconsistent
  263.         templates = {}
  264.         sources = []
  265.         sources.extend(self.distro.main_sources)
  266.         sources.extend(self.distro.child_sources)
  267.         for source in sources:
  268.             if templates.has_key(source.template):
  269.                 for comp in source.comps:
  270.                     templates[source.template].add(comp)
  271.             else:
  272.                 templates[source.template] = set(source.comps)
  273.         # add fake http sources for the cdrom, since the sources
  274.         # for the cdrom are only available in the internet
  275.         if len(self.distro.cdrom_sources) > 0:
  276.             templates[self.distro.source_template] = self.distro.cdrom_comps
  277.         for source in self.distro.source_code_sources:
  278.             if not templates.has_key(source.template) or \
  279.                (templates.has_key(source.template) and not \
  280.                 (len(set(templates[source.template]) ^ set(source.comps)) == 0\
  281.                  or (len(set(source.comps) ^ self.distro.enabled_comps) == 0))):
  282.                 self.distro.get_source_code = False
  283.                 return None
  284.                 break
  285.     return True
  286.  
  287.   def print_source_entry(self, source):
  288.     """Print the data of a source entry to the command line"""
  289.     for (label, value) in [("URI:", source.uri),
  290.                            ("Comps:", source.comps),
  291.                            ("Enabled:", not source.disabled),
  292.                            ("Valid:", not source.invalid)]:
  293.         print " %s %s" % (label, value)
  294.     if source.template:
  295.         for (label, value) in [("MatchURI:", source.template.match_uri),
  296.                                ("BaseURI:", source.template.base_uri)]:
  297.             print " %s %s" % (label, value)
  298.     print "\n"
  299.  
  300.   def massive_debug_output(self):
  301.     """Print the complete sources.list""" 
  302.     print "START SOURCES.LIST:"
  303.     for source in self.sourceslist:
  304.         print source.str()
  305.     print "END SOURCES.LIST\n"
  306.  
  307.   def enable_component(self, comp):
  308.     """Enable a component of the distro"""
  309.     self.distro.enable_component(comp) 
  310.     self.set_modified_sourceslist()
  311.  
  312.   def disable_component(self, comp):
  313.     """Disable a component of the distro"""
  314.     self.distro.disable_component(comp) 
  315.     self.set_modified_sourceslist()
  316.  
  317.   def disable_child_source(self, template):
  318.     """Enable a child repo of the distribution main repository"""
  319.     for source in self.distro.child_sources:
  320.         if source.template == template:
  321.             self.sourceslist.remove(source)
  322.     for source in self.distro.source_code_sources:
  323.         if source.template == template:
  324.             self.sourceslist.remove(source)
  325.     self.set_modified_sourceslist()
  326.  
  327.   def enable_child_source(self, template):
  328.     """Enable a child repo of the distribution main repository"""
  329.     # Use the currently selected mirror only if the child source
  330.     # did not override the server
  331.     if template.base_uri == None:
  332.         child_uri = self.distro.default_server
  333.     else:
  334.         child_uri = template.base_uri
  335.     self.distro.add_source(uri=child_uri, dist=template.name)
  336.     self.set_modified_sourceslist()
  337.  
  338.   def disable_source_code_sources(self):
  339.     """Remove all distro source code sources"""
  340.     sources = []
  341.     sources.extend(self.distro.main_sources)
  342.     sources.extend(self.distro.child_sources)
  343.     # remove all exisiting sources
  344.     for source in self.distro.source_code_sources:
  345.         self.sourceslist.remove(source)
  346.     self.set_modified_sourceslist()
  347.   
  348.   def enable_source_code_sources(self):
  349.     """Enable source code source for all distro sources"""
  350.     sources = []
  351.     sources.extend(self.distro.main_sources)
  352.     sources.extend(self.distro.child_sources)
  353.  
  354.     # remove all exisiting sources
  355.     for source in self.distro.source_code_sources:
  356.         self.sourceslist.remove(source)
  357.  
  358.     for source in sources:
  359.         self.sourceslist.add("deb-src",
  360.                              source.uri,
  361.                              source.dist,
  362.                              source.comps,
  363.                              "Added by software-properties",
  364.                              self.sourceslist.list.index(source)+1,
  365.                              source.file)
  366.     for source in self.distro.cdrom_sources:
  367.         self.sourceslist.add("deb-src",
  368.                              self.distro.source_template.base_uri,
  369.                              self.distro.source_template.name,
  370.                              source.comps,
  371.                              "Added by software-properties",
  372.                              self.sourceslist.list.index(source)+1,
  373.                              source.file)
  374.     self.set_modified_sourceslist()
  375.  
  376.   def backup_sourceslist(self):
  377.     """Store a backup of the source.list in memory"""
  378.     self.sourceslist_backup = []
  379.     for source in self.sourceslist.list:
  380.         source_bkp = SourceEntry(line=source.line,file=source.file)
  381.         self.sourceslist_backup.append(source_bkp)
  382.   
  383.   def toggle_source_use(self, source):
  384.     """Enable or disable the selected channel"""
  385.     #FIXME cdroms need to disable the comps in the childs and sources
  386.     source.disabled = not source.disabled
  387.     self.set_modified_sourceslist()
  388.  
  389.   def revert(self):
  390.     """Revert all settings to the state when software-properties 
  391.        was launched"""
  392.     #FIXME: GPG keys are still missing
  393.     self.restore_apt_conf()
  394.     self.revert_sourceslist()
  395.  
  396.   def revert_sourceslist(self):
  397.     """Restore the source list from the startup of the dialog"""
  398.     self.sourceslist.list = []
  399.     for source in self.sourceslist_backup:
  400.         source_reset = SourceEntry(line=source.line,file=source.file)
  401.         self.sourceslist.list.append(source_reset)
  402.     self.save_sourceslist()
  403.     self.reload_sourceslist()
  404.  
  405.   def set_modified_sourceslist(self):
  406.     """The sources list was changed and now needs to be saved and reloaded"""
  407.     self.modified_sourceslist = True
  408.     if self.options.massive_debug == True:
  409.         self.massive_debug_output()
  410.     self.save_sourceslist()
  411.     self.reload_sourceslist()
  412.  
  413.   def set_modified_config(self):
  414.     """Write the changed apt configuration to file"""
  415.     self.write_config()
  416.  
  417.   def render_source(self, source):
  418.     """Render a nice output to show the source in a treeview"""
  419.     if source.template == None:
  420.         if source.comment:
  421.             contents = "<b>%s</b>" % escape(source.comment)
  422.             # Only show the components if there are more than one
  423.             if len(source.comps) > 1:
  424.                 for c in source.comps:
  425.                     contents += " %s" % c
  426.         else:
  427.             contents = "<b>%s %s</b>" % (source.uri, source.dist)
  428.             for c in source.comps:
  429.                 contents += " %s" % c
  430.         if source.type in ("deb-src", "rpm-src"):
  431.             contents += " %s" % _("(Source Code)")
  432.         return contents
  433.     else:
  434.         # try to make use of a corresponding template
  435.         contents = "<b>%s</b>" % source.template.description
  436.         if source.type in ("deb-src", "rpm-src"):
  437.             contents += " (%s)" % _("Source Code")
  438.         if source.comment:
  439.             contents +=" %s" % source.comment
  440.         if source.template.child == False:
  441.             for comp in source.comps:
  442.                 if source.template.has_component(comp):
  443.                     # fixme: move something like this into distinfo.Template
  444.                     #        (why not use a dictionary again?)
  445.                     for c in source.template.components:
  446.                         if c.name == comp:
  447.                             contents += "\n%s" % c.description
  448.                 else:
  449.                     contents += "\n%s" % comp
  450.         return contents
  451.  
  452.   def get_comparable(self, source):
  453.       """extract attributes to sort the sources"""
  454.       cur_sys = 1
  455.       has_template = 1
  456.       has_comment = 1
  457.       is_source = 1
  458.       revert_numbers = string.maketrans("0123456789", "9876543210")
  459.       if source.template:
  460.         has_template = 0
  461.         desc = source.template.description
  462.         if source.template.distribution == self.distro:
  463.             cur_sys = 0
  464.       else:
  465.           desc = "%s %s %s" % (source.uri, source.dist, source.comps)
  466.           if source.comment:
  467.               has_comment = 0
  468.       if source.type.find("src"):
  469.           is_source = 0
  470.       return (cur_sys, has_template, has_comment, is_source,
  471.               desc.translate(revert_numbers))
  472.  
  473.   def get_isv_sources(self):
  474.     """Return a list of sources that are not part of the distribution"""
  475.     isv_sources = []
  476.     for source in self.sourceslist.list:
  477.         if not source.invalid and\
  478.            (source not in self.distro.main_sources and\
  479.             source not in self.distro.cdrom_sources and\
  480.             source not in self.distro.child_sources and\
  481.             source not in self.distro.disabled_sources) and\
  482.            source not in self.distro.source_code_sources:
  483.             isv_sources.append(source)
  484.     return isv_sources
  485.  
  486.   def get_cdrom_sources(self):
  487.     """Return the list of CDROM based distro sources"""
  488.     return self.distro.cdrom_sources
  489.       
  490.   def get_comp_download_state(self, comp):
  491.     """Return a tuple: the first value describes if a component is enabled
  492.        in the Internet repositories. The second value describes if the
  493.        first value is inconsistent."""
  494.     #FIXME: also return a correct inconsistent value
  495.     return (comp.name in self.distro.download_comps, False)
  496.  
  497.   def get_comp_child_state(self, template):
  498.     """Return a tuple: the first value describes if a component is enabled
  499.        in one of the child source that matcth the given template. 
  500.        The second value describes if the first value is inconsistent."""
  501.     comps = []
  502.     for child in self.distro.child_sources:
  503.         if child.template == template:
  504.             comps.extend(child.comps)
  505.     if len(comps) > 0 and \
  506.         len(self.distro.enabled_comps ^ set(comps)) == 0:
  507.         # All enabled distro components are also enabled for the child source
  508.         return (True, False)
  509.     elif len(comps) > 0 and\
  510.         len(self.distro.enabled_comps ^ set(comps)) != 0:
  511.         # A matching child source does exist but doesn't include all 
  512.         # enabled distro components
  513.         return(False, True)
  514.     else:
  515.         # There is no corresponding child source at all
  516.         return (False, False)
  517.   
  518.   def reload_sourceslist(self):
  519.     self.sourceslist.refresh()
  520.     self.sourceslist_visible=[]
  521.     self.distro.get_sources(self.sourceslist)    
  522.  
  523.   def write_config(self):
  524.     """Write the current apt configuration to file"""
  525.     # update the adept file as well if it is there
  526.     conffiles = ["/etc/apt/apt.conf.d/10periodic",
  527.                  "/etc/apt/apt.conf.d/15adept-periodic-update"]
  528.  
  529.     # check (beforehand) if one exists, if not create one
  530.     for f in conffiles:
  531.       if os.path.isfile(f):
  532.         break
  533.     else:
  534.       print "No config found, creating one"
  535.       open(conffiles[0], "w")
  536.  
  537.     # now update them
  538.     for periodic in conffiles:
  539.       # read the old content first
  540.       content = []
  541.       if os.path.isfile(periodic):
  542.         content = open(periodic, "r").readlines()
  543.         cnf = apt_pkg.Config.SubTree("APT::Periodic")
  544.  
  545.         # then write a new file without the updated keys
  546.         f = open(periodic, "w")
  547.         for line in content:
  548.           for key in cnf.List():
  549.             if line.find("APT::Periodic::%s" % (key)) >= 0:
  550.               break
  551.           else:
  552.             f.write(line)
  553.  
  554.         # and append the updated keys
  555.         for i in cnf.List():
  556.           f.write("APT::Periodic::%s \"%s\";\n" % (i, cnf.FindI(i)))
  557.         f.close()    
  558.  
  559.   def save_sourceslist(self):
  560.     """Backup the existing sources.list files and write the current 
  561.        configuration"""
  562.     self.sourceslist.backup(".save")
  563.     self.sourceslist.save()
  564.  
  565.   def _is_line_in_whitelisted_channel(self, srcline):
  566.     """
  567.     helper that checks if a given line is in the source list
  568.     return the channel name or None if not found
  569.     """
  570.     srcentry = SourceEntry(srcline)    
  571.     if os.path.exists(self.CHANNEL_PATH):
  572.       for f in glob.glob("%s/*.list" % self.CHANNEL_PATH):
  573.         for line in open(f):
  574.           if line.strip().startswith("#"):
  575.             continue
  576.           if srcentry == SourceEntry(line):
  577.             return os.path.splitext(os.path.basename(f))[0]
  578.     return None
  579.  
  580.   def check_and_add_key_for_whitelisted_channels(self, srcline):
  581.     """
  582.     helper that adds the gpg key of the channel to the apt
  583.     keyring *if* the channel is in the whitelist
  584.     /usr/share/app-install/channels
  585.     """
  586.     channel = self._is_line_in_whitelisted_channel(srcline)
  587.     if channel:
  588.       keyp = "%s/%s.key" % (self.CHANNEL_PATH, channel)
  589.       if os.path.exists(keyp):
  590.         subprocess.call(["apt-key","add",keyp])
  591.  
  592.   def add_source_from_line(self, line):
  593.     """
  594.     Add a source with the given apt line and auto-add
  595.     signing key if we have it in the whitelist
  596.     """
  597.     self.check_and_add_key_for_whitelisted_channels(line)
  598.     self.sourceslist.list.append(SourceEntry(line))
  599.     self.set_modified_sourceslist()
  600.  
  601.   def remove_source(self, source):
  602.     """Remove the given source"""
  603.     self.sourceslist.remove(source)
  604.     self.set_modified_sourceslist()
  605.  
  606.   def add_key(self, path):
  607.     """Add a gnupg key to the list of trusted software vendors"""
  608.     if not os.path.exists(path):
  609.         return False
  610.     try:
  611.         self.apt_key.add(path)
  612.         return True
  613.     except:
  614.         return False
  615.  
  616.   def remove_key(self, fingerprint):
  617.     """Remove a gnupg key from the list of trusted software vendors"""
  618.     try:
  619.         self.apt_key.rm(fingerprint)
  620.         return True
  621.     except:
  622.         return False
  623.  
  624.  
  625.  
  626. if __name__ == "__main__":
  627.   sp = SoftwareProperties()
  628.   print sp.get_release_upgrades_policy()
  629.   sp.set_release_upgrades_policy(0)
  630.