home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / GDebi / DebPackage.py < prev    next >
Encoding:
Python Source  |  2009-04-01  |  18.7 KB  |  484 lines

  1. # Copyright (c) 2005-2009 Canonical Ltd
  2. #
  3. # AUTHOR:
  4. # Michael Vogt <mvo@ubuntu.com>
  5. #
  6. # This file is part of GDebi
  7. #
  8. # GDebi is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License as published
  10. # by the Free Software Foundation; either version 2 of the License, or (at
  11. # your option) any later version.
  12. #
  13. # GDebi is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. # General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with GDebi; if not, write to the Free Software
  20. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. #
  22.  
  23. import warnings
  24. from warnings import warn
  25. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  26. import apt_inst, apt_pkg
  27. import apt
  28. import sys
  29. import os
  30. from gettext import gettext as _
  31. from Cache import Cache
  32.  
  33. class DebPackage(object):
  34.     debug = 0
  35.  
  36.     def __init__(self, cache, file=None):
  37.         cache.clear()
  38.         self._cache = cache
  39.         self.file = file
  40.         self._needPkgs = []
  41.         self._sections = {}
  42.         self._installedConflicts = set()
  43.         self._failureString = ""
  44.         if file != None:
  45.             self.open(file)
  46.             
  47.     def open(self, file):
  48.         """ read a deb """
  49.         control = apt_inst.debExtractControl(open(file))
  50.         self._sections = apt_pkg.ParseSection(control)
  51.         self.pkgName = self._sections["Package"]
  52.  
  53.     def _isOrGroupSatisfied(self, or_group):
  54.         """ this function gets a 'or_group' and analyzes if
  55.             at least one dependency of this group is already satisfied """
  56.         self._dbg(2,"_checkOrGroup(): %s " % (or_group))
  57.  
  58.         for dep in or_group:
  59.             depname = dep[0]
  60.             ver = dep[1]
  61.             oper = dep[2]
  62.  
  63.             # check for virtual pkgs
  64.             if not self._cache.has_key(depname):
  65.                 if self._cache.isVirtualPkg(depname):
  66.                     self._dbg(3,"_isOrGroupSatisfied(): %s is virtual dep" % depname)
  67.                     for pkg in self._cache.getProvidersForVirtual(depname):
  68.                         if pkg.isInstalled:
  69.                             return True
  70.                 continue
  71.             # check real dependency
  72.             inst = self._cache[depname]
  73.             instver = inst.installedVersion
  74.             if instver != None and apt_pkg.CheckDep(instver,oper,ver) == True:
  75.                 return True
  76.             # if no real dependency is installed, check if there is
  77.             # a package installed that provides this dependency
  78.             # (e.g. scrollkeeper dependecies are provided by rarian-compat)
  79.             for ppkg in self._cache.getProvidersFor(depname):
  80.                 if ppkg.isInstalled:
  81.                     self._dbg(3, "found installed '%s' that provides '%s'" % (ppkg.name, depname))
  82.                     return True
  83.         return False
  84.             
  85.  
  86.     def _satisfyOrGroup(self, or_group):
  87.         """ try to satisfy the or_group """
  88.  
  89.         or_found = False
  90.         virtual_pkg = None
  91.  
  92.         for dep in or_group:
  93.             depname = dep[0]
  94.             ver = dep[1]
  95.             oper = dep[2]
  96.  
  97.             # if we don't have it in the cache, it may be virtual
  98.             if not self._cache.has_key(depname):
  99.                 if not self._cache.isVirtualPkg(depname):
  100.                     continue
  101.                 providers = self._cache.getProvidersForVirtual(depname)
  102.                 # if a package just has a single virtual provider, we
  103.                 # just pick that (just like apt)
  104.                 if len(providers) != 1:
  105.                     continue
  106.                 depname = providers[0].name
  107.                 
  108.             # now check if we can satisfy the deps with the candidate(s)
  109.             # in the cache
  110.             cand = self._cache[depname]
  111.             candver = self._cache._depcache.GetCandidateVer(cand._pkg)
  112.             if not candver:
  113.                 continue
  114.             if not apt_pkg.CheckDep(candver.VerStr,oper,ver):
  115.                 continue
  116.  
  117.             # check if we need to install it
  118.             self._dbg(2,"Need to get: %s" % depname)
  119.             self._needPkgs.append(depname)
  120.             return True
  121.  
  122.         # if we reach this point, we failed
  123.         or_str = ""
  124.         for dep in or_group:
  125.             or_str += dep[0]
  126.             if ver and oper:
  127.                 or_str += " (%s %s)" % (dep[2], dep[1])
  128.             if dep != or_group[len(or_group)-1]:
  129.                 or_str += "|"
  130.         self._failureString += _("Dependency is not satisfiable: %s\n") % or_str
  131.         return False
  132.  
  133.     def _checkSinglePkgConflict(self, pkgname, ver, oper):
  134.         """ returns true if a pkg conflicts with a real installed/marked
  135.             pkg """
  136.         # FIXME: deal with conflicts against its own provides
  137.         #        (e.g. Provides: ftp-server, Conflicts: ftp-server)
  138.         self._dbg(3, "_checkSinglePkgConflict() pkg='%s' ver='%s' oper='%s'" % (pkgname, ver, oper))
  139.         pkgver = None
  140.         cand = self._cache[pkgname]
  141.         if cand.isInstalled:
  142.             pkgver = cand.installedVersion
  143.         elif cand.markedInstall:
  144.             pkgver = cand.candidateVersion
  145.         #print "pkg: %s" % pkgname
  146.         #print "ver: %s" % ver
  147.         #print "pkgver: %s " % pkgver
  148.         #print "oper: %s " % oper
  149.         if (pkgver and apt_pkg.CheckDep(pkgver,oper,ver) and 
  150.             not self.replacesRealPkg(pkgname, oper, ver)):
  151.             self._failureString += _("Conflicts with the installed package '%s'\n") % cand.name
  152.             return True
  153.         return False
  154.  
  155.     def _checkConflictsOrGroup(self, or_group):
  156.         """ check the or-group for conflicts with installed pkgs """
  157.         self._dbg(2,"_checkConflictsOrGroup(): %s " % (or_group))
  158.  
  159.         or_found = False
  160.         virtual_pkg = None
  161.  
  162.         for dep in or_group:
  163.             depname = dep[0]
  164.             ver = dep[1]
  165.             oper = dep[2]
  166.  
  167.             # check conflicts with virtual pkgs
  168.             if not self._cache.has_key(depname):
  169.                 # FIXME: we have to check for virtual replaces here as 
  170.                 #        well (to pass tests/gdebi-test8.deb)
  171.                 if self._cache.isVirtualPkg(depname):
  172.                     for pkg in self._cache.getProvidersForVirtual(depname):
  173.                         self._dbg(3, "conflicts virtual check: %s" % pkg.name)
  174.                         # P/C/R on virtal pkg, e.g. ftpd
  175.                         if self.pkgName == pkg.name:
  176.                             self._dbg(3, "conflict on self, ignoring")
  177.                             continue
  178.                         if self._checkSinglePkgConflict(pkg.name,ver,oper):
  179.                             self._installedConflicts.add(pkg.name)
  180.                 continue
  181.             if self._checkSinglePkgConflict(depname,ver,oper):
  182.                 self._installedConflicts.add(depname)
  183.         return len(self._installedConflicts) != 0
  184.  
  185.     def getConflicts(self):
  186.         conflicts = []
  187.         key = "Conflicts"
  188.         if self._sections.has_key(key):
  189.             conflicts = apt_pkg.ParseDepends(self._sections[key])
  190.         return conflicts
  191.  
  192.     def getDepends(self):
  193.         depends = []
  194.         # find depends
  195.         for key in ["Depends","PreDepends"]:
  196.             if self._sections.has_key(key):
  197.                 depends.extend(apt_pkg.ParseDepends(self._sections[key]))
  198.         return depends
  199.  
  200.     def getProvides(self):
  201.         provides = []
  202.         key = "Provides"
  203.         if self._sections.has_key(key):
  204.             provides = apt_pkg.ParseDepends(self._sections[key])
  205.         return provides
  206.  
  207.     def getReplaces(self):
  208.         replaces = []
  209.         key = "Replaces"
  210.         if self._sections.has_key(key):
  211.             replaces = apt_pkg.ParseDepends(self._sections[key])
  212.         return replaces
  213.  
  214.     def replacesRealPkg(self, pkgname, oper, ver):
  215.         """ 
  216.         return True if the deb packages replaces a real (not virtual)
  217.         packages named pkgname, oper, ver 
  218.         """
  219.         self._dbg(3, "replacesPkg() %s %s %s" % (pkgname,oper,ver))
  220.         pkgver = None
  221.         cand = self._cache[pkgname]
  222.         if cand.isInstalled:
  223.             pkgver = cand.installedVersion
  224.         elif cand.markedInstall:
  225.             pkgver = cand.candidateVersion
  226.         for or_group in self.getReplaces():
  227.             for (name, ver, oper) in or_group:
  228.                 if (name == pkgname and 
  229.                     apt_pkg.CheckDep(pkgver,oper,ver)):
  230.                     self._dbg(3, "we have a replaces in our package for the conflict against '%s'" % (pkgname))
  231.                     return True
  232.         return False
  233.  
  234.     def checkConflicts(self):
  235.         """ check if the pkg conflicts with a existing or to be installed
  236.             package. Return True if the pkg is ok """
  237.         res = True
  238.         for or_group in self.getConflicts():
  239.             if self._checkConflictsOrGroup(or_group):
  240.                 #print "Conflicts with a exisiting pkg!"
  241.                 #self._failureString = "Conflicts with a exisiting pkg!"
  242.                 res = False
  243.         return res
  244.  
  245.     def checkBreaksExistingPackages(self):
  246.         """ 
  247.         check if installing the package would break exsisting 
  248.         package on the system, e.g. system has:
  249.         smc depends on smc-data (= 1.4)
  250.         and user tries to installs smc-data 1.6
  251.         """
  252.         # show progress information as this step may take some time
  253.         size = float(len(self._cache))
  254.         steps = int(size/50)
  255.         debver = self._sections["Version"]
  256.         for (i, pkg) in enumerate(self._cache):
  257.             if i%steps == 0:
  258.                 self._cache.op_progress.update(float(i)/size*100.0)
  259.             if not pkg.isInstalled:
  260.                 continue
  261.             # check if the exising dependencies are still satisfied
  262.             # with the package
  263.             ver = pkg._pkg.CurrentVer
  264.             for dep_or in pkg.installedDependencies:
  265.                 for dep in dep_or.or_dependencies:
  266.                     if dep.name == self.pkgName:
  267.                         if not apt_pkg.CheckDep(debver,dep.relation,dep.version):
  268.                             self._dbg(2, "would break (depends) %s" % pkg.name)
  269.                             # TRANSLATORS: the first '%s' is the package that breaks, the second the dependency that makes it break, the third the releation (e.g. >=) and the latest the version for the releation
  270.                             self._failureString += _("Breaks exisiting package '%s' dependency %s (%s %s)\n") % (pkg.name, dep.name, dep.relation, dep.version)
  271.                             self._cache.op_progress.done()
  272.                             return False
  273.             # now check if there are conflicts against this package on
  274.             # the existing system
  275.             if ver.DependsList.has_key("Conflicts"):
  276.                 for conflictsVerList in ver.DependsList["Conflicts"]:
  277.                     for cOr in conflictsVerList:
  278.                         if cOr.TargetPkg.Name == self.pkgName:
  279.                             if apt_pkg.CheckDep(debver, cOr.CompType, cOr.TargetVer):
  280.                                 self._dbg(2, "would break (conflicts) %s" % pkg.name)
  281.                 # TRANSLATORS: the first '%s' is the package that conflicts, the second the packagename that it conflicts with (so the name of the deb the user tries to install), the third is the relation (e.g. >=) and the last is the version for the relation
  282.                                 self._failureString += _("Breaks exisiting package '%s' conflict: %s (%s %s)\n") % (pkg.name, cOr.TargetPkg.Name, cOr.CompType, cOr.TargetVer)
  283.                                 self._cache.op_progress.done()
  284.                                 return False
  285.         self._cache.op_progress.done()
  286.         return True
  287.  
  288.     # some constants
  289.     (NO_VERSION,
  290.      VERSION_OUTDATED,
  291.      VERSION_SAME,
  292.      VERSION_IS_NEWER) = range(4)
  293.     
  294.     def compareToVersionInCache(self, useInstalled=True):
  295.         """ checks if the pkg is already installed or availabe in the cache
  296.             and if so in what version, returns if the version of the deb
  297.             is not available,older,same,newer
  298.         """
  299.         self._dbg(3,"compareToVersionInCache")
  300.         pkgname = self._sections["Package"]
  301.         debver = self._sections["Version"]
  302.         self._dbg(1,"debver: %s" % debver)
  303.         if self._cache.has_key(pkgname):
  304.             if useInstalled:
  305.                 cachever = self._cache[pkgname].installedVersion
  306.             else:
  307.                 cachever = self._cache[pkgname].candidateVersion
  308.             if cachever != None:
  309.                 cmp = apt_pkg.VersionCompare(cachever,debver)
  310.                 self._dbg(1, "CompareVersion(debver,instver): %s" % cmp)
  311.                 if cmp == 0:
  312.                     return self.VERSION_SAME
  313.                 elif cmp < 0:
  314.                     return self.VERSION_IS_NEWER
  315.                 elif cmp > 0:
  316.                     return self.VERSION_OUTDATED
  317.         return self.NO_VERSION
  318.  
  319.     def checkDeb(self):
  320.         self._dbg(3,"checkDepends")
  321.  
  322.         # check arch
  323.         if not self._sections.has_key("Architecture"):
  324.             self._dbg(1, "ERROR: no architecture field")
  325.             self._failureString = _("No Architecture field in the package")
  326.             return False
  327.         arch = self._sections["Architecture"]
  328.         if  arch != "all" and arch != apt_pkg.Config.Find("APT::Architecture"):
  329.             self._dbg(1,"ERROR: Wrong architecture dude!")
  330.             self._failureString = _("Wrong architecture '%s'") % arch
  331.             return False
  332.  
  333.         # check version
  334.         res = self.compareToVersionInCache()
  335.         if res == self.VERSION_OUTDATED: # the deb is older than the installed
  336.             self._failureString = _("A later version is already installed")
  337.             return False
  338.  
  339.         # FIXME: this sort of error handling sux
  340.         self._failureString = ""
  341.  
  342.         # check conflicts
  343.         if not self.checkConflicts():
  344.             return False
  345.  
  346.         # set progress information
  347.         self._cache.op_progress.subOp = _("Analysing dependencies")
  348.  
  349.         # check if installing it would break anything on the 
  350.         # current system
  351.         if not self.checkBreaksExistingPackages():
  352.             return False
  353.  
  354.         # try to satisfy the dependencies
  355.         res = self._satisfyDepends(self.getDepends())
  356.         if not res:
  357.             return False
  358.  
  359.         # check for conflicts again (this time with the packages that are
  360.         # makeed for install)
  361.         if not self.checkConflicts():
  362.             return False
  363.  
  364.         if self._cache._depcache.BrokenCount > 0:
  365.             self._failureString = _("Failed to satisfy all dependencies (broken cache)")
  366.             # clean the cache again
  367.             self._cache.clear()
  368.             return False
  369.         return True
  370.  
  371.     def satisfyDependsStr(self, dependsstr):
  372.         return self._satisfyDepends(apt_pkg.ParseDepends(dependsstr))
  373.  
  374.     def _satisfyDepends(self, depends):
  375.         # turn off MarkAndSweep via a action group (if available)
  376.         try:
  377.             _actiongroup = apt_pkg.GetPkgActionGroup(self._cache._depcache)
  378.         except AttributeError, e:
  379.             pass
  380.         # check depends
  381.         for or_group in depends:
  382.             #print "or_group: %s" % or_group
  383.             #print "or_group satified: %s" % self._isOrGroupSatisfied(or_group)
  384.             if not self._isOrGroupSatisfied(or_group):
  385.                 if not self._satisfyOrGroup(or_group):
  386.                     return False
  387.         # now try it out in the cache
  388.             for pkg in self._needPkgs:
  389.                 try:
  390.                     self._cache[pkg].markInstall(fromUser=False)
  391.                 except SystemError, e:
  392.                     self._failureString = _("Cannot install '%s'") % pkg
  393.                     self._cache.clear()
  394.                     return False
  395.         return True
  396.  
  397.     def missingDeps(self):
  398.         self._dbg(1, "Installing: %s" % self._needPkgs)
  399.         if self._needPkgs == None:
  400.             self.checkDeb()
  401.         return self._needPkgs
  402.     missingDeps = property(missingDeps)
  403.  
  404.     def requiredChanges(self):
  405.         """ gets the required changes to satisfy the depends.
  406.             returns a tuple with (install, remove, unauthenticated)
  407.         """
  408.         install = []
  409.         remove = []
  410.         unauthenticated = []
  411.         for pkg in self._cache:
  412.             if pkg.markedInstall or pkg.markedUpgrade:
  413.                 install.append(pkg.name)
  414.                 # check authentication, one authenticated origin is enough
  415.                 # libapt will skip non-authenticated origins then
  416.                 authenticated = False
  417.                 for origin in pkg.candidateOrigin:
  418.                     authenticated |= origin.trusted
  419.                 if not authenticated:
  420.                     unauthenticated.append(pkg.name)
  421.             if pkg.markedDelete:
  422.                 remove.append(pkg.name)
  423.         return (install,remove, unauthenticated)
  424.     requiredChanges = property(requiredChanges)
  425.  
  426.     def filelist(self):
  427.         """ return the list of files in the deb """
  428.         files = []
  429.         def extract_cb(What,Name,Link,Mode,UID,GID,Size,MTime,Major,Minor):
  430.             #print "%s '%s','%s',%u,%u,%u,%u,%u,%u,%u"\
  431.             #      % (What,Name,Link,Mode,UID,GID,Size, MTime, Major, Minor)
  432.             files.append(Name)
  433.         try:
  434.             try:
  435.                 apt_inst.debExtract(open(self.file), extract_cb, "data.tar.gz")
  436.             except SystemError, e:
  437.                 try:
  438.                     apt_inst.debExtract(open(self.file), extract_cb, "data.tar.bz2")
  439.                 except SystemError, e:
  440.                     try:
  441.                         apt_inst.debExtract(open(self.file), extract_cb, "data.tar.lzma")
  442.                     except SystemError, e:
  443.                         return [_("List of files could not be read, please report this as a bug")]
  444.         # IOError may happen because of gvfs madness (LP: #211822)
  445.         except IOError, e:
  446.             return [_("IOError during filelist read: %s") % e]
  447.         return files
  448.     filelist = property(filelist)
  449.     
  450.     # properties
  451.     def __getitem__(self,item):
  452.         if not self._sections.has_key(item):
  453.             # Translators: it's for missing entries in the deb package,
  454.             # e.g. a missing "Maintainer" field
  455.             return _("%s is not available") % item
  456.         return self._sections[item]
  457.  
  458.     def _dbg(self, level, msg):
  459.         """Write debugging output to sys.stderr.
  460.         """
  461.         if level <= self.debug:
  462.             print >> sys.stderr, msg
  463.  
  464.  
  465. if __name__ == "__main__":
  466.  
  467.     cache = Cache()
  468.  
  469.     vp = "www-browser"
  470.     print "%s virtual: %s" % (vp,cache.isVirtualPkg(vp))
  471.     providers = cache.getProvidersForVirtual(vp)
  472.     print "Providers for %s :" % vp
  473.     for pkg in providers:
  474.         print " %s" % pkg.name
  475.     
  476.     d = DebPackage(cache, sys.argv[1])
  477.     print "Deb: %s" % d.pkgName
  478.     if not d.checkDeb():
  479.         print "can't be satified"
  480.         print d._failureString
  481.     print "missing deps: %s" % d.missingDeps
  482.     print d.requiredChanges
  483.  
  484.