]> git.ipfire.org Git - ipfire-3.x.git/commitdiff
Initial import of pakfire.
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 31 May 2009 11:11:08 +0000 (13:11 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 31 May 2009 11:11:08 +0000 (13:11 +0200)
13 files changed:
lfs/pakfire [new file with mode: 0644]
make.sh
src/pakfire/Makefile [new file with mode: 0644]
src/pakfire/functions [new file with mode: 0755]
src/pakfire/pakfire.in [new file with mode: 0644]
src/pakfire/python/__init__.py [new file with mode: 0644]
src/pakfire/python/db.py [new file with mode: 0644]
src/pakfire/python/io.py [new file with mode: 0644]
src/pakfire/python/package.py [new file with mode: 0644]
src/pakfire/python/repo.py [new file with mode: 0644]
src/pakfire/python/servers.py [new file with mode: 0644]
src/pakfire/python/transactionset.py [new file with mode: 0644]
src/rootfiles/core/pakfire [new file with mode: 0644]

diff --git a/lfs/pakfire b/lfs/pakfire
new file mode 100644 (file)
index 0000000..d9e4677
--- /dev/null
@@ -0,0 +1,82 @@
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007, 2008, 2009 Michael Tremer & Christian Schmidt           #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+###############################################################################
+# Definitions
+###############################################################################
+
+include Config
+
+PKG_NAME   = pakfire
+PKG_VER    = 0.9
+PKG_REL    = 0
+
+THISAPP    = $(PKG_NAME)-$(PKG_VER)
+DIR_APP    = $(DIR_SOURCE)/$(PKG_NAME)
+
+OBJECT     = $(DIR_INFO)/$(STAGE_ORDER)_$(STAGE)/$(THISAPP)
+
+MAINTAINER = Michael Tremer <michael.tremer@ipfire.org>
+GROUP      = System/Packaging
+CORE       = yes
+EXTRA      = no
+DEBUG      = no
+BUILD_DEPS =
+DEPS       = cpio xz python python-sqlite2 python-urlgrabber system-release
+
+URL        = http://www.ipfire.org/
+LICENSE    = GPLv3+
+SHORT_DESC = Package installer/updater.
+
+define LONG_DESC
+       Pakfire optains package lists from the mirrors and can install and update \
+       packages.
+endef
+
+###############################################################################
+# Top-level Rules
+###############################################################################
+
+objects =
+
+download: $(objects)
+
+info:
+       $(DO_PKG_INFO)
+
+install: $(OBJECT)
+
+package:
+       @$(DO_PACKAGE)
+
+$(objects):
+       @$(LOAD)
+
+###############################################################################
+# Installation Details
+###############################################################################
+
+$(OBJECT): $(objects)
+       @$(PREBUILD)
+       cd $(DIR_APP) && make VERSION="$(PKG_VER)" $(PARALLELISMFLAGS)
+       cd $(DIR_APP) && make install
+       cd $(DIR_APP) && make clean
+       $(PYTHON_COMPILE)
+       @$(POSTBUILD)
diff --git a/make.sh b/make.sh
index bf45e39702076711e66abcd66c82cf6e424b3b6a..1975e42705a510cb0d3219f14c880c5545033e85 100755 (executable)
--- a/make.sh
+++ b/make.sh
@@ -254,6 +254,7 @@ ipfire_build() {
        ipfire_make nss_ldap
        ipfire_make ldapvi
        ipfire_make sqlite
+       ipfire_make python-sqlite2
        ipfire_make curl
        ipfire_make pinentry
        ipfire_make gnupg2
@@ -344,6 +345,7 @@ ipfire_build() {
        ipfire_make pyfire
        ipfire_make network
        ipfire_make firewall
+       ipfire_make pakfire
 }
 
 ################################################################################
diff --git a/src/pakfire/Makefile b/src/pakfire/Makefile
new file mode 100644 (file)
index 0000000..ed4061d
--- /dev/null
@@ -0,0 +1,19 @@
+
+SUBSTITUDE = sed -e "s/@VERSION@/$(VERSION)/g"
+
+PYTHON_MAJ = "2.6"
+PYTHON_DIR = $(DESTDIR)/usr/lib/python$(PYTHON_MAJ)/site-packages/pakfire
+
+all: pakfire
+
+pakfire: pakfire.in
+       $(SUBSTITUDE) pakfire.in > pakfire
+
+clean:
+       rm -vf pakfire python/*.py[co]
+
+install: pakfire
+       install -v -m 755 pakfire $(DESTDIR)/usr/bin
+       -mkdir -pv $(PYTHON_DIR)
+       cp -vf python/*.py $(PYTHON_DIR)/
+       -mkdir -pv $(DESTDIR)/etc/pakfire.repos.d $(DESTDIR)/var/cache/pakfire
diff --git a/src/pakfire/functions b/src/pakfire/functions
new file mode 100755 (executable)
index 0000000..3d9e023
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007, 2008, 2009 Michael Tremer & Christian Schmidt           #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+function pkg_get_file() {
+       cpio --extract --quiet --to-stdout $1 < $2
+}
+
+function pkg_info() {
+       pkg_get_file info $1
+}
+
+function pkg_data() {
+       pkg_get_file data.img $1
+}
+
+function pkg_control() {
+       pkg_get_file control $1
+}
+
+function pkg_verify() {
+       local pak=$1
+       (
+               eval $(pkg_info $pak | grep ^PKG_DATA_SHA1)
+               if [ "$(pkg_data $pak | sha1sum | awk '{ print $1 }')" = "$PKG_DATA_SHA1" ]; then
+                       exit 0
+               else
+                       exit 1
+               fi
+       )
+       return $?
+}
diff --git a/src/pakfire/pakfire.in b/src/pakfire/pakfire.in
new file mode 100644 (file)
index 0000000..c861c33
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+import sys
+
+from optparse import OptionParser
+
+import pakfire
+from pakfire.package import Package
+from pakfire.transactionset import Transactionset
+
+__version__ = "@VERSION@"
+
+op = OptionParser(usage="%prog [options] <action> ...", version="%prog " + __version__)
+op.add_option("-r", "--root", dest="root", metavar="DIR")
+op.add_option("-v", "--verbose", dest="verbose",
+              action="store_true", default=False,
+              help="Be verbose.")
+op.add_option("-y", "--yes", dest="allyes",
+              action="store_true", default=False,
+              help="Answer all questions with \"yes\".")
+op.add_option("--nogpgcheck", dest="gpgcheck",
+              action="store_false", default=True,
+              help="Omit gpg check (unsafe).")
+
+def usage(exit=1):
+    op.print_help()
+    sys.exit(exit)
+
+def parse_options():
+    (options, args) = op.parse_args()
+    
+    # When no command was given
+    if not args:
+        usage()
+    
+    # The first argument is the action
+    action = args.pop(0)
+    if not action in ["install", "remove", "info", "provides",]:
+        op.error("\"%s\" is not a valid action." % action)
+    
+    if not args:
+        op.error("Action \"%s\" needs at least one argument.")
+
+    return (action, options, args)
+
+(action, options, args) = parse_options()
+
+pakfire = pakfire.Pakfire()
+
+if action == "install":
+    pakfire.ts = Transactionset()
+    for a in args:
+        p = Package(a)
+        pakfire.ts.addPackage(p)
+
+    pakfire.ts.run()
+
+elif action == "info":
+    for a in args:
+        p = Package(a)
+        print p.print_info()
diff --git a/src/pakfire/python/__init__.py b/src/pakfire/python/__init__.py
new file mode 100644 (file)
index 0000000..4cdc253
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+
+from repo import Repositories
+from transactionset import Transactionset
+
+class Pakfire(object):
+    repos = Repositories()
+    ts = Transactionset()
+    
+    def __init__(self):
+        pass
diff --git a/src/pakfire/python/db.py b/src/pakfire/python/db.py
new file mode 100644 (file)
index 0000000..986b44d
--- /dev/null
@@ -0,0 +1,52 @@
+
+
+from pysqlite2 import dbapi2 as sqlite
+
+DATABASE_PATH = "."
+
+class Database(object):
+    def __init__(self, filename):
+        self.filename = filename
+
+        self.connection = sqlite.connect(self.filename)
+    
+    def add(self, table):
+        c = self.cursor
+        c.executescript("CREATE TABLE IF NOT EXISTS %s(id, key, value);" % table)
+        c.close()
+
+    def commit(self):
+        self.connection.commit()
+
+    def destroy(self, table):
+        c = self.cursor
+        c.execute("DELETE FROM %s" % table)
+        c.close()
+        self.commit()
+    
+    def get(self, table, id, key):
+        ret = None
+        c = self.cursor
+        c.execute("SELECT value FROM %s WHERE id='%s' AND key='%s';" % \
+                  (table, id, key,))
+        try:
+            ret = c.fetchone()[0]
+        except TypeError:
+            pass
+        c.close()
+        return ret
+
+    def set(self, table, id, key, value):
+        c = self.cursor
+        if not self.get(id, key):
+            c.execute("INSERT INTO %s(id, key, value) VALUES('%s', '%s', '%s');" \
+                      % (table, id, key, value,))
+        else:
+            c.execute("UPDATE %s SET value='%s' WHERE id='%s' AND key='%s';" \
+                      % (table, value, id, key,))
+        c.close()
+        self.commit()
+
+    @property
+    def cursor(self):
+        return self.connection.cursor()
diff --git a/src/pakfire/python/io.py b/src/pakfire/python/io.py
new file mode 100644 (file)
index 0000000..2e142de
--- /dev/null
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+
+import grp
+import os
+import pwd
+import stat
+import time
+
+def ftype(mode):
+    if stat.S_ISBLK(mode):
+        return "b"
+    elif stat.S_ISCHR(mode):
+        return "c"
+    elif stat.S_ISDIR(mode):
+        return "d"
+    elif stat.S_ISREG(mode):
+        return "-"
+    elif stat.S_ISFIFO(mode):
+        return "p"
+    elif stat.S_ISLINK(mode):
+        return "l"
+    elif stat.S_ISSOCK(mode):
+        return "s"
+    return "?"
+
+def rwx(mode):
+    ret = ""
+    if mode & stat.S_IRUSR:
+        ret += "r"
+    else:
+        ret += "-"
+    
+    if mode & stat.S_IWUSR:
+        ret += "w"
+    else:
+        ret += "-"
+    
+    if mode & stat.S_IXUSR:
+        ret += "x"
+    else:
+        ret += "-"
+    
+    return ret
+
+def fmode(mode):
+    ret = ftype(mode)
+    ret += rwx((mode & 0700) << 0)
+    ret += rwx((mode & 0070) << 3)
+    ret += rwx((mode & 0007) << 6)
+    return ret
+
+class CpioError(Exception):
+    pass
+
+
+class CpioEntry(object):
+    def __init__(self, hdr, archive, offset):
+        self.archive = archive
+        self.hdr = hdr
+        
+        self.offset  = offset + 110 + self.namesize
+        self.offset += (4 - (self.offset % 4)) % 4
+        self.current = 0
+        
+        self.closed = False
+        
+        if len(self.hdr) < 110:
+            raise CpioError("Header too short.")
+        
+        if not self.hdr.startswith("070701") and not self.hdr.startswith("070702"):
+            raise CpioError("Invalid header: %s" % self.hdr[:6])
+    
+    def close(self):
+        self.closed = True
+
+    def flush(self):
+        pass # noop
+
+    def read(self, size=None):
+        """Read data from the entry.
+
+        Keyword arguments:
+        size -- Number of bytes to read (default: whole entry)
+        """
+        if self.closed:
+            raise ValueError("Read operation on closed file.")
+
+        self.archive.file.seek(self.offset + self.current, os.SEEK_SET)
+
+        if size and size < self.size - self.current:
+            ret = self.archive.file.read(size)
+        else:
+            ret = self.archive.file.read(self.size - self.current)
+        self.current += len(ret)
+        return ret
+
+    def seek(self, offset, whence=0):
+        """Move to new position within an entry.
+    
+        Keyword arguments:
+        offset -- Byte count
+        whence -- Describes how offset is used.
+        0: From beginning of file
+        1: Forwards from current position
+        2: Backwards from current position
+        Other values are ignored.
+        """
+        if self.closed:
+            raise ValueError("Seek operation on closed file.")
+
+        if whence == os.SEEK_SET:
+            self.current = offset
+        elif whence == os.SEEK_REL:
+            self.current += offset
+        elif whence == os.SEEK_END:
+            self.current -= offset
+
+        self.current = min(max(0, self.current), self.size)
+
+    def tell(self):
+        """Get current position within an entry"""
+        if self.closed:
+            raise ValueError("Tell operation on closed file.")
+        return self.current
+
+    def __repr__(self):
+        return "<CpioEntry %s 0x%s>" % (self.name, self.checksum,)
+
+    @property
+    def checksum(self):
+        return int(self.hdr[102:110], 16)
+    
+    @property
+    def devmajor(self):
+        return int(self.hdr[62:70], 16)
+    
+    @property
+    def devminor(self):
+        return int(self.hdr[70:78], 16)
+
+    @property
+    def gid(self):
+        return int(self.hdr[30:38], 16)
+
+    @property
+    def inode(self):
+        return int(self.hdr[6:14], 16)
+
+    @property
+    def mode(self):
+        return int(self.hdr[14:22], 16)
+    
+    @property
+    def mtime(self):
+        return int(self.hdr[46:54], 16)
+
+    @property
+    def name(self):
+        end = 110 + self.namesize - 1
+        return self.hdr[110:end]
+
+    @property
+    def namesize(self):
+        return int(self.hdr[94:102], 16)
+    
+    @property
+    def nlinks(self):
+        return int(self.hdr[38:46], 16)
+
+    @property
+    def rdevmajor(self):
+        return int(self.hdr[78:86], 16)
+    
+    @property
+    def rdevminor(self):
+        return int(self.hdr[86:94], 16)
+
+    @property
+    def size(self):
+        return int(self.hdr[54:62], 16)
+    
+    @property
+    def uid(self):
+        return int(self.hdr[22:30], 16)
+
+
+class CpioArchive(object):
+    _entries = []
+    file = None
+
+    def __init__(self, filename):
+        
+        self.filename = filename
+        self.file = open(self.filename, "r")
+        self.__readfile()
+
+        self.closed = False
+    
+    def close(self):
+        if self.closed:
+            return
+        self.closed = True
+
+        self.file.close()
+    
+    def __readfile(self):
+        if not self.file:
+            raise CpioError("File was not yet opened.")
+
+        self._entries = []
+        sposition = self.file.tell()
+        hdr = self.file.read(110)
+        while hdr:
+            namelen = int(hdr[94:102], 16) # Length of the name
+            hdr += self.file.read(namelen)
+            ce = CpioEntry(hdr, self, sposition)
+            if ce.name == "TRAILER!!!":
+                return
+            self._entries.append(ce)
+
+            self.file.seek((4 - (self.file.tell()-sposition) % 4) % 4, os.SEEK_CUR)
+            self.file.seek(ce.size, os.SEEK_CUR)
+            self.file.seek((4 - (self.file.tell()-sposition) % 4) % 4, os.SEEK_CUR)
+
+            sposition = self.file.tell()
+            hdr = self.file.read(110)
+        else:
+            raise CpioError("Premature end of headers.")
+    
+    @property
+    def entries(self):
+        return sorted(self._entries)
+    
+    @property
+    def size(self):
+        return os.path.getsize(self.filename)
+    
+    def ls(self):
+        for x in self.entries:
+            print x.name
+    
+    def ll(self):
+        for x in self.entries:        
+            print "%s %s %s %s %9d %s %s" % \
+                (fmode(x.mode),
+                 x.nlinks,
+                 pwd.getpwuid(x.uid)[0],
+                 grp.getgrgid(x.gid)[0],
+                 x.size,
+                 time.strftime("%Y-%m-%d %H:%M", time.localtime(x.mtime)),
+                 x.name,)
+    
+    def get(self, item):
+        for x in self.entries:
+            if x.name == item:
+                return x
+        raise KeyError("No such file or directory.")
+    
+    def __getitem__(self, item):
+        x = self.get(item)
+        x.seek(0)
+        return x.read()
diff --git a/src/pakfire/python/package.py b/src/pakfire/python/package.py
new file mode 100644 (file)
index 0000000..4bbcb24
--- /dev/null
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+
+import hashlib
+import os
+import subprocess
+
+import io
+
+class Package(object):
+    _info = {}
+
+    def __init__(self, archive):
+        self.archive = io.CpioArchive(archive)
+    
+    def check(self):
+        print "Checking package %s..." % self.name
+        return self.verify()
+    
+    def extract(self, root="/"):
+        if not os.path.exists(root):
+            os.makedirs(root)
+
+        lzma = subprocess.Popen(["lzma", "-dc"],
+                                stdin=subprocess.PIPE,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,)
+
+        cpio = subprocess.Popen(["cpio",
+                                 "--quiet",
+                                 "--extract",
+                                 "--unconditional",
+                                 "--make-directories",
+                                 "--no-absolute-filenames",],
+                                stdin=subprocess.PIPE,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,
+                                cwd=root)
+
+        BUF = 1024
+        file = self.archive["data.img"]
+
+        # Decompress in one big swoop.
+        lzma.stdin.write(file)
+        lzma.stdin.close()
+
+        lzmaerr = lzma.stderr.read()
+        if lzmaerr:
+            raise Exception("Decompression error: %s" % lzmaerr)
+
+        while True:
+            buf = lzma.stdout.read(BUF)
+            if not buf:
+                break
+            cpio.stdin.write(buf)
+        lzma.stdout.close()
+
+        cpioerr = cpio.stderr.read()
+        if cpioerr:
+            raise Exception("Archiving error: %s" % cpioerr)
+
+    def install(self, root="/"):
+        print "Installing %s..." % self.name
+        self.extract(root)
+    
+    def print_info(self):
+        ret = ""
+        info = (("Name", self.name),
+                ("Version", self.version),
+                ("Arch", self.arch),
+                ("Release", self.release),
+                ("Size", self.size),
+                ("Summary", self.summary),
+                ("URL", self.url),
+                ("License", self.license),
+                ("Description", self.description))
+
+        for (key, value) in info:
+            ret += "%-12s: %s\n" % (key, value,)
+        
+        if self.verify:
+            ret += "%-12s: %s\n" % ("Signature", "OK")
+        else:
+            ret += "%-12s: %s\n" % ("Signature", "Broken")
+        
+        return ret
+        
+    def verify(self):
+        hash = hashlib.sha1(self.archive["data.img"]).hexdigest()
+        if hash == self.sha1:
+            return True
+        return False
+
+    @property
+    def arch(self):
+        return self.info.get("PKG_ARCH", None)
+
+    @property
+    def deps(self):
+        return self.info.get("PKG_DEPS", None)
+
+    @property
+    def description(self):
+        return self.info.get("PKG_DESC", None)
+
+    @property
+    def group(self):
+        return self.info.get("PKG_GROUP", None)
+
+    @property
+    def license(self):
+        return self.info.get("PKG_LICENSE", None)
+
+    @property
+    def name(self):
+        return self.info.get("PKG_NAME", None)
+
+    @property
+    def info(self):
+        if not self._info:
+            self._info = {}
+            for line in self.archive["info"].split("\n"):
+                if not line or line.startswith("#"):
+                    continue
+                (key, value) = line.split("=")
+                self._info[key] = value.strip("\"")
+        return self._info
+
+    @property
+    def release(self):
+        return self.info.get("PKG_REL", None)
+
+    @property
+    def sha1(self):
+        return self.info.get("PKG_DATA_SHA1", None)
+
+    @property
+    def size(self):
+        return self.archive.size
+
+    @property
+    def summary(self):
+        return self.info.get("PKG_SUMMARY", None)
+
+    @property
+    def url(self):
+        return self.info.get("PKG_URL", None)
+
+    @property
+    def version(self):
+        return self.info.get("PKG_VER", None)
+
diff --git a/src/pakfire/python/repo.py b/src/pakfire/python/repo.py
new file mode 100644 (file)
index 0000000..2e6214f
--- /dev/null
@@ -0,0 +1,88 @@
+
+import os
+import ConfigParser as configparser
+import urlgrabber
+
+import db as database
+from servers import Servers
+
+REPOS_PATH = "/etc/pakfire.repos.d"
+
+class Repositories(object):
+    _repositories = []
+    
+    def __init__(self):
+        for file in os.listdir(REPOS_PATH):
+            if not file.endswith(".repo"):
+                continue
+            cp = configparser.ConfigParser()
+            cp.read(os.path.join(REPOS_PATH, file))
+            for section in cp.sections():
+                self._repositories.append(Repository(section, cp.items(section)))
+
+    @property
+    def all(self):
+        return sorted(self._repositories)
+    
+    @property
+    def enabled(self):
+        ret = []
+        for r in self._repositories:
+            if r.enabled:
+                ret.append(r)
+        return ret
+    
+    @property
+    def repositories(self):
+        return self.enabled
+
+class Repository(object):
+    def __init__(self, name, items=None):
+        self.name = name
+
+        if items:
+            config = {}
+            for (key, value) in items:
+                config[key] = value
+            self.config = config
+        
+        self.db = database.Database("%s.db" % self.name)
+        self.servers = Servers(self.db)
+    
+    def __cmp__(self, other):
+        return cmp(self.name, other.name)
+    
+    def __str__(self):
+        return self.name
+    
+    def __repr__(self):
+        return "<Repository %s>" % self.name
+    
+    def update_mirrorlist(self, mirrorlist=None):
+        if not mirrorlist:
+            mirrorlist = self.mirrorlist
+        f = urlgrabber.urlopen(mirrorlist)
+        for line in f.readlines():
+            self.servers.add(line)
+        f.close()
+
+    @property
+    def enabled(self):
+        value = self.config.get("enabled")
+        if value == "1":
+            return True
+        return False
+
+    @property
+    def gpgkey(self):
+        return self.config.get("gpgkey", None)
+    
+    @property
+    def mirrorlist(self):
+        return self.config.get("mirrorlist", None)
+
+
+if __name__ == "__main__":
+    rs = Repositories()
+    for r in rs.repositories:
+        r.update_mirrorlist()
diff --git a/src/pakfire/python/servers.py b/src/pakfire/python/servers.py
new file mode 100644 (file)
index 0000000..d27617d
--- /dev/null
@@ -0,0 +1,88 @@
+
+import random
+import urlparse
+import uuid
+
+SERVER_DEFAULT_PROTOCOL = "http"
+SERVER_DEFAULT_PORT = "80"
+SERVER_DEFAULT_PATH = ""
+
+class Servers(object):
+    table = "servers"
+
+    def __init__(self, db):
+        self.db = db
+        self.db.add(self.table)
+    
+    def add(self, url):
+        p = urlparse.urlparse(url)
+        for server in self.all:
+            if str(server) == url:
+                return
+        server = Server(self.db)
+        (server.protocol, server.hostname, server.port, server.path) = \
+            (p.scheme, p.hostname, p.port, p.path)
+
+    @property
+    def all(self):
+        ret = []
+        c = self.db.cursor
+        c.execute("SELECT DISTINCT id FROM %s" % self.table)
+        for id in c.fetchall():
+            s = Server(self.db, id)
+            ret.append(s)
+        return sorted(ret)
+    
+    @property
+    def random(self):
+        return random.choice(self.all)
+
+
+class Server(object):
+    table = Servers.table
+
+    def __init__(self, db, id=None):
+        self.db = db
+
+        if not id:
+            id = str(uuid.uuid4())
+        self.id = "%s" % id
+    
+    def __str__(self):
+        return urlparse.urlunparse((self.protocol, \
+            "%s:%s" % (self.hostname, self.port), self.path, None, None, None))
+    
+    def __repr__(self):
+        return "<Server %s>" % self.id
+    
+    def _getHostname(self):
+        return self.db.get(self.table, self.id, "hostname")
+    
+    def _setHostname(self, hostname):
+        return self.db.set(self.table, self.id, "hostname", hostname)
+
+    hostname = property(_getHostname, _setHostname)
+    
+    def _getProtocol(self):
+        return self.db.get(self.table, self.id, "protocol") or SERVER_DEFAULT_PROTOCOL
+    
+    def _setProtocol(self, protocol):
+        return self.db.set(self.table, self.id, "protocol", protocol)
+    
+    protocol = property(_getProtocol, _setProtocol)
+
+    def _getPort(self):
+        return self.db.get(self.table, self.id, "port") or SERVER_DEFAULT_PORT
+    
+    def _setPort(self, port):
+        return self.db.set(self.table, self.id, "port", port)
+
+    port = property(_getPort, _setPort)
+    
+    def _getPath(self):
+        return self.db.get(self.table, self.id, "path") or SERVER_DEFAULT_PATH
+    
+    def _setPath(self, path):
+        return self.db.set(self.table, self.id, "path", path)
+    
+    path = property(_getPath, _setPath)
diff --git a/src/pakfire/python/transactionset.py b/src/pakfire/python/transactionset.py
new file mode 100644 (file)
index 0000000..7a1c95f
--- /dev/null
@@ -0,0 +1,35 @@
+
+
+class Transactionset(object):
+    _packages = []
+
+    def __init__(self):
+        pass
+
+    def addPackage(self, package):
+        self._packages.append(package)
+    
+    def check(self):
+        print "Checking Transactionset..."
+        for package in self.packages:
+            if not package.check():
+                return False
+        return True
+
+    def install(self, root="/"):
+        for package in self.packages:
+            package.install(root)
+
+    def resolveDeps(self):
+        pass
+
+    def run(self, root="/"):
+        print "Running transactionset..."
+        if self.check():
+            self.install(root)
+        else:
+            print "Error, when verifying the packages..."
+
+    @property
+    def packages(self):
+        return sorted(self._packages)
diff --git a/src/rootfiles/core/pakfire b/src/rootfiles/core/pakfire
new file mode 100644 (file)
index 0000000..d341245
--- /dev/null
@@ -0,0 +1,25 @@
+etc/pakfire.repos.d
+usr/bin/pakfire
+usr/lib/python2.6/site-packages/pakfire
+usr/lib/python2.6/site-packages/pakfire/__init__.py
+usr/lib/python2.6/site-packages/pakfire/__init__.pyc
+usr/lib/python2.6/site-packages/pakfire/__init__.pyo
+usr/lib/python2.6/site-packages/pakfire/db.py
+usr/lib/python2.6/site-packages/pakfire/db.pyc
+usr/lib/python2.6/site-packages/pakfire/db.pyo
+usr/lib/python2.6/site-packages/pakfire/io.py
+usr/lib/python2.6/site-packages/pakfire/io.pyc
+usr/lib/python2.6/site-packages/pakfire/io.pyo
+usr/lib/python2.6/site-packages/pakfire/package.py
+usr/lib/python2.6/site-packages/pakfire/package.pyc
+usr/lib/python2.6/site-packages/pakfire/package.pyo
+usr/lib/python2.6/site-packages/pakfire/repo.py
+usr/lib/python2.6/site-packages/pakfire/repo.pyc
+usr/lib/python2.6/site-packages/pakfire/repo.pyo
+usr/lib/python2.6/site-packages/pakfire/servers.py
+usr/lib/python2.6/site-packages/pakfire/servers.pyc
+usr/lib/python2.6/site-packages/pakfire/servers.pyo
+usr/lib/python2.6/site-packages/pakfire/transactionset.py
+usr/lib/python2.6/site-packages/pakfire/transactionset.pyc
+usr/lib/python2.6/site-packages/pakfire/transactionset.pyo
+var/cache/pakfire