Пример #1
0
class TestPathMapper(unittest.TestCase):
    def setUp(self):
        unittest.TestCase.setUp(self);
        self._var_root = "";
        self._var_cygwin_p = sys.platform.startswith("cygwin");
        self.obj = PathMapper(self._var_root, self._var_cygwin_p);

    def test__init__(self):
        self.assertTrue(isinstance(self.obj, PathMapper));
        self.assertEqual(self._var_root, self.obj.getRoot());

    def testAddMapping(self):
        mount = (
        "C:/cygwin/bin on /usr/bin type ntfs (binary,auto){LF}"
        "C:/cygwin/lib on /usr/lib type ntfs (binary,auto){LF}"
        "C:/cygwin on / type ntfs (binary,auto){LF}"
        "C: on /cygdrive/c type ntfs (binary,posix=0,user,noumount,auto){LF}"
        "".format(LF="\n")
        );
        f = TemporaryFile(mode='w+');
        f.writelines(mount);
        f.seek(0);
        mtab = f.readlines();
        f.close();

        mapping = {
            '/usr/bin/': "C:/cygwin/bin/",
            '/usr/lib/': "C:/cygwin/lib/",
            '/cygdrive/c/': "C:/",
        };
        self.obj._addMapping(mtab);
        self.assertEqual(self.obj.getMap(), mapping);
        self.assertEqual(self.obj.getMountRoot(), "C:/cygwin/");

    def testMapPath(self):
        if self._var_cygwin_p:
            self.assertEqual(self.obj.mapPath("/usr/bin/"), "/usr/bin/");
            return;

        mapping = {
             '/usr/bin/': 'C:/cygwin/bin/',
             '/usr/lib/': 'C:/cygwin/lib/',
             '/cygdrive/c/': 'C:/',
        };

        self.obj.setMap(mapping);

        for cyg in list(mapping.keys()):
            self.assertEqual(self.obj.mapPath(cyg), mapping[cyg]);
Пример #2
0
    def getRessource(self, filename):
        self.__rc = cautils.parse_rc(filename)

        if not self.__rc.cache:
            msg = "{0} doesn't define cache.".format(self.__rcFile)
            raise UnexpectedValueException(msg)
        if not self.__rc.mirror:
            msg = "{0} doesn't define mirror.".format(self.__rcFile)
            raise UnexpectedValueException(msg)

        # We want ROOT + "/etc/setup" and cd(ROOT) to work:
        # necessitates two different forms, prefix and absolute
        if (self.__cygwinPlatform):
            self.setRoot("/")
        else:
            self.setRoot(self.__rc.ROOT)
        self.__rc.ROOT = None
        self.__pm = PathMapper(self.__prefixRoot, self.__cygwinPlatform)
        self.__setupDir = self.__pm.mapPath(self.__setupDir)
        self.__rc.cache = self.__pm.mapPath(self.__rc.cache)
        self.__downloadDir = os.path.join(
            self.__rc.cache,
            urllib.quote(
                self.__rc.mirror +
                ('' if self.__rc.mirror.endswith('/') else '/'), '').lower())
        self.__installedDbFile = os.path.join(self.__setupDir, "installed.db")

        self.__setupIniPath = os.path.join(
            self.__downloadDir,
            self.__arch,
            "setup.ini",
        )
        self.__dosBash = "{0}bin/bash".format(self.__pm.getMountRoot())
        self.__dosLn = "{0}bin/ln".format(self.__pm.getMountRoot())
        self.__dosXz = self.__pm.mapPath("/usr/bin/xz")
        self.__dosDash = "{0}bin/dash".format(self.__pm.getMountRoot())
        return 0
Пример #3
0
    def getRessource(self, filename):
        self.__rc = cautils.parse_rc(filename);

        if not self.__rc.cache:
            msg = "{0} doesn't define cache.".format(self.__rcFile);
            raise UnexpectedValueException(msg);
        if not self.__rc.mirror:
            msg = "{0} doesn't define mirror.".format(self.__rcFile);
            raise UnexpectedValueException(msg);

        # We want ROOT + "/etc/setup" and cd(ROOT) to work:
        # necessitates two different forms, prefix and absolute
        if(self.__cygwinPlatform):
            self.setRoot("/");
        else:
            self.setRoot(self.__rc.ROOT);
        self.__rc.ROOT = None;
        self.__pm = PathMapper(self.__prefixRoot, self.__cygwinPlatform);
        self.__setupDir = self.__pm.mapPath(self.__setupDir);
        self.__rc.cache = self.__pm.mapPath(self.__rc.cache);
        self.__downloadDir = os.path.join(
            self.__rc.cache,
            urllib.quote(self.__rc.mirror+('' if self.__rc.mirror.endswith('/') else '/'), '').lower()
        );
        self.__installedDbFile = os.path.join(self.__setupDir, "installed.db");

        self.__setupIniPath = os.path.join(
            self.__downloadDir,
            self.__arch,
            "setup.ini",
        );
        self.__dosBash = "{0}bin/bash".format(self.__pm.getMountRoot());
        self.__dosLn = "{0}bin/ln".format(self.__pm.getMountRoot());
        self.__dosXz = self.__pm.mapPath("/usr/bin/xz");
        self.__dosDash = "{0}bin/dash".format(self.__pm.getMountRoot());
        return 0;
Пример #4
0
class CygAptSetup:
    RC_OPTIONS = [
        'ROOT',
        'mirror',
        'cache',

        # BC layer for `setup_ini` configuration field
        'setup_ini',
        'distname',
        'barred',
        'always_update'
    ]
    RC_COMMENTS = {
        'ROOT':
        "# The root of your Cygwin installation as a windows path\n",
        'mirror': ("# URL of your Cygwin mirror: example "
                   "http://mirror.internode.on.net/pub/cygwin/\n"),
        'cache': ("# Your package cache as a POSIX path: example "
                  "/e/home/cygwin_package_cache\n"),

        # BC layer for `setup_ini` configuration field
        "setup_ini":
        ("# setup.ini lists available packages and is "
         "downloaded from the top level\n"
         "# of the downloaded mirror. Standard location is "
         "/etc/setup/setup.ini,\n"
         "# seutp-2.ini for Cygwin 1.7 Beta\n"
         "# Deprecated since version 1.1 and will be removed in 2.0.\n"
         "# "),
        "distname":
        ("# The distribution, current previous or test "
         "[curr, prev, test].\n"
         "# Usually you want the \"curr\" version of a package.\n"),
        "barred": ("# Packages which cyg-apt can't change under Cygwin "
                   "since it depends on them.\n"
                   "# Run cyg-apt under DOS with -f (force) option to change "
                   "these packages.\n"
                   "# Treat Cygwin core packages with CAUTION.\n"),
        "always_update":
        ("# Always update setup.ini before any command "
         "that uses it. cyg-apt will be\n# faster and use less bandwidth if "
         "False but you will have to run the update\n# command manually.\n"),
    }
    GPG_GOOD_FINGER = "1169 DF9F 2273 4F74 3AA5  9232 A9A2 62FF 6760 41BA"
    GPG_CYG_PUBLIC_RING_URI = "http://cygwin.com/key/pubring.asc"
    VERSION = version.__version__

    def __init__(self, cygwin_p, verbose, arch="x86"):
        self.__cygwinPlatform = cygwin_p
        self.__verbose = verbose
        self.__appName = os.path.basename(sys.argv[0])
        self.setTmpDir()
        self.setPathMapper()
        self.__setupDir = "/etc/setup"
        self.__rc = ConfigStructure()
        self.__arch = arch

        self.__rc.ROOT = self.__pm.getMountRoot()

    def getCygwinPlatform(self):
        return self.__cygwinPlatform

    def setCygwinPlatform(self, cygwin_p):
        self.__cygwinPlatform = bool(cygwin_p)

    def setVerbose(self, verbose):
        self.__verbose = verbose

    def getVerbose(self):
        return self.__verbose

    def getAppName(self):
        return self.__appName

    def setAppName(self, app_name):
        self.__appName = str(app_name)

    def getTmpDir(self):
        return self.__tmpDir

    def setTmpDir(self, tmp_dir=None):
        if tmp_dir:
            self.__tmpDir = tmp_dir
        elif 'TMP' in os.environ:
            self.__tmpDir = os.environ['TMP']
        else:
            self.__tmpDir = "/usr/share/cyg-apt"

    def getPathMapper(self):
        return self.__pm

    def setPathMapper(self, path_mapper=None):
        if path_mapper:
            assert isinstance(path_mapper, PathMapper)
            self.__pm = path_mapper
        else:
            self.__pm = PathMapper("", self.__cygwinPlatform)

    def getRC(self):
        return self.__rc

    def setRC(self, rc_structure):
        assert isinstance(rc_structure, ConfigStructure)
        self.__rc = rc_structure

    def getSetupDir(self):
        return self.__setupDir

    def setSetupDir(self, setup_dir):
        self.__setupDir = str(setup_dir)

    def setArchitecture(self, architecture):
        self.__arch = architecture

    def _cygwinVersion(self):
        return float(platform.release()[:3])

    def getSetupRc(self, location):
        filename = os.path.join(location, "setup.rc")
        if not (os.path.exists(filename)):
            return (None, None)
        f = open(filename)
        setup_rc = f.readlines()
        f.close()
        last_cache = None
        last_mirror = None
        for i in range(0, (len(setup_rc) - 1)):
            if 'last-cache' in setup_rc[i]:
                last_cache = setup_rc[i + 1].strip()
            if 'last-mirror' in setup_rc[i]:
                last_mirror = setup_rc[i + 1].strip()
        last_cache = self.__pm.mapPath(last_cache)
        return (last_cache, last_mirror)

    def setup(self, force=False):
        """create cyg-apt configuration file, it overwrite with -f option"""
        if not self.__cygwinPlatform:
            msg = "setup outside Cygwin not supported."
            raise PlatformException(msg)
        if "HOME" in os.environ:
            rc_file = os.path.join(os.environ['HOME'],
                                   ".{0}".format(self.__appName))
        else:
            msg = "Can't locate home directory. Setup failed."
            raise EnvironementException(msg)
        if os.path.exists(rc_file) and not force:
            msg = "{0} exists, not overwriting.".format(rc_file)
            raise PathExistsException(msg, code=0)

        installed_db = os.path.join(self.__setupDir, "installed.db")
        missing_cache_marker = ""
        missing_mirror_marker = ""
        self.__rc.distname = 'curr'
        # Refuse to remove/install any package including these substrings
        # since cyg-apt is dependent on them
        self.__rc.barred = ""
        self.__rc.always_update = False

        if not self.__cygwinPlatform:
            msg = "Setup only supported under Cygwin."
            raise PlatformException(msg)

        (last_cache, last_mirror) = self.getSetupRc(self.__setupDir)
        if ((not last_cache) or (not last_mirror)):
            (last_cache, last_mirror) = self._getPre17Last(self.__setupDir)
            if ((not last_cache) or (not last_mirror)):
                print("{0}: {1}/setup.rc not found. Please edit {2} to "\
                "provide mirror and cache. See cygwin.com/mirrors.html "\
                "for the list of mirrors.".format(
                    self.__appName,
                    self.__setupDir,
                    rc_file
                ))
                last_cache = missing_cache_marker
                last_mirror = missing_mirror_marker
        self.__rc.mirror = last_mirror
        self.__rc.cache = last_cache

        # BC layer for `setup_ini` configuration field
        self.__rc.setup_ini = "{0}/setup.ini".format(self.__setupDir)

        contents = ""
        for i in self.__rc.__dict__:
            if i in list(self.RC_COMMENTS.keys()):
                contents += self.RC_COMMENTS[i]
            contents += "{0}=\"{1}\"\n\n".format(i, self.__rc.__dict__[i])
        f = open(rc_file, 'w')
        f.write(contents)
        f.close()
        print("{0}: creating {1}".format(self.__appName, rc_file))

        if not os.path.isdir(self.__rc.ROOT):
            msg = "{0} no root directory".format(self.__rc.ROOT)
            raise UnexpectedValueException(msg)
        if not os.path.isdir(self.__setupDir):
            sys.stderr.write('creating {0}\n'.format(self.__setupDir))
            os.makedirs(self.__setupDir)
        if not os.path.isfile(installed_db):
            self._writeInstalled(installed_db)

        setupIniPath = os.path.join(
            self.__pm.mapPath(self.__rc.cache),
            urllib.quote(
                self.__rc.mirror +
                ('' if self.__rc.mirror.endswith('/') else '/'), '').lower(),
            self.__arch,
            'setup.ini',
        )
        if not os.path.isfile(setupIniPath):
            sys.stderr.write('getting {0}\n'.format(setupIniPath))
            self.update(rc_file, True)

    def usage(self, cyg_apt_rc=None):
        print("{0}, version {1}".format(self.__appName, self.VERSION))
        print(copying.help_message, end="\n\n")
        if (cyg_apt_rc):
            print("Configuration: {0}".format(cyg_apt_rc))
        print("Usage: {0} [OPTION]... COMMAND [PACKAGE]...".format(
            self.__appName))
        print("\n  Commands:")
        members = []
        for m in inspect.getmembers(CygAptSetup) + inspect.getmembers(CygApt):
            if isinstance(m[1], type(self.usage)) and m[1].__doc__:
                members.append(m)

        pad = max(len(m[0]) for m in members)
        for m in members:
            print("    {0} : {1}".format(m[0].ljust(pad), m[1].__doc__))
        sys.stdout.write(
            "{LF}"
            "  Options:{LF}"
            "    -d, --download       download only{LF}"
            "    -h, --help           show brief usage{LF}"
            "    -m, --mirror=URL     use mirror{LF}"
            "    -t, --dist=NAME      set dist name (curr, test, prev){LF}"
            "    -x, --no-deps        ignore dependencies{LF}"
            "    -s, --regexp         search as regex pattern{LF}"
            "    -f, --nobarred       add/remove packages cyg-apt depends on{LF}"
            "    -X, --no-verify      do not verify setup.ini signatures{LF}"
            "    -y, --nopostinstall  do not run postinstall scripts{LF}"
            "    -z, --nopostremove   do not run preremove/postremove scripts{LF}"
            "    -q, --quiet          loggable output - no progress indicator{LF}"
            "".format(LF="\n"))

    def update(self, cyg_apt_rc, verify, main_mirror=None):
        """fetch current package database from mirror"""
        sig_name = None
        self.__rc = cautils.parse_rc(cyg_apt_rc)

        if (not self.__cygwinPlatform):
            self.__pm = PathMapper(self.__rc.ROOT[:-1], False)

        if (main_mirror):
            mirror = main_mirror
        else:
            mirror = self.__rc.mirror

        if not mirror:
            raise UnexpectedValueException(
                "A mirror must be specified on the configuration file \"{0}\" "
                "or with the command line option \"--mirror\". "
                "See cygwin.com/mirrors.html for the list of mirrors."
                "".format(cyg_apt_rc))

        if not mirror[-1] == "/":
            sep = "/"
        else:
            sep = ""

        setup_ini_names = [
            "setup.bz2",
            "setup.ini",
        ]

        bag = zip(setup_ini_names, list(range(len(setup_ini_names))))
        platform_dir = self.__arch + "/"

        for (setup_ini_name, index) in bag:
            setup_ini_url = '{0}{1}{2}{3}'.format(mirror, sep, platform_dir,
                                                  setup_ini_name)
            try:
                cautils.uri_get(self.__tmpDir,
                                setup_ini_url,
                                verbose=self.__verbose)
            except ApplicationException as e:
                # Failed to find a possible .ini
                if index == len(setup_ini_names) - 1:
                    raise e
                else:
                    continue
                    # Not an error to fail to find the first one
            # Take the first one we find
            break

        if setup_ini_name[-4:] == ".bz2":
            bz_file = os.path.join(self.__tmpDir, setup_ini_name)
            f = open(bz_file, "rb")
            compressed = f.read()
            f.close()

            decomp = bz2.decompress(compressed)
            os.remove(bz_file)
            setup_ini_name = "setup.ini"

            f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb")
            f.write(decomp)
            f.close()

        if not self.__cygwinPlatform:
            sys.stderr.write(
                "WARNING can't verify setup.ini outside Cygwin.\n")
            verify = False

        if verify:
            sig_name = "{0}.sig".format(setup_ini_name)
            sig_url = "{0}{1}{2}{3}".format(mirror, sep, platform_dir,
                                            sig_name)
            try:
                cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose)
            except RequestException as e:
                msg = ("Failed to download signature {0} Use -X to ignore "
                       "signatures.".format(sig_url))
                raise RequestException(msg, previous=e)

            if self.__cygwinPlatform:
                gpg_path = "gpg"
            else:
                if self._cygwinVersion() < 1.7:
                    gpg_path = "/usr/bin/gpg"
                else:
                    gpg_path = "/usr/local/bin/gpg"
            cmd = [gpg_path, "--verify", "--no-secmem-warning"]
            cmd.append("{0}/{1}".format(self.__tmpDir, sig_name))
            cmd.append("{0}/{1}".format(self.__tmpDir, setup_ini_name))
            p = Process(cmd)
            p.run()
            verify = p.getErrorOutput()
            if isinstance(verify, bytes):
                marker = self.GPG_GOOD_FINGER.encode()
            else:
                marker = self.GPG_GOOD_FINGER
            if not marker in verify:
                msg = ("{0} not signed by Cygwin's public key. "
                       "Use -X to ignore signatures.".format(setup_ini_url))
                raise SignatureException(msg)

        downloads = os.path.join(
            self.__pm.mapPath(self.__rc.cache),
            urllib.quote(mirror + ('' if mirror.endswith('/') else '/'),
                         '').lower(),
            platform_dir,
        )

        if not os.path.exists(downloads):
            os.makedirs(downloads)

        shutil.copy(os.path.join(self.__tmpDir, setup_ini_name),
                    os.path.join(downloads, setup_ini_name))

        # BC layer for `setup_ini` configuration field
        if self.__rc.setup_ini:
            setup_ini = self.__pm.mapPath(self.__rc.setup_ini)
            if os.path.exists(setup_ini):
                shutil.copy(setup_ini, "{0}.bak".format(setup_ini))
            shutil.copy(os.path.join(downloads, setup_ini_name), setup_ini)

        if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)):
            os.remove(os.path.join(self.__tmpDir, setup_ini_name))
        if sig_name:
            if os.path.exists(os.path.join(self.__tmpDir, sig_name)):
                os.remove(os.path.join(self.__tmpDir, sig_name))

    def _getPre17Last(self, location):
        if not os.path.exists(os.path.join(location, "last-mirror")) \
            or not os.path.exists(os.path.join(location, "last-cache")):
            return (None, None)
        else:
            f = open(os.path.join(location, "last-cache"))
            last_cache = f.read().strip()
            f.close()
            last_cache = self.__pm.mapPath(last_cache)
            f = open(os.path.join(location, "last-mirror"))
            last_mirror = f.read().strip()
            f.close()
            return (last_cache, last_mirror)

    def _writeInstalled(self, installed_db):
        if not self.__cygwinPlatform:
            raise PlatformException(
                "fail to create {0} only supported under Cygwin."
                "".format(installed_db))

        sys.stderr.write("creating {0} ... ".format(installed_db))

        db_contents = CygApt.INSTALLED_DB_MAGIC
        cygcheck_path = self.__pm.mapPath("/bin/cygcheck")

        if os.path.exists(cygcheck_path):
            cmd = [cygcheck_path, "-cd"]
            proc = Process(cmd)
            proc.mustRun()

            lines = proc.getOutput().splitlines(True)
            # remove first two lines
            pkgs = lines[2:]

            for pkg in pkgs:
                pkg = pkg.split()
                db_contents += "{0} {0}-{1}.tar.bz2 0\n".format(
                    pkg[0], pkg[1])

        f = open(installed_db, 'w')
        f.write(db_contents)
        f.close()

        sys.stderr.write("OK\n")

    def _gpgImport(self, uri):
        if not self.__cygwinPlatform:
            return

        cautils.uri_get(self.__tmpDir, uri, verbose=self.__verbose)
        tmpfile = os.path.join(self.__tmpDir, os.path.basename(uri))
        cmd = ["gpg"]
        cmd.append("--no-secmem-warning")
        cmd += ["--import", tmpfile]
        Process(cmd).mustRun()
Пример #5
0
    def update(self, cyg_apt_rc, verify, main_mirror=None):
        """fetch current package database from mirror"""
        sig_name = None
        self.__rc = cautils.parse_rc(cyg_apt_rc)

        if (not self.__cygwinPlatform):
            self.__pm = PathMapper(self.__rc.ROOT[:-1], False)

        if (main_mirror):
            mirror = main_mirror
        else:
            mirror = self.__rc.mirror

        if not mirror:
            raise UnexpectedValueException(
                "A mirror must be specified on the configuration file \"{0}\" "
                "or with the command line option \"--mirror\". "
                "See cygwin.com/mirrors.html for the list of mirrors."
                "".format(cyg_apt_rc))

        if not mirror[-1] == "/":
            sep = "/"
        else:
            sep = ""

        setup_ini_names = [
            "setup.bz2",
            "setup.ini",
        ]

        bag = zip(setup_ini_names, list(range(len(setup_ini_names))))
        platform_dir = self.__arch + "/"

        for (setup_ini_name, index) in bag:
            setup_ini_url = '{0}{1}{2}{3}'.format(mirror, sep, platform_dir,
                                                  setup_ini_name)
            try:
                cautils.uri_get(self.__tmpDir,
                                setup_ini_url,
                                verbose=self.__verbose)
            except ApplicationException as e:
                # Failed to find a possible .ini
                if index == len(setup_ini_names) - 1:
                    raise e
                else:
                    continue
                    # Not an error to fail to find the first one
            # Take the first one we find
            break

        if setup_ini_name[-4:] == ".bz2":
            bz_file = os.path.join(self.__tmpDir, setup_ini_name)
            f = open(bz_file, "rb")
            compressed = f.read()
            f.close()

            decomp = bz2.decompress(compressed)
            os.remove(bz_file)
            setup_ini_name = "setup.ini"

            f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb")
            f.write(decomp)
            f.close()

        if not self.__cygwinPlatform:
            sys.stderr.write(
                "WARNING can't verify setup.ini outside Cygwin.\n")
            verify = False

        if verify:
            sig_name = "{0}.sig".format(setup_ini_name)
            sig_url = "{0}{1}{2}{3}".format(mirror, sep, platform_dir,
                                            sig_name)
            try:
                cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose)
            except RequestException as e:
                msg = ("Failed to download signature {0} Use -X to ignore "
                       "signatures.".format(sig_url))
                raise RequestException(msg, previous=e)

            if self.__cygwinPlatform:
                gpg_path = "gpg"
            else:
                if self._cygwinVersion() < 1.7:
                    gpg_path = "/usr/bin/gpg"
                else:
                    gpg_path = "/usr/local/bin/gpg"
            cmd = [gpg_path, "--verify", "--no-secmem-warning"]
            cmd.append("{0}/{1}".format(self.__tmpDir, sig_name))
            cmd.append("{0}/{1}".format(self.__tmpDir, setup_ini_name))
            p = Process(cmd)
            p.run()
            verify = p.getErrorOutput()
            if isinstance(verify, bytes):
                marker = self.GPG_GOOD_FINGER.encode()
            else:
                marker = self.GPG_GOOD_FINGER
            if not marker in verify:
                msg = ("{0} not signed by Cygwin's public key. "
                       "Use -X to ignore signatures.".format(setup_ini_url))
                raise SignatureException(msg)

        downloads = os.path.join(
            self.__pm.mapPath(self.__rc.cache),
            urllib.quote(mirror + ('' if mirror.endswith('/') else '/'),
                         '').lower(),
            platform_dir,
        )

        if not os.path.exists(downloads):
            os.makedirs(downloads)

        shutil.copy(os.path.join(self.__tmpDir, setup_ini_name),
                    os.path.join(downloads, setup_ini_name))

        # BC layer for `setup_ini` configuration field
        if self.__rc.setup_ini:
            setup_ini = self.__pm.mapPath(self.__rc.setup_ini)
            if os.path.exists(setup_ini):
                shutil.copy(setup_ini, "{0}.bak".format(setup_ini))
            shutil.copy(os.path.join(downloads, setup_ini_name), setup_ini)

        if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)):
            os.remove(os.path.join(self.__tmpDir, setup_ini_name))
        if sig_name:
            if os.path.exists(os.path.join(self.__tmpDir, sig_name)):
                os.remove(os.path.join(self.__tmpDir, sig_name))
Пример #6
0
 def setPathMapper(self, path_mapper=None):
     if path_mapper:
         assert isinstance(path_mapper, PathMapper)
         self.__pm = path_mapper
     else:
         self.__pm = PathMapper("", self.__cygwinPlatform)
Пример #7
0
class CygAptSetup:
    RC_OPTIONS = [
        'ROOT',
        'mirror',
        'cache',
        'setup_ini',
        'distname',
        'barred',
        'always_update'
    ];
    RC_COMMENTS = {
        'ROOT' : "# The root of your Cygwin installation as a windows path\n",
        'mirror' : (
            "# URL of your Cygwin mirror: example "
            "http://mirror.internode.on.net/pub/cygwin/\n"
        ),
        'cache' : (
            "# Your package cache as a POSIX path: example "
            "/e/home/cygwin_package_cache\n"
        ),
        "setup_ini" : (
            "# setup.ini lists available packages and is "
            "downloaded from the top level\n"
            "# of the downloaded mirror. Standard location is "
            "/etc/setup/setup.ini,\n"
            "# seutp-2.ini for Cygwin 1.7 Beta\n"
        ),
        "distname" : (
            "# The distribution, current previous or test "
            "[curr, prev, test].\n"
            "# Usually you want the \"curr\" version of a package.\n"
        ),
        "barred" : (
            "# Packages which cyg-apt can't change under Cygwin "
            "since it depends on them.\n"
            "# Run cyg-apt under DOS with -f (force) option to change "
            "these packages.\n"
            "# Treat Cygwin core packages with CAUTION.\n"
        ),
        "always_update" : (
            "# Always update setup.ini before any command "
            "that uses it. cyg-apt will be\n# faster and use less bandwidth if "
            "False but you will have to run the update\n# command manually.\n"
        ),
    };
    RC_REGEX = re.compile(r"^\s*(\w+)\s*=\s*(.*)\s*$");
    GPG_GOOD_FINGER = "1169 DF9F 2273 4F74 3AA5  9232 A9A2 62FF 6760 41BA";
    GPG_CYG_PUBLIC_RING_URI = "http://cygwin.com/key/pubring.asc";
    VERSION = version.__version__;

    def __init__(self, cygwin_p, verbose):
        self.__cygwinPlatform = cygwin_p;
        self.__verbose = verbose;
        self.__appName = os.path.basename(sys.argv[0]);
        self.setTmpDir();
        self.setPathMapper();
        self.__setupDir = "/etc/setup";
        self.__rc = ConfigStructure();
        
        self.__rc.ROOT = self.__pm.getMountRoot();

    def getCygwinPlatform(self):
        return self.__cygwinPlatform;

    def setCygwinPlatform(self, cygwin_p):
        self.__cygwinPlatform = bool(cygwin_p);

    def setVerbose(self, verbose):
        self.__verbose = verbose;

    def getVerbose(self):
        return self.__verbose;

    def getAppName(self):
        return self.__appName;

    def setAppName(self, app_name):
        self.__appName = str(app_name);

    def getTmpDir(self):
        return self.__tmpDir;

    def setTmpDir(self, tmp_dir=None):
        if tmp_dir:
            self.__tmpDir = tmp_dir;
        elif 'TMP' in os.environ:
            self.__tmpDir = os.environ['TMP'];
        else:
            self.__tmpDir = "/usr/share/cyg-apt";

    def getPathMapper(self):
        return self.__pm;

    def setPathMapper(self, path_mapper=None):
        if path_mapper:
            assert isinstance(path_mapper, PathMapper);
            self.__pm = path_mapper;
        else:
            self.__pm = PathMapper("", self.__cygwinPlatform);

    def getRC(self):
        return self.__rc;

    def setRC(self, rc_structure):
        assert isinstance(rc_structure, ConfigStructure);
        self.__rc = rc_structure;

    def getSetupDir(self):
        return self.__setupDir;

    def setSetupDir(self, setup_dir):
        self.__setupDir = str(setup_dir);

    def _cygwinVersion(self):
        return float(platform.release()[:3]);

    def getSetupRc(self, location):
        filename = os.path.join(location, "setup.rc");
        if not (os.path.exists(filename)):
            return (None, None);
        f = open(filename);
        setup_rc = f.readlines();
        f.close();
        last_cache = None;
        last_mirror = None;
        for i in range(0, (len(setup_rc) -1)):
            if 'last-cache' in setup_rc[i]:
                last_cache = setup_rc[i+1].strip();
            if 'last-mirror' in setup_rc[i]:
                last_mirror = setup_rc[i+1].strip();
        last_cache = cautils.cygpath(last_cache);
        return (last_cache, last_mirror);

    def setup(self, force=False):
        """create cyg-apt configuration file, it overwrite with -f option"""
        if not self.__cygwinPlatform:
            msg = "setup outside Cygwin not supported.";
            raise PlatformException(msg);
        if "HOME" in os.environ:
            rc_file = os.path.join(
                os.environ['HOME'],
                ".{0}".format(self.__appName)
            );
        else:
            msg = "Can't locate home directory. Setup failed.";
            raise EnvironementException(msg);
        if os.path.exists(rc_file) and not force:
            msg = "{0} exists, not overwriting.".format(rc_file);
            raise PathExistsException(msg, code=0);

        installed_db = os.path.join(self.__setupDir, "installed.db");
        missing_cache_marker = "";
        missing_mirror_marker = "";
        self.__rc.distname = 'curr';
        # Refuse to remove/install any package including these substrings
        # since cyg-apt is dependent on them
        self.__rc.barred = "";
        self.__rc.always_update = False;

        if not self.__cygwinPlatform:
            msg = "Setup only supported under Cygwin.";
            raise PlatformException(msg);

        (last_cache, last_mirror) = self.getSetupRc(self.__setupDir);
        if ((not last_cache) or (not last_mirror)):
            (last_cache, last_mirror) = self._getPre17Last(self.__setupDir);
            if ((not last_cache) or (not last_mirror)):
                print("{0}: {1}/setup.rc not found. Please edit {2} to "\
                "provide mirror and cache.".format(
                    self.__appName,
                    self.__setupDir,
                    rc_file
                ));
                last_cache = missing_cache_marker;
                last_mirror  = missing_mirror_marker;
        self.__rc.mirror = last_mirror;
        self.__rc.cache = last_cache;
        self.__rc.setup_ini = "{0}/setup.ini".format(self.__setupDir);

        contents = "";
        for i in self.__rc.__dict__:
            if i in list(self.RC_COMMENTS.keys()):
                contents += self.RC_COMMENTS[i];
            contents += "{0}=\"{1}\"\n\n".format(i, self.__rc.__dict__[i]);
        f = open(rc_file, 'w');
        f.write(contents);
        f.close();
        print("{0}: creating {1}".format(self.__appName, rc_file));

        if not os.path.isdir(self.__rc.ROOT):
            msg = "{0} no root directory".format(self.__rc.ROOT);
            raise UnexpectedValueException(msg);
        if not os.path.isdir(self.__setupDir):
            sys.stderr.write('creating {0}\n'.format(self.__setupDir));
            os.makedirs(self.__setupDir);
        if not os.path.isfile(installed_db):
            self._writeInstalled(installed_db);
        if not os.path.isfile(self.__rc.setup_ini):
            sys.stderr.write('getting {0}\n'.format(self.__rc.setup_ini));
            self.update(rc_file, True);

    def usage(self, cyg_apt_rc=None):
        print("{0}, version {1}".format(self.__appName, self.VERSION));
        print(copying.help_message, end="\n\n");
        if (cyg_apt_rc):
            print("Configuration: {0}".format(cyg_apt_rc));
        print("Usage: {0} [OPTION]... COMMAND [PACKAGE]...".format(
            self.__appName
        ));
        print("\n  Commands:");
        members = [];
        for m in inspect.getmembers(CygAptSetup) + inspect.getmembers(CygApt):
            if isinstance(m[1], type(self.usage)) and m[1].__doc__:
                members.append(m);

        pad = max(len(m[0]) for m in members);
        for m in members:
            print("    {0} : {1}".format(m[0].ljust(pad), m[1].__doc__));
        sys.stdout.write(
        "{LF}"
        "  Options:{LF}"
        "    -d, --download       download only{LF}"
        "    -h, --help           show brief usage{LF}"
        "    -m, --mirror=URL     use mirror{LF}"
        "    -t, --dist=NAME      set dist name (curr, test, prev){LF}"
        "    -x, --no-deps        ignore dependencies{LF}"
        "    -s, --regexp         search as regex pattern{LF}"
        "    -f, --nobarred       add/remove packages cyg-apt depends on{LF}"
        "    -X, --no-verify      do not verify setup.ini signatures{LF}"
        "    -y, --nopostinstall  do not run postinstall scripts{LF}"
        "    -z, --nopostremove   do not run preremove/postremove scripts{LF}"
        "    -q, --quiet          loggable output - no progress indicator{LF}"
        "".format(LF="\n")
        );

    def update(self, cyg_apt_rc, verify, main_mirror=None):
        """fetch current package database from mirror"""
        sig_name = None;
        f = open(cyg_apt_rc);
        lines = f.readlines();
        f.close();
        for i in lines:
            result = self.RC_REGEX.search(i);
            if result:
                k = result.group(1);
                v = result.group(2);
                if k in self.__rc.__dict__:
                    self.__rc.__dict__[k] = eval(v);

        if(not self.__cygwinPlatform):
            self.__pm = PathMapper(self.__rc.ROOT[:-1], False);

        setup_ini = self.__pm.mapPath(self.__rc.setup_ini);
        if (main_mirror):
            mirror = main_mirror;
        else:
            mirror = self.__rc.mirror;
        downloads = os.path.join(
            self.__pm.mapPath(self.__rc.cache),
            urllib.quote(mirror, '').lower()
        );
        if not mirror[-1] == "/":
            sep = "/";
        else:
            sep = "";

        setup_ini_names = [
            os.path.basename(setup_ini).replace(".ini", ".bz2"),
            os.path.basename(setup_ini)
        ];

        bag = zip(setup_ini_names, list(range(len(setup_ini_names))));

        for (setup_ini_name, index) in bag:
            setup_ini_url = '{0}{1}{2}'.format(mirror, sep, setup_ini_name);
            try:
                cautils.uri_get(
                    self.__tmpDir,
                    setup_ini_url,
                    verbose=self.__verbose
                );
            except ApplicationException as e:
                # Failed to find a possible .ini
                if index == len(setup_ini_names) - 1:
                    raise e;
                else:
                    continue;
                    # Not an error to fail to find the first one
            # Take the first one we find
            break;

        if setup_ini_name[-4:] == ".bz2":
            bz_file = os.path.join(self.__tmpDir, setup_ini_name);
            f = open(bz_file, "rb");
            compressed = f.read();
            f.close();

            decomp = bz2.decompress(compressed);
            os.remove(bz_file);
            setup_ini_name =  os.path.basename(setup_ini);

            f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb");
            f.write(decomp);
            f.close();

        if not self.__cygwinPlatform:
            sys.stderr.write("WARNING can't verify setup.ini outside Cygwin.\n");
            verify = False;

        if verify:
            sig_name = "{0}.sig".format(setup_ini_name);
            sig_url = "{0}{1}{2}".format(mirror, sep, sig_name);
            try:
                cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose);
            except RequestException as e:
                msg = (
                    "Failed to download signature {0} Use -X to ignore "
                    "signatures.".format(sig_url)
                );
                raise RequestException(msg, previous=e);

            if self.__cygwinPlatform:
                gpg_path = "gpg ";
            else:
                if self._cygwinVersion() < 1.7:
                    gpg_path = "/usr/bin/gpg ";
                else:
                    gpg_path = "/usr/local/bin/gpg ";
            cmd = gpg_path + "--verify --no-secmem-warning ";
            cmd += "{0}/{1} ".format(self.__tmpDir, sig_name);
            cmd += "{0}/{1} ".format(self.__tmpDir, setup_ini_name);
            p = subprocess.Popen(
                cmd,
                shell=True,
                stderr=subprocess.PIPE
            );
            p.wait();
            verify = p.stderr.read();
            if isinstance(verify, bytes):
                marker = self.GPG_GOOD_FINGER.encode();
            else:
                marker = self.GPG_GOOD_FINGER;
            if not marker in verify:
                msg = (
                    "{0} not signed by Cygwin's public key. "
                    "Use -X to ignore signatures.".format(setup_ini_url)
                );
                raise SignatureException(msg);

        if not os.path.exists(downloads):
            os.makedirs(downloads);

        shutil.copy(
            os.path.join(self.__tmpDir, setup_ini_name),
            os.path.join(downloads, setup_ini_name)
        );
        if os.path.exists(setup_ini):
            shutil.copy(setup_ini, "{0}.bak".format(setup_ini));
        shutil.copy(
            os.path.join(downloads, setup_ini_name),
            setup_ini
        );
        if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)):
            os.remove(os.path.join(self.__tmpDir, setup_ini_name));
        if sig_name:
            if os.path.exists(os.path.join(self.__tmpDir, sig_name)):
                os.remove(os.path.join(self.__tmpDir, sig_name));

    def _getPre17Last(self, location):
        if not os.path.exists(os.path.join(location, "last-mirror")) \
            or not os.path.exists(os.path.join(location, "last-cache")):
            return (None, None);
        else:
            f = open(os.path.join(location, "last-cache"));
            last_cache = f.read().strip();
            f.close();
            last_cache = cautils.cygpath(last_cache);
            f = open(os.path.join(location, "last-mirror"));
            last_mirror = f.read().strip();
            f.close();
            return (last_cache, last_mirror);

    def _writeInstalled(self, installed_db):
        if not self.__cygwinPlatform:
            raise PlatformException(
                "fail to create {0} only supported under Cygwin."
                "".format(installed_db)
            );

        sys.stderr.write("creating {0} ... ".format(installed_db));

        db_contents = CygApt.INSTALLED_DB_MAGIC;
        cygcheck_path = self.__pm.mapPath("/bin/cygcheck");

        if os.path.exists(cygcheck_path):
            cmd = cygcheck_path + " -cd ";
            proc = subprocess.Popen(
                cmd,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            );
            if proc.wait():
                raise ProcessException(proc.stderr.readlines());

            lines = proc.stdout.readlines();
            # remove first two lines
            pkgs = lines[2:];

            for pkg in pkgs:
                pkg = pkg.split();
                db_contents += "{0} {0}-{1}.tar.bz2 0\n".format(pkg[0], pkg[1]);

        f = open(installed_db, 'w');
        f.write(db_contents);
        f.close();

        sys.stderr.write("OK\n");

    def _gpgImport(self, uri):
        if not self.__cygwinPlatform:
            return;

        cautils.uri_get(self.__tmpDir, uri, verbose=self.__verbose);
        tmpfile = os.path.join(self.__tmpDir, os.path.basename(uri));
        cmd = "gpg ";
        cmd += "--no-secmem-warning ";
        cmd += "--import {0}".format(tmpfile);
        p = subprocess.Popen(
            cmd,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        );
        if p.wait():
            raise ProcessException(p.stderr.readlines());
Пример #8
0
    def update(self, cyg_apt_rc, verify, main_mirror=None):
        """fetch current package database from mirror"""
        sig_name = None;
        f = open(cyg_apt_rc);
        lines = f.readlines();
        f.close();
        for i in lines:
            result = self.RC_REGEX.search(i);
            if result:
                k = result.group(1);
                v = result.group(2);
                if k in self.__rc.__dict__:
                    self.__rc.__dict__[k] = eval(v);

        if(not self.__cygwinPlatform):
            self.__pm = PathMapper(self.__rc.ROOT[:-1], False);

        setup_ini = self.__pm.mapPath(self.__rc.setup_ini);
        if (main_mirror):
            mirror = main_mirror;
        else:
            mirror = self.__rc.mirror;
        downloads = os.path.join(
            self.__pm.mapPath(self.__rc.cache),
            urllib.quote(mirror, '').lower()
        );
        if not mirror[-1] == "/":
            sep = "/";
        else:
            sep = "";

        setup_ini_names = [
            os.path.basename(setup_ini).replace(".ini", ".bz2"),
            os.path.basename(setup_ini)
        ];

        bag = zip(setup_ini_names, list(range(len(setup_ini_names))));

        for (setup_ini_name, index) in bag:
            setup_ini_url = '{0}{1}{2}'.format(mirror, sep, setup_ini_name);
            try:
                cautils.uri_get(
                    self.__tmpDir,
                    setup_ini_url,
                    verbose=self.__verbose
                );
            except ApplicationException as e:
                # Failed to find a possible .ini
                if index == len(setup_ini_names) - 1:
                    raise e;
                else:
                    continue;
                    # Not an error to fail to find the first one
            # Take the first one we find
            break;

        if setup_ini_name[-4:] == ".bz2":
            bz_file = os.path.join(self.__tmpDir, setup_ini_name);
            f = open(bz_file, "rb");
            compressed = f.read();
            f.close();

            decomp = bz2.decompress(compressed);
            os.remove(bz_file);
            setup_ini_name =  os.path.basename(setup_ini);

            f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb");
            f.write(decomp);
            f.close();

        if not self.__cygwinPlatform:
            sys.stderr.write("WARNING can't verify setup.ini outside Cygwin.\n");
            verify = False;

        if verify:
            sig_name = "{0}.sig".format(setup_ini_name);
            sig_url = "{0}{1}{2}".format(mirror, sep, sig_name);
            try:
                cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose);
            except RequestException as e:
                msg = (
                    "Failed to download signature {0} Use -X to ignore "
                    "signatures.".format(sig_url)
                );
                raise RequestException(msg, previous=e);

            if self.__cygwinPlatform:
                gpg_path = "gpg ";
            else:
                if self._cygwinVersion() < 1.7:
                    gpg_path = "/usr/bin/gpg ";
                else:
                    gpg_path = "/usr/local/bin/gpg ";
            cmd = gpg_path + "--verify --no-secmem-warning ";
            cmd += "{0}/{1} ".format(self.__tmpDir, sig_name);
            cmd += "{0}/{1} ".format(self.__tmpDir, setup_ini_name);
            p = subprocess.Popen(
                cmd,
                shell=True,
                stderr=subprocess.PIPE
            );
            p.wait();
            verify = p.stderr.read();
            if isinstance(verify, bytes):
                marker = self.GPG_GOOD_FINGER.encode();
            else:
                marker = self.GPG_GOOD_FINGER;
            if not marker in verify:
                msg = (
                    "{0} not signed by Cygwin's public key. "
                    "Use -X to ignore signatures.".format(setup_ini_url)
                );
                raise SignatureException(msg);

        if not os.path.exists(downloads):
            os.makedirs(downloads);

        shutil.copy(
            os.path.join(self.__tmpDir, setup_ini_name),
            os.path.join(downloads, setup_ini_name)
        );
        if os.path.exists(setup_ini):
            shutil.copy(setup_ini, "{0}.bak".format(setup_ini));
        shutil.copy(
            os.path.join(downloads, setup_ini_name),
            setup_ini
        );
        if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)):
            os.remove(os.path.join(self.__tmpDir, setup_ini_name));
        if sig_name:
            if os.path.exists(os.path.join(self.__tmpDir, sig_name)):
                os.remove(os.path.join(self.__tmpDir, sig_name));
Пример #9
0
    def _createCygApt(self):
        """Creates a CygApt instance.

        @return: CygApt
        """
        cygapt = CygApt(
            self._var_packagename,
            self._var_files,
            self._file_user_config,
            self._var_cygwin_p,
            self._var_download_p,
            self._var_mirror,
            self._var_downloads,
            self._var_distname,
            self._var_nodeps_p,
            self._var_regex_search,
            self._var_nobarred,
            self._var_nopostinstall,
            self._var_nopostremove,
            self._var_dists,
            self._var_installed,
            self._var_exename,
            self._var_verbose,
            self._var_arch,
            self._dir_confsetup,
        )

        # set attributes
        rc = ConfigStructure()
        rc.cache = self._dir_execache
        rc.distname = 'curr'

        # BC layer for `setup_ini` configuration field
        del rc.__dict__['setup_ini']

        rc.ROOT = self._dir_mtroot
        rc.always_update = False
        rc.mirror = self._var_mirror
        cygapt.setRC(rc)

        cygapt.setInstalledDbFile(self._file_installed_db)
        self.assertEqual(self._dir_confsetup, cygapt.getSetupDir())

        pm = PathMapper("", False)
        pm.setRoot(self._dir_mtroot[:-1])
        pm.setMountRoot(self._dir_mtroot)
        pm.setMap({self._dir_mtroot: self._dir_mtroot})

        expected = self._dir_mtroot
        ret = pm.mapPath(self._dir_mtroot)
        self.assertEqual(ret, expected)
        expected = os.path.join(self._dir_mtroot, "diranme")
        ret = pm.mapPath(expected)
        self.assertEqual(ret, expected)

        cygapt.setPathMapper(pm)

        cygapt.setDists(self._var_setupIni.dists.__dict__)

        if self._var_cygwin_p:
            cygapt.CYG_POSTINSTALL_DIR = self._dir_postinstall
            cygapt.CYG_PREREMOVE_DIR = self._dir_preremove
            cygapt.CYG_POSTREMOVE_DIR = self._dir_postremove

        # requires bash, ln and xz on PATH
        cygapt.setDosBash("bash")
        cygapt.setDosLn("ln")
        cygapt.setDosXz('xz')
        cygapt.setDosDash('dash')

        cygapt.setPrefixRoot(self._dir_mtroot[:-1])
        cygapt.setAbsRoot(self._dir_mtroot)
        cygapt.setInstalled({0: {}})

        cygapt.FORCE_BARRED.extend([
            self._var_setupIni.barredpkg.name,
        ])

        return cygapt
Пример #10
0
 def setUp(self):
     unittest.TestCase.setUp(self);
     self._var_root = "";
     self._var_cygwin_p = sys.platform.startswith("cygwin");
     self.obj = PathMapper(self._var_root, self._var_cygwin_p);
Пример #11
0
    def _createCygApt(self):
        """Creates a CygApt instance.

        @return: CygApt
        """
        cygapt = CygApt(
            self._var_packagename,
            self._var_files,
            self._file_user_config,
            self._var_cygwin_p,
            self._var_download_p,
            self._var_mirror,
            self._var_downloads,
            self._var_distname,
            self._var_nodeps_p,
            self._var_regex_search,
            self._var_nobarred,
            self._var_nopostinstall,
            self._var_nopostremove,
            self._var_dists,
            self._var_installed,
            self._var_exename,
            self._var_verbose,
            self._var_arch,
            self._dir_confsetup,
        );

        # set attributes
        rc = ConfigStructure();
        rc.cache = self._dir_execache;
        rc.distname = 'curr';

        # BC layer for `setup_ini` configuration field
        del rc.__dict__['setup_ini'];

        rc.ROOT = self._dir_mtroot;
        rc.always_update = False;
        rc.mirror = self._var_mirror;
        cygapt.setRC(rc);

        cygapt.setInstalledDbFile(self._file_installed_db);
        self.assertEqual(self._dir_confsetup, cygapt.getSetupDir());

        pm = PathMapper("", False);
        pm.setRoot(self._dir_mtroot[:-1]);
        pm.setMountRoot(self._dir_mtroot);
        pm.setMap({self._dir_mtroot:self._dir_mtroot});

        expected = self._dir_mtroot;
        ret = pm.mapPath(self._dir_mtroot);
        self.assertEqual(ret, expected);
        expected = os.path.join(self._dir_mtroot, "diranme");
        ret = pm.mapPath(expected);
        self.assertEqual(ret, expected);

        cygapt.setPathMapper(pm);

        cygapt.setDists(self._var_setupIni.dists.__dict__);

        if self._var_cygwin_p :
            cygapt.CYG_POSTINSTALL_DIR = self._dir_postinstall;
            cygapt.CYG_PREREMOVE_DIR = self._dir_preremove;
            cygapt.CYG_POSTREMOVE_DIR = self._dir_postremove;

        # requires bash, ln and xz on PATH
        cygapt.setDosBash("bash");
        cygapt.setDosLn("ln");
        cygapt.setDosXz('xz');
        cygapt.setDosDash('dash');

        cygapt.setPrefixRoot(self._dir_mtroot[:-1]);
        cygapt.setAbsRoot(self._dir_mtroot);
        cygapt.setInstalled({0:{}});

        cygapt.FORCE_BARRED.extend([
            self._var_setupIni.barredpkg.name,
        ]);

        return cygapt;
Пример #12
0
    def update(self, cyg_apt_rc, verify, main_mirror=None):
        """fetch current package database from mirror"""
        sig_name = None;
        self.__rc = cautils.parse_rc(cyg_apt_rc);

        if(not self.__cygwinPlatform):
            self.__pm = PathMapper(self.__rc.ROOT[:-1], False);

        if (main_mirror):
            mirror = main_mirror;
        else:
            mirror = self.__rc.mirror;

        if not mirror :
            raise UnexpectedValueException(
                "A mirror must be specified on the configuration file \"{0}\" "
                "or with the command line option \"--mirror\". "
                "See cygwin.com/mirrors.html for the list of mirrors."
                "".format(cyg_apt_rc)
            );

        if not mirror[-1] == "/":
            sep = "/";
        else:
            sep = "";

        setup_ini_names = [
            "setup.bz2",
            "setup.ini",
        ];

        bag = zip(setup_ini_names, list(range(len(setup_ini_names))));
        platform_dir = self.__arch+"/";

        for (setup_ini_name, index) in bag:
            setup_ini_url = '{0}{1}{2}{3}'.format(mirror, sep, platform_dir, setup_ini_name);
            try:
                cautils.uri_get(
                    self.__tmpDir,
                    setup_ini_url,
                    verbose=self.__verbose
                );
            except ApplicationException as e:
                # Failed to find a possible .ini
                if index == len(setup_ini_names) - 1:
                    raise e;
                else:
                    continue;
                    # Not an error to fail to find the first one
            # Take the first one we find
            break;

        if setup_ini_name[-4:] == ".bz2":
            bz_file = os.path.join(self.__tmpDir, setup_ini_name);
            f = open(bz_file, "rb");
            compressed = f.read();
            f.close();

            decomp = bz2.decompress(compressed);
            os.remove(bz_file);
            setup_ini_name = "setup.ini";

            f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb");
            f.write(decomp);
            f.close();

        if not self.__cygwinPlatform:
            sys.stderr.write("WARNING can't verify setup.ini outside Cygwin.\n");
            verify = False;

        if verify:
            sig_name = "{0}.sig".format(setup_ini_name);
            sig_url = "{0}{1}{2}{3}".format(mirror, sep, platform_dir, sig_name);
            try:
                cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose);
            except RequestException as e:
                msg = (
                    "Failed to download signature {0} Use -X to ignore "
                    "signatures.".format(sig_url)
                );
                raise RequestException(msg, previous=e);

            if self.__cygwinPlatform:
                gpg_path = "gpg";
            else:
                if self._cygwinVersion() < 1.7:
                    gpg_path = "/usr/bin/gpg";
                else:
                    gpg_path = "/usr/local/bin/gpg";
            cmd = [gpg_path, "--verify", "--no-secmem-warning"];
            cmd.append("{0}/{1}".format(self.__tmpDir, sig_name));
            cmd.append("{0}/{1}".format(self.__tmpDir, setup_ini_name));
            p = Process(cmd);
            p.run();
            verify = p.getErrorOutput();
            if isinstance(verify, bytes):
                marker = self.GPG_GOOD_FINGER.encode();
            else:
                marker = self.GPG_GOOD_FINGER;
            if not marker in verify:
                msg = (
                    "{0} not signed by Cygwin's public key. "
                    "Use -X to ignore signatures.".format(setup_ini_url)
                );
                raise SignatureException(msg);

        downloads = os.path.join(
            self.__pm.mapPath(self.__rc.cache),
            urllib.quote(mirror+('' if mirror.endswith('/') else '/'), '').lower(),
            platform_dir,
        );

        if not os.path.exists(downloads):
            os.makedirs(downloads);

        shutil.copy(
            os.path.join(self.__tmpDir, setup_ini_name),
            os.path.join(downloads, setup_ini_name)
        );

        # BC layer for `setup_ini` configuration field
        if self.__rc.setup_ini :
            setup_ini = self.__pm.mapPath(self.__rc.setup_ini);
            if os.path.exists(setup_ini):
                shutil.copy(setup_ini, "{0}.bak".format(setup_ini));
            shutil.copy(
                os.path.join(downloads, setup_ini_name),
                setup_ini
            );

        if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)):
            os.remove(os.path.join(self.__tmpDir, setup_ini_name));
        if sig_name:
            if os.path.exists(os.path.join(self.__tmpDir, sig_name)):
                os.remove(os.path.join(self.__tmpDir, sig_name));
Пример #13
0
class CygApt:
    INSTALLED_DB_MAGIC = "INSTALLED.DB 2\n"
    DIST_NAMES = ('curr', 'test', 'prev')
    FORCE_BARRED = ["python", "python-argparse", "gnupg", "xz"]
    SH_OPTIONS = ["--norc", "--noprofile"]
    DASH_OPTIONS = []
    CMD_OPTIONS = ["/V:ON", "/E:ON", "/C"]
    CYG_POSTINSTALL_DIR = "/etc/postinstall"
    CYG_PREREMOVE_DIR = "/etc/preremove"
    CYG_POSTREMOVE_DIR = "/etc/postremove"

    def __init__(
        self,
        main_packagename,
        main_files,
        main_cyg_apt_rc,
        main_cygwin_p,
        main_download_p,
        main_mirror,
        main_downloads,
        main_distname,
        main_nodeps_p,
        main_regex_search,
        main_nobarred,
        main_nopostinstall,
        main_nopostremove,
        main_dists,
        main_installed,
        main_scriptname,
        main_verbose,
        arch,
        setupDir="/etc/setup",
    ):

        # Define private properties
        self.__ballTarget = 'install'
        self.__regexSearch = main_regex_search
        self.__noBarred = main_nobarred
        self.__noPostInstall = main_nopostinstall
        self.__noPostRemove = main_nopostremove
        self.__appName = main_scriptname
        self.__files = main_files
        self.__downloadOnly = main_download_p
        self.__downloadDir = main_downloads
        self.__noDeps = main_nodeps_p
        self.__rcFile = main_cyg_apt_rc
        self.__verbose = main_verbose
        self.__setupDir = setupDir
        self.__rc = ConfigStructure()
        self.__setupIniPath = None
        self.__arch = arch

        # Init
        self.setPkgName(main_packagename)
        self.setCygwinPlatform(main_cygwin_p)
        self.setDists(main_dists)
        self.setInstalled(main_installed)

        # Read in our configuration
        self.getRessource(self.__rcFile)

        # Now we have a path mapper, check setup.exe is not running
        self._checkForSetupExe()

        # DOS specific
        if not self.__cygwinPlatform:
            self.__lnExists = os.path.exists("{0}/bin/ln.exe".format(
                self.__prefixRoot))
        else:
            self.__lnExists = True

        # Overrides to the .rc
        if (main_mirror):
            self.__rc.mirror = main_mirror
            self.__downloadDir = os.path.join(
                self.__rc.cache,
                urllib.quote(
                    self.__rc.mirror +
                    ('' if self.__rc.mirror.endswith('/') else '/'),
                    '').lower())

        if (main_distname):
            self.__rc.distname = main_distname

        if not (os.path.isfile(self.__installedDbFile) \
                or os.path.isfile(self.__setupIniPath)):
            msg = "{0} no such file, run {1} setup?".format(
                self.__installedDbFile, self.__appName)
            raise PackageCacheException(msg)
        else:
            self._getSetupIni()
            self.getInstalled()

    def getPkgName(self):
        return self.__pkgName

    def setPkgName(self, pkg_name):
        self.__pkgName = str(pkg_name)

    def getCygwinPlatform(self):
        return self.__cygwinPlatform

    def setCygwinPlatform(self, cygwin_p):
        self.__cygwinPlatform = bool(cygwin_p)

    def getDownloadDir(self):
        return self.__downloadDir

    def setDownlaodDir(self, download_dir):
        self.__downloadDir = str(download_dir)

    def getDists(self):
        return self.__dists

    def setDists(self, dists):
        self.__dists = dists

    def getSetupDir(self):
        return self.__setupDir

    def setSetupDir(self, setup_dir):
        self.__setupDir = str(setup_dir)

    def getInstalledDbFile(self):
        return self.__installedDbFile

    def setInstalledDbFile(self, installed_db_file):
        self.__installedDbFile = str(installed_db_file)

    def getDosBash(self):
        return self.__dosBash

    def setDosBash(self, dos_bash):
        self.__dosBash = str(dos_bash)

    def getDosLn(self):
        return self.__dosLn

    def setDosLn(self, dos_ln):
        self.__dosLn = str(dos_ln)

    def getDosXz(self):
        return self.__dosXz

    def setDosXz(self, dos_xz):
        self.__dosXz = str(dos_xz)

    def getDosDash(self):
        return self.__dosDash

    def setDosDash(self, dosDash):
        self.__dosDash = str(dosDash)

    def getPrefixRoot(self):
        return self.__prefixRoot

    def setPrefixRoot(self, prefix_root):
        self.__prefixRoot = prefix_root

    def getAbsRoot(self):
        return self.__absRoot

    def setAbsRoot(self, absolute_root):
        self.__absRoot = absolute_root

    def getPathMapper(self):
        return self.__pm

    def setPathMapper(self, path_mapper):
        assert isinstance(path_mapper, PathMapper)
        self.__pm = path_mapper

    def getRC(self):
        return self.__rc

    def setRC(self, rc_structure):
        assert isinstance(rc_structure, ConfigStructure)
        self.__rc = rc_structure

    def _checkForSetupExe(self):
        # It's far from bulletproof, but it's surprisingly hard to detect
        # setup.exe running since it doesn't lock any files.
        p = Process([self.__pm.mapPath("/bin/ps"), "-W"])
        p.run()
        psout = p.getOutput().splitlines(True)
        setup_re = re.compile(
            r"(?<![a-z0-9_ -])setup(|-1\.7|-x86|-x86_64)\.exe", re.IGNORECASE)
        for l in psout:
            m = setup_re.search(l)
            if m:
                raise AppConflictException("Please close {0} while "
                                           "running {1}".format(
                                               m.group(0), self.__appName))

    def _versionToString(self, t):
        def try_itoa(x):
            if isinstance(x, int):
                return "{0:d}".format(x)
            return x

        return "{0}-{1}".format(".".join(list(map(try_itoa, t[:-1]))), t[-1])

    def _stringToVersion(self, s):
        s = re.sub(r"([^0-9][^0-9]*)", " \\1 ", s)
        s = re.sub(r"[ _.-][ _.-]*", " ", s)

        def try_atoi(x):
            if re.match(r"^[0-9]*$", x):
                return int(x)
            return x

        return tuple(map(try_atoi, (s.split(' '))))

    def _splitBall(self, p):
        m = re.match(r"^(.*)-([0-9].*-[0-9]+)(.tar.(bz2|xz))?$", p)
        if not m:
            print("splitBall: {0}".format(p))
            return (p[:2], (0, 0))
        t = (m.group(1), self._stringToVersion(m.group(2)))
        return t

    def _joinBall(self, t):
        return "{0}-{1}".format(t[0], self._versionToString(t[1]))

    def _debug(self, s):
        s

    def help(self, ):
        """this help message"""
        pass

    def _getSetupIni(self):
        if self.__dists:
            return
        self.__dists = {
            'test': {},
            'curr': {},
            'prev': {}
        }
        f = open(self.__setupIniPath)
        contents = f.read()
        f.close()
        chunks = contents.split("\n\n@ ")
        for i in chunks[1:]:
            lines = i.split("\n")
            name = lines[0].strip()
            self._debug("package: {0}".format(name))
            packages = self.__dists['curr']
            records = {
                'sdesc': name
            }
            j = 1
            while j < len(lines) and lines[j].strip():
                self._debug("raw: {0}".format(lines[j]))
                if lines[j][0] == '#':
                    j = j + 1
                    continue
                elif lines[j][0] == '[':
                    self._debug('dist: {0}'.format(lines[j][1:5]))
                    packages[name] = records.copy()
                    packages = self.__dists[lines[j][1:5]]
                    j = j + 1
                    continue

                duo = lines[j].split(": ", 1)
                key = duo[0].strip()
                value = duo[1].strip()

                if value.find('"') != -1 \
                   and value.find('"', value.find('"') + 1) == -1:
                    while True:
                        j = j + 1
                        value += "\n" + lines[j]
                        if lines[j].find('"') != -1:
                            break
                records[key] = value
                j = j + 1
            packages[name] = records

    def _getUrl(self):
        if self.__pkgName not in self.__dists[self.__rc.distname] \
           or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]:
            self._printErr(self._noPackage())
            install = 0
            for d in self.DIST_NAMES:
                if self.__pkgName in self.__dists[d] \
                   and self.__ballTarget in self.__dists[d][self.__pkgName]:
                    install = self.__dists[d][self.__pkgName][
                        self.__ballTarget]
                    print("warning: using [{0}]\n".format(d), file=sys.stderr)
                    break
            if not install:
                raise PackageException("{0} is not in {1}".format(
                    self.__pkgName, self.__setupIniPath))
        else:
            install = self.__dists[self.__rc.distname][self.__pkgName][
                self.__ballTarget]
        filename, size, md5 = install.split()
        return filename, md5

    def url(self):
        """print tarball url"""
        if not self.__pkgName:
            raise CommandLineException("url command requires a package name")
        print("{0}/{1}".format(self.__rc.mirror,
                               self._getUrl()[0]))

    def getBall(self):
        url, md5 = self._getUrl()
        return os.path.join(self.__downloadDir, url)

    def ball(self):
        """print tarball name"""
        print(self.getBall())

    def _doDownload(self):
        url, md5 = self._getUrl()
        directory = os.path.join(self.__downloadDir,
                                 os.path.split(url)[0])
        if not os.path.exists(self.getBall()) or not self._checkMd5():
            if not os.path.exists(directory):
                os.makedirs(directory)
            status = cautils.uri_get(directory,
                                     "{0}/{1}".format(self.__rc.mirror, url))
            if status:
                raise PackageCacheException(
                    "didn't find {0} "
                    "on mirror {1}: possible mismatch between setup.ini and "
                    "mirror requiring {2} update?".format(
                        self.__pkgName, self.__rc.mirror, self.__appName))

    def download(self):
        """download package (only, do not install)"""
        self._doDownload()
        self.ball()
        self.checksum()

    def _noPackage(self):
        return "{0} is not on mirror {1} in [{2}].".format(
            self.__pkgName, self.__rc.mirror, self.__rc.distname)

    # return an array contents all dependencies of self.__pkgName
    def getRequires(self):
        # Looking for dependencies on curr not prev or test
        dist = self.__dists['curr']
        if self.__pkgName not in self.__dists[self.__rc.distname]:
            raise PackageException(self._noPackage())
        if self.__noDeps:
            return []
        reqs = {
            self.__pkgName: 0
        }
        if self.__ballTarget == 'source' \
            and 'external-source' in dist[self.__pkgName]:
            reqs[dist[self.__pkgName]['external-source']] = 0
        n = 0
        while len(reqs) > n:
            n = len(reqs)
            for i in list(reqs.keys()):
                if i not in dist:
                    sys.stderr.write("error: {0} not in [{1}]\n".format(
                        i, self.__rc.distname))
                    if i != self.__pkgName:
                        del reqs[i]
                    continue
                reqs[i] = '0'
                p = dist[i]
                if 'requires' not in p:
                    continue
                update_list = [(x, 0) for x in p['requires'].split()]
                reqs.update(update_list)
        # Delete the ask package it is not require by it self (joke)
        reqs.pop(self.__pkgName)
        rlist = sorted(reqs.keys())
        return rlist

    def requires(self):
        """print requires: for package"""
        reqs = self.getRequires()
        if len(reqs) == 0:
            print("No dependencies for package {0}".format(self.__pkgName))
        else:
            print("\n".join(reqs))

    def getInstalled(self):
        if self.__installed:
            return self.__installed
        self.__installed = {
            0: {}
        }
        f = open(self.__installedDbFile)
        lines = f.readlines()
        f.close()
        for i in lines[1:]:
            name, ball, status = i.split()
            self.__installed[int(status)][name] = ball
        return self.__installed

    def setInstalled(self, installed):
        self.__installed = installed

    def _writeInstalled(self):
        file_db = open(self.__installedDbFile, 'w')
        file_db.write(self.INSTALLED_DB_MAGIC)
        lines = []
        for x in list(self.__installed[0].keys()):
            lines.append("{0} {1} 0\n".format(x, self.__installed[0][x]))
        file_db.writelines(lines)
        if file_db.close():
            raise IOError(self.__installedDbFile)

    def getField(self, field, default=''):
        for d in (self.__rc.distname, ) + self.DIST_NAMES:
            if self.__pkgName in self.__dists[d] \
               and field in self.__dists[d][self.__pkgName]:
                return self.__dists[d][self.__pkgName][field]
        return default

    def _psort(self, lst):
        lst.sort()
        return lst

    def _preverse(self, lst):
        lst.reverse()
        return lst

    def list(self):
        """list installed packages"""
        print("--- Installed packages ---")
        for self.__pkgName in self._psort(list(self.__installed[0].keys())):
            ins = self.getInstalledVersion()
            new = 0
            if self.__pkgName in self.__dists[self.__rc.distname] \
               and self.__ballTarget in self.__dists[self.__rc.distname][self.__pkgName]:
                new = self.getVersion()
            s = "{0:<19} {1:<15}".format(self.__pkgName,
                                         self._versionToString(ins))
            if new and new != ins:
                s += "({0})".format(self._versionToString(new))
            print(s)

    def filelist(self):
        """list files installed by given packages"""
        if not self.__pkgName:
            msg = "no package name given."
            raise CommandLineException(msg)
        else:
            print("\n".join(self.getFileList()))

    def _postInstall(self):
        self._runAll(self.CYG_POSTINSTALL_DIR)

    def _postRemove(self):
        if len(self.__files[1:]) == 0:
            msg = "must specify package to run postremove."
            raise CommandLineException(msg)
        else:
            for self.__pkgName in self.__files[1:]:
                preremove_sh = os.path.join(self.CYG_PREREMOVE_DIR,
                                            "{0}.sh".format(self.__pkgName))
                postremove_sh = os.path.join(self.CYG_POSTREMOVE_DIR,
                                             "{0}.sh".format(self.__pkgName))
                self._runScript(preremove_sh)
                self._runScript(postremove_sh)

    def getVersion(self):
        if self.__pkgName not in self.__dists[self.__rc.distname] \
           or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]:
            self._printErr(self._noPackage())
            return (0, 0)
        package = self.__dists[self.__rc.distname][self.__pkgName]
        if 'ver' not in package:
            filename = package[self.__ballTarget].split()[0]
            ball = os.path.split(filename)[1]
            package['ver'] = self._splitBall(ball)[1]
        return package['ver']

    def getInstalledVersion(self):
        return self._splitBall(self.__installed[0][self.__pkgName])[1]

    def version(self):
        """print installed version"""
        if self.__pkgName:
            if self.__pkgName not in self.__installed[0]:
                msg = "{0} is not installed".format(self.__pkgName)
                raise PackageException(msg)
            print(self._versionToString(self.getInstalledVersion()))
        else:
            for self.__pkgName in self._psort(list(
                    self.__installed[0].keys())):
                if self.__pkgName not in self.__installed[0]:
                    self.__rc.distname = 'installed'
                    raise PackageException(self._noPackage())
                print("{0:<20}{1:<12}".format(
                    self.__pkgName,
                    self._versionToString(self.getInstalledVersion())))

    def getNew(self):
        lst = []
        for self.__pkgName in list(self.__installed[0].keys()):
            new = self.getVersion()
            ins = self.getInstalledVersion()
            if new > ins:
                self._debug(" {0} > {1}".format(new, ins))
                lst.append(self.__pkgName)
        return lst

    def new(self):
        """list new (upgradable) packages in distribution"""
        for self.__pkgName in self._psort(self.getNew()):
            print("{0:<20}{1:<12}".format(
                self.__pkgName, self._versionToString(self.getVersion())))

    def getMd5(self):
        url, md5 = self._getUrl()
        f = open(os.path.join(self.__downloadDir, url), 'rb')
        data = f.read()
        f.close()
        m = hashlib.md5()
        if 64 == len(md5):
            m = hashlib.sha256()
        if 128 == len(md5):
            m = hashlib.sha512()
        m.update(data)
        digest = m.hexdigest()
        return digest

    def _checkMd5(self):
        return self._getUrl()[1] == self.getMd5()

    def checksum(self):
        """check digest of cached package against database"""
        if not os.path.exists(self.getBall()):
            msg = "{0} not downloaded.".format(self.__pkgName)
            raise PackageCacheException(msg)
        url, md5 = self._getUrl()
        ball = os.path.basename(url)
        print("{0}  {1}".format(md5, ball))
        actual_md5 = self.getMd5()
        print("{0}  {1}".format(actual_md5, ball))
        if actual_md5 != md5:
            raise HashException(
                "digest of cached package doesn't match digest "
                "in setup.ini from mirror")

    def search(self):
        """search all package descriptions for string"""
        if not self.__pkgName:
            raise CommandLineException(
                "search command requires a string to search for")
        if not self.__regexSearch:
            regexp = re.escape(self.__pkgName)
        else:
            regexp = self.__pkgName
        packages = []
        keys = []
        if self.__rc.distname in self.__dists:
            keys = list(self.__dists[self.__rc.distname].keys())
        else:
            for i in list(self.__dists.keys()):
                for j in list(self.__dists[i].keys()):
                    if not j in keys:
                        keys.append(j)
        for i in keys:
            self.__pkgName = i
            if not regexp or re.search(regexp, i) \
               or re.search(regexp, self.getField('sdesc'), re.IGNORECASE) \
               or re.search(regexp, self.getField('ldesc'), re.IGNORECASE):
                if self.__rc.distname in self.__dists:
                    if self.__ballTarget in self.__dists[
                            self.__rc.distname][i]:
                        packages.append(i)
                else:
                    packages.append(i)
        for self.__pkgName in self._psort(packages):
            s = self.__pkgName
            d = self.getField('sdesc')
            if d:
                s += " - {0}".format(d[1:-1])
            print(s)

    def show(self):
        """print package description"""
        s = self.__pkgName
        d = self.getField('sdesc')
        if d:
            s += ' - {0}'.format(d[1:-1])
        ldesc = self.getField('ldesc')
        if ldesc != "":
            print(s + "\n")
            print(ldesc)
        else:
            print("{0}: not found in setup.ini: {1}".format(self.__appName, s))

    # return an array with all packages that must to be install
    def getMissing(self):
        reqs = self.getRequires()
        missingreqs = []
        # List of missing package on requires list
        for i in reqs:
            if i not in self.__installed[0]:
                missingreqs.append(i)
        if self.__pkgName not in self.__installed[0]:
            missingreqs.append(self.__pkgName)
        if missingreqs and self.__pkgName not in missingreqs:
            sys.stderr.write("warning: missing packages: {0}\n".format(
                " ".join(missingreqs)))
        elif self.__pkgName in self.__installed[0]:  # Check version
            ins = self.getInstalledVersion()
            new = self.getVersion()
            if ins >= new:
                sys.stderr.write("{0} is already the newest version\n".format(
                    self.__pkgName))
                # missingreqs.remove(self.__pkgName)
            elif self.__pkgName not in missingreqs:
                missingreqs.append(self.__pkgName)
        return missingreqs

    def missing(self):
        """print missing dependencies for package"""
        missing = self.getMissing()
        if len(missing) > 0:
            print("\n".join(missing))
        else:
            print("All dependent packages for {0} installed".format(
                self.__pkgName))

    def _runScript(self, file_name, optional=True):
        mapped_file = self.__pm.mapPath(file_name)
        mapped_file_done = mapped_file + ".done"
        if os.path.isfile(mapped_file):
            sys.stderr.write("running: {0}\n".format(file_name))
            cmd = ["bash"] + self.SH_OPTIONS + [mapped_file]
            if not self.__cygwinPlatform:
                cmd[0] = self.__dosBash

            cwd = None
            extension = os.path.splitext(mapped_file)[1]

            if ".dash" == extension:
                cmd = ["dash"] + self.DASH_OPTIONS + [mapped_file]
                if not self.__cygwinPlatform:
                    cmd[0] = self.__dosDash

            if extension in [".bat", ".cmd"]:
                cmd = ["cmd"
                       ] + self.CMD_OPTIONS + [os.path.basename(mapped_file)]
                cwd = os.path.dirname(mapped_file)

            retval = Process(cmd, cwd).run(True)

            if os.path.exists(mapped_file_done):
                os.remove(mapped_file_done)
            if retval == 0 and os.path.basename(file_name)[:3] not in [
                    '0p_', 'zp_'
            ]:
                shutil.move(mapped_file, mapped_file_done)
        else:
            if not optional:
                sys.stderr.write("{0}: WARNING couldn't find {1}.\n".format(
                    self.__appName, mapped_file))

    def _runAll(self, dirname):
        dirname = self.__pm.mapPath(dirname)

        if os.path.isdir(dirname):
            lst = list()
            for filename in os.listdir(dirname):
                if os.path.splitext(filename)[1] in [
                        '.sh', '.dash', '.bat', '.cmd'
                ]:
                    lst.append(filename)

            perpetualScripts = list()
            regularScripts = list()
            for filename in lst:
                if filename[:3] in ['0p_', 'zp_']:
                    perpetualScripts.append(filename)
                else:
                    regularScripts.append(filename)

            perpetualScripts.sort()
            lst = perpetualScripts + regularScripts

            for i in lst:
                self._runScript("{0}/{1}".format(dirname, i))

    def _doInstallExternal(self, ball):
        # Currently we use a temporary directory and extractall() then copy:
        # this is very slow. The Python documentation warns more sophisticated
        # approaches have pitfalls without specifying what they are.
        tf = cautils.open_tarfile(ball, self.__dosXz)
        members = tf.getmembers()
        tempdir = tempfile.mkdtemp()
        try:
            tf.extractall(tempdir)
            for m in members:
                if m.isdir():
                    path = self.__pm.mapPath("/" + m.name)
                    if not os.path.exists(path):
                        os.makedirs(path, m.mode)
            for m in members:
                if m.isdir():
                    path = self.__pm.mapPath("/" + m.name)
                    if not os.path.exists(path):
                        os.makedirs(path, m.mode)
                else:
                    path = self.__pm.mapPath("/" + m.name)
                    dirname = os.path.dirname(path)
                    if not os.path.exists(dirname):
                        os.makedirs(dirname)
                    if os.path.exists(path):
                        os.chmod(path, 0o777)
                        os.remove(path)
                    # Windows extract() is robust but can't form Cygwin links
                    # (It produces copies instead: bulky and bugbait.)
                    # Convert to links if possible -- depends on coreutils being installed
                    if m.issym() and self.__lnExists:
                        link_target = m.linkname
                        Process([self.__dosLn, "-s", link_target,
                                 path]).run(True)
                    elif m.islnk() and self.__lnExists:
                        # Hard link -- expect these to be very rare
                        link_target = m.linkname
                        mapped_target = self.__pm.mapPath("/" + m.linkname)
                        # Must ensure target exists before forming hard link
                        if not os.path.exists(mapped_target):
                            shutil.move(os.path.join(tempdir, link_target),
                                        mapped_target)
                        Process([self.__dosLn, mapped_target, path]).run(True)
                    else:
                        shutil.move(os.path.join(tempdir, m.name), path)
        finally:
            tf.close()
            cautils.rmtree(tempdir)

    def _doInstall(self):
        ball = self.getBall()
        if cautils.is_tarfile(ball):
            if not self.__cygwinPlatform:
                self._doInstallExternal(ball)
            tf = cautils.open_tarfile(ball, self.__dosXz)
            if self.__cygwinPlatform:
                tf.extractall(self.__absRoot)
            # Force slash to the end of each directories
            members = tf.getmembers()
            tf.close()
            lst = []
            for m in members:
                if m.isdir() and not m.name.endswith("/"):
                    lst.append(m.name + "/")
                else:
                    lst.append(m.name)
        else:
            print("{0}: bad tarball {1}. Install failed.".format(
                self.__appName, ball),
                  file=sys.stderr)
            return
        self._writeFileList(lst)

        status = 1
        if not self.__pkgName in self._integrityControl():
            status = 0
        self.__installed[status][self.__pkgName] = os.path.basename(ball)

        self._writeInstalled()

    def getFileList(self):
        filelist_file = os.path.join(self.__setupDir,
                                     "{0}.lst.gz".format(self.__pkgName))
        if not os.path.exists(filelist_file):
            if self.__pkgName not in self.__installed[0]:
                raise PackageException("{0} is not installed".format(
                    self.__pkgName))
            else:
                raise PackageException(
                    "{0} is installed, but {1} is missing".format(
                        self.__pkgName, filelist_file))
        gzf = gzip.GzipFile(filelist_file)
        lst = gzf.readlines()
        gzf.close()
        lst = [x.decode().strip() for x in lst]
        return lst

    def _touch(self, fname, times=None):
        f = open(fname, 'a')
        os.utime(fname, times)
        f.close()

    def _writeFileList(self, lst):
        gz_filename = os.path.join(self.__setupDir,
                                   "{0}.lst.gz".format(self.__pkgName))

        lst_cr = [x + "\n" for x in lst]

        # create iostring and write in gzip
        lst_io = io.BytesIO()
        lst_io_gz = gzip.GzipFile(fileobj=lst_io, mode='w')
        lst_io_gz.writelines([x.encode() for x in lst_cr])
        lst_io_gz.close()

        # save it in the file
        lst_gz = open(gz_filename, 'wb')
        lst_gz.write(lst_io.getvalue())
        lst_gz.close()
        lst_io.close()

        stat_struct = os.stat(self.__setupIniPath)
        atime = stat_struct[7]
        mtime = stat_struct[8]
        self._touch(gz_filename, (atime, mtime))

    def _removeFileList(self):
        lst_name = os.path.join(self.__setupDir,
                                "{0}.lst.gz".format(self.__pkgName))
        if os.path.exists(lst_name):
            os.remove(lst_name)
        else:
            sys.stderr.write("{0}: warning {1} no such file\n".format(
                sys.argv[0], lst_name))

    def _uninstallWantFileRemoved(self, filename, noremoves, nowarns):
        # Returns true if the path from the tarball should result in a file
        # removal operation, false if not.
        if not os.path.exists(filename) and not os.path.islink(filename):
            if filename not in nowarns:
                sys.stderr.write(
                    "warning: {0} no such file\n".format(filename))
            return False
        elif not os.path.isdir(filename) and filename not in noremoves:
            return True

    def _doUninstall(self):
        postremove_sh = "{0}/{1}.sh".format(self.CYG_POSTREMOVE_DIR,
                                            self.__pkgName)
        postinstall_sh = "{0}/{1}.sh".format(self.CYG_POSTINSTALL_DIR,
                                             self.__pkgName)
        preremove_sh = "{0}/{1}.sh".format(self.CYG_PREREMOVE_DIR,
                                           self.__pkgName)

        postinstall_done = "{0}.done".format(postinstall_sh)
        suppression_msg = ("{0}: postremove suppressed: "
                           "\"{0} postremove {1}\" to complete.".format(
                               self.__appName, self.__pkgName))

        lst = self.getFileList()
        expect_preremove = preremove_sh[1:] in lst
        expect_postremove = postremove_sh[1:] in lst

        if not self.__noPostRemove:
            if expect_preremove:
                self._runScript(preremove_sh, optional=False)
        else:
            print("{0}".format(suppression_msg), file=sys.stderr)

        # We don't expect these to be present: they are executed
        # and moved to $(packagename).sh.done
        nowarns = []
        nowarns.append(self.__pm.mapPath(postinstall_sh))
        nowarns.append(self.__pm.mapPath(preremove_sh))

        noremoves = []
        if self.__noPostRemove:
            noremoves.append(self.__pm.mapPath(preremove_sh))
        noremoves.append(self.__pm.mapPath(postremove_sh))

        # remove files
        for i in lst:
            filename = self.__pm.mapPath("/" + i)
            if os.path.islink(filename):
                os.remove(filename)
                continue
            if os.path.isdir(filename):
                continue
            if (self._uninstallWantFileRemoved(filename, noremoves, nowarns)):
                if os.path.exists(filename):
                    if self.__cygwinPlatform:
                        os.chmod(filename, 0o777)
                    if os.remove(filename):
                        raise IOError(filename)
                else:
                    if os.path.islink(filename):
                        os.remove(filename)
                    else:
                        print("{0}: warning: expected to remove {1} but it was"
                              " not there.".format(self.__appName, filename))
        if not self.__noPostRemove:
            if expect_postremove:
                self._runScript(postremove_sh, optional=False)
            postremove_sh_mapped = self.__pm.mapPath(postremove_sh)
            if os.path.isfile(postremove_sh_mapped):
                if os.remove(postremove_sh_mapped):
                    raise IOError(postremove_sh_mapped)

        # We don't remove empty directories: the problem is are we sure no other
        # package is depending on them.

        # setup.exe removes the filelist when a package is uninstalled: we try to be
        # as much like setup.exe as possible
        self._removeFileList()

        # Clean up the postintall script: it's been moved to .done
        if os.path.isfile(self.__pm.mapPath(postinstall_done)):
            os.remove(self.__pm.mapPath(postinstall_done))

        # update installed[]
        del (self.__installed[0][self.__pkgName])
        self._writeInstalled()

    def remove(self):
        """uninstall packages"""
        barred = []
        for self.__pkgName in self.__files[1:]:
            if self.__pkgName not in self.__installed[0]:
                sys.stderr.write("warning: {0} not installed\n".format(
                    self.__pkgName))
                continue
            if self._isBarredPackage(self.__pkgName):
                barred.append(self.__pkgName)
                continue
            sys.stderr.write("uninstalling {0} {1}\n".format(
                self.__pkgName,
                self._versionToString(self.getInstalledVersion())))
            self._doUninstall()
        self._barredWarnIfNeed(barred, "removing")

    def purge(self):
        """uninstall packages and delete from cache"""
        barred = []

        for self.__pkgName in self.__files[1:]:
            if self.__pkgName in self.__installed[0]:
                if self._isBarredPackage(self.__pkgName):
                    barred.append(self.__pkgName)
                    continue
                sys.stderr.write("uninstalling {0} {1}\n".format(
                    self.__pkgName,
                    self._versionToString(self.getInstalledVersion())))
                self._doUninstall()
            scripts = [
                self.CYG_POSTINSTALL_DIR + "/{0}.sh.done",
                self.CYG_PREREMOVE_DIR + "/{0}.sh.done",
                self.CYG_POSTREMOVE_DIR + "/{0}.sh.done"
            ]
            scripts = [s.format(self.__pkgName) for s in scripts]
            scripts = [self.__pm.mapPath(s) for s in scripts]
            for s in scripts:
                if os.path.exists(s):
                    os.remove(s)
            ball = self.getBall()
            if os.path.exists(ball):
                print("{0}: removing {1}".format(self.__appName, ball))
                os.remove(ball)

        self._barredWarnIfNeed(barred, "purging")

    def _barredWarnIfNeed(self, barred, command):
        num_barred = len(barred)
        if num_barred > 0:
            if num_barred == 1:
                this_these = "this package"
            else:
                this_these = "these packages"
            barredstr = ", ".join(barred)
            helpfull = ""
            close_all_cygwin_programs = ""
            if command == "installing":
                helpfull += (
                    "You can force the installation with the option -f. But it is recommended\n"
                    "to upgrade the Cygwin distribution, with the official Setup program\n"
                    "(e.g., setup.exe).")
                if "_autorebase" in barred:
                    close_all_cygwin_programs += (
                        "\n"
                        "Before that, you must close all Cygwin programs to perform rebasing\n"
                        "(e.g., rebaseall).")
            print("BarredWarning: NOT {1}:"
                  "    {2}\n{0} is dependent on {3} under Cygwin."
                  "{4}{5}".format(self.__appName, command, barredstr,
                                  this_these, helpfull,
                                  close_all_cygwin_programs),
                  file=sys.stderr)
            if not self.__cygwinPlatform:
                print("Use -f to override but proceed with caution.",
                      file=sys.stderr)

    def install(self):
        """download and install packages with dependencies"""
        suppression_msg = ("{0}: postinstall suppressed: "
                           "\"{0} postinstall\" to complete.".format(
                               self.__appName))
        missing = {}
        barred = []
        for self.__pkgName in self.__files[1:]:
            missing.update(dict([(x, 0) for x in self.getMissing()]))

        if len(missing) > 1:
            sys.stderr.write("to install: \n")
            sys.stderr.write("    {0}".format(" ".join(list(missing.keys()))))
            sys.stderr.write("\n")

        for self.__pkgName in list(missing.keys()):
            if not self._getUrl():
                del missing[self.__pkgName]

        for self.__pkgName in list(missing.keys()):
            if self._isBarredPackage(self.__pkgName):
                barred.append(self.__pkgName)
                del missing[self.__pkgName]

        for self.__pkgName in list(missing.keys()):
            self.download()
        if self.__downloadOnly:
            return
        for self.__pkgName in list(missing.keys()):
            if self.__pkgName in self.__installed[0]:
                sys.stderr.write("preparing to replace {0} {1}\n".format(
                    self.__pkgName,
                    self._versionToString(self.getInstalledVersion())))
                self._doUninstall()
            sys.stderr.write("installing {0} {1}\n".format(
                self.__pkgName, self._versionToString(self.getVersion())))
            self._doInstall()

        if self.__noPostInstall:
            print(suppression_msg, file=sys.stderr)
        else:
            self._postInstall()

        self._barredWarnIfNeed(barred, "installing")

    def postinstall(self):
        """Executes all undone postinstall scripts."""
        self._postInstall()

    def postremove(self):
        """Executes all undone preremove and postremove scripts."""
        self._postRemove()

    def _integrityControl(self, checklist=None):
        if None is checklist:
            checklist = list()

        options = ["-c"]
        if self.__verbose:
            options.append("-v")

        if len(checklist) == 0:
            checklist.append(self.__pkgName)

        command = ["/bin/cygcheck"] + options + checklist
        if not self.__cygwinPlatform:
            command = subprocess.list2cmdline(command)
            command = [self.__dosBash] + self.SH_OPTIONS + ["-c"] + [command]

        p = Process(command)
        p.run()
        outputlines = p.getOutput().splitlines(True)

        unformat = ""
        start = False
        incomplete = []
        for res in outputlines:
            try:
                res_split = res.split()
                package, version, status = res_split
            except ValueError:
                if len(res_split) > 0:
                    unformat += res
                continue

            if package == 'Package' \
               and version == 'Version' \
               and status == 'Status':
                start = True
                unformat = ''
            elif not start:
                continue

            if start and status == 'Incomplete':
                print(res[:-2])
                print(unformat)
                unformat = ""
                incomplete.append(package)

        return incomplete

    def upgrade(self):
        """all installed packages"""
        self.__files[1:] = self.getNew()
        self.install()

    def _printErr(self, err):
        print("{0}: {1}".format(self.__appName, err), file=sys.stderr)

    def _doUnpack(self):
        ball = self.getBall()
        basename = os.path.basename(ball)
        self.__pkgName = re.sub(r"(-src)*\.tar\.(bz2|gz)", '', basename)
        if os.path.exists(self.__pkgName):
            self._printErr("{0} already exists. Not overwriting.".format(
                self.__pkgName))
            return 1
        os.mkdir(self.__pkgName)
        if cautils.is_tarfile(ball):
            tf = cautils.open_tarfile(ball, self.__dosXz)
            tf.extractall(self.__pkgName)
            tf.close()
        else:
            raise InvalidFileException("Bad source tarball {0}, \n"
                                       "SOURCE UNPACK FAILED".format(ball))
        if not os.path.exists(self.__pkgName):
            raise IOError(self.__pkgName)
        print(self.__pkgName)

    def source(self):
        """download source package"""
        self.__ballTarget = 'source'
        for self.__pkgName in self.__files[1:]:
            self.download()
            self._doUnpack()

    def find(self):
        """find package containing file"""
        if self.__regexSearch:
            file_to_find = self.__pkgName
        else:
            file_to_find = re.escape(self.__pkgName)
        hits = []
        for self.__pkgName in self._psort(list(self.__installed[0].keys())):
            filenames_file = os.path.join(self.__setupDir,
                                          "{0}.lst.gz".format(self.__pkgName))
            if not os.path.exists(filenames_file):
                continue
            files = self.getFileList()
            for i in files:
                if re.search(file_to_find, "/{0}".format(i)):
                    hits.append("{0}: /{1}".format(self.__pkgName, i))
        print("\n".join(hits))

    def setRoot(self, root):
        if len(root) < 1 or root[-1] != "/":
            raise InvalidArgumentException("ROOT must end in a slash.")
        self.__prefixRoot = root[:-1]
        self.__absRoot = root

    def getRessource(self, filename):
        self.__rc = cautils.parse_rc(filename)

        if not self.__rc.cache:
            msg = "{0} doesn't define cache.".format(self.__rcFile)
            raise UnexpectedValueException(msg)
        if not self.__rc.mirror:
            msg = "{0} doesn't define mirror.".format(self.__rcFile)
            raise UnexpectedValueException(msg)

        # We want ROOT + "/etc/setup" and cd(ROOT) to work:
        # necessitates two different forms, prefix and absolute
        if (self.__cygwinPlatform):
            self.setRoot("/")
        else:
            self.setRoot(self.__rc.ROOT)
        self.__rc.ROOT = None
        self.__pm = PathMapper(self.__prefixRoot, self.__cygwinPlatform)
        self.__setupDir = self.__pm.mapPath(self.__setupDir)
        self.__rc.cache = self.__pm.mapPath(self.__rc.cache)
        self.__downloadDir = os.path.join(
            self.__rc.cache,
            urllib.quote(
                self.__rc.mirror +
                ('' if self.__rc.mirror.endswith('/') else '/'), '').lower())
        self.__installedDbFile = os.path.join(self.__setupDir, "installed.db")

        self.__setupIniPath = os.path.join(
            self.__downloadDir,
            self.__arch,
            "setup.ini",
        )
        self.__dosBash = "{0}bin/bash".format(self.__pm.getMountRoot())
        self.__dosLn = "{0}bin/ln".format(self.__pm.getMountRoot())
        self.__dosXz = self.__pm.mapPath("/usr/bin/xz")
        self.__dosDash = "{0}bin/dash".format(self.__pm.getMountRoot())
        return 0

    def _isBarredPackage(self, package):
        barred = []
        # add user barred package
        barred.extend(self.__rc.barred.split())
        # add force barred package
        barred.extend(self.FORCE_BARRED)

        # store current package name
        curr_pkgname = self.__pkgName

        # get barred package requires
        depbarred = []
        for self.__pkgName in barred:
            try:
                depbarred.extend(self.getRequires())
            except PackageException:
                pass

        barred.extend(depbarred)

        # set current package name
        self.__pkgName = curr_pkgname

        return (not self.__noBarred) and package in barred
Пример #14
0
class CygApt:
    INSTALLED_DB_MAGIC = "INSTALLED.DB 2\n";
    DIST_NAMES = ('curr', 'test', 'prev');
    FORCE_BARRED = ["python", "python-argparse", "gnupg", "xz"];
    SH_OPTIONS = ["--norc", "--noprofile"];
    DASH_OPTIONS = [];
    CMD_OPTIONS = ["/V:ON", "/E:ON", "/C"];
    CYG_POSTINSTALL_DIR = "/etc/postinstall";
    CYG_PREREMOVE_DIR = "/etc/preremove";
    CYG_POSTREMOVE_DIR = "/etc/postremove";

    def __init__(self,
        main_packagename,
        main_files,
        main_cyg_apt_rc,
        main_cygwin_p,
        main_download_p,
        main_mirror,
        main_downloads,
        main_distname,
        main_nodeps_p,
        main_regex_search,
        main_nobarred,
        main_nopostinstall,
        main_nopostremove,
        main_dists,
        main_installed,
        main_scriptname,
        main_verbose,
        arch,
        setupDir="/etc/setup",
    ):

        # Define private properties
        self.__ballTarget = 'install';
        self.__regexSearch = main_regex_search;
        self.__noBarred = main_nobarred;
        self.__noPostInstall = main_nopostinstall;
        self.__noPostRemove = main_nopostremove;
        self.__appName = main_scriptname;
        self.__files = main_files;
        self.__downloadOnly = main_download_p;
        self.__downloadDir = main_downloads;
        self.__noDeps = main_nodeps_p;
        self.__rcFile = main_cyg_apt_rc;
        self.__verbose = main_verbose;
        self.__setupDir = setupDir;
        self.__rc = ConfigStructure();
        self.__setupIniPath = None;
        self.__arch = arch;

        # Init
        self.setPkgName(main_packagename);
        self.setCygwinPlatform(main_cygwin_p);
        self.setDists(main_dists);
        self.setInstalled(main_installed);

        # Read in our configuration
        self.getRessource(self.__rcFile);

        # Now we have a path mapper, check setup.exe is not running
        self._checkForSetupExe();

        # DOS specific
        if not self.__cygwinPlatform:
            self.__lnExists = os.path.exists(
                "{0}/bin/ln.exe".format(self.__prefixRoot)
            );
        else:
            self.__lnExists = True;

        # Overrides to the .rc
        if (main_mirror):
            self.__rc.mirror = main_mirror;
            self.__downloadDir = os.path.join(
                self.__rc.cache,
                urllib.quote(self.__rc.mirror+('' if self.__rc.mirror.endswith('/') else '/'), '').lower()
            );

        if (main_distname):
            self.__rc.distname = main_distname;

        if not (os.path.isfile(self.__installedDbFile) \
                or os.path.isfile(self.__setupIniPath)):
            msg = "{0} no such file, run {1} setup?".format(
                self.__installedDbFile,
                self.__appName
            );
            raise PackageCacheException(msg);
        else:
            self._getSetupIni();
            self.getInstalled();

    def getPkgName(self):
        return self.__pkgName;

    def setPkgName(self, pkg_name):
        self.__pkgName = str(pkg_name);

    def getCygwinPlatform(self):
        return self.__cygwinPlatform;

    def setCygwinPlatform(self, cygwin_p):
        self.__cygwinPlatform = bool(cygwin_p);

    def getDownloadDir(self):
        return self.__downloadDir;

    def setDownlaodDir(self, download_dir):
        self.__downloadDir = str(download_dir);

    def getDists(self):
        return self.__dists;

    def setDists(self, dists):
        self.__dists = dists;

    def getSetupDir(self):
        return self.__setupDir;

    def setSetupDir(self, setup_dir):
        self.__setupDir = str(setup_dir);

    def getInstalledDbFile(self):
        return self.__installedDbFile;

    def setInstalledDbFile(self, installed_db_file):
        self.__installedDbFile = str(installed_db_file);

    def getDosBash(self):
        return self.__dosBash;

    def setDosBash(self, dos_bash):
        self.__dosBash = str(dos_bash);

    def getDosLn(self):
        return self.__dosLn;

    def setDosLn(self, dos_ln):
        self.__dosLn = str(dos_ln);

    def getDosXz(self):
        return self.__dosXz;

    def setDosXz(self, dos_xz):
        self.__dosXz = str(dos_xz);

    def getDosDash(self):
        return self.__dosDash;

    def setDosDash(self, dosDash):
        self.__dosDash = str(dosDash);

    def getPrefixRoot(self):
        return self.__prefixRoot;

    def setPrefixRoot(self, prefix_root):
        self.__prefixRoot = prefix_root;

    def getAbsRoot(self):
        return self.__absRoot;

    def setAbsRoot(self, absolute_root):
        self.__absRoot = absolute_root;

    def getPathMapper(self):
        return self.__pm;

    def setPathMapper(self, path_mapper):
        assert isinstance(path_mapper, PathMapper);
        self.__pm = path_mapper;

    def getRC(self):
        return self.__rc;

    def setRC(self, rc_structure):
        assert isinstance(rc_structure, ConfigStructure);
        self.__rc = rc_structure;

    def _checkForSetupExe(self):
        # It's far from bulletproof, but it's surprisingly hard to detect
        # setup.exe running since it doesn't lock any files.
        p = Process([self.__pm.mapPath("/bin/ps"), "-W"]);
        p.run();
        psout = p.getOutput().splitlines(True);
        setup_re = re.compile(r"(?<![a-z0-9_ -])setup(|-1\.7|-x86|-x86_64)\.exe", re.IGNORECASE);
        for l in psout:
            m = setup_re.search(l);
            if m:
                raise AppConflictException(
                    "Please close {0} while "
                    "running {1}".format(m.group(0), self.__appName)
                );

    def _versionToString(self, t):
        def try_itoa(x):
            if isinstance(x, int):
                return "{0:d}".format(x);
            return x
        return "{0}-{1}".format(
            ".".join(list(map(try_itoa, t[:-1]))),
            t[-1]
        );

    def _stringToVersion(self, s):
        s = re.sub(r"([^0-9][^0-9]*)", " \\1 ", s);
        s = re.sub(r"[ _.-][ _.-]*", " ", s);
        def try_atoi(x):
            if re.match(r"^[0-9]*$", x):
                return int(x);
            return x
        return tuple(map(try_atoi, (s.split(' '))));

    def _splitBall(self, p):
        m = re.match(r"^(.*)-([0-9].*-[0-9]+)(.tar.(bz2|xz))?$", p);
        if not m:
            print("splitBall: {0}".format(p));
            return (p[:2], (0, 0));
        t = (m.group(1), self._stringToVersion(m.group(2)));
        return t;

    def _joinBall(self, t):
        return "{0}-{1}".format(t[0], self._versionToString(t[1]));

    def _debug(self, s):
        s;

    def help(self,):
        """this help message"""
        pass;

    def _getSetupIni(self):
        if self.__dists:
            return;
        self.__dists = {'test': {}, 'curr': {}, 'prev' : {}};
        f = open(self.__setupIniPath);
        contents = f.read();
        f.close();
        chunks = contents.split("\n\n@ ");
        for i in chunks[1:]:
            lines = i.split("\n");
            name = lines[0].strip();
            self._debug("package: {0}".format(name));
            packages = self.__dists['curr'];
            records = {'sdesc': name};
            j = 1;
            while j < len(lines) and lines[j].strip():
                self._debug("raw: {0}".format(lines[j]));
                if lines[j][0] == '#':
                    j = j + 1;
                    continue;
                elif lines[j][0] == '[':
                    self._debug('dist: {0}'.format(lines[j][1:5]));
                    packages[name] = records.copy();
                    packages = self.__dists[lines[j][1:5]];
                    j = j + 1;
                    continue;

                duo = lines[j].split(": ", 1);
                key = duo[0].strip();
                value = duo[1].strip();

                if value.find('"') != -1 \
                   and value.find('"', value.find('"') + 1) == -1:
                    while True:
                        j = j + 1;
                        value += "\n" + lines[j];
                        if lines[j].find('"') != -1:
                            break;
                records[key] = value;
                j = j + 1;
            packages[name] = records;

    def _getUrl(self):
        if self.__pkgName not in self.__dists[self.__rc.distname] \
           or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]:
            self._printErr(self._noPackage());
            install = 0;
            for d in self.DIST_NAMES:
                if self.__pkgName in self.__dists[d] \
                   and self.__ballTarget in self.__dists[d][self.__pkgName]:
                    install = self.__dists[d][self.__pkgName][self.__ballTarget];
                    print("warning: using [{0}]\n".format(d), file=sys.stderr);
                    break;
            if not install:
                raise PackageException("{0} is not in {1}".format(
                    self.__pkgName,
                    self.__setupIniPath
                ));
        else:
            install = self.__dists[self.__rc.distname][self.__pkgName][self.__ballTarget];
        filename, size, md5 = install.split();
        return filename, md5;

    def url(self):
        """print tarball url"""
        if not self.__pkgName:
            raise CommandLineException("url command requires a package name");
        print("{0}/{1}".format(self.__rc.mirror, self._getUrl()[0]));

    def getBall(self):
        url, md5 = self._getUrl();
        return os.path.join(self.__downloadDir, url);

    def ball(self):
        """print tarball name"""
        print(self.getBall());

    def _doDownload(self):
        url, md5 = self._getUrl();
        directory = os.path.join(self.__downloadDir, os.path.split(url)[0]);
        if not os.path.exists(self.getBall()) or not self._checkMd5():
            if not os.path.exists(directory):
                os.makedirs(directory);
            status = cautils.uri_get(
                directory,
                "{0}/{1}".format(self.__rc.mirror, url)
            );
            if status:
                raise PackageCacheException(
                    "didn't find {0} "
                    "on mirror {1}: possible mismatch between setup.ini and "
                    "mirror requiring {2} update?".format(
                    self.__pkgName,
                    self.__rc.mirror,
                    self.__appName
                ));

    def download(self):
        """download package (only, do not install)"""
        self._doDownload();
        self.ball();
        self.checksum();

    def _noPackage(self):
        return "{0} is not on mirror {1} in [{2}].".format(
            self.__pkgName,
            self.__rc.mirror,
            self.__rc.distname
        );

    # return an array contents all dependencies of self.__pkgName
    def getRequires(self):
        # Looking for dependencies on curr not prev or test
        dist = self.__dists['curr'];
        if self.__pkgName not in self.__dists[self.__rc.distname]:
            raise PackageException(self._noPackage());
        if self.__noDeps:
            return [];
        reqs = {self.__pkgName:0};
        if self.__ballTarget == 'source' \
            and 'external-source' in dist[self.__pkgName]:
            reqs[dist[self.__pkgName]['external-source']] = 0;
        n = 0;
        while len(reqs) > n:
            n = len(reqs);
            for i in list(reqs.keys()):
                if i not in dist:
                    sys.stderr.write("error: {0} not in [{1}]\n".format(
                        i, self.__rc.distname
                    ));
                    if i != self.__pkgName:
                        del reqs[i];
                    continue;
                reqs[i] = '0';
                p = dist[i];
                if 'requires' not in p:
                    continue;
                update_list = [(x, 0) for x in p['requires'].split()];
                reqs.update(update_list);
        # Delete the ask package it is not require by it self (joke)
        reqs.pop(self.__pkgName);
        rlist = sorted(reqs.keys());
        return rlist;

    def requires(self):
        """print requires: for package"""
        reqs = self.getRequires();
        if len(reqs) == 0:
            print("No dependencies for package {0}".format(self.__pkgName));
        else:
            print("\n".join(reqs));

    def getInstalled(self):
        if self.__installed:
            return self.__installed;
        self.__installed = {0:{}};
        f = open(self.__installedDbFile);
        lines = f.readlines();
        f.close();
        for i in lines[1:]:
            name, ball, status = i.split();
            self.__installed[int(status)][name] = ball;
        return self.__installed;

    def setInstalled(self, installed):
        self.__installed = installed;

    def _writeInstalled(self):
        file_db = open(self.__installedDbFile, 'w');
        file_db.write(self.INSTALLED_DB_MAGIC);
        lines = [];
        for x in list(self.__installed[0].keys()):
            lines.append("{0} {1} 0\n".format(x, self.__installed[0][x]));
        file_db.writelines(lines);
        if file_db.close():
            raise IOError(self.__installedDbFile);

    def getField(self, field, default=''):
        for d in (self.__rc.distname,) + self.DIST_NAMES:
            if self.__pkgName in self.__dists[d] \
               and field in self.__dists[d][self.__pkgName]:
                return self.__dists[d][self.__pkgName][field];
        return default;

    def _psort(self, lst):
        lst.sort();
        return lst;

    def _preverse(self, lst):
        lst.reverse();
        return lst;

    def list(self):
        """list installed packages"""
        print("--- Installed packages ---");
        for self.__pkgName in self._psort(list(self.__installed[0].keys())):
            ins = self.getInstalledVersion();
            new = 0;
            if self.__pkgName in self.__dists[self.__rc.distname] \
               and self.__ballTarget in self.__dists[self.__rc.distname][self.__pkgName]:
                new = self.getVersion();
            s = "{0:<19} {1:<15}".format(
                self.__pkgName,
                self._versionToString(ins)
            );
            if new and new != ins:
                s += "({0})".format(self._versionToString(new));
            print(s);

    def filelist(self):
        """list files installed by given packages"""
        if not self.__pkgName:
            msg = "no package name given.";
            raise CommandLineException(msg);
        else:
            print("\n".join(self.getFileList()));

    def _postInstall(self):
        self._runAll(self.CYG_POSTINSTALL_DIR);

    def _postRemove(self):
        if len(self.__files[1:]) == 0:
            msg = "must specify package to run postremove.";
            raise CommandLineException(msg);
        else:
            for self.__pkgName in self.__files[1:]:
                preremove_sh = os.path.join(
                    self.CYG_PREREMOVE_DIR,
                    "{0}.sh".format(self.__pkgName)
                );
                postremove_sh = os.path.join(
                    self.CYG_POSTREMOVE_DIR,
                    "{0}.sh".format(self.__pkgName)
                );
                self._runScript(preremove_sh);
                self._runScript(postremove_sh);

    def getVersion(self):
        if self.__pkgName not in self.__dists[self.__rc.distname] \
           or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]:
            self._printErr(self._noPackage());
            return (0, 0);
        package = self.__dists[self.__rc.distname][self.__pkgName];
        if 'ver' not in package:
            filename = package[self.__ballTarget].split()[0];
            ball = os.path.split(filename)[1];
            package['ver'] = self._splitBall(ball)[1];
        return package['ver'];

    def getInstalledVersion(self):
        return self._splitBall(self.__installed[0][self.__pkgName])[1];

    def version(self):
        """print installed version"""
        if self.__pkgName:
            if self.__pkgName not in self.__installed[0]:
                msg = "{0} is not installed".format(self.__pkgName);
                raise PackageException(msg);
            print(self._versionToString(self.getInstalledVersion()));
        else:
            for self.__pkgName in self._psort(list(self.__installed[0].keys())):
                if self.__pkgName not in self.__installed[0]:
                    self.__rc.distname = 'installed';
                    raise PackageException(self._noPackage());
                print("{0:<20}{1:<12}".format(
                    self.__pkgName,
                    self._versionToString(self.getInstalledVersion())
                ));

    def getNew(self):
        lst = [];
        for self.__pkgName in list(self.__installed[0].keys()):
            new = self.getVersion();
            ins = self.getInstalledVersion();
            if new > ins:
                self._debug(" {0} > {1}".format(new, ins));
                lst.append(self.__pkgName);
        return lst;

    def new(self):
        """list new (upgradable) packages in distribution"""
        for self.__pkgName in self._psort(self.getNew()):
            print("{0:<20}{1:<12}".format(
                self.__pkgName,
                self._versionToString(self.getVersion())
            ));

    def getMd5(self):
        url, md5 = self._getUrl();
        f = open(os.path.join(self.__downloadDir, url), 'rb');
        data = f.read();
        f.close();
        m = hashlib.md5();
        if 64 == len(md5) :
            m = hashlib.sha256();
        if 128 == len(md5) :
            m = hashlib.sha512();
        m.update(data);
        digest = m.hexdigest();
        return digest;

    def _checkMd5(self):
        return self._getUrl()[1] == self.getMd5();

    def checksum(self):
        """check digest of cached package against database"""
        if not os.path.exists(self.getBall()):
            msg = "{0} not downloaded.".format(self.__pkgName);
            raise PackageCacheException(msg);
        url, md5 = self._getUrl();
        ball = os.path.basename(url);
        print("{0}  {1}".format(md5, ball));
        actual_md5 = self.getMd5();
        print("{0}  {1}".format(actual_md5, ball));
        if actual_md5 != md5:
            raise HashException(
                "digest of cached package doesn't match digest "
                "in setup.ini from mirror"
            );

    def search(self):
        """search all package descriptions for string"""
        if not self.__pkgName:
            raise CommandLineException(
                "search command requires a string to search for"
            );
        if not self.__regexSearch:
            regexp = re.escape(self.__pkgName);
        else:
            regexp = self.__pkgName;
        packages = [];
        keys = [];
        if self.__rc.distname in self.__dists:
            keys = list(self.__dists[self.__rc.distname].keys());
        else:
            for i in list(self.__dists.keys()):
                for j in list(self.__dists[i].keys()):
                    if not j in keys:
                        keys.append(j);
        for i in keys:
            self.__pkgName = i;
            if not regexp or re.search(regexp, i) \
               or re.search(regexp, self.getField('sdesc'), re.IGNORECASE) \
               or re.search(regexp, self.getField('ldesc'), re.IGNORECASE):
                if self.__rc.distname in self.__dists:
                    if self.__ballTarget in self.__dists[self.__rc.distname][i]:
                        packages.append(i);
                else:
                    packages.append(i);
        for self.__pkgName in self._psort(packages):
            s = self.__pkgName;
            d = self.getField('sdesc');
            if d:
                s += " - {0}".format(d[1:-1]);
            print(s);

    def show(self):
        """print package description"""
        s = self.__pkgName;
        d = self.getField('sdesc');
        if d:
            s += ' - {0}'.format(d[1:-1]);
        ldesc = self.getField('ldesc');
        if ldesc != "":
            print(s + "\n");
            print(ldesc);
        else:
            print("{0}: not found in setup.ini: {1}".format(
                self.__appName,
                s
            ));

    # return an array with all packages that must to be install
    def getMissing(self):
        reqs = self.getRequires();
        missingreqs = [];  # List of missing package on requires list
        for i in reqs:
            if i not in self.__installed[0]:
                missingreqs.append(i);
        if self.__pkgName not in self.__installed[0]:
            missingreqs.append(self.__pkgName);
        if missingreqs and self.__pkgName not in missingreqs:
            sys.stderr.write("warning: missing packages: {0}\n".format(
                " ".join(missingreqs)
            ));
        elif self.__pkgName in self.__installed[0]:  # Check version
            ins = self.getInstalledVersion();
            new = self.getVersion();
            if ins >= new:
                sys.stderr.write("{0} is already the newest version\n".format(
                    self.__pkgName
                ));
                # missingreqs.remove(self.__pkgName)
            elif self.__pkgName not in missingreqs:
                missingreqs.append(self.__pkgName);
        return missingreqs;

    def missing(self):
        """print missing dependencies for package"""
        missing = self.getMissing();
        if len(missing) > 0:
            print("\n".join(missing));
        else:
            print("All dependent packages for {0} installed".format(
                self.__pkgName
            ));

    def _runScript(self, file_name, optional=True):
        mapped_file = self.__pm.mapPath(file_name);
        mapped_file_done = mapped_file + ".done";
        if os.path.isfile(mapped_file):
            sys.stderr.write("running: {0}\n".format(file_name));
            cmd = ["bash"] + self.SH_OPTIONS + [mapped_file];
            if not self.__cygwinPlatform:
                cmd[0] = self.__dosBash;

            cwd = None;
            extension = os.path.splitext(mapped_file)[1];

            if ".dash" == extension :
                cmd = ["dash"] + self.DASH_OPTIONS + [mapped_file];
                if not self.__cygwinPlatform:
                    cmd[0] = self.__dosDash;

            if extension in [".bat", ".cmd"] :
                cmd = ["cmd"] + self.CMD_OPTIONS + [os.path.basename(mapped_file)];
                cwd = os.path.dirname(mapped_file);

            retval = Process(cmd, cwd).run(True);

            if os.path.exists(mapped_file_done):
                os.remove(mapped_file_done);
            if retval == 0 and os.path.basename(file_name)[:3] not in ['0p_', 'zp_']:
                shutil.move(mapped_file, mapped_file_done);
        else:
            if not optional:
                sys.stderr.write("{0}: WARNING couldn't find {1}.\n".format(
                    self.__appName,
                    mapped_file
                ));

    def _runAll(self, dirname):
        dirname = self.__pm.mapPath(dirname);

        if os.path.isdir(dirname):
            lst = list();
            for filename in os.listdir(dirname) :
                if os.path.splitext(filename)[1] in ['.sh', '.dash', '.bat', '.cmd'] :
                    lst.append(filename);

            perpetualScripts = list();
            regularScripts = list();
            for filename in lst:
                if filename[:3] in ['0p_', 'zp_'] :
                    perpetualScripts.append(filename);
                else:
                    regularScripts.append(filename);

            perpetualScripts.sort();
            lst = perpetualScripts + regularScripts;

            for i in lst:
                self._runScript("{0}/{1}".format(dirname, i));

    def _doInstallExternal(self, ball):
        # Currently we use a temporary directory and extractall() then copy:
        # this is very slow. The Python documentation warns more sophisticated
        # approaches have pitfalls without specifying what they are.
        tf = cautils.open_tarfile(ball, self.__dosXz);
        members = tf.getmembers();
        tempdir = tempfile.mkdtemp();
        try:
            tf.extractall(tempdir);
            for m in members:
                if m.isdir():
                    path = self.__pm.mapPath("/" + m.name);
                    if not os.path.exists(path):
                        os.makedirs(path, m.mode);
            for m in members:
                if m.isdir():
                    path = self.__pm.mapPath("/" + m.name);
                    if not os.path.exists(path):
                        os.makedirs(path, m.mode);
                else:
                    path = self.__pm.mapPath("/" + m.name);
                    dirname = os.path.dirname(path);
                    if not os.path.exists(dirname):
                        os.makedirs(dirname);
                    if os.path.exists(path):
                        os.chmod(path, 0o777);
                        os.remove(path);
                    # Windows extract() is robust but can't form Cygwin links
                    # (It produces copies instead: bulky and bugbait.)
                    # Convert to links if possible -- depends on coreutils being installed
                    if m.issym() and self.__lnExists:
                        link_target = m.linkname;
                        Process([
                            self.__dosLn,
                            "-s",
                            link_target,
                            path
                        ]).run(True);
                    elif m.islnk() and self.__lnExists:
                        # Hard link -- expect these to be very rare
                        link_target = m.linkname;
                        mapped_target = self.__pm.mapPath("/" + m.linkname);
                        # Must ensure target exists before forming hard link
                        if not os.path.exists(mapped_target):
                            shutil.move(
                                os.path.join(tempdir, link_target),
                                mapped_target
                            );
                        Process([
                            self.__dosLn,
                            mapped_target,
                            path
                        ]).run(True);
                    else:
                        shutil.move(os.path.join(tempdir, m.name), path);
        finally:
            tf.close();
            cautils.rmtree(tempdir);

    def _doInstall(self):
        ball = self.getBall();
        if cautils.is_tarfile(ball):
            if not self.__cygwinPlatform:
                self._doInstallExternal(ball);
            tf = cautils.open_tarfile(ball, self.__dosXz);
            if self.__cygwinPlatform:
                tf.extractall(self.__absRoot);
            # Force slash to the end of each directories
            members = tf.getmembers();
            tf.close();
            lst = [];
            for m in members:
                if m.isdir() and not m.name.endswith("/"):
                    lst.append(m.name + "/");
                else:
                    lst.append(m.name);
        else:
            print("{0}: bad tarball {1}. Install failed.".format(
                self.__appName,
                ball
            ), file=sys.stderr);
            return;
        self._writeFileList(lst);

        status = 1;
        if not self.__pkgName in self._integrityControl():
            status = 0;
        self.__installed[status][self.__pkgName] = os.path.basename(ball);

        self._writeInstalled();

    def getFileList(self):
        filelist_file = os.path.join(
            self.__setupDir,
            "{0}.lst.gz".format(self.__pkgName)
        );
        if not os.path.exists(filelist_file):
            if self.__pkgName not in self.__installed[0]:
                raise PackageException(
                    "{0} is not installed".format(self.__pkgName)
                );
            else:
                raise PackageException(
                    "{0} is installed, but {1} is missing".format(
                    self.__pkgName,
                    filelist_file
                ));
        gzf = gzip.GzipFile(filelist_file);
        lst = gzf.readlines();
        gzf.close();
        lst = [x.decode().strip() for x in lst];
        return lst;

    def _touch(self, fname, times=None):
        f = open(fname, 'a');
        os.utime(fname, times);
        f.close();

    def _writeFileList(self, lst):
        gz_filename = os.path.join(
            self.__setupDir,
            "{0}.lst.gz".format(self.__pkgName)
        );

        lst_cr = [x + "\n" for x in lst];

        # create iostring and write in gzip
        lst_io = io.BytesIO();
        lst_io_gz = gzip.GzipFile(fileobj=lst_io, mode='w');
        lst_io_gz.writelines([x.encode() for x in lst_cr]);
        lst_io_gz.close();

        # save it in the file
        lst_gz = open(gz_filename, 'wb');
        lst_gz.write(lst_io.getvalue());
        lst_gz.close();
        lst_io.close();

        stat_struct = os.stat(self.__setupIniPath);
        atime = stat_struct[7];
        mtime = stat_struct[8];
        self._touch(gz_filename, (atime, mtime));

    def _removeFileList(self):
        lst_name = os.path.join(
            self.__setupDir,
            "{0}.lst.gz".format(self.__pkgName)
        );
        if os.path.exists(lst_name):
            os.remove(lst_name);
        else:
            sys.stderr.write("{0}: warning {1} no such file\n".format(
                 sys.argv[0], lst_name
            ));

    def _uninstallWantFileRemoved(self, filename, noremoves, nowarns):
        # Returns true if the path from the tarball should result in a file 
        # removal operation, false if not.
        if not os.path.exists(filename) and not os.path.islink(filename):
            if filename not in nowarns:
                sys.stderr.write("warning: {0} no such file\n".format(
                    filename
                ));
            return False;
        elif not os.path.isdir(filename) and filename not in noremoves:
            return True;

    def _doUninstall(self):
        postremove_sh = "{0}/{1}.sh".format(
            self.CYG_POSTREMOVE_DIR,
            self.__pkgName
        );
        postinstall_sh = "{0}/{1}.sh".format(
            self.CYG_POSTINSTALL_DIR,
            self.__pkgName
        );
        preremove_sh = "{0}/{1}.sh".format(
            self.CYG_PREREMOVE_DIR,
            self.__pkgName
        );

        postinstall_done = "{0}.done".format(postinstall_sh);
        suppression_msg = (
            "{0}: postremove suppressed: "
            "\"{0} postremove {1}\" to complete.".format(
            self.__appName,
            self.__pkgName
        ));

        lst = self.getFileList();
        expect_preremove = preremove_sh[1:] in lst;
        expect_postremove = postremove_sh[1:] in lst;

        if not self.__noPostRemove:
            if expect_preremove:
                self._runScript(preremove_sh, optional=False);
        else:
            print("{0}".format(suppression_msg),
                  file=sys.stderr);

        # We don't expect these to be present: they are executed
        # and moved to $(packagename).sh.done
        nowarns = [];
        nowarns.append(self.__pm.mapPath(postinstall_sh));
        nowarns.append(self.__pm.mapPath(preremove_sh));

        noremoves = [];
        if self.__noPostRemove:
            noremoves.append(self.__pm.mapPath(preremove_sh));
        noremoves.append(self.__pm.mapPath(postremove_sh));

        # remove files
        for i in lst:
            filename = self.__pm.mapPath("/" + i);
            if os.path.islink(filename):
                os.remove(filename);
                continue;
            if os.path.isdir(filename):
                continue;
            if (self._uninstallWantFileRemoved(filename, noremoves, nowarns)):
                if os.path.exists(filename):
                    if self.__cygwinPlatform:
                        os.chmod(filename, 0o777);
                    if os.remove(filename):
                        raise IOError(filename);
                else:
                    if os.path.islink(filename):
                        os.remove(filename);
                    else:
                        print(
                            "{0}: warning: expected to remove {1} but it was"
                            " not there.".format(
                            self.__appName,
                            filename
                        ));
        if not self.__noPostRemove:
            if expect_postremove:
                self._runScript(postremove_sh, optional=False);
            postremove_sh_mapped = self.__pm.mapPath(postremove_sh)
            if os.path.isfile(postremove_sh_mapped):
                if os.remove(postremove_sh_mapped):
                    raise IOError(postremove_sh_mapped);

        # We don't remove empty directories: the problem is are we sure no other
        # package is depending on them.

        # setup.exe removes the filelist when a package is uninstalled: we try to be
        # as much like setup.exe as possible
        self._removeFileList();

        # Clean up the postintall script: it's been moved to .done
        if os.path.isfile(self.__pm.mapPath(postinstall_done)):
            os.remove(self.__pm.mapPath(postinstall_done));

        # update installed[]
        del(self.__installed[0][self.__pkgName]);
        self._writeInstalled();

    def remove(self):
        """uninstall packages"""
        barred = [];
        for self.__pkgName in self.__files[1:]:
            if self.__pkgName not in self.__installed[0]:
                sys.stderr.write("warning: {0} not installed\n".format(
                    self.__pkgName
                ));
                continue;
            if self._isBarredPackage(self.__pkgName):
                barred.append(self.__pkgName);
                continue;
            sys.stderr.write("uninstalling {0} {1}\n".format(
                self.__pkgName,
                self._versionToString(self.getInstalledVersion())
            ));
            self._doUninstall();
        self._barredWarnIfNeed(barred, "removing");

    def purge(self):
        """uninstall packages and delete from cache"""
        barred = [];

        for self.__pkgName in self.__files[1:]:
            if self.__pkgName in self.__installed[0]:
                if self._isBarredPackage(self.__pkgName):
                    barred.append(self.__pkgName);
                    continue;
                sys.stderr.write("uninstalling {0} {1}\n".format(
                    self.__pkgName,
                    self._versionToString(self.getInstalledVersion())
                ));
                self._doUninstall();
            scripts = [
                self.CYG_POSTINSTALL_DIR + "/{0}.sh.done",
                self.CYG_PREREMOVE_DIR + "/{0}.sh.done",
                self.CYG_POSTREMOVE_DIR + "/{0}.sh.done"
            ];
            scripts = [s.format(self.__pkgName) for s in scripts];
            scripts = [self.__pm.mapPath(s) for s in scripts];
            for s in scripts:
                if os.path.exists(s):
                    os.remove(s);
            ball = self.getBall();
            if os.path.exists(ball):
                print("{0}: removing {1}".format(self.__appName, ball));
                os.remove(ball);

        self._barredWarnIfNeed(barred, "purging");

    def _barredWarnIfNeed(self, barred, command):
        num_barred = len(barred);
        if num_barred > 0:
            if num_barred == 1:
                this_these = "this package";
            else:
                this_these = "these packages";
            barredstr = ", ".join(barred);
            helpfull = "";
            close_all_cygwin_programs = "";
            if command == "installing":
                helpfull += (
"You can force the installation with the option -f. But it is recommended\n"
"to upgrade the Cygwin distribution, with the official Setup program\n"
"(e.g., setup.exe)."
                );
                if "_autorebase" in barred:
                    close_all_cygwin_programs += (
"\n"
"Before that, you must close all Cygwin programs to perform rebasing\n"
"(e.g., rebaseall)."
                    );
            print(
                "BarredWarning: NOT {1}:"
                "    {2}\n{0} is dependent on {3} under Cygwin."
                "{4}{5}".format(
                    self.__appName,
                    command,
                    barredstr,
                    this_these,
                    helpfull,
                    close_all_cygwin_programs
            ), file=sys.stderr);
            if not self.__cygwinPlatform:
                print(
                    "Use -f to override but proceed with caution.",
                    file=sys.stderr
                );
    def install(self):
        """download and install packages with dependencies"""
        suppression_msg = (
            "{0}: postinstall suppressed: "
            "\"{0} postinstall\" to complete.".format(
            self.__appName
        ));
        missing = {};
        barred = [];
        for self.__pkgName in self.__files[1:]:
            missing.update(dict([(x, 0) for x in self.getMissing()]));

        if len(missing) > 1:
            sys.stderr.write("to install: \n");
            sys.stderr.write("    {0}".format(" ".join(list(missing.keys()))));
            sys.stderr.write("\n");

        for self.__pkgName in list(missing.keys()):
            if not self._getUrl():
                del missing[self.__pkgName];

        for self.__pkgName in list(missing.keys()):
            if self._isBarredPackage(self.__pkgName):
                barred.append(self.__pkgName);
                del missing[self.__pkgName];

        for self.__pkgName in list(missing.keys()):
            self.download();
        if self.__downloadOnly:
            return;
        for self.__pkgName in list(missing.keys()):
            if self.__pkgName in self.__installed[0]:
                sys.stderr.write("preparing to replace {0} {1}\n".format(
                    self.__pkgName,
                    self._versionToString(self.getInstalledVersion())
                ));
                self._doUninstall();
            sys.stderr.write("installing {0} {1}\n".format(
                self.__pkgName,
                self._versionToString(self.getVersion())
            ));
            self._doInstall();

        if self.__noPostInstall:
            print(suppression_msg, file=sys.stderr);
        else:
            self._postInstall();

        self._barredWarnIfNeed(barred, "installing");

    def postinstall(self):
        """Executes all undone postinstall scripts."""
        self._postInstall();

    def postremove(self):
        """Executes all undone preremove and postremove scripts."""
        self._postRemove();

    def _integrityControl(self, checklist=None):
        if None is checklist :
            checklist = list();

        options = ["-c"];
        if self.__verbose:
            options.append("-v");

        if len(checklist) == 0:
            checklist.append(self.__pkgName);

        command = ["/bin/cygcheck"] + options + checklist;
        if not self.__cygwinPlatform:
            command = subprocess.list2cmdline(command);
            command = [self.__dosBash] + self.SH_OPTIONS + ["-c"] + [command];

        p = Process(command);
        p.run();
        outputlines = p.getOutput().splitlines(True);

        unformat = "";
        start = False;
        incomplete = [];
        for res in outputlines:
            try:
                res_split = res.split();
                package, version, status = res_split;
            except ValueError:
                if len(res_split) > 0:
                    unformat += res;
                continue;

            if package == 'Package' \
               and version == 'Version' \
               and status == 'Status':
                start = True;
                unformat = '';
            elif not start:
                continue;

            if start and status == 'Incomplete':
                print(res[:-2]);
                print(unformat);
                unformat = "";
                incomplete.append(package);

        return incomplete;

    def upgrade(self):
        """all installed packages"""
        self.__files[1:] = self.getNew();
        self.install();

    def _printErr(self, err):
        print("{0}: {1}".format(self.__appName, err), file=sys.stderr);

    def _doUnpack(self):
        ball = self.getBall();
        basename = os.path.basename(ball);
        self.__pkgName = re.sub(r"(-src)*\.tar\.(bz2|gz)", '', basename);
        if os.path.exists(self.__pkgName):
            self._printErr("{0} already exists. Not overwriting.".format(
                self.__pkgName
            ));
            return 1;
        os.mkdir(self.__pkgName);
        if cautils.is_tarfile(ball):
            tf = cautils.open_tarfile(ball, self.__dosXz);
            tf.extractall(self.__pkgName);
            tf.close();
        else:
            raise InvalidFileException(
                "Bad source tarball {0}, \n"
                "SOURCE UNPACK FAILED".format(ball)
            );
        if not os.path.exists(self.__pkgName):
            raise IOError(self.__pkgName);
        print(self.__pkgName);

    def source(self):
        """download source package"""
        self.__ballTarget = 'source';
        for self.__pkgName in self.__files[1:]:
            self.download();
            self._doUnpack();

    def find(self):
        """find package containing file"""
        if self.__regexSearch:
            file_to_find = self.__pkgName;
        else:
            file_to_find = re.escape(self.__pkgName);
        hits = [];
        for self.__pkgName in self._psort(list(self.__installed[0].keys())):
            filenames_file = os.path.join(
                self.__setupDir,
                "{0}.lst.gz".format(self.__pkgName)
            );
            if not os.path.exists(filenames_file):
                continue;
            files = self.getFileList();
            for i in files:
                if re.search(file_to_find, "/{0}".format(i)):
                    hits.append("{0}: /{1}".format(self.__pkgName, i));
        print("\n".join(hits));

    def setRoot(self, root):
        if len(root) < 1 or root[-1] != "/":
            raise InvalidArgumentException("ROOT must end in a slash.");
        self.__prefixRoot = root[:-1];
        self.__absRoot = root;

    def getRessource(self, filename):
        self.__rc = cautils.parse_rc(filename);

        if not self.__rc.cache:
            msg = "{0} doesn't define cache.".format(self.__rcFile);
            raise UnexpectedValueException(msg);
        if not self.__rc.mirror:
            msg = "{0} doesn't define mirror.".format(self.__rcFile);
            raise UnexpectedValueException(msg);

        # We want ROOT + "/etc/setup" and cd(ROOT) to work:
        # necessitates two different forms, prefix and absolute
        if(self.__cygwinPlatform):
            self.setRoot("/");
        else:
            self.setRoot(self.__rc.ROOT);
        self.__rc.ROOT = None;
        self.__pm = PathMapper(self.__prefixRoot, self.__cygwinPlatform);
        self.__setupDir = self.__pm.mapPath(self.__setupDir);
        self.__rc.cache = self.__pm.mapPath(self.__rc.cache);
        self.__downloadDir = os.path.join(
            self.__rc.cache,
            urllib.quote(self.__rc.mirror+('' if self.__rc.mirror.endswith('/') else '/'), '').lower()
        );
        self.__installedDbFile = os.path.join(self.__setupDir, "installed.db");

        self.__setupIniPath = os.path.join(
            self.__downloadDir,
            self.__arch,
            "setup.ini",
        );
        self.__dosBash = "{0}bin/bash".format(self.__pm.getMountRoot());
        self.__dosLn = "{0}bin/ln".format(self.__pm.getMountRoot());
        self.__dosXz = self.__pm.mapPath("/usr/bin/xz");
        self.__dosDash = "{0}bin/dash".format(self.__pm.getMountRoot());
        return 0;

    def _isBarredPackage(self, package):
        barred = [];
        # add user barred package
        barred.extend(self.__rc.barred.split());
        # add force barred package
        barred.extend(self.FORCE_BARRED);

        # store current package name
        curr_pkgname = self.__pkgName;

        # get barred package requires
        depbarred = [];
        for self.__pkgName in barred:
            try:
                depbarred.extend(self.getRequires());
            except PackageException:
                pass;

        barred.extend(depbarred);

        # set current package name
        self.__pkgName = curr_pkgname;

        return (not self.__noBarred) and package in barred;
Пример #15
0
    def setUp(self):
        TestCase.setUp(self);

        self._var_verbose = False;
        self._var_cygwin_p = sys.platform.startswith("cygwin");

        if not self._var_cygwin_p:
            self.skipTest("requires cygwin");

        setup = CygAptSetup(self._var_cygwin_p, self._var_verbose);
        setup.setTmpDir(self._dir_tmp);
        setup.setAppName(self._var_exename);
        setup.setSetupDir(self._dir_confsetup);
        setup.getRC().ROOT = self._dir_mtroot;

        setup._gpgImport(setup.GPG_CYG_PUBLIC_RING_URI);
        setup.setup();

        f = open(self._file_setup_ini, 'w');
        f.write(self._var_setupIni.contents);
        f.close();

        f = open(self._file_installed_db, 'w');
        f.write(CygApt.INSTALLED_DB_MAGIC);
        f.close();

        self._var_packagename = self._var_setupIni.pkg.name;
        self._var_files = ["", self._var_packagename];
        self._var_download_p = False;
        self._var_downloads = None;
        self._var_distname = None;
        self._var_nodeps_p = False;
        self._var_regex_search = False;
        self._var_nobarred = False;
        self._var_nopostinstall = False;
        self._var_nopostremove = False;
        self._var_dists = 0;
        self._var_installed = 0;

        self.obj = CygApt(
            self._var_packagename,
            self._var_files,
            self._file_user_config,
            self._var_cygwin_p,
            self._var_download_p,
            self._var_mirror,
            self._var_downloads,
            self._var_distname,
            self._var_nodeps_p,
            self._var_regex_search,
            self._var_nobarred,
            self._var_nopostinstall,
            self._var_nopostremove,
            self._var_dists,
            self._var_installed,
            self._var_exename,
            self._var_verbose
        );

        # set attributes
        rc = ConfigStructure();
        rc.cache = self._dir_execache;
        rc.distname = 'curr';
        rc.setup_ini = self._file_setup_ini;
        rc.ROOT = self._dir_mtroot;
        rc.always_update = False;
        rc.mirror = self._var_mirror;
        self.obj.setRC(rc);

        self.obj.setDownlaodDir(self._dir_downloads);
        self.obj.setInstalledDbFile(self._file_installed_db);
        self.obj.setSetupDir(self._dir_confsetup);

        pm = PathMapper("", False);
        pm.setRoot(self._dir_mtroot[:-1]);
        pm.setMountRoot(self._dir_mtroot);
        pm.setMap({self._dir_mtroot:self._dir_mtroot});

        expected = self._dir_mtroot;
        ret = pm.mapPath(self._dir_mtroot);
        self.assertEqual(ret, expected);
        expected = os.path.join(self._dir_mtroot, "diranme");
        ret = pm.mapPath(expected);
        self.assertEqual(ret, expected);

        self.obj.setPathMapper(pm);

        self.obj.setDists(self._var_setupIni.dists.__dict__);

        self.obj.CYG_POSTINSTALL_DIR = self._dir_postinstall;
        self.obj.CYG_PREREMOVE_DIR = self._dir_preremove;
        self.obj.CYG_POSTREMOVE_DIR = self._dir_postremove;

        self.obj.setDosBash("/usr/bin/bash");
        self.obj.setDosLn("/usr/bin/ln");

        self.obj.setPrefixRoot(self._dir_mtroot[:-1]);
        self.obj.setAbsRoot(self._dir_mtroot);
        self.obj.setInstalled({0:{}});

        self.obj.FORCE_BARRED = [self._var_setupIni.barredpkg.name];
Пример #16
0
 def _createPathMapper(self, root='', cygwin_p=False):
     return PathMapper(root, cygwin_p)
Пример #17
0
 def setPathMapper(self, path_mapper=None):
     if path_mapper:
         assert isinstance(path_mapper, PathMapper);
         self.__pm = path_mapper;
     else:
         self.__pm = PathMapper("", self.__cygwinPlatform);
Пример #18
0
    def main(self):
        # parse command line arguments
        cap = CygAptArgParser(scriptname=self.getAppName())
        args = cap.parse()

        # initialize main variables with command line arguments and options
        main_command = args.command
        main_files = args.package[:]
        main_files.insert(0, main_command)
        main_packagename = None
        if len(args.package) > 0:
            main_packagename = args.package[0]
        main_verbose = args.verbose
        main_download_p = args.download_p
        main_mirror = args.mirror
        main_distname = args.distname
        main_noupdate = args.noupdate
        main_nodeps_p = args.nodeps_p
        main_regex_search = args.regex_search
        main_nobarred = args.force
        main_verify = args.verify
        main_nopostinstall = args.nopostinstall
        main_nopostremove = args.nopostremove
        main_downloads = None
        main_dists = 0
        main_installed = 0

        # locate and parse the configuration file
        main_cyg_apt_rc = self.getConfigPath()

        config = None
        if main_cyg_apt_rc:
            config = cautils.parse_rc(main_cyg_apt_rc)
        elif (main_command != "setup"):
            print("{0}: no .{0}: run \"{0} setup\"".format(self.getAppName()),
                  file=sys.stderr)
            return 1

        # create a CygAptSetup instance and its dependencies
        main_cygwin_p = (sys.platform == "cygwin")

        is_64_bit = False
        if main_cygwin_p:
            # Running Cygwin python, so python architecture == Cygwin architecture
            if 2**32 < sys.maxsize:
                is_64_bit = True
        elif config and main_command != 'setup':
            # Running Windows python, so examine cygwin1.dll
            pathMapper = PathMapper(config.ROOT.rstrip('\\/'), main_cygwin_p)
            if cautils.pe_is_64_bit(pathMapper.mapPath("/bin/cygwin1.dll")):
                is_64_bit = True

        if is_64_bit:
            main_arch = 'x86_64'
        else:
            main_arch = 'x86'

        cas = CygAptSetup(main_cygwin_p, main_verbose, main_arch)

        # run command
        if (main_command == "setup"):
            cas.setup(args.force)
            return 0
        elif (main_command == "help"):
            cas.usage(main_cyg_apt_rc)
            return 0
        elif (main_command == "update"):
            cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror)
            return 0

        # make an update if needed
        update_not_needed = [
            "ball",
            "find",
            "help",
            "purge",
            "remove",
            "version",
            "filelist",
            "update",
            "setup",
            "md5",
        ]
        always_update = config.always_update
        always_update = always_update and\
            main_command not in update_not_needed and\
            not main_noupdate
        if always_update:
            cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror)

        if main_command and main_command in dir(CygApt):
            cyg_apt = CygApt(
                main_packagename,
                main_files,
                main_cyg_apt_rc,
                main_cygwin_p,
                main_download_p,
                main_mirror,
                main_downloads,
                main_distname,
                main_nodeps_p,
                main_regex_search,
                main_nobarred,
                main_nopostinstall,
                main_nopostremove,
                main_dists,
                main_installed,
                self.getAppName(),
                main_verbose,
                main_arch,
            )

            getattr(cyg_apt, main_command)()
        else:
            cas.usage(main_cyg_apt_rc)

        return 0
Пример #19
0
    def main(self):
        # parse command line arguments
        cap = CygAptArgParser(scriptname=self.getAppName());
        args = cap.parse();

        # initialize main variables with command line arguments and options
        main_command = args.command;
        main_files = args.package[:];
        main_files.insert(0, main_command);
        main_packagename = None;
        if len(args.package) > 0:
            main_packagename = args.package[0];
        main_verbose = args.verbose;
        main_download_p = args.download_p;
        main_mirror = args.mirror;
        main_distname = args.distname;
        main_noupdate = args.noupdate;
        main_nodeps_p = args.nodeps_p;
        main_regex_search = args.regex_search;
        main_nobarred = args.force;
        main_verify = args.verify;
        main_nopostinstall = args.nopostinstall;
        main_nopostremove = args.nopostremove;
        main_downloads = None;
        main_dists = 0;
        main_installed = 0;

        # locate and parse the configuration file
        main_cyg_apt_rc = self.getConfigPath();

        config = None;
        if main_cyg_apt_rc:
            config = cautils.parse_rc(main_cyg_apt_rc);
        elif (main_command != "setup"):
            print(
                "{0}: no .{0}: run \"{0} setup\"".format(self.getAppName()),
                file=sys.stderr
            );
            return 1;

        # create a CygAptSetup instance and its dependencies
        main_cygwin_p = (sys.platform == "cygwin");

        is_64_bit = False;
        if main_cygwin_p :
            # Running Cygwin python, so python architecture == Cygwin architecture
            if 2**32 < sys.maxsize :
                is_64_bit = True;
        elif config and main_command != 'setup' :
            # Running Windows python, so examine cygwin1.dll
            pathMapper = PathMapper(config.ROOT.rstrip('\\/'), main_cygwin_p);
            if cautils.pe_is_64_bit(pathMapper.mapPath("/bin/cygwin1.dll")) :
                is_64_bit = True;

        if is_64_bit :
            main_arch = 'x86_64';
        else:
            main_arch = 'x86';

        cas = CygAptSetup(main_cygwin_p, main_verbose, main_arch);

        # run command
        if (main_command == "setup"):
            cas.setup(args.force);
            return 0;
        elif (main_command == "help"):
            cas.usage(main_cyg_apt_rc);
            return 0;
        elif (main_command == "update"):
            cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror);
            return 0;

        # make an update if needed
        update_not_needed = [
            "ball", "find", "help", "purge", "remove", "version",
            "filelist", "update", "setup", "md5",
        ];
        always_update = config.always_update;
        always_update = always_update and\
            main_command not in update_not_needed and\
            not main_noupdate;
        if always_update:
            cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror);

        if main_command and main_command in dir(CygApt):
            cyg_apt = CygApt(
                main_packagename,
                main_files,
                main_cyg_apt_rc,
                main_cygwin_p,
                main_download_p,
                main_mirror,
                main_downloads,
                main_distname,
                main_nodeps_p,
                main_regex_search,
                main_nobarred,
                main_nopostinstall,
                main_nopostremove,
                main_dists,
                main_installed,
                self.getAppName(),
                main_verbose,
                main_arch,
            );

            getattr(cyg_apt, main_command)();
        else:
            cas.usage(main_cyg_apt_rc);

        return 0;