]> TLD Linux GIT Repositories - tld-ftp-admin.git/commitdiff
- raw from PLD
authorMarcin Krol <hawk@tld-linux.org>
Thu, 28 Jul 2016 21:38:56 +0000 (23:38 +0200)
committerMarcin Krol <hawk@tld-linux.org>
Thu, 28 Jul 2016 21:38:56 +0000 (23:38 +0200)
58 files changed:
bin/pfa-checksign [new file with mode: 0755]
bin/pfa-checktree [new file with mode: 0755]
bin/pfa-dump-locks [new file with mode: 0755]
bin/pfa-from-incoming [new file with mode: 0755]
bin/pfa-genindex [new file with mode: 0755]
bin/pfa-lintpkg [new file with mode: 0755]
bin/pfa-maintainer [new file with mode: 0755]
bin/pfa-mvpkg [new file with mode: 0755]
bin/pfa-rmpkg [new file with mode: 0755]
bin/pfa-signpkg [new file with mode: 0755]
bin/pfa-testmvpkg [new file with mode: 0755]
bin/pfa-unlocktree [new file with mode: 0755]
cgi-bin/index.py [new file with mode: 0755]
doc/README [new file with mode: 0644]
etc/rpmlint [new file with mode: 0644]
ftpiod/ftpiod.py [new file with mode: 0755]
html/footer.html [new file with mode: 0644]
html/header.html [new file with mode: 0644]
html/index.html [new file with mode: 0644]
html/layout.css [new file with mode: 0644]
html/loggedinmenu.html [new file with mode: 0644]
html/menufooter.html [new file with mode: 0644]
html/qa.php [new file with mode: 0644]
html/regform.html [new file with mode: 0644]
modules/.gitignore [new file with mode: 0644]
modules/baseftptree.py [new file with mode: 0644]
modules/cmds.py [new file with mode: 0644]
modules/common.py [new file with mode: 0644]
modules/config.py [new file with mode: 0644]
modules/cons.py [new file with mode: 0644]
modules/ftpio.py [new file with mode: 0644]
modules/ftptree.py [new file with mode: 0644]
modules/mailer.py [new file with mode: 0644]
modules/sign.py [new file with mode: 0644]
modules/user.py [new file with mode: 0644]
modules/wwwcmds.py [new file with mode: 0644]
modules/wwwiface.py [new file with mode: 0644]
repodata/comps.xml [new file with mode: 0644]
shell/bash-completion.sh [new file with mode: 0644]
shell/bash_profile [new file with mode: 0644]
shell/bashrc [new file with mode: 0644]
ucred/make.sh [new file with mode: 0755]
ucred/setup.py [new file with mode: 0644]
ucred/ucred.c [new file with mode: 0644]
var/.keep [new file with mode: 0644]
wwwbin/ac-th-diff.py [new file with mode: 0755]
wwwbin/by-group.sh [new file with mode: 0755]
wwwbin/checkrepo.sh [new file with mode: 0755]
wwwbin/clean-dups-archive.py [new file with mode: 0755]
wwwbin/clean-dups-old.py [new file with mode: 0755]
wwwbin/clean-dups.py [new file with mode: 0755]
wwwbin/clean-test.sh [new file with mode: 0755]
wwwbin/consistency-check.sh [new file with mode: 0755]
wwwbin/dump-packagenames.py [new file with mode: 0755]
wwwbin/freshness.sh [new file with mode: 0755]
wwwbin/ftp-freshness.py [new file with mode: 0755]
wwwbin/obsolete-check.sh [new file with mode: 0755]
wwwbin/rpmcheck.sh [new file with mode: 0755]

diff --git a/bin/pfa-checksign b/bin/pfa-checksign
new file mode 100755 (executable)
index 0000000..14060e2
--- /dev/null
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+import getopt
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+import getpass
+from common import checkdir
+import ftpio
+from config import sign_key
+from sign import is_signed, signpkgs
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], '')
+except getopt.GetoptError:
+    print >>sys.stderr, "ERR: options error"
+    print >>sys.stderr, "checksign.py tree package1 [package2...]"
+    sys.exit(1)
+
+if len(args) < 1:
+    print >>sys.stderr, "ERR: missing tree name"
+    print >>sys.stderr, "checksign.py tree package1 [package2...]"
+    sys.exit(1)
+
+if sign_key == None:
+    print >>sys.stderr, "ERR: sign_key not defined in config"
+    sys.exit(1)
+
+treename = args[0]
+packages = args[1:]
+
+checkdir(treename)
+
+ftpio.connect('sign')
+
+if not ftpio.lock(treename, True):
+    print >>sys.stderr, "ERR: %s tree already locked" % treename
+    sys.exit(1)
+
+files = []
+try:
+    if len(packages) < 1:
+        loadall = True
+    else:
+        loadall = False
+
+    # if no files specified, grab whole tree contents
+    tree = ftptree.FtpTree(treename, loadall = loadall)
+    if loadall:
+        # this is hack, should be a param, not access private .loadedpkgs element
+        tree.mark4moving(tree.loadedpkgs)
+    else:
+        tree.mark4moving(packages)
+
+except ftptree.SomeError:
+    # In case of problems we need to unlock the tree before exiting
+    ftpio.unlock(treename)
+    sys.exit(1)
+
+ftpio.unlock(treename)
+
+print "Checking signatures of %d packages" % len(tree.loadedpkgs)
+sign = []
+for pkg in tree.marked4moving:
+    unsigned = 0
+    for file in pkg.rpmfiles():
+        if not is_signed(file):
+            unsigned += 1
+
+    if unsigned != 0:
+        print '%s: %d files NOT signed' % (pkg.nvr, unsigned)
+    else:
+        print '%s signed' % pkg.nvr
+
+sys.exit(0)
diff --git a/bin/pfa-checktree b/bin/pfa-checktree
new file mode 100755 (executable)
index 0000000..226e4df
--- /dev/null
@@ -0,0 +1,299 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+ftptree.quietmode=True
+from common import checkdir
+import ftpio
+import config
+cval=config.value
+
+import curses
+
+if len(sys.argv) != 3:
+    print "ERR: wrong number of arguments"
+    print "check-tree.py srctree dsttree"
+    sys.exit(1)
+
+checkdir(sys.argv[1])
+
+ftpio.connect('check-tree')
+
+if not ftpio.lock(sys.argv[1], True):
+    print "ERR: %s tree already locked" % sys.argv[1]
+    sys.exit(1)
+
+if not ftpio.lock(sys.argv[2], True):
+    ftpio.unlock(sys.argv[1])
+    print "ERR: %s tree already locked" % sys.argv[2]
+    sys.exit(1)
+
+srctree=ftptree.FtpTree(sys.argv[1], loadall=True)
+dsttree=ftptree.FtpTree(sys.argv[2])
+
+srctree.checktree(dsttree)
+
+# We've loaded all interesting info, so we can continue without locks
+ftpio.unlock(sys.argv[1])
+ftpio.unlock(sys.argv[2])
+
+# The list(set(list)) cast is a fancy way to remove dups from a list
+names=sorted(list(set([pkg.name for pkg in srctree.values()])))
+
+pkgtree={}
+
+for n in names:
+    pkgtree[n]=[]
+
+for pkg in srctree.values():
+    pkgtree[pkg.name].append(pkg)
+
+for key in pkgtree.keys():
+    pkgtree[key]=sorted(pkgtree[key], reverse=True)
+
+for pkg in srctree.values():
+    pkg.marked4movingpool=pkgtree[pkg.name]
+
+pkglist=[]
+
+for key in sorted(pkgtree.keys()):
+    pkglist=pkglist+pkgtree[key]
+
+DoQuit="OMG!"
+
+class Screen:
+    def __init__(self, srctreename, dsttreename, elements):
+        self.srctree=srctreename
+        self.dsttree=dsttreename
+        self.elements=elements
+        self.pkglistselected=0
+        self.pkgliststartpad=0
+        self.win2startpad=0
+        self.window=1
+        try:
+            # initial
+            self.screen=curses.initscr()
+            self.screen.keypad(1)
+            curses.start_color()
+            curses.noecho()
+            curses.cbreak()
+            curses.meta(1)
+            curses.curs_set(0)
+
+             # Without this refresh(), nothing will show up before first getch()
+            self.screen.refresh()
+
+            self.__init_colors()
+            self.__init_windows()
+            self.__bind_keys()
+
+            try:
+                # Main loop
+                while True:
+                    if self.window==1: #pkglist
+                        self.__redraw_pkglist()
+                        self.__redraw_statusbox()
+                    elif self.window==2: 
+                        pass
+                    curses.doupdate()
+                    self.__handle_input()
+            except DoQuit:
+                pass
+        finally:
+            curses.echo()
+            curses.nocbreak()
+            curses.curs_set(1)
+            curses.endwin()
+
+    def __bind_keys(self):
+        self.globalkeys=(('qQ', 'quit'), ('KEY_RESIZE', 'resize'),
+                ('KEY_CLOSE', 'quit'))
+        self.perwindowkeys=[(), ()]
+        self.perwindowkeys[0]=(('k', 'move_up'), ('J', 'move_pgdown'),
+                ('K', 'move_pgup'), (' ', 'mark'), ('j', 'move_down'),
+                ('KEY_DOWN', 'move_down'), ('KEY_UP', 'move_up'),
+                ('KEY_HOME', 'move_home'), ('KEY_END', 'move_end'),
+                ('KEY_NPAGE', 'move_pgdown'), ('KEY_PPAGE', 'move_pgup'))
+                
+
+    def __init_colors(self):
+        curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN)
+        self.COLOR_TITLEBAR=curses.color_pair(1)
+        curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE)
+        self.COLOR_STANDARD=curses.color_pair(2)
+        curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLUE)
+        self.COLOR_ERROR=curses.color_pair(3)
+        curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLUE)
+        self.COLOR_WARNING=curses.color_pair(4)
+        curses.init_pair(5, curses.COLOR_WHITE, curses.COLOR_RED)
+        self.COLOR_ERRORMSG=curses.color_pair(5)
+        curses.init_pair(6, curses.COLOR_WHITE, curses.COLOR_YELLOW)
+        self.COLOR_WARNINGMSG=curses.color_pair(6)
+        curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_WHITE)
+        self.COLOR_WHITEBG=curses.color_pair(7)
+
+    def __init_windows(self):
+        self.__set_screen_coordinates()
+
+        # Titlebar
+        self.titlebar = curses.newpad(1, 200)
+        self.titlebar.bkgdset(ord(' '), self.COLOR_TITLEBAR)
+        self.__redraw_titlebar()
+
+        # Pkg list pad
+        self.pkglist = curses.newpad(len(self.elements), 200)
+        self.pkglist.bkgdset(ord(' '), self.COLOR_STANDARD)
+        self.pkglist.clrtobot()
+
+        # Status bar
+        self.statusbar = curses.newpad(1, 200)
+
+        # Pkg status box
+        self.statusbox = curses.newpad(5, 200)
+        self.statusbox.bkgdset(ord(' '), self.COLOR_STANDARD)
+        self.statusbox.clrtobot()
+        self.__redraw_statusbox()
+
+    def __set_screen_coordinates(self):
+        self.scr_h, self.scr_w = self.screen.getmaxyx()
+        self.pad_h = self.scr_h-5
+        self.sbar_y = self.scr_h-4
+        self.sbox_y = self.scr_h-3
+
+    def __redraw_titlebar(self):
+        self.titlebar.clrtobot()
+#        self.titlebar.addstr(0, self.scr_w-14, "pld-ftp-admin")
+        self.titlebar.addstr(0, 1, 'Window #%s' % self.window)
+        tmp="%s => %s" % (self.srctree, self.dsttree)
+        self.titlebar.addstr(0, int(self.scr_w/2-len(tmp)/2)-1, tmp)
+        self.titlebar.addstr(0, self.scr_w-20, "Press 'h' for help.")
+        self.titlebar.noutrefresh(0, 0, 0, 0, 1, self.scr_w-1)
+
+    def __redraw_statusbox(self):
+        self.statusbox.erase()
+        pkg=self.elements[self.pkglistselected]
+        i=0
+        for error in pkg.errors:
+            self.statusbox.bkgdset(ord(' '), self.COLOR_ERRORMSG)
+            self.statusbox.addstr(i, 0, 'ERR: '+error, curses.A_BOLD)
+            self.statusbox.bkgdset(ord(' '), self.COLOR_STANDARD)
+            i+=1
+        for warning in pkg.warnings:
+            self.statusbox.bkgdset(ord(' '), self.COLOR_WARNINGMSG)
+            self.statusbox.addstr(i, 0, 'WARN: '+warning, curses.A_BOLD)
+            self.statusbox.bkgdset(ord(' '), self.COLOR_STANDARD)
+            i+=1
+        self.statusbox.noutrefresh(0,0,self.sbox_y,0,self.scr_h-1,self.scr_w-1)
+
+    def __redraw_pkglist(self):
+        # Safety in case there's a bug somewhere
+        if self.pkgliststartpad>len(self.elements)-self.pad_h:
+            self.pkgliststartpad=len(self.elements)-self.pad_h
+        if self.pkgliststartpad<0:
+            self.pkgliststartpad=0
+
+        self.pkglist.erase()
+        for i in range(len(self.elements)):
+            if self.elements[i].marked4moving:
+                if self.elements[i].warnings:
+                    self.pkglist.bkgdset(ord(' '), self.COLOR_WARNINGMSG)
+                    self.pkglist.addstr(i, 0, '+', curses.A_BOLD)
+                else:
+                    self.pkglist.bkgdset(ord(' '), self.COLOR_WHITEBG)
+                    self.pkglist.addstr(i, 0, '+')
+
+            if self.elements[i].errors:
+                self.pkglist.bkgdset(ord(' '), self.COLOR_ERROR)
+            elif self.elements[i].warnings:
+                self.pkglist.bkgdset(ord(' '), self.COLOR_WARNING)
+            else:
+                self.pkglist.bkgdset(ord(' '), self.COLOR_STANDARD)
+            if i == self.pkglistselected:
+                self.pkglist.addstr(i, 2, `self.elements[i]`, curses.A_REVERSE)
+            else:
+                self.pkglist.addstr(i, 2, `self.elements[i]`, curses.A_BOLD)
+            self.pkglist.bkgdset(ord(' '), self.COLOR_STANDARD)
+        self.pkglist.noutrefresh(self.pkgliststartpad,0,1,0,self.pad_h,self.scr_w-1)
+
+    def __do_resize(self):
+        self.__set_screen_coordinates()
+        self.__redraw_titlebar()
+        self.__redraw_statusbox()
+
+    def __do_move_down(self):
+        if 0 <= self.pkglistselected < len(self.elements)-1:
+            self.pkglistselected+=1
+            if (self.pkgliststartpad!=len(self.elements)-self.pad_h and
+                self.pad_h-(self.pkglistselected-self.pkgliststartpad)<5):
+                self.pkgliststartpad+=1
+
+    def __do_move_up(self):
+        if 0 < self.pkglistselected <= len(self.elements)-1:
+            self.pkglistselected-=1
+            if self.pkgliststartpad!=0 and self.pkglistselected-self.pkgliststartpad<4:
+                self.pkgliststartpad-=1
+
+    def __do_move_home(self):
+        self.pkglistselected=0
+        self.pkgliststartpad=0
+
+    def __do_move_end(self):
+        self.pkglistselected=len(self.elements)-1
+        self.pkgliststartpad=len(self.elements)-self.pad_h
+
+    def __do_move_pgdown(self):
+        self.pkglistselected+=self.pad_h
+        if self.pkglistselected >= len(self.elements):
+            self.pkglistselected = len(self.elements)-1
+        self.pkgliststartpad+=self.pad_h
+        if self.pkgliststartpad > len(self.elements)-self.pad_h:
+            self.pkgliststartpad=len(self.elements)-self.pad_h
+
+    def __do_move_pgup(self):
+        self.pkglistselected-=self.pad_h
+        if self.pkglistselected < 0:
+            self.pkglistselected = 0
+        self.pkgliststartpad-=self.pad_h
+        if self.pkgliststartpad < 0:
+            self.pkgliststartpad = 0
+
+    def __do_quit(self):
+        raise DoQuit
+
+    def __do_mark(self):
+        pkg=self.elements[self.pkglistselected]
+        if pkg.errors:
+            return
+        elif pkg.marked4moving:
+            pkg.unmark4moving()
+        else:
+            pkg.mark4moving()
+
+    def __handle_input(self):
+        key=self.screen.getch()
+        if key==27:
+            # Switch window
+            metakey=self.screen.getch()
+            if 0<metakey<256:
+                c=chr(metakey)
+                if c=='1':
+                    self.window=1
+                elif c=='2':
+                    self.window=2
+        elif 0<key<256:
+            c=chr(key)
+            for bind in self.globalkeys+self.perwindowkeys[self.window-1]:
+                if not bind[0].startswith('KEY_') and c in bind[0]:
+                    exec 'self._Screen__do_%s()' % bind[1]
+
+        else:
+#            self.elements[0]=`key`
+            for bind in self.globalkeys+self.perwindowkeys[self.window-1]:
+                if bind[0].startswith('KEY_'):
+                    if key == getattr(curses, bind[0]):
+                        exec 'self._Screen__do_%s()' % bind[1]
+
+s = Screen(sys.argv[1], sys.argv[2], pkglist)
+
diff --git a/bin/pfa-dump-locks b/bin/pfa-dump-locks
new file mode 100755 (executable)
index 0000000..32fdfdf
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+import ftpio
+import config
+
+ftpio.connect()
+print ftpio.locks_dump()
diff --git a/bin/pfa-from-incoming b/bin/pfa-from-incoming
new file mode 100755 (executable)
index 0000000..6523ea1
--- /dev/null
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os, stat, time, re
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+from config import incoming_dir, default_to, ftp_archs
+from config import value as cval
+import config
+from common import noarchcachedir, tmpdir, fileexists
+from baseftptree import BaseFtpTree, BasePkg
+from ftptree import FtpTree, Pkg
+import ftpio
+
+os.umask(022)
+
+def rm(file):
+    os.remove(file)
+
+def mv(src, dst):
+    os.rename(src, dst + '/' + src.split('/')[-1])
+
+# duplicate code in ftptree.py
+def is_debuginfo(nvr):
+    """
+    returns true if NVR is debuginfo package and separate debuginfo is enabled
+    """
+    if not config.separate_debuginfo:
+        return False
+    pkg = nvr.split('-')[:-2]
+    return pkg[-1] == 'debuginfo'
+
+def findfiles(dir):
+    def filterinfos(x):
+        if x[-11:] == '.uploadinfo':
+            return True
+        else:
+            return False
+    return filter(filterinfos, os.listdir(dir))
+
+def getcontent(file):
+    f = open(file, 'r')
+    content = f.read()
+    f.close()
+    if not content[-5:] == '\nEND\n':
+        return None
+    else:
+        return content[:-4]
+
+def send_noarch_msg(files_differ, reqs_differ, pkg, rpmfile, arch):
+    req_email=pkg.build[pkg.lastbid].requester_email
+    req_bid=pkg.lastbid
+    cc_list=[]
+    if 'logs_list' in cval:
+        cc_list.append(cval['logs_list'])
+    m_subject="Noarch error: %s files differ among builders" % rpmfile
+    bids=pkg.build.keys()
+    if len(bids)>1:
+        for bid in bids:
+            newcc=pkg.build[bid].requester_email
+            if req_email!=newcc and newcc not in cc_list:
+                cc_list.append(newcc)
+
+    msg="From: %s\nTo: %s\n" % (cval['from_field'], req_email)
+    if cc_list:
+        msg=msg+"Cc: %s\n" % ", ".join(cc_list)
+    msg=msg+"""X-PLD-Builder: %s
+References: <%s@pld.src.builder>
+In-Reply-To: <%s@pld.src.builder>
+Subject: %s
+
+""" % (cval['xpldbuilder'], req_bid, req_bid, m_subject)
+
+    sm = os.popen("/usr/sbin/sendmail -t", "w")
+
+    sm.write(msg)
+
+    if files_differ:
+        f=open("%s/files.diff" % tmpdir, 'r')
+        sm.write("Difference between %s (currently in %s) and %s FILES\n" %
+              (pkg.noarch_arch[rpmfile], `ftptree`, arch)),
+        for line in f.readlines(True)[2:]:
+            sm.write(line)
+        f.close()
+
+    sm.write('\n')
+
+    if reqs_differ:
+        f=open("%s/reqs.diff" % tmpdir, 'r')
+        sm.write("Difference between %s (currently in %s) and %s REQS\n" %
+              (pkg.noarch_arch[rpmfile], `ftptree`, arch)),
+        for line in f.readlines(True)[2:]:
+            sm.write(line)
+        f.close()
+
+    sm.close()
+
+def move_noarch(f, arch, rpmfile, dstpkg):
+    if dstpkg.noarch_arch.has_key(rpmfile):
+        os.system("LC_ALL=C rpm -qlp %s | LC_ALL=C sort > %s/files.new" %
+                  (incoming_dir + arch + '/' + rpmfile, tmpdir))
+        os.system("rpm -qRp %s | LC_ALL=C sort | LC_ALL=C uniq > %s/reqs.new" %
+                  (incoming_dir + arch + '/' + rpmfile, tmpdir))
+
+        files_differ = False
+        reqs_differ = False
+
+        if os.system("diff -u %s/%s.filelist %s/files.new > %s/files.diff" %
+                     (noarchcachedir, rpmfile, tmpdir, tmpdir)):
+            files_differ = True
+        if os.system("diff -u %s/%s.reqlist %s/reqs.new > %s/reqs.diff" %
+                     (noarchcachedir, rpmfile, tmpdir, tmpdir)):
+            reqs_differ = True
+
+        if files_differ or reqs_differ:
+            send_noarch_msg(files_differ, reqs_differ, dstpkg, rpmfile, arch)
+
+        rm(incoming_dir + arch + '/' + rpmfile)
+    else:
+        os.system("LC_ALL=C rpm -qlp %s | LC_ALL=C sort > %s/%s.filelist" %
+                  (incoming_dir + arch + '/' + rpmfile, noarchcachedir, rpmfile))
+        os.system("rpm -qRp %s | LC_ALL=C sort | LC_ALL=C uniq > %s/%s.reqlist" %
+                  (incoming_dir + arch + '/' + rpmfile, noarchcachedir, rpmfile))
+        if not dstpkg.files.has_key(arch):
+            f.write("file:noarch:%s\ninfo:noarch_arch:%s:%s\n" % (rpmfile, rpmfile, arch))
+        mv(incoming_dir + arch + '/' + rpmfile, default_to + 'noarch/RPMS')
+
+def send_vr_msg(snvr, anvr, pkg, arch):
+    req_email=pkg.build[pkg.lastbid].requester_email
+    req_bid=pkg.lastbid
+    cc_list=[]
+    if 'logs_list' in cval:
+        cc_list.append(cval['logs_list'])
+    m_subject="NVR error: %s version or relese differ among subpackages" % snvr[0]
+    bids=pkg.build.keys()
+    if len(bids)>1:
+        for bid in bids:
+            newcc=pkg.build[bid].requester_email
+            if req_email!=newcc and newcc not in cc_list:
+                cc_list.append(newcc)
+
+    msg="From: %s\nTo: %s\n" % (cval['from_field'], req_email)
+    if cc_list:
+        msg=msg+"Cc: %s\n" % ", ".join(cc_list)
+    msg=msg+"""X-PLD-Builder: %s
+References: <%s@pld.src.builder>
+In-Reply-To: <%s@pld.src.builder>
+Subject: %s
+
+""" % (cval['xpldbuilder'], req_bid, req_bid, m_subject)
+
+    sm = os.popen("/usr/sbin/sendmail -t", "w")
+
+    sm.write(msg)
+
+    sm.write("Difference between %s SRPM (currently in %s) and %s RPM NVR:\n\n" %
+          (snvr[0], `ftptree`, arch)),
+    sm.write("Expected (%s):\nV: %s\nR: %s\n\n" % snvr)
+    sm.write("RPM:\nN: %s\nV: %s\nR: %s\n" % anvr)
+    sm.write('\n')
+
+    sm.close()
+
+# main()
+try:
+    ftpio.connect('from-incoming-pid-%s' % os.getpid())
+except:
+    print "Can't get ftpiod connection"
+    sys.exit(1)
+
+ftptree = BaseFtpTree(cval['default_to'])
+
+if not ftpio.lock(cval['default_to']):
+    print "Can't get lock: %s" % cval['default_to']
+    sys.exit(1)
+
+moved_anything = False
+
+for uploadinfo in findfiles(incoming_dir + 'SRPMS'):
+    content = getcontent(incoming_dir + 'SRPMS/' + uploadinfo)
+    if not content:
+        continue # Uploading not finished
+
+    pkg = BasePkg(uploadinfo[:-19], content = content)
+    srpm = pkg.files['SRPMS'][0]
+
+    if not os.path.exists(incoming_dir + 'SRPMS/' + srpm):
+        ftpio.log("%s file missing; skipping move until next round" % (srpm))
+        continue
+
+    if ftptree.has_key(`pkg`):
+        ftpio.log("%s already present in %s; removing older files" % (srpm, ftptree))
+        rm(default_to + 'SRPMS/RPMS/' + srpm)
+        f = open(default_to + 'SRPMS/.metadata/' + srpm+'.info', 'a')
+        bid = pkg.build.keys()[0]
+        build = pkg.build[bid]
+        f.write("info:build:%s:requester:%s\ninfo:build:%s:requester_email:%s\n"
+                 % (bid, build.requester, bid, build.requester_email))
+        f.close()
+    else:
+        f = open(default_to + 'SRPMS/.metadata/' + srpm + '.info', 'w')
+        f.write(content)
+        f.close()
+
+    mv(incoming_dir + 'SRPMS/' + srpm, default_to + 'SRPMS/RPMS')
+    rm(incoming_dir + 'SRPMS/' + uploadinfo)
+
+for arch in ftp_archs:
+    for uploadinfo in findfiles(incoming_dir + arch):
+        content = getcontent(incoming_dir + arch + '/' + uploadinfo)
+        if not content:
+            ftpio.log("%s not finished uploading" % uploadinfo)
+            continue # Uploading not finished
+
+        srcpkg = BasePkg(uploadinfo[:-19], content = content)
+        srpm = srcpkg.files['SRPMS'][0]
+
+        if not ftptree.has_key(`srcpkg`):
+            continue # We require the src.rpm to be present
+
+        renvr = re.compile(r'(.*)-(.*)-(.*)\.[^.]*\.rpm')
+        srcnvr = renvr.match(srpm).groups()
+
+        rpmfile_missing = [f for f in srcpkg.files['ARCH'] if not os.path.exists(incoming_dir + arch + '/'+f)]
+        if len(rpmfile_missing):
+            for filem in rpmfile_missing:
+                ftpio.log("%s file missing; skipping move until next round" % (filem))
+            continue
+
+        dstpkg = BasePkg(`srcpkg`, ftptree)
+
+        if dstpkg.files.has_key(arch):
+            ftpio.log("files from %s for arch %s already present in %s; removing older files" % (`srcpkg`, arch, ftptree))
+            for rpmfile in dstpkg.files[arch]:
+                if is_debuginfo(rpmfile):
+                    dstfile = default_to + arch + '/debuginfo'
+                else:
+                    dstfile = default_to + arch + '/RPMS'
+                try:
+                    rm(dstfile + '/' + rpmfile)
+                except OSError, e:
+                    l = "Removing %s problem: %s" % (dstfile + '/' + rpmfile, e)
+                    ftpio.log(l)
+                    print l
+
+        f = open(default_to + 'SRPMS/.metadata/' + srpm + '.info', 'a')
+        for rpmfile in srcpkg.files['ARCH']:
+            moved_anything = True
+
+# Too much noise, too little use
+#            archnvr = renvr.match(rpmfile).groups()
+#            if srcnvr[1] != archnvr[1] or srcnvr[2] != archnvr[2]:
+#                send_vr_msg(srcnvr, archnvr, dstpkg, arch)
+
+            if rpmfile[-11:] == '.noarch.rpm' and config.separate_noarch:
+                move_noarch(f, arch, rpmfile, dstpkg)
+            else:
+                if not dstpkg.files.has_key(arch):
+                    f.write("file:%s:%s\n" % (arch, rpmfile))
+                srcfile = incoming_dir + arch + '/' + rpmfile
+
+                if is_debuginfo(rpmfile):
+                    dstfile = default_to + arch + '/debuginfo'
+                else:
+                    dstfile = default_to + arch + '/RPMS'
+
+                try:
+                    mv(srcfile, dstfile)
+                except OSError, e:
+                    l = "Moving %s to %s problem: %s" % (srcfile, dstfile, e)
+                    ftpio.log(l)
+                    print l
+        f.close()
+
+        rm(incoming_dir + arch + '/' + uploadinfo)
+
+ftpio.unlock(cval['default_to'])
+
+if moved_anything:
+    os.system("%s/pld-ftp-admin/bin/pfa-genindex --quiet test > /dev/null" % (os.getenv("HOME")))
diff --git a/bin/pfa-genindex b/bin/pfa-genindex
new file mode 100755 (executable)
index 0000000..dc12b64
--- /dev/null
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import getopt
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+from common import checkdir
+from config import ftp_dir,all_ftp_archs
+import config
+import ftpio
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], 'q',
+        [
+            "quiet", "index=",
+            "nopoldek", "noyum", "norpmrepo",
+            "poldek", "yum", "rpmrepo"
+        ]
+    )
+except getopt.GetoptError:
+    print >>sys.stderr, "ERR: not enough parameters given"
+    print >>sys.stderr, "gen-indexes.py [--quiet] [--[no]poldek] [--[no]yum] [--[no]rpmrepo] tree [tree2...]"
+    sys.exit(1)
+
+do_poldek = True
+do_yum = False
+do_rpmrepo = False
+quiet = False
+# update only if changed (applicable to yum)
+freshen = True
+
+for o, a in opts:
+    if o == "--nopoldek":
+        do_poldek = False
+    if o == "--noyum":
+        do_yum = False
+    if o == "--norpmrepo":
+        do_rpmrepo = False
+
+    if o == "--poldek":
+        do_poldek = True
+    if o == "--yum":
+        do_yum = True
+    if o == "--rpmrepo":
+        do_rpmrepo = True
+
+    if o == "-q" or o == "--quiet":
+        quiet = True
+
+    if o == "--index":
+        do_poldek = do_yum = do_rpmrepo = False
+        for v in a.split(','):
+            if v == 'poldek':
+                do_poldek = True
+            if v == 'yum':
+                do_yum = True
+            if v == 'rpmrepo':
+                do_rpmrepo = True
+
+if not quiet:
+    print "poldek: %s; yum: %s; rpmrepo: %s" % (do_poldek, do_yum, do_rpmrepo)
+
+if not do_poldek and not do_yum and not do_rpmrepo:
+    print >>sys.stderr, "ERR: speciy at least one action"
+    sys.exit(1)
+
+trees = args
+
+for tree in trees:
+    checkdir(tree)
+
+ftpio.connect('gen-indexes')
+
+locked = []
+
+for tree in trees:
+    if ftpio.lock(tree, True):
+        locked.append(tree)
+    else:
+        if not quiet:
+            print >>sys.stderr, "ERR: %s tree already locked" % tree
+        for i in locked:
+            ftpio.unlock(i)
+        sys.exit(1)
+
+home = os.environ['HOME']
+
+os.umask(022)
+os.nice(19)
+
+if do_poldek:
+    poldek = '%s.stat/bin/poldek-new --cachedir=%s/tmp/poldek --conf %s.stat/etc/poldek.conf --mkidxz' % (ftp_dir, home, ftp_dir)
+
+    for tree in trees:
+        print '\n-------------------------- %s --------------------------' % tree
+        for arch in all_ftp_archs:
+            print '\ngenerate poldek index for %s' % arch
+            print '%s -s %s%s/%s/RPMS/ --mkidxz --mkidx-type pndir' % (poldek, ftp_dir, tree, arch)
+            os.system('%s -s %s%s/%s/RPMS/ --mkidxz --mkidx-type pndir' % (poldek, ftp_dir, tree, arch))
+            if arch != 'noarch' and config.separate_debuginfo:
+                os.system('%s -s %s%s/%s/debuginfo/ --mkidxz --mkidx-type pndir' % (poldek, ftp_dir, tree, arch))
+
+if do_yum:
+    os.system('%s cd %s.stat/repodata && cvs %s up comps.xml' % ("" if quiet else "set -x;", ftp_dir, "" if quiet else "-Q"))
+    yum = '%s.stat/bin/createrepo -d -v --update --checkts --skip-stat --workers=12 -g %s.stat/repodata/comps.xml' % (ftp_dir, ftp_dir)
+    comps_file = '%s.stat/repodata/comps.xml' % ftp_dir
+    for tree in trees:
+        print '\n-------------------------- %s --------------------------' % tree
+        cachedir = '%s/tmp/createrepo/%s' % (home, tree)
+        treedir = "%s%s" % (ftp_dir, tree)
+        for arch in all_ftp_archs:
+            print '\ngenerate repodata for %s using createrepo' % arch
+            archdir = "%s/%s" % (treedir, arch)
+            poldek_idx = "%s/RPMS/packages.ndir.md" % archdir
+            repodata_idx = "%s/RPMS/repodata/repomd.xml" % archdir
+            if freshen and os.path.exists(poldek_idx) and os.path.exists(repodata_idx) \
+                and not os.path.getmtime(comps_file) > os.path.getmtime(repodata_idx) \
+                and not os.path.getmtime(poldek_idx) > os.path.getmtime(repodata_idx):
+                print "repodata indexes already fresh"
+                continue
+            print ('%s %s --cache %s-%s %s/RPMS' % ("" if quiet else "time", yum, cachedir, arch, archdir))
+            os.system('%s %s --cache %s-%s %s/RPMS' % ("" if quiet else "time", yum, cachedir, arch, archdir))
+            if arch != 'noarch' and config.separate_debuginfo:
+                os.system('%s %s --cache %s-%s %s/debuginfo' % ("" if quiet else "time", yum, cachedir, arch, archdir))
+
+if do_rpmrepo:
+    os.system('%s cd %s.stat/repodata && cvs %s up comps.xml' % ("" if quiet else "set -x;", ftp_dir, "" if quiet else "-Q"))
+    for tree in trees:
+        print '\n-------------------------- %s --------------------------' % tree
+        for arch in all_ftp_archs:
+            dir = '%s/%s/%s/RPMS' % (ftp_dir, tree, arch)
+            if not quiet:
+                print '\ngenerate repodata for %s using rpmrepo (in %s)' % (arch, dir)
+            os.system('%s rpmrepo %s -o %s' % ("" if quiet else "set -x;", dir, dir))
+            if not quiet:
+                print 'copy comps.xml'
+            comps = '%s.stat/repodata/comps.xml' % ftp_dir
+            os.system('%s cp -p %s %s/repodata' % ("" if quiet else "set -x;", comps, dir))
+
+for tree in trees:
+    ftpio.unlock(tree)
diff --git a/bin/pfa-lintpkg b/bin/pfa-lintpkg
new file mode 100755 (executable)
index 0000000..c9ae537
--- /dev/null
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os, re
+import getopt
+import subprocess
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+from common import checkdir
+import ftpio
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], 'qsdo:', [ "quiet" ])
+except getopt.GetoptError:
+    print >>sys.stderr, "ERR: options error"
+    print >>sys.stderr, "rpmlint.py tree package1 [package2...]"
+    sys.exit(1)
+
+quiet = False
+show = False
+debugfiles = False
+outstream = sys.stdout
+for o, a in opts:
+    if o == "-q" or o == "--quiet":
+        quiet = True
+    if o == "-s":
+        show = True
+    if o == "-d":
+        debugfiles = True
+    if o == "-o":
+        outstream = open(a, 'w')
+
+if len(args) < 1:
+    print >>sys.stderr, "ERR: missing tree name"
+    print >>sys.stderr, "rpmlint.py tree package1 [package2...]"
+    sys.exit(1)
+
+treename = args[0]
+packages = args[1:]
+
+checkdir(treename)
+
+ftpio.connect('rpmlint')
+
+if not ftpio.lock(treename, True):
+    print >>sys.stderr, "ERR: %s tree already locked" % treename
+    sys.exit(1)
+
+files = []
+try:
+    if len(packages) < 1:
+        loadall = True
+    else:
+        loadall = False
+
+    # if no files specified, grab whole tree contents
+    tree = ftptree.FtpTree(treename, loadall = loadall)
+    tree.do_checkbuild = False
+    if loadall:
+        # this is hack, should be a param, not access private .loadedpkgs element
+        tree.mark4moving(tree.loadedpkgs)
+    else:
+        tree.mark4moving(packages)
+    files = tree.rpmfiles(debugfiles = debugfiles, sourcefiles = False)
+
+except (ftptree.SomeError, KeyboardInterrupt), e:
+    # In case of problems we need to unlock the tree before exiting
+    ftpio.unlock(treename)
+    sys.exit(1)
+
+ftpio.unlock(treename)
+
+class LintPkg:
+    def __init__(self, cachedir):
+        self.outstream = sys.stdout
+
+        # for rpmlint stats
+        self.packages = self.specfiles = self.errors = self.warnings = 0
+        # 1 packages and 0 specfiles checked; 0 errors, 0 warnings.
+        self.lintre = re.compile('(?P<packages>\d+) packages and (?P<specfiles>\d+) specfiles checked; (?P<errors>\d+) errors, (?P<warnings>\d+) warnings.')
+
+        self._rpmlint = '/usr/bin/rpmlint'
+
+        # mtime, which invalidates all caches
+        self.mtime = None
+        rpmlintrc = os.path.expanduser("~/.config/rpmlint")
+        if os.path.exists(rpmlintrc):
+            self.mtime = os.stat(rpmlintrc).st_mtime
+
+        self.cachedir = os.path.expanduser(cachedir)
+        if not os.path.isdir(self.cachedir):
+            os.makedirs(self.cachedir)
+
+    def cachefile(self, file):
+        (dirname, filename) = os.path.split(file)
+        return os.path.join(self.cachedir, filename+'.txt')
+
+    def get_stats(self, file):
+        cachefile = self.cachefile(file)
+        if not os.path.exists(cachefile):
+            return None
+
+        # show last line (that contains status)
+        l = (open(cachefile, 'r').readlines())[-1]
+        m = self.lintre.match(l)
+        if not m:
+            return None
+
+        return {
+            'packages': int(m.group('packages')),
+            'specfiles': int(m.group('specfiles')),
+            'errors': int(m.group('errors')),
+            'warnings': int(m.group('warnings')),
+        }
+
+    """
+    update stats from cachefile
+    """
+    def update_stats(self, file):
+        m = self.get_stats(file)
+        if not m:
+            return False
+        self.packages += m['packages']
+        self.specfiles += m['specfiles']
+        self.errors += m['errors']
+        self.warnings += m['warnings']
+        return True
+
+    def print_stats(self, file = None):
+        if file:
+            (dirname, filename) = os.path.split(file)
+            print >>self.outstream, "\r\033[0K%d packages and %d specfiles checked; %d errors, %d warnings. [%s]" % (self.packages, self.specfiles, self.errors, self.warnings, filename),
+        else:
+            print >>self.outstream, "\r\033[0K%d packages and %d specfiles checked; %d errors, %d warnings." % (self.packages, self.specfiles, self.errors, self.warnings)
+        sys.stdout.flush()
+
+    def cat(self, file):
+        print >>self.outstream, "".join(open(file, 'r').readlines())
+
+    def show_results(self, file):
+        m = self.get_stats(file)
+        if not m:
+            return False
+
+        cachefile = self.cachefile(file)
+        if not os.path.exists(cachefile):
+            print >>self.outsteram, "MISSING: report: %s" % file
+
+        if m['errors'] > 0 or m['warnings'] > 0:
+            (dirname, filename) = os.path.split(file)
+            print >>self.outstream, "rpmlint: %s" % filename
+            self.cat(cachefile)
+
+    def rpmlint(self, file):
+        cachefile = self.cachefile(file)
+
+        rc = None
+        if not os.path.exists(cachefile) \
+            or os.stat(file).st_mtime > os.stat(cachefile).st_mtime \
+            or (self.mtime and self.mtime > os.stat(cachefile).st_mtime):
+            cmd = [self._rpmlint, file]
+            outfd = open(cachefile, 'w')
+            try:
+                env = {'TZ': 'GMT'}
+                rc = subprocess.call(cmd, stdin = subprocess.PIPE, stdout = outfd, stderr = outfd, env = env, close_fds = True)
+            except KeyboardInterrupt:
+                os.unlink(cachefile)
+                raise
+            outfd.close()
+        if not self.update_stats(file):
+            # update failed, dump cache and remove it
+            self.cat(cachefile)
+            os.unlink(cachefile)
+            rc = 1
+        return rc == 0
+
+try:
+    lock = 'rpmlint:'+treename
+    if not ftpio.lock(lock, True):
+        print >>sys.stderr, "ERR: %s tree already locked for rpmlint" % treename
+        sys.exit(1)
+
+    if not quiet:
+        print >>outstream, "rpmlint of %d files from %d packages" % (len(files), len(tree.loadedpkgs))
+    lint = LintPkg("~/tmp/rpmlint")
+    lint.outstream = outstream
+    for file in files:
+        lint.rpmlint(file)
+        if not quiet:
+            lint.print_stats(file)
+        if show:
+            lint.show_results(file)
+
+    if not quiet:
+        lint.print_stats()
+
+    ftpio.unlock(lock)
+except (Exception, KeyboardInterrupt):
+    ftpio.unlock(lock)
+    raise
diff --git a/bin/pfa-maintainer b/bin/pfa-maintainer
new file mode 100755 (executable)
index 0000000..0d2ea04
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import time
+from config import test_builds_dir, ftp_archs
+
+def clean_dir(path, max):
+    curtime=time.time()
+    for i in os.listdir(path):
+        if curtime - os.path.getmtime(path+'/'+i) > max:
+            os.unlink(path+'/'+i)
+
+for arch in ftp_archs + ['SRPMS']:
+    clean_dir(test_builds_dir+arch, 60*60*24*3)
+
diff --git a/bin/pfa-mvpkg b/bin/pfa-mvpkg
new file mode 100755 (executable)
index 0000000..5693465
--- /dev/null
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+from common import checkdir
+import ftpio
+from mailer import Message
+from config import archived_trees, logs_list
+
+os.umask(022)
+
+nocheckbuild = False
+if len(sys.argv) > 4 and sys.argv[1] == '-nb':
+    nocheckbuild = True
+    sys.argv = sys.argv[1:]
+
+if len(sys.argv) < 4:
+    print >>sys.stderr, "ERR: not enough parameters given"
+    print >>sys.stderr, "move.py [options] src-tree dst-tree package [package2 package3 ...]"
+    print >>sys.stderr, "\nOptions:"
+    print >>sys.stderr, "      -nb    Do not check if builds are finished.\n"
+    sys.exit(1)
+
+checkdir(sys.argv[1])
+checkdir(sys.argv[2])
+
+if sys.argv[2] in archived_trees:
+    archivetreename = ".archive/" + sys.argv[2]
+    checkdir(archivetreename)
+else:
+    archivetreename = None
+
+ftpio.connect('move')
+
+if not ftpio.lock(sys.argv[1], True):
+    print >>sys.stderr, "ERR: %s tree already locked" % sys.argv[1]
+    sys.exit(1)
+
+if not ftpio.lock(sys.argv[2], True):
+    ftpio.unlock(sys.argv[1])
+    print >>sys.stderr, "ERR: %s tree already locked" % sys.argv[2]
+    sys.exit(1)
+
+if archivetreename != None and archivetreename != sys.argv[1] and not ftpio.lock(archivetreename, True):
+    ftpio.unlock(sys.argv[2])
+    ftpio.unlock(sys.argv[1])
+    print "ERR: %s tree already locked" % archivetreename
+    sys.exit(1)
+
+try:
+    srctree = ftptree.FtpTree(sys.argv[1], loadall = True)
+    dsttree = ftptree.FtpTree(sys.argv[2])
+    if archivetreename != None:
+        archivetree = ftptree.FtpTree(archivetreename)
+    else:
+        archivetree = None
+    if nocheckbuild:
+        srctree.do_checkbuild = False
+    pkgs = list(set(sys.argv[3:]))
+    srctree.mark4moving(pkgs)
+
+    srctree.movepkgs(dsttree, archivetree = archivetree)
+except ftptree.SomeError:
+    # In case of problems we need to unlock the trees before exiting
+    ftpio.unlock(sys.argv[1])
+    ftpio.unlock(sys.argv[2])
+    if archivetreename != None:
+        ftpio.unlock(archivetreename)
+    sys.exit(1)
+
+ftpio.unlock(sys.argv[1])
+ftpio.unlock(sys.argv[2])
+if archivetreename != None and archivetreename != sys.argv[1]:
+    ftpio.unlock(archivetreename)
+
+if logs_list == None:
+    sys.exit(0)
+
+print 'Sending mail notification to %s...' % logs_list
+
+pkgs = {}
+
+for pkg in srctree.marked4moving:
+    requesters = []
+    for bid in pkg.build.keys():
+        if pkg.build[bid].requester not in requesters:
+            requesters.append(pkg.build[bid].requester)
+    pkgs[pkg.nvr] = requesters
+
+ftpadmin = "(unknown)"
+try:
+    ftpadmin = os.environ['FTPADM']
+except KeyError, e:
+    pass
+m = Message()
+m.set_headers(
+        to = logs_list,
+        subject = 'MOVED: %s => %s... %d packages' % (sys.argv[1], sys.argv[2], len(pkgs))
+)
+
+m.write(
+    (
+        'FTP admin %(ftpadmin)s moved from *%(srctree)s* to *%(dsttree)s* %(count)d packages\n\n' +
+        '---- Package name ---- Package built by:\n\n'
+    ) % {
+        'ftpadmin' : ftpadmin,
+        'srctree' : sys.argv[1],
+        'dsttree' : sys.argv[2],
+        'count' : len(pkgs),
+    }
+)
+
+for nvr in sorted(pkgs.keys()):
+    m.write_line('%s ---- %s' % (nvr, ', '.join(pkgs[nvr])))
+
+m.send()
+
+print 'done.'
diff --git a/bin/pfa-rmpkg b/bin/pfa-rmpkg
new file mode 100755 (executable)
index 0000000..8054135
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+from common import checkdir
+import ftpio
+
+if len(sys.argv) < 3:
+    print "ERR: not enough parameters given"
+    print "remove.py tree package1 [package2...]"
+    sys.exit(1)
+
+checkdir(sys.argv[1])
+
+ftpio.connect('remove')
+
+if not ftpio.lock(sys.argv[1], True):
+    print "ERR: %s tree already locked" % sys.argv[1]
+    sys.exit(1)
+
+try:
+    tree=ftptree.FtpTree(sys.argv[1])
+    #tree.do_checkbuild=False
+    pkgs = list(set(sys.argv[2:]))
+    tree.mark4removal(pkgs)
+    tree.removepkgs()
+except ftptree.SomeError:
+    # In case of problems we need to unlock the tree before exiting
+    ftpio.unlock(sys.argv[1])
+    sys.exit(1)
+
+ftpio.unlock(sys.argv[1])
+
diff --git a/bin/pfa-signpkg b/bin/pfa-signpkg
new file mode 100755 (executable)
index 0000000..d45fc7c
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+import getopt
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+import getpass
+from common import checkdir
+import ftpio
+from config import sign_key
+from sign import is_signed, signpkgs
+
+os.umask(022)
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], '')
+except getopt.GetoptError:
+    print >>sys.stderr, "ERR: options error"
+    print >>sys.stderr, "sign.py tree package1 [package2...]"
+    sys.exit(1)
+
+if len(args) < 1:
+    print >>sys.stderr, "ERR: missing tree name"
+    print >>sys.stderr, "sign.py tree package1 [package2...]"
+    sys.exit(1)
+
+if sign_key == None:
+    print >>sys.stderr, "ERR: sign_key not defined in config"
+    sys.exit(1)
+
+treename = args[0]
+packages = args[1:]
+
+checkdir(treename)
+
+ftpio.connect('sign')
+
+if not ftpio.lock(treename, True):
+    print >>sys.stderr, "ERR: %s tree already locked" % treename
+    sys.exit(1)
+
+files = []
+try:
+    if len(packages) < 1:
+        loadall = True
+    else:
+        loadall = False
+
+    # if no files specified, grab whole tree contents
+    tree = ftptree.FtpTree(treename, loadall = loadall)
+    if loadall:
+        # this is hack, should be a param, not access private .loadedpkgs element
+        tree.mark4moving(tree.loadedpkgs)
+    else:
+        tree.mark4moving(packages)
+    files = tree.rpmfiles()
+
+except ftptree.SomeError:
+    # In case of problems we need to unlock the tree before exiting
+    ftpio.unlock(treename)
+    sys.exit(1)
+
+ftpio.unlock(treename)
+
+print "Checking signatures of %d files from %d packages" % (len(files), len(tree.loadedpkgs))
+sign = []
+n = c = 0
+for file in files:
+    if not is_signed(file):
+        sign.append(file)
+        c += 1
+    n += 1
+    print "\r%d/%d %s\033[0K" % (n, c, file),
+
+print ""
+
+if len(sign) == 0:
+    print "No files to sign"
+    sys.exit(0)
+
+# http://mail.python.org/pipermail/python-list/2009-February/700658.html
+def chunk(seq, size, pad=None):
+     '''
+     Slice a list into consecutive disjoint 'chunks' of
+     length equal to size. The last chunk is padded if necessary.
+
+     >>> list(chunk(range(1,10),3))
+     [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+     >>> list(chunk(range(1,9),3))
+     [[1, 2, 3], [4, 5, 6], [7, 8, None]]
+     >>> list(chunk(range(1,8),3))
+     [[1, 2, 3], [4, 5, 6], [7, None, None]]
+     >>> list(chunk(range(1,10),1))
+     [[1], [2], [3], [4], [5], [6], [7], [8], [9]]
+     >>> list(chunk(range(1,10),9))
+     [[1, 2, 3, 4, 5, 6, 7, 8, 9]]
+     >>> for X in chunk([],3): print X
+     >>>
+     '''
+     n = len(seq)
+     mod = n % size
+     for i in xrange(0, n - mod, size):
+         yield seq[i : i + size]
+     if mod:
+         yield seq[-mod:]
+
+print "Total %d files to sign" % len(sign)
+password = getpass.getpass("Enter signing password: ")
+try:
+    for x in chunk(sign, 512):
+        print "Signing %d files" % len(x)
+        signpkgs(x, password)
+except OSError, e:
+    print >>sys.stderr, "ERR: %s" % e
+    exit(1)
diff --git a/bin/pfa-testmvpkg b/bin/pfa-testmvpkg
new file mode 100755 (executable)
index 0000000..53a0e48
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+from common import checkdir
+import ftpio
+from config import archived_trees, logs_list
+
+if len(sys.argv) < 4:
+    print "ERR: not enough parameters given"
+    print "test-move.py src-tree dst-tree package [package2 package3 ...]"
+    sys.exit(1)
+
+checkdir(sys.argv[1])
+checkdir(sys.argv[2])
+
+if sys.argv[2] in archived_trees:
+    archivetreename = ".archive/" + sys.argv[2]
+    checkdir(archivetreename)
+else:
+    archivetreename = None
+
+ftpio.connect('test-move')
+
+if not ftpio.lock(sys.argv[1], True):
+    print "ERR: %s tree already locked" % sys.argv[1]
+    sys.exit(1)
+
+if not ftpio.lock(sys.argv[2], True):
+    ftpio.unlock(sys.argv[1])
+    print "ERR: %s tree already locked" % sys.argv[2]
+    sys.exit(1)
+
+if archivetreename != None and archivetreename != sys.argv[1] and not ftpio.lock(archivetreename, True):
+    ftpio.unlock(sys.argv[2])
+    ftpio.unlock(sys.argv[1])
+    print "ERR: %s tree already locked" % archivetreename
+    sys.exit(1)
+
+try:
+    srctree = ftptree.FtpTree(sys.argv[1], loadall = True)
+    dsttree = ftptree.FtpTree(sys.argv[2])
+    if archivetreename != None:
+        archivetree = ftptree.FtpTree(archivetreename)
+    else:
+        archivetree = None
+    pkgs = list(set(sys.argv[3:]))
+    srctree.mark4moving(pkgs)
+except ftptree.SomeError:
+    # In case of problems we need to unlock the trees before exiting
+    ftpio.unlock(sys.argv[1])
+    ftpio.unlock(sys.argv[2])
+    if archivetreename != None:
+        ftpio.unlock(archivetreename)
+    sys.exit(1)
+
+# We don't 'try' as in move.py cause this function doesn't force exit
+srctree.testmove(dsttree, archivetree = archivetree)
+
+ftpio.unlock(sys.argv[1])
+ftpio.unlock(sys.argv[2])
+if archivetreename != None and archivetreename != sys.argv[1]:
+    ftpio.unlock(archivetreename)
diff --git a/bin/pfa-unlocktree b/bin/pfa-unlocktree
new file mode 100755 (executable)
index 0000000..43acb09
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import ftptree
+import ftpio
+import config
+
+if len(sys.argv) < 2:
+    print "ERR: not enough parameters given"
+    print "unlock.py tree"
+    sys.exit(1)
+
+for tree in sys.argv[1:]:
+    ftpio.connect()
+    print "Unlock %s: %s" % (tree, ftpio.unlock(sys.argv[1]))
diff --git a/cgi-bin/index.py b/cgi-bin/index.py
new file mode 100755 (executable)
index 0000000..208a66a
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+# Printing errors (no SyntaxErrors though, but that's not a problem)
+import sys, os
+sys.stderr=sys.stdout
+
+contenttypesent=False
+
+def myexceptionhandler(type, value, traceback):
+    if contenttypesent:
+        print '<pre>'
+        sys.__excepthook__(type, value, traceback)
+        print '</pre>'
+    else:
+        print "Content-Type: text/plain\n"
+        sys.__excepthook__(type, value, traceback)
+    sys.exit(1)
+
+sys.excepthook=myexceptionhandler
+# End printing errors
+
+sys.path.insert(0, '../modules')
+
+import wwwiface, user
+
+opts, cks = wwwiface.getopts()
+u=user.User(cks, opts)
+
+if u.loggedin:
+    wwwiface.addheader('Logged in: '+u.login)
+    wwwiface.addmenu(file='loggedinmenu')
+    import wwwcmds
+    wwwcmds.handlecmds(opts)
+else:
+    wwwiface.addheader('Login form')
+    wwwiface.addcontent(file='regform')
+
+wwwiface.sendhttpheaders()
+contenttypesent=True
+
+wwwiface.sendhtml()
+
diff --git a/doc/README b/doc/README
new file mode 100644 (file)
index 0000000..5b15bc4
--- /dev/null
@@ -0,0 +1,3 @@
+You can find a comprehensive description of current features at
+http://cvs.pld-linux.org/cgi-bin/cvsweb/PLD-doc/PLD_2.0_ftp_administration
+
diff --git a/etc/rpmlint b/etc/rpmlint
new file mode 100644 (file)
index 0000000..3a84113
--- /dev/null
@@ -0,0 +1,233 @@
+# vim:ft=python
+# see  global config for more options: /etc/rpmlint/config
+
+# Configure the checks if you don't want the default ones.
+# --------------------------------------------------------
+
+#addCheck("FHSCheck")
+#addCheck("BinariesCheck")
+
+# Configuration options used by the checks shipped with rpmlint.
+# The values in the commented out setOption() calls represent default
+# or typical example values for the option.
+# -------------------------------------------------------------------
+setOption("UseEnchant", False)
+setOption("UseVersionInChangelog", False)
+
+# Base directory where to extract uninstalled packages while checking.
+# Type: string, default: tempfile.gettempdir()
+#setOption("ExtractDir", "/tmp")
+
+# Output filters.
+# ---------------
+#UseVersionInChangelog
+
+# these are against pld own policy
+addFilter("E: incoherent-version-in-name")
+addFilter("E: no-packager-tag")
+addFilter("E: no-signature")
+addFilter("W: macro-in-%changelog")
+addFilter("W: no-major-in-name")
+addFilter("W: python-bytecode-without-source")
+addFilter("W: requires-on-release")
+addFilter("E: init-script-non-executable /etc/rc.d/init.d/functions")
+
+# hard to fix at this time
+addFilter("E: non-executable-script")
+
+setOption("ValidLicenses", (
+       # These are the short names for all of the PLD Linux approved licenses.
+       'BSD',
+       'GPL, Open Data License',
+       'GPL v2',
+       'LGPL v2.1',
+       'LGPL v3',
+       'MPL v1.1',
+       'LGPL',
+))
+
+# filter out stuff that is not critical for pkg move bypass
+addFilter("W: no-documentation")
+addFilter("W: no-manual-page-for-binary")
+
+# epoch match error or sth
+#dirac-libs.i486: W: ghost-files-without-postin
+#dirac-devel.i486: W: incoherent-version-dependency-on dirac/dirac-libs/libdirac 1.0.2 0:1.0.2
+addFilter("W: ghost-files-without-postin")
+addFilter("W: incoherent-version-dependency-on")
+
+# we have no strict lib package policy
+addFilter("E: outside-libdir-files")
+addFilter("E: executable-in-library-package")
+
+# not our problem to fix
+addFilter("E: incorrect-fsf-address")
+
+#addFilter("E: backup-file-in-package")
+#addFilter("E: arch-dependent-file-in-usr-share")
+#addFilter("E: world-writable")
+#addFilter("W: unexpanded-macro")
+#addFilter("E: init-script-non-executable")
+#addFilter("E: filename-not-utf8")
+#addFilter("E: info-dir-file")
+#addFilter("E: info-files-without-install-info-post")
+#addFilter("E: init-script-without-chkconfig-postin")
+#addFilter("E: init-script-without-chkconfig-preun")
+#addFilter("E: invalid-desktopfile /usr/share/applications")
+#addFilter("E: invalid-ldconfig-symlink")
+#addFilter("E: invalid-shell-in-")
+#addFilter("E: no-chkconfig-line")
+#addFilter("E: no-description-tag")
+#addFilter("E: no-ldconfig-symlink")
+#addFilter("E: non-empty-%postun")
+#addFilter("E: non-executable-script")
+#addFilter("E: non-ghost-file")
+#addFilter("E: non-root-group-log-file")
+#addFilter("E: non-utf8-desktopfile")
+#addFilter("E: tag-not-utf8")
+#addFilter("W: non-etc-or-var-file-marked-as-conffile")
+#addFilter("W: one-line-command-in-%post")
+#addFilter("W: one-line-command-in-%postun")
+#addFilter("W: one-line-command-in-%trigger")
+#addFilter("W: spurious-bracket-in-%post")
+#addFilter("W: spurious-bracket-in-%trigger")
+#addFilter("W: spurious-executable-perm")
+
+# to get important errors out
+addFilter("E: arch-dependent-file-in-usr-share")
+addFilter("E: arch-independent-package-contains-binary-or-object")
+addFilter("E: binary-or-shlib-defines-rpath")
+addFilter("E: compressed-symlink-with-wrong-ext")
+addFilter("E: description-line-too-long")
+addFilter("E: devel-dependency")
+addFilter("E: dir-or-file-in-home")
+addFilter("E: dir-or-file-in-tmp")
+addFilter("E: dir-or-file-in-usr-local")
+addFilter("E: executable-crontab-file")
+addFilter("E: executable-marked-as-config-file")
+addFilter("E: executable-sourced-script")
+addFilter("E: explicit-lib-dependency")
+addFilter("E: htaccess-file")
+addFilter("E: incoherent-logrotate-file")
+addFilter("E: incoherent-subsys")
+addFilter("E: incorrect-locale-subdir")
+addFilter("E: invalid-directory-reference")
+addFilter("E: invalid-soname")
+addFilter("E: invalid-version")
+addFilter("E: library-not-linked-against-libc")
+addFilter("E: library-without-ldconfig-postin")
+addFilter("E: library-without-ldconfig-postun")
+addFilter("E: menu-in-wrong-dir")
+addFilter("E: missing-PT_GNU_STACK-section")
+addFilter("E: no-binary")
+addFilter("E: no-dependency-on")
+addFilter("E: no-dependency-on locales-")
+addFilter("E: non-root-user-log-file")
+addFilter("E: non-standard-dir-perm")
+addFilter("E: non-standard-executable-perm")
+addFilter("E: non-standard-executable-perm /etc/rc.d/init.d/")
+addFilter("E: non-versioned-file-in-library-package")
+addFilter("E: no-signature")
+addFilter("E: no-status-entry")
+addFilter("E: no-summary-tag")
+addFilter("E: postin-without-chkconfig")
+addFilter("E: postin-without-install-info /usr/share/info")
+addFilter("E: postin-without-ldconfig")
+addFilter("E: postun-without-ldconfig")
+addFilter("E: preun-without-chkconfig")
+addFilter("E: python-bytecode-inconsistent-mtime")
+addFilter("E: rpath-in-buildconfig")
+addFilter("E: script-without-shebang")
+addFilter("E: setgid-binary")
+addFilter("E: setuid-binary")
+addFilter("E: shared-lib-without-dependency-information")
+addFilter("E: shlib-with-non-pic-code")
+addFilter("E: sourced-script-with-shebang")
+addFilter("E: standard-dir-owned-by-package")
+addFilter("E: statically-linked-binary")
+addFilter("E: subdir-in-bin")
+addFilter("E: subsys-not-used")
+addFilter("E: summary-too-long")
+addFilter("E: unknown-key GPG#e4f1bc2d")
+addFilter("E: unknown-lsb-keyword")
+addFilter("E: useless-provides")
+addFilter("E: use-of-home-in-%post")
+addFilter("E: use-old-pam-stack")
+addFilter("E: use-tmp-in-%pre")
+addFilter("E: version-control-internal-file")
+addFilter("E: wrong-script-end-of-line-encoding")
+addFilter("E: wrong-script-interpreter")
+addFilter("E: zero-length")
+addFilter("W: binaryinfo-readelf-failed")
+addFilter("W: binaryinfo-tail-failed")
+addFilter("W: class-path-in-manifest")
+addFilter("W: conffile-without-noreplace-flag")
+addFilter("W: cross-directory-hard-link")
+addFilter("W: dangerous-command-in-")
+addFilter("W: dangling-relative-symlink")
+addFilter("W: dangling-symlink")
+addFilter("W: devel-file-in-non-devel-package")
+addFilter("W: doc-file-dependency")
+addFilter("W: duplicate-executable")
+addFilter("W: executable-stack")
+addFilter("W: filename-too-long-for-joliet")
+addFilter("W: file-not-in-%lang")
+addFilter("W: file-not-utf8")
+addFilter("W: file-not-utf8 /usr/share/doc/")
+addFilter("W: file-not-utf8 /usr/share/man/")
+addFilter("W: hidden-file-or-dir")
+addFilter("W: incoherent-init-script-name")
+addFilter("W: incoherent-subsys")
+addFilter("W: infopage-not-compressed")
+addFilter("W: invalid-license")
+addFilter("W: invalid-url")
+addFilter("W: jar-not-indexed")
+addFilter("W: log-files-without-logrotate")
+addFilter("W: manpage-not-compressed")
+addFilter("W: manual-page-warning /usr/share/man/")
+addFilter("W: missing-lsb-keyword")
+addFilter("W: name-repeated-in-summary")
+addFilter("W: no-default-runlevel")
+addFilter("W: no-dependency-on")
+addFilter("W: non-conffile-in-etc")
+addFilter("W: non-executable-in-bin")
+addFilter("W: non-standard-dir-in-usr")
+addFilter("W: non-standard-dir-in-var")
+addFilter("W: non-standard-gid")
+addFilter("W: non-standard-group")
+addFilter("W: non-standard-uid")
+addFilter("W: no-provides")
+addFilter("W: no-reload-entry")
+addFilter("W: no-soname")
+addFilter("W: no-url-tag")
+addFilter("W: no-version-in-last-changelog")
+addFilter("W: obsolete-not-provided")
+addFilter("W: ocaml-naming-policy-not-applied")
+addFilter("W: only-non-binary-in-usr-lib")
+addFilter("W: percent-in-%post")
+addFilter("W: percent-in-%trigger")
+addFilter("W: perl5-naming-policy-not-applied")
+addFilter("W: postin-without-ghost-file-creation")
+addFilter("W: private-shared-object-provides")
+addFilter("W: python-naming-policy-not-applied")
+addFilter("W: read-error")
+addFilter("W: requires-on-release")
+addFilter("W: service-default-enabled")
+addFilter("W: shared-lib-calls-exit")
+addFilter("W: spelling-error")
+addFilter("W: summary-ended-with-dot")
+addFilter("W: summary-not-capitalized")
+addFilter("W: symlink-should-be-relative")
+addFilter("W: tag-in-description")
+addFilter("W: unable-to-read-zip")
+addFilter("W: uncompressed-zip")
+addFilter("W: unexpanded-macro /usr/share/ri/");
+addFilter("W: unexpanded-macro /var/lib/gdm/.gconf.mandatory/%gconf-tree.xml")
+addFilter("W: unstripped-binary-or-object")
+addFilter("W: wrong-file-end-of-line-encoding")
+addFilter("W: xmms-naming-policy-not-applied")
+addFilter("E: non-readable")
+# kde4-kdenetwork-kppp.x86_64
+addFilter("W: unexpanded-macro /usr/share/apps/kppp/");
+addFilter("kde4-.* W: self-obsoletion");
+
diff --git a/ftpiod/ftpiod.py b/ftpiod/ftpiod.py
new file mode 100755 (executable)
index 0000000..1726904
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import sys, os
+sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules')
+import cons
+
+def daemonize():
+    sys.stdin.close()
+    sys.stdout.close()
+    sys.stderr.close()
+    for fd in range(256):
+        try:
+            os.close(fd)
+        except:
+            pass
+    pid=os.fork()
+    if pid!=0:
+        sys.exit(0)
+    os.setsid()
+
+
+#daemonize()
+
+
+while True:
+    for readable in cons.readables():
+        if readable==cons.privlistener:
+            newsock,addr=readable.accept()
+            cons.add(cons.Connection(newsock, True))
+        elif readable==cons.publistener:
+            newsock,addr=readable.accept()
+            cons.add(cons.Connection(newsock, False))
+        else:
+            readable.handleinput()
+
diff --git a/html/footer.html b/html/footer.html
new file mode 100644 (file)
index 0000000..69aae56
--- /dev/null
@@ -0,0 +1,3 @@
+</body>
+</html>
+
diff --git a/html/header.html b/html/header.html
new file mode 100644 (file)
index 0000000..80a3694
--- /dev/null
@@ -0,0 +1,10 @@
+<html>
+
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+<title>PLD ftp manager - Th</title>
+<style type="text/css" media="all">@import "../html/layout.css";</style>
+</head>
+
+<body>
+
diff --git a/html/index.html b/html/index.html
new file mode 100644 (file)
index 0000000..b54a43e
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+  <link rel="StyleSheet" href="styles.css" type="text/css">
+  <title>PLD Th :: Info Page</title>
+</head>
+<body>
+
+<h3>
+  <a href="http://www.pld-linux.org/ThInfo">About PLD Th</a>
+</h3>
+
+<h3>Builder queue</h3>
+<p>
+<li><a href="http://src.th.pld-linux.org/queue.html">PLD Th</a></li>
+</p>
+
+<h3>Dep checks</h3>
+<p>
+<li><a href="qa.php?q=main">Main deps x86_64</a></li>
+<li><a href="qa.php?q=main-ready">Main ready deps x86_64</a></li>
+<li><a href="qa.php?q=main-ready-test">Main+ready+test deps x86_64</a></li>
+
+<li><a href="qa.php?q=main-i686">Main deps i686</a></li>
+<li><a href="qa.php?q=main-ready-i686">Main ready deps i686</a></li>
+<li><a href="qa.php?q=main-ready-test-i686">Main+ready+test deps i686</a></li>
+
+<li><a href="qa.php?q=main-x32">Main deps x32</a></li>
+<li><a href="qa.php?q=main-ready-x32">Main ready deps x32</a></li>
+<li><a href="qa.php?q=main-ready-test-x32">Main+ready+test deps x32</a></li>
+
+</p>
+
+<h3>Freshness checks</h3>
+<p>
+<li><a href="qa.php?q=ac-th-diff">Differences between AC and TH</a></li>
+<li><a href="qa.php?q=freshness">Th main+test freshness (compare to git)</a></li>
+</p>
+
+<h3>Rpmlint checks</h3>
+<p>
+<li><a href="qa.php?q=lint-PLD">rpmlint: PLD tree</a></li>
+<li><a href="qa.php?q=lint-ready">rpmlint: ready tree</a></li>
+<li><a href="qa.php?q=lint-test">rpmlint: test tree</a></li>
+</p>
+</body>
+</html>
diff --git a/html/layout.css b/html/layout.css
new file mode 100644 (file)
index 0000000..d7ead8a
--- /dev/null
@@ -0,0 +1,82 @@
+body {\r
+       margin:0px;\r
+       padding:0px;\r
+       font-family:verdana, arial, helvetica, sans-serif;\r
+       color:#333;\r
+       background-color:white;\r
+       }\r
+h1 {\r
+       margin:0px 0px 15px 0px;\r
+       padding:0px;\r
+       font-size:28px;\r
+       line-height:28px;\r
+       font-weight:900;\r
+       color:#ccc;\r
+       }\r
+p {\r
+       font:11px/20px verdana, arial, helvetica, sans-serif;\r
+       margin:0px 0px 16px 0px;\r
+       padding:0px;\r
+       }\r
+#Content>p {margin:0px;}\r
+#Content>p+p {text-indent:30px;}\r
+\r
+a {\r
+       color:#09c;\r
+       font-size:11px;\r
+       text-decoration:none;\r
+       font-weight:600;\r
+       font-family:verdana, arial, helvetica, sans-serif;\r
+       }\r
+a:link {color:#09c;}\r
+a:visited {color:#07a;}\r
+a:hover {background-color:#eee;}\r
+\r
+#Header {\r
+       margin:25px 0px 10px 0px;\r
+       padding:17px 0px 0px 20px;\r
+       /* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */\r
+       height:33px; /* 14px + 17px + 2px = 33px */\r
+       border-style:solid;\r
+       border-color:black;\r
+       border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */\r
+       line-height:11px;\r
+       background-color:#eee;\r
+\r
+/* Here is the ugly brilliant hack that protects IE5/Win from its own stupidity. \r
+Thanks to Tantek Celik for the hack and to Eric Costello for publicizing it. \r
+IE5/Win incorrectly parses the "\"}"" value, prematurely closing the style \r
+declaration. The incorrect IE5/Win value is above, while the correct value is \r
+below. See http://glish.com/css/hacks.asp for details. */\r
+       voice-family: "\"}\"";\r
+       voice-family:inherit;\r
+       height:14px; /* the correct height */\r
+       }\r
+/* I've heard this called the "be nice to Opera 5" rule. Basically, it feeds correct \r
+length values to user agents that exhibit the parsing error exploited above yet get \r
+the CSS box model right and understand the CSS2 parent-child selector. ALWAYS include\r
+a "be nice to Opera 5" rule every time you use the Tantek Celik hack (above). */\r
+body>#Header {height:14px;}\r
+\r
+#Content {\r
+       margin:0px 50px 50px 200px;\r
+       padding:10px;\r
+       }\r
+\r
+#Menu {\r
+       position:absolute;\r
+       top:100px;\r
+       left:20px;\r
+       width:172px;\r
+       padding:10px;\r
+       background-color:#eee;\r
+       border:1px dashed #999;\r
+       line-height:17px;\r
+/* Again, the ugly brilliant hack. */\r
+       voice-family: "\"}\"";\r
+       voice-family:inherit;\r
+       width:150px;\r
+       }\r
+/* Again, "be nice to Opera 5". */\r
+body>#Menu {width:150px;}\r
+\r
diff --git a/html/loggedinmenu.html b/html/loggedinmenu.html
new file mode 100644 (file)
index 0000000..cd63ab1
--- /dev/null
@@ -0,0 +1 @@
+<a href="index.py?action=logout">Logout</a><br/>
diff --git a/html/menufooter.html b/html/menufooter.html
new file mode 100644 (file)
index 0000000..c1f08de
--- /dev/null
@@ -0,0 +1,2 @@
+<a href="http://bugs.pld-linux.org/index.php?tasks=all&project=3">Th bugs</a><br/>
+<a href="http://buildlogs.pld-linux.org/">Buildlogs</a><br/>
diff --git a/html/qa.php b/html/qa.php
new file mode 100644 (file)
index 0000000..a24416e
--- /dev/null
@@ -0,0 +1,103 @@
+<html>
+<head>
+<link rel="Shortcut Icon" href="//www.pld-linux.org/favicon.ico"/>
+       <title>PLD QA Reports</title>
+       <link rel="stylesheet" type="text/css" charset="utf-8" media="all" href="//srcbuilder.pld-linux.org/th/style.css">
+       <style>
+       .hidden { display: none; }
+       span#count { font-weight: bold; }
+       </style>
+</head>
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
+<script>
+jQuery(function($) {
+       $('#q').on('change', function(e) {
+               var el = e.target;
+               var o = el.options;
+               var v = o[o.selectedIndex].value;
+               location.href = "?q=" + v;
+       });
+
+       var $lines = $('#lines>li');
+       var $count = $('#count');
+       $('#filter').on('keyup', function() {
+               var search = $(this).val();
+               var count = 0;
+               $lines.each(function() {
+                       var $line = $(this);
+                       var text = $line.text();
+                       var fn = text.match(search) && ++count ? 'removeClass' : 'addClass';
+                       $line[fn]('hidden');
+               });
+               $count.html(count);
+       });
+       $count.html($lines.length);
+});
+</script>
+<body>
+<?php
+
+$report = isset($_GET['q']) ? basename($_GET['q']) : null;
+$reports = array(
+       "main" => "Main deps x86_64",
+       "main-ready" => "Main ready deps x86_64",
+       "main-ready-test" => "Main+ready+test deps x86_64",
+
+       "main-i686" => "Main deps i686",
+       "main-ready-i686" => "Main ready deps i686",
+       "main-ready-test-i686" => "Main+ready+test deps i686",
+
+       "main-x32" => "Main deps x32",
+       "main-ready-x32" => "Main ready deps x32",
+       "main-ready-test-x32" => "Main+ready+test deps x32",
+
+       "freshness" => "GIT vs FTP freshness",
+
+       "lint-PLD" => "rpmlint: main",
+       "lint-test" => "rpmlint: test",
+       "lint-ready" => "rpmlint: ready",
+);
+
+function reports_selection($reports) {
+       global $report;
+       echo "Select report:";
+       echo "<select id=q name=q>\n";
+       foreach ($reports as $q => $title) {
+               printf("<option value=%s %s>%s</option>\n", $q, $q == $report ? 'selected' :'',  $title);
+       }
+       echo "</select>\n";
+}
+
+/**
+ * Create text input for filtering results
+ */
+function filter_box() {
+       echo '<br>Filter results: <input id="filter">';
+       echo '<br><span id="count">?</span> errors';
+}
+
+function format_report($report) {
+       echo "<br>View the <a href=$report.txt>raw</a> report<br/>\n";
+       $file = "$report.txt";
+       $giturl = 'http://git.pld-linux.org/gitweb.cgi?p=packages/%1$s.git;f=%1$s.spec;h=HEAD;a=shortlog';
+       echo '<ol id="lines">';
+       foreach (file($file) as $line) {
+               $line = preg_replace_callback('/^(?P<prefix>error:|GIT:)\s*\[(?P<spec>[^]]+)\]\s*(?P<msg>.+)$/', function($m) use ($giturl) {
+                       $package = basename($m['spec'], '.spec');
+                       $url = sprintf($giturl, $package);
+                       return sprintf('<li><font color=red>%s</font> [<a href="%s">%s</a>] %s', $m['prefix'], $url, $m['spec'], $m['msg']);
+               }, $line);
+               echo $line, "<br/>\n";
+       }
+       echo '</ol>';
+}
+
+reports_selection($reports);
+filter_box();
+if (isset($reports[$report])) {
+       format_report($report);
+}
+
+?>
+</body>
+</html>
diff --git a/html/regform.html b/html/regform.html
new file mode 100644 (file)
index 0000000..dde6db4
--- /dev/null
@@ -0,0 +1,8 @@
+<form action="index.py" method="post">
+<input type="hidden" name="action" value="register">
+<label for="login">Username:</label>
+<input type="text" name="login"><br>
+<label for="pass">Password:</label>
+<input type="password" name="pass"><br>
+<input type="submit" value="Log in">
+</form>
diff --git a/modules/.gitignore b/modules/.gitignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/modules/baseftptree.py b/modules/baseftptree.py
new file mode 100644 (file)
index 0000000..1f3febc
--- /dev/null
@@ -0,0 +1,68 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import string, config
+from common import fileexists
+
+class Build:
+    def __init__(self):
+        self.requester=''
+        self.requester_email=''
+
+class BasePkg:
+    def __init__(self, nvr, tree=None, content=None):
+        self.files={}
+        self.info={}
+        self.build={}
+        self.noarch_arch={}
+        self.tree=tree
+        self.nvr=nvr
+        self.load(content)
+
+    def __repr__(self):
+        return self.nvr
+
+    def load(self, content=None):
+        if content:
+            lines=content.splitlines()
+        else:
+            f=open("%s/SRPMS/.metadata/%s.src.rpm.info" % 
+                   (self.tree.basedir, self.nvr), 'r')
+            lines=f.readlines()
+            f.close()
+
+        for entry in lines:
+            i=string.split(string.strip(entry), ':')
+            if i[0] == 'info':
+                if len(i)==3:
+                    self.info[i[1]]=i[2]
+                elif i[1]=='build':
+                    if not self.build.has_key(i[2]):
+                        self.build[i[2]]=Build()
+                        self.lastbid=i[2]
+                    if i[3]=='requester':
+                        self.build[i[2]].requester=i[4]
+                    elif i[3]=='requester_email':
+                        self.build[i[2]].requester_email=i[4]
+                elif i[1]=='noarch_arch':
+                    self.noarch_arch[i[2]]=i[3]
+                else:
+                    self.info[i[1]]=i[2:]
+            elif i[0] == 'file':
+                if not self.files.has_key(i[1]):
+                    self.files[i[1]]=[]
+                self.files[i[1]].append(i[2])
+
+class BaseFtpTree:
+    def __init__(self, tree):
+        self.basedir=config.value['ftp_dir']+'/'+tree
+        self.treename=tree
+
+    def __repr__(self):
+        return self.treename
+
+    def has_key(self, key):
+        if fileexists(self.basedir+'/SRPMS/.metadata/'+key+'.src.rpm.info'):
+            return True
+        else:
+            return False
+
diff --git a/modules/cmds.py b/modules/cmds.py
new file mode 100644 (file)
index 0000000..192dbf6
--- /dev/null
@@ -0,0 +1,188 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import os
+import time
+import config
+import common
+import md5
+import ftptree
+
+
+def parse(con):
+    if '\0' not in con.data:
+        return
+    cmds=con.data.split('\0')[:-1]
+
+    for cmd in cmds:
+        con.data=con.data[len(cmd)+1:]
+        cmdname=cmd[:4]
+        if not con.authorized and cmdname not in ('linp', 'linc', 'name'):
+            raise BailOut
+            # TODO: log unauthorized access
+        if cmdname in cmdlist_noargs:
+            if len(cmd)==4:
+                cmdlist_noargs[cmdname](con)
+            else:
+                pass
+                # TODO: log malicious msg
+        elif cmdname in cmdlist_args:
+            if len(cmd)>5:
+                cmdlist_args[cmdname](con, cmd[5:])
+            else:
+                pass
+                # TODO: log malicious msg
+        else:
+            raise BailOut
+            # TODO: log this
+
+def lock(con, arg, hard):
+    if arg not in locks:
+        locks[arg]={'hard': hard, 'name': con.name, 'time': int(time.time())}
+        con.sock.send("OK")
+    elif locks[arg]['hard']:
+        con.sock.send("HARD") # Hard lock - you can go get a cup of tea
+    else:
+        con.sock.send("SOFT") # Soft lock - try in a second or two
+
+def cmd_unlock(con, arg):
+    if arg in locks:
+        del locks[arg]
+        con.sock.send("OK")
+    else:
+        con.sock.send("FAIL")
+
+def cmd_lock_soft(con, arg):
+    lock(con, arg, False)
+
+def cmd_lock_hard(con, arg):
+    lock(con, arg, True)
+
+def cmd_show_locks(con):
+    cmd_log(con, "Dumping locks data:");
+    if len(locks):
+        res = ""
+        for lockdata in locks.iteritems():
+            tree, data = lockdata
+            msg = "Tree: %s, Conn name: %s, Hard Lock: %s, Time: %s" % (
+                    tree, data['name'], data['hard'], time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data['time'])))
+            cmd_log(con, msg)
+            res = res + msg
+#        con.sock.send("BLOB:%d" % len(res))
+        con.sock.send(res)
+    else:
+        cmd_log(con, "No locks found.");
+        con.sock.send("NLCK");
+
+def cmd_log(con, msg):
+    logfile.write('%s [%s] -- %s\n' % (time.strftime('%Y-%m-%d %H:%M:%S'), con.name, msg))
+    logfile.flush()
+
+def cmd_name(con, name):
+    con.name=name
+
+def load_creds():
+    global users, cookies
+    users={}
+    cookies={}
+    if not common.fileexists(common.ftpadmdir+'/var/passwd'):
+        return
+    else:
+        f=open(common.ftpadmdir+'/var/passwd', 'r')
+        for line in f.xreadlines():
+            x=line.strip().split(':')
+            if len(x)>=2:
+                users[x[0]]=x[1]
+        f.close()
+    if not common.fileexists(common.ftpadmdir+'/var/cookies'):
+        return
+    else:
+        f=open(common.ftpadmdir+'/var/cookies', 'r')
+        for line in f.xreadlines():
+            x=line.strip().split(':')
+            if len(x)>=2:
+                users[x[0]]=x[1]
+        f.close()
+
+def write_cookies():
+    f=open(common.ftpadmdir+'/var/cookies', 'w')
+    for key in cookies.keys():
+        f.write('%s:%s\n' % (key, cookies[key]))
+    f.close()
+
+def cmd_login_passwd(con, data):
+    tmp=data.split('\n')
+    if len(tmp)!=2:
+        raise BailOut
+    login=tmp[0]
+    passwd=tmp[1]
+    md5pass=md5.new(passwd).hexdigest()
+    if login in users and users[login]==md5pass:
+        cookie=`time.time()`.split('.')[0]+'_'+md5.new(md5pass+salt).hexdigest()
+        cookies[cookie]=login
+        write_cookies()
+        con.username=login
+        con.authorized=True
+        con.sock.send('OK '+cookie)
+    else:
+        # TODO: log this
+        con.sock.send('FAIL')
+        raise BailOut
+
+def cmd_login_cookie(con, cookie):
+    if cookie in cookies:
+        con.cookie=cookie
+        con.authorized=True
+        con.username=cookies[cookie]
+        con.sock.send('OK '+cookies[cookie])
+    else:
+        # TODO: log this (or not)
+        con.sock.send('FAIL')
+
+def cmd_logout(con):
+    if con.cookie in cookies:
+        del cookies[con.cookie]
+        write_cookies()
+
+def reloadftptree():
+    global srctree, pkglist
+    srctree=ftptree.FtpTree(config.value['default_to'], loadall=True)
+    pkglist=srctree.keys()
+    pkglist.sort()
+
+def cmd_gettree(con):
+    buf=''
+    for pkgnvr in pkglist:
+        # TODO: show only user's own pkgs
+        pkg=srctree[pkgnvr]
+        line=pkgnvr
+        if pkg.marked4moving:
+            line=line+'\n1'
+        else:
+            line=line+'\n0'
+        if pkg.marked4removal:
+            line=line+'\n1'
+        else:
+            line=line+'\n0'
+        buf=buf+'\0'+line
+    if buf:
+        con.sock.send('%.6d' % (len(buf)-1))
+        con.sock.send(buf[1:])
+    else:
+        con.sock.send('000000')
+
+
+cmdlist_args={'lcks':cmd_lock_soft, 'lckh':cmd_lock_hard, 'ulck':cmd_unlock,
+         'log1':cmd_log, 'name':cmd_name, 'linp':cmd_login_passwd,
+         'linc':cmd_login_cookie}
+
+cmdlist_noargs={'lout':cmd_logout, 'gett':cmd_gettree, 'slck':cmd_show_locks}
+
+# Global stuff and initializations
+
+BailOut="BailOut"
+locks={}
+logfile=open(common.ftpadmdir+'/var/log', 'a')
+load_creds()
+reloadftptree()
+salt=md5.new(`time.time()`).hexdigest()
+
diff --git a/modules/common.py b/modules/common.py
new file mode 100644 (file)
index 0000000..19f3aa8
--- /dev/null
@@ -0,0 +1,26 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import os, sys, config
+
+def fileexists(path):
+    if path[0] == '/':
+        fullpath = path
+    else:
+        fullpath = config.ftp_dir + path
+    return os.path.exists(fullpath)
+
+def checkdir(dir):
+    if not fileexists(dir):
+        print >>sys.stderr, 'ERR: ' + config.value['ftp_dir']+'/' + dir + " does not exist"
+        sys.exit(1)
+
+if 'HOME' in os.environ:
+    ftpadmdir = os.environ['HOME'] + '/pld-ftp-admin'
+else:
+    ftpadmdir = '..'
+
+# noarchcachedir is dir where noarch files contents are stored for AI
+# XXX: file reference where the AI resides
+noarchcachedir = ftpadmdir + '/var/noarch-cache/'
+
+tmpdir = ftpadmdir + '/var/tmp/'
diff --git a/modules/config.py b/modules/config.py
new file mode 100644 (file)
index 0000000..ee32dfa
--- /dev/null
@@ -0,0 +1,76 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import string, os
+
+value = {}
+
+if os.environ.has_key('HOME'):
+    path = os.environ['HOME']
+else:
+    path = '../../' # cgi-bin interface
+
+f = open(path + '/.ftpadmrc', 'r')
+
+for line in f.readlines():
+    if line[0] == '#' or string.find(line, '=') == -1:
+        continue
+    tuple = string.split(string.strip(line), '=')
+    if tuple[1][0] == '"':
+        value[string.strip(tuple[0])] = tuple[1][1:-1]
+    else:
+        value[string.strip(tuple[0])] = string.strip(tuple[1])
+
+f.close()
+
+default_to = value['ftp_dir'] + '/' + value['default_to'] + '/'
+ftp_dir = value['ftp_dir'] + '/'
+incoming_dir = value['ftp_dir'] + '/' + value['incoming_dir'] + '/'
+test_builds_dir = value['ftp_dir'] + '/' + value['test_builds_dir'] + '/'
+ftp_archs = value['ftp_archs'].split(' ')
+
+builderqueue = value['builderqueue']
+
+if 'sign_key' in value:
+    sign_key = value['sign_key']
+else:
+    sign_key = None
+
+if 'logs_list' in value:
+    logs_list = value['logs_list']
+else:
+    logs_list = None
+
+if 'signed_trees' in value:
+    signed_trees = value['signed_trees'].split(' ')
+else:
+    signed_trees = None
+
+if 'archived_trees' in value:
+    archived_trees = value['archived_trees'].split(' ')
+else:
+    archived_trees = None
+
+if 'old_poldek' in value and value['old_poldek'] == 'yes':
+    old_poldek = True
+else:
+    old_poldek = False
+
+if value['separate_noarch'] == 'yes':
+    separate_noarch = True
+else:
+    separate_noarch = False
+
+if value['separate_debuginfo'] == 'yes':
+    separate_debuginfo = True
+else:
+    separate_debuginfo = False
+
+if separate_noarch:
+    all_ftp_archs = ['noarch'] + ftp_archs
+else:
+    all_ftp_archs = ftp_archs
+
+if 'ftp_dist' in value:
+    ftp_dist = value['ftp_dist']
+else:
+    ftp_dist = 'pld'
diff --git a/modules/cons.py b/modules/cons.py
new file mode 100644 (file)
index 0000000..61569f6
--- /dev/null
@@ -0,0 +1,73 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import socket
+import os
+import select
+from common import fileexists
+import ftpio
+import cmds
+
+class Connection:
+    def __init__(self, sock, authorized):
+        sock.setblocking(False)
+        self.sock=sock
+        self.authorized=authorized
+        self.fileno=sock.fileno
+        self.name=""
+        self.data=""
+
+    def destroy(self):
+        self.sock.close()
+        rm(self)
+
+    def handleinput(self):
+        newdata = None
+        try:
+            newdata = self.sock.recv(8192)
+        except:
+            self.destroy()
+
+        if not newdata:
+            self.destroy()
+        else:
+            self.data = self.data + newdata
+
+        try:
+            cmds.parse(self)
+        except cmds.BailOut:
+            self.destroy()
+
+def add(con):
+    cons.append(con)
+   
+def rm(con):
+    cons.remove(con)
+
+def readables():
+    lst=cons[:]
+    lst.append(privlistener)
+    lst.append(publistener)
+    inlst,outlst,errlst = select.select(lst, [], [], 0.1)
+    return inlst
+
+def createlistener(path):
+    if fileexists(path):
+        os.remove(path)
+
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    s.setblocking(False)
+    s.bind(path)
+    if path==ftpio.pubsock:
+        os.chmod(path, 0606)
+    else:
+        os.chmod(path, 0600)
+    s.listen(3)
+    return s
+
+cons=[]
+
+privlistener=createlistener(ftpio.privsock)
+publistener=createlistener(ftpio.pubsock)
+
+
+
diff --git a/modules/ftpio.py b/modules/ftpio.py
new file mode 100644 (file)
index 0000000..5e0132b
--- /dev/null
@@ -0,0 +1,92 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import os
+import sys
+import socket
+import time
+import config
+
+pubsock=config.value['pubsock']
+
+if os.environ.has_key('HOME'):
+    privsock=os.environ['HOME']+'/pld-ftp-admin/var/privsock'
+    socketname=privsock
+else:
+    socketname=pubsock
+
+def connect(name=None):
+    global sock
+    sock=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    sock.connect(socketname)
+    if not name:
+        name = "pid_%d_name_%s" % (os.getpid(), sys.argv[0])
+    sock.send('name %s\0' % name)
+
+def login_passwd(login, passwd):
+    'Return cookie if ok'
+    sock.send('linp %s\n%s\0' % (login, passwd))
+    retval=sock.recv(256)
+    if retval=='FAIL':
+        return ''
+    else:
+        return retval[3:]
+
+def login_cookie(cookie):
+    'Return login if ok'
+    sock.send('linc %s\0' % cookie)
+    retval=sock.recv(256)
+    if retval=='FAIL':
+        return ''
+    else:
+        return retval[3:]
+
+def logout():
+    sock.send('lout\0')
+
+def lock(path, hard=False):
+    def dolock():
+        if hard:
+            sock.send('lckh %s\0' % path)
+        else:
+            sock.send('lcks %s\0' % path)
+        return sock.recv(20)
+    for i in range(3):
+        retcode=dolock()
+        if retcode=="OK":
+            return True
+        elif retcode=="HARD":
+            return False
+        if i!=2:
+            time.sleep(1)
+    return False
+    
+def unlock(path):
+    sock.send('ulck %s\0' % path)
+    ret = sock.recv(20)
+    if ret == "OK":
+        return True
+    return False
+
+def log(msg):
+    sock.send('log1 %s\0' % msg)
+
+def locks_dump():
+    sock.send('slck\0')
+    ret = sock.recv(4096)
+    if ret == "NLCK":
+        return "No locks held"
+
+#    nbytes = int(ret.split("BLOB:")[1])
+#    ret = sock.recv(nbytes)
+    return ret
+
+def gettree():
+    sock.send('gett\0')
+    pkgs=[]
+    len=int(sock.recv(6))
+    if len:
+        for pkg in sock.recv(len).split('\0'):
+            tmp=pkg.split('\n')
+            pkgs.append((tmp[0], int(tmp[1]), int(tmp[2])))
+    return pkgs
+
diff --git a/modules/ftptree.py b/modules/ftptree.py
new file mode 100644 (file)
index 0000000..70db8a0
--- /dev/null
@@ -0,0 +1,561 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import os, config, string, urllib, re, rpm
+from common import fileexists, noarchcachedir
+from baseftptree import BasePkg, BaseFtpTree
+from sign import is_signed
+
+errnum = 0
+quietmode = False
+
+class SomeError(Exception):
+    def __init__(self):
+        return
+
+    def __str__(self):
+        print "","An Error occured!"
+
+def bailoutonerror():
+    if not errnum == 0:
+        print "%d error(s) encountered... aborting" % errnum
+        raise SomeError
+
+def pinfo(msg):
+    print 'INFO: ' + msg
+
+def perror(msg):
+    global errnum
+    errnum = errnum + 1
+    print 'ERR: ' + msg
+
+def pwarning(msg):
+    print 'WARN: ' + msg
+
+def rm(file, test = False):
+    if test:
+        if not os.path.exists(file):
+            pinfo("TEST os.remove(%s): file doesn't exists" % file)
+    else:
+        try:
+            os.remove(file)
+        except OSError, e:
+            pinfo("os.remove(%s): %s" % (file, e))
+            #raise
+
+def mv(src, dst, test = False):
+    fsrc = src
+    fdst = dst + '/' + src.split('/')[-1]
+    if test:
+        if not os.path.exists(fsrc):
+            pinfo("TEST os.rename(%s, %s): source doesn't exists" % (fsrc, fdst))
+        if not os.path.exists(dst):
+            pinfo("TEST destination doesn't exist: %s" % dst)
+    else:
+        try:
+            os.rename(fsrc, fdst)
+        except OSError, e:
+            pinfo("os.rename(%s, %s): %s" % (fsrc, fdst, e))
+            raise
+
+class Pkg(BasePkg):
+    def __init__(self, nvr, tree):
+        BasePkg.__init__(self, nvr, tree)
+        self.name = string.join(nvr.split('-')[:-2], '-')
+        self.version = nvr.split('-')[-2]
+        self.release = nvr.split('-')[-1]
+        self.marked4removal = False
+        self.marked4moving = False
+        self.marked4movingpool = []
+        self.errors = []
+        self.warnings = []
+
+    def __cmp__(self, pkg):
+        if self.name > pkg.name:
+            return 1
+        elif self.name < pkg.name:
+            return -1
+        else:
+            return rpm.labelCompare(('0', self.version, self.release),
+                                    ('0', pkg.version, pkg.release))
+
+
+    # unfortunately can't do new Pkg(NVR), and have no "tree" in this pkg context
+    # so this static function
+    def is_debuginfo(self, nvr):
+        """
+        returns true if NVR is debuginfo package and separate debuginfo is enabled
+        """
+        if not config.separate_debuginfo:
+            return False
+        pkg = nvr.split('-')[:-2]
+        return pkg[-1] == 'debuginfo'
+
+    def is_sourcefile(self, file):
+        """
+        returns true if file is source package
+        """
+        return file[-8:] == '.src.rpm'
+
+    # returns true if package build is integer
+    def is_release(self):
+        """
+        To account Release tags with subver macros, we consider integer release
+        if it contains odd number of dots:
+
+        1 -> True
+        0.1 -> False
+        0.%{subver}.%{rel}, %{rel} = 1 -> 0.20010.1 -> True
+        0.%{subver}.%{rel}, %{rel} = 0.1 -> 0.20010.0.1 -> False
+        """
+        return self.release.count('.') % 2 == 0
+
+    def mark4moving(self):
+        if not self.marked4moving:
+            # Only one pkg in this pool can be marked for moving
+            for pkg in self.marked4movingpool:
+                pkg.unmark4moving()
+            self.tree.marked4moving.append(self)
+            self.marked4moving=True
+
+    def unmark4moving(self):
+        if self.marked4moving:
+            self.tree.marked4moving.remove(self)
+            self.marked4moving=False
+
+    def mark4removal(self):
+        if not self.marked4removal:
+            self.tree.marked4removal.append(self)
+            self.marked4removal=True
+
+    def error(self, msg):
+        self.errors.append(msg)
+        if not quietmode:
+            perror('%s %s' % (self.nvr, msg))
+
+    def warning(self, msg):
+        self.warnings.append(msg)
+        if not quietmode:
+            pwarning('%s %s' % (self.nvr, msg))
+
+    def load(self, content=None):
+        BasePkg.load(self, content)
+        if self.info.has_key('move'):
+            self.mark4moving()
+
+    def writeinfo(self):
+        f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
+        for bid in self.build.keys():
+            f.write("info:build:%s:requester:%s\ninfo:build:%s:requester_email:%s\n" % (bid, self.build[bid].requester, bid, self.build[bid].requester_email))
+        for key in self.info.keys():
+            f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
+        for arch in self.files.keys():
+            for rpm in self.files[arch]:
+                f.write("file:%s:%s\n" % (arch, rpm))
+
+    def remove(self, test = False):
+        """
+        Remove package from ftp
+        """
+        for arch in self.files.keys():
+            for rpm in self.files[arch]:
+                if self.is_debuginfo(rpm):
+                    rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
+                else:
+                    rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
+                if arch == 'noarch':
+                    if fileexists(noarchcachedir + rpm + '.filelist'):
+                        rm(noarchcachedir + rpm + '.filelist', test)
+                    if fileexists(noarchcachedir + rpm + '.reqlist'):
+                        rm(noarchcachedir + rpm + '.reqlist', test)
+        rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
+
+    def rpmfiles(self, debugfiles = True, sourcefiles  = True):
+        """
+        Return rpm files related to this package
+        """
+        files = []
+        for arch, rpms in self.files.items():
+            for nvr in rpms:
+                if self.is_debuginfo(nvr):
+                    if debugfiles:
+                        files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
+                else:
+                    if self.is_sourcefile(nvr):
+                        if sourcefiles:
+                            files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
+                    else:
+                        files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
+        return files
+
+    def obsoletes(self):
+        """
+        Return obsoletes for all packages in Pkg:
+
+        {'php-geshi': set(['geshi'])}
+
+        """
+        def rpmhdr(pkg):
+            ts = rpm.ts()
+            ts.setVSFlags(rpm.RPMVSF_NODSAHEADER)
+            fdno = os.open(pkg, os.O_RDONLY)
+            hdr = ts.hdrFromFdno(fdno)
+            os.close(fdno)
+            return hdr
+
+        obsoletes = {}
+        for rpmfile in self.rpmfiles():
+            if not os.path.exists(rpmfile):
+                continue
+            hdr = rpmhdr(rpmfile)
+            if not hdr[rpm.RPMTAG_OBSOLETES]:
+                continue
+
+            name = hdr[rpm.RPMTAG_NAME]
+            if not name in obsoletes:
+                obsoletes[name] = set()
+
+            for tag in hdr[rpm.RPMTAG_OBSOLETES]:
+                obsoletes[name].add(tag)
+
+        return obsoletes
+
+    def move(self, dsttree, test = False):
+        if dsttree.has_key(self.nvr):
+            movedany = False
+            for arch in self.files.keys():
+                if arch in dsttree[self.nvr].files.keys():
+                    msg = ""
+                    if test:
+                        msg = "TEST "
+                    pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
+                    for rpm in self.files[arch]:
+                        if self.is_debuginfo(rpm):
+                            rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
+                        else:
+                            rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
+                else:
+                    movedany = True
+                    dsttree[self.nvr].files[arch] = self.files[arch]
+                    for rpm in self.files[arch]:
+                        if self.is_debuginfo(rpm):
+                            mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
+                        else:
+                            mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
+            if not test and movedany:
+                for bid in self.build.keys():
+                    dsttree[self.nvr].build[bid] = self.build[bid]
+                dsttree[self.nvr].writeinfo()
+            rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
+        else:
+            # move files
+            for arch in self.files.keys():
+                for rpm in self.files[arch]:
+                    if self.is_debuginfo(rpm):
+                        mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
+                    else:
+                        mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
+
+            # move metadata
+            mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
+
+class FtpTree(BaseFtpTree):
+    def __init__(self, tree, loadall=False):
+        BaseFtpTree.__init__(self, tree)
+        self.loadedpkgs = {}
+        self.marked4removal = []
+        self.marked4moving = []
+        self.pkgnames = []
+        self.__loadpkgnames()
+        if loadall:
+            for pkgname in self.pkgnames:
+                self.loadedpkgs[pkgname] = Pkg(pkgname, self)
+        # Tests:
+        self.do_checkbuild = True
+
+    def __getitem__(self, key):
+        if self.loadedpkgs.has_key(key):
+            return self.loadedpkgs[key]
+        elif key in self.pkgnames:
+            pkg=Pkg(key, self)
+            self.loadedpkgs[key]=pkg
+            return pkg
+        else:
+            raise KeyError, key
+
+    def has_key(self, key):
+        if key in self.pkgnames:
+            return True
+        else:
+            return False
+
+    def keys(self):
+        return self.pkgnames
+
+    def values(self):
+        return self.loadedpkgs.values()
+
+    def checktree(self, dsttree):
+        self.__checkbuild(self.loadedpkgs.values())
+        self.__checkarchs(dsttree, self.loadedpkgs.values())
+
+    def testmove(self, dsttree, archivetree = None):
+        self.__checkbuild(self.marked4moving)
+        self.__checkarchs(dsttree, self.marked4moving)
+        if not self.treename.count("archive"):
+            self.__checkduplicates(self.marked4moving)
+
+        self.__checksigns(dsttree, self.marked4moving, test = True)
+        self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
+        self.__checkforrelease(dsttree, self.marked4moving, test = True)
+
+        if not self.treename.count("archive"):
+            self.__rmolderfromsrc(test = True)
+            self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
+
+        for pkg in self.marked4moving:
+            pkg.move(dsttree, test = True)
+
+    def movepkgs(self, dsttree, archivetree = None):
+        if self.do_checkbuild:
+            self.__checkbuild(self.marked4moving)
+        bailoutonerror()
+
+        self.__checkarchs(dsttree, self.marked4moving)
+        bailoutonerror()
+
+        self.__checksigns(dsttree, self.marked4moving)
+        bailoutonerror()
+
+        if not self.treename.count("archive"):
+            self.__rmolderfromsrc()
+            self.__rmotherfromdst(dsttree, archivetree = archivetree)
+
+        for pkg in self.marked4moving:
+            pkg.move(dsttree)
+
+    def rpmfiles(self, debugfiles = True, sourcefiles = True):
+        if self.do_checkbuild:
+            self.__checkbuild(self.marked4moving)
+
+        files = []
+        for pkg in self.marked4moving:
+            files += pkg.rpmfiles(debugfiles = debugfiles, sourcefiles = sourcefiles)
+        return files
+
+    def removepkgs(self):
+        if self.do_checkbuild:
+            self.__checkbuild(self.marked4removal)
+        bailoutonerror()
+        for pkg in self.marked4removal:
+            pkg.remove()
+
+    def mark4removal(self, wannabepkgs):
+        self.__mark4something(wannabepkgs, Pkg.mark4removal)
+
+    def mark4moving(self, wannabepkgs):
+        self.__mark4something(wannabepkgs, Pkg.mark4moving)
+
+    # Internal functions below
+    def __arch_stringify(self, list):
+        ret = []
+        dist = config.ftp_dist;
+        for arch in list:
+            ret.append(dist + '-' + arch)
+        return ' '.join(ret)
+
+    def __loadpkgnames(self):
+        def checkfiletype(name):
+            if name[-13:]=='.src.rpm.info':
+                return True
+            else:
+                return False
+        list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
+        self.pkgnames = map((lambda x: x[:-13]), list)
+
+    def __mark4something(self, wannabepkgs, markfunction):
+        def chopoffextension(pkg):
+            found = pkg.find('.src.rpm')
+            if found == -1:
+                return pkg
+            else:
+                return pkg[:found]
+
+        for wannabepkg in wannabepkgs:
+            pkgname = chopoffextension(wannabepkg)
+            if pkgname in self.pkgnames:
+                if not pkgname in self.loadedpkgs.keys():
+                    self.loadedpkgs[pkgname]=Pkg(pkgname, self)
+                markfunction(self.loadedpkgs[pkgname])
+            else:
+                perror('%s not found in source tree' % pkgname)
+        bailoutonerror()
+
+    def __checkbuild(self, marked):
+        """
+        Checks queue file if all arches are built
+
+        Reads config.builderqueue to grab the info
+        """
+        f = urllib.urlopen(config.builderqueue)
+        requests = {}
+        reid = re.compile(r'^.*id=(.*) pri.*$')
+        regb = re.compile(r'^group:.*$|builders:.*$', re.M)
+        for i in re.findall(regb, f.read()):
+            if i[0] == 'g':
+                id = reid.sub(r'\1', i)
+                requests[id] = ""
+            elif i[0]=='b':
+                requests[id] = requests[id] + i
+        f.close()
+
+        for pkg in marked:
+            for bid in pkg.build.keys():
+                if requests.has_key(bid) and not requests[bid].find('?') == -1:
+                    pkg.error("(buildid %s) building not finished" % bid)
+
+    def __checkarchs(self, dsttree, marked):
+        """
+        Checks marked pkgs it is built on all archs.
+        """
+        for pkg in marked:
+            if len(pkg.files.keys()) <= 1:
+                pkg.error('has only src.rpm built')
+                continue
+            otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
+
+            # check if we're not removing some archs
+            if otherpkgnames:
+                curarchs = []
+                missingarchs = []
+                for somepkg in otherpkgnames:
+                    curarchs.extend(Pkg(somepkg, dsttree).files.keys())
+                for arch in curarchs:
+                    if arch not in pkg.files.keys():
+                        missingarchs.append(arch)
+                if missingarchs:
+                    pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
+            else:
+                # warn if a package isn't built for all archs
+                # ftp_archs + SRPMS
+                ftp_archs_num = len(config.ftp_archs) + 1
+                if (config.separate_noarch and 'noarch' in pkg.files.keys()):
+                    # ftp_archs + SRPMS + noarch subpackages
+                    ftp_archs_num += 1
+                    # plain simple noarch package
+                    if (len(pkg.files.keys()) == 2):
+                        continue
+
+                if len(pkg.files.keys()) != ftp_archs_num:
+                    missingarchs = []
+                    for arch in config.ftp_archs:
+                        if arch not in pkg.files.keys():
+                            missingarchs.append(arch)
+                    pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
+
+    def __checkduplicates(self, marked):
+        """
+        Checks if marked packages contain duplicate packages (with different versions)
+        """
+        for pkg in marked:
+            olderpkgnames = self.__find_older_pkgs(pkg)
+            for i in olderpkgnames:
+                markednames = [str(x) for x in marked]
+                if i in markednames:
+                    pkg.error('duplicate package: %s' % i)
+
+    def __rmolderfromsrc(self, test = False):
+        for pkg in self.marked4moving:
+            olderpkgnames = self.__find_older_pkgs(pkg)
+            for i in olderpkgnames:
+                Pkg(i, self).remove(test)
+
+    def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
+        for pkg in self.marked4moving:
+            pkgnames = self.__find_other_pkgs(pkg, dsttree)
+            for i in pkgnames:
+                if archivetree == None:
+                    Pkg(i, dsttree).remove(test)
+                else:
+                    Pkg(i, dsttree).move(archivetree, test = test)
+
+    # Used more than once filter functions
+    def __find_other_pkgs(self, pkg, tree):
+        escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
+        ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
+        def filter_other_pkgs(x):
+            if ziewre.match(x) and not x == pkg.nvr:
+                return True
+            else:
+                return False
+        return filter(filter_other_pkgs, tree.pkgnames)
+
+    def __find_older_pkgs(self, pkg):
+        def filter_older_pkgs(x):
+            c = x.split('-')
+            rc = rpm.labelCompare(('0', pkg.version, pkg.release),
+                                                        ('0', c[-2], c[-1]))
+            if rc == 1: # pkg > x
+                return True
+            else:
+                return False
+        return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
+
+    def __checksigns(self, tree, pkgs, test = False):
+        """
+        Checks if pkgs in tree are all signed.
+
+        in case of test = true, error flag is set for unsigned packages
+        """
+        if not tree.treename in config.signed_trees:
+            return
+
+        for pkg in pkgs:
+            unsigned = 0
+            for file in pkg.rpmfiles():
+                if not is_signed(file):
+                    unsigned += 1
+
+            if unsigned != 0:
+                if test == True:
+                    if not quietmode:
+                        pkg.warning('%d files not signed' % unsigned)
+                else:
+                    pkg.error('%d files not signed' % unsigned)
+
+    def __checkforobsoletes(self, tree, pkgs, test = False):
+        """
+        Checks queue file if package obsoletes something in destination tree and suggest for removal.
+
+        Only NAME tag is compared, i.e virtual packages do not get reported.
+
+        """
+        if test != True:
+            return
+
+        def findbyname(name):
+            def x(nvr):
+                return '-'.join(nvr.split('-')[:-2]) == name
+            return filter(x, tree.pkgnames)
+
+        for pkg in pkgs:
+            obsoletes = pkg.obsoletes()
+            if not obsoletes:
+                continue
+
+            for pn, setlist in obsoletes.items():
+                for item in setlist:
+                    p = findbyname(item)
+                    if p:
+                        pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
+
+    def __checkforrelease(self, tree, pkgs, test = False):
+        """
+        Checks queue file if package release is non integer.
+
+        """
+        if test != True:
+            return
+
+        for pkg in pkgs:
+            if not pkg.is_release():
+                pkg.warning('non-integer release: %s' % pkg.release)
diff --git a/modules/mailer.py b/modules/mailer.py
new file mode 100644 (file)
index 0000000..bf13ef5
--- /dev/null
@@ -0,0 +1,63 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import time
+import os
+import sys
+import StringIO
+
+import config
+cval=config.value
+
+class Message:
+    def __init__(self):
+        self.headers = {}
+        self.body = StringIO.StringIO()
+        self.__set_std_headers()
+
+    def set_header(self, n, v):
+        self.headers[n] = v
+
+    def set_headers(self, to = None, cc = None, subject = None):
+        if to != None:
+            self.set_header("To", to)
+        if cc != None:
+            self.set_header("Cc", cc)
+        if subject != None:
+            self.set_header("Subject", subject)
+
+    def write_line(self, l):
+        self.body.write("%s\n" % l)
+
+    def write(self, s):
+        self.body.write(s)
+
+    def send(self):
+        send_sendmail = "/usr/sbin/sendmail -t"
+        f = os.popen(send_sendmail, "w")
+        self.__write_to(f)
+        f.close()
+
+    def __set_std_headers(self):
+        self.headers["Date"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
+        self.headers["Message-ID"] = "<pld-builder.%f.%d@%s>" \
+                % (time.time(), os.getpid(), os.uname()[1])
+        self.headers["From"] = cval['from_field']
+        self.headers["X-PLD-Builder"] = cval['xpldbuilder']
+
+    def __write_to(self, f):
+        for k, v in self.headers.items():
+            f.write("%s: %s\n" % (k, v))
+        f.write("\n")
+        self.body.seek(0)
+        self.__sendfile(self.body, f)
+
+    def __sendfile(self, src, dst):
+        cnt = 0
+        while 1:
+            s = src.read(10000)
+            if s == "": break
+            cnt += len(s)
+            dst.write(s)
+        return cnt
+
diff --git a/modules/sign.py b/modules/sign.py
new file mode 100644 (file)
index 0000000..377ee51
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import os
+import sys
+import rpm
+import pexpect
+from config import sign_key
+
+def getSigInfo(hdr):
+    """checks signature from an hdr hand back signature information and/or
+       an error code"""
+    # yum-3.2.22/rpmUtils/miscutils.py
+
+    string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|'
+    siginfo = hdr.sprintf(string)
+    if siginfo == '(none)':
+        return None
+
+    return siginfo.split(',')[2].lstrip()
+
+def is_signed(rpm_file):
+    """Returns rpm information is package signed by the same key"""
+    # http://code.activestate.com/recipes/306705/
+
+    if sign_key == None:
+        return None
+
+    ts = rpm.ts()
+    ts.setVSFlags(rpm.RPMVSF_NODSAHEADER)
+    fdno = os.open(rpm_file, os.O_RDONLY)
+    hdr = ts.hdrFromFdno(fdno)
+    os.close(fdno)
+
+    sigid = getSigInfo(hdr)
+    if sigid == None:
+        return None
+
+    return sign_key == sigid[-len(sign_key):]
+
+def signpkgs(files, password):
+    if not os.path.isfile('/usr/bin/gpg'):
+        raise OSError, 'Missing gnupg binary'
+    if not os.path.isfile('/bin/rpm'):
+        raise OSError, 'Missing rpm binary'
+
+    os.putenv('LC_ALL', 'C')
+    args = ['--resign', '--define', '_signature gpg', '--define', '_gpg_name ' + sign_key] + files
+    child = pexpect.spawn('/bin/rpm', args)
+    child.logfile_read = sys.stderr
+    child.expect('Enter pass phrase:', timeout=30)
+    child.sendline(password)
+    child.expect(pexpect.EOF, timeout=None)
+    child.close()
+    rc = child.exitstatus
+    if rc != 0:
+        raise OSError, 'package signing failed'
+    for rpm in files:
+        os.chmod(rpm, 0644)
diff --git a/modules/user.py b/modules/user.py
new file mode 100644 (file)
index 0000000..2b31f1a
--- /dev/null
@@ -0,0 +1,44 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import Cookie, time, ftpio
+
+UserNotLoggedIn="UserNotLoggedIn"
+
+class User:
+    def __init__(self, cookies, options):
+        self.loggedin=False
+        ftpio.connect('wwwiface')
+       if 'ftpsessid' in cookies and cookies['ftpsessid']:
+            self.login=ftpio.login_cookie(cookies['ftpsessid'])
+            if self.login:
+                self.loggedin=True
+
+        if 'action' in options:
+            if options['action'] == 'register':
+                self.checkloginpass(options)
+            elif options['action'] == 'logout':
+                self.logout()
+
+    def checkloginpass(self, options):
+        if 'login' not in options or 'pass' not in options:
+            return
+        self.cookie=ftpio.login_passwd(options['login'], options['pass'])
+        if self.cookie:
+            self.login=options['login']
+            self.loggedin=True
+            C = Cookie.SimpleCookie()
+            C['ftpsessid']=self.cookie
+            #C['ftpsessid']['expires']=time.strftime(
+                                        #"%a, %d-%b-%y %H:%M:%S GMT",
+                                        #time.gmtime(time.time()+86400))
+            print C
+    
+    def logout(self):
+        self.loggedin=False
+        ftpio.logout()
+        C = Cookie.SimpleCookie()
+        C['ftpsessid']=''
+        C['ftpsessid']['expires']=time.strftime("%a, %d-%b-%y %H:%M:%S GMT",
+                                    time.gmtime(time.time()-31536000))
+        print C
+
diff --git a/modules/wwwcmds.py b/modules/wwwcmds.py
new file mode 100644 (file)
index 0000000..ea6c63c
--- /dev/null
@@ -0,0 +1,26 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import wwwiface, ftpio
+
+
+def handlecmds(options):
+    def chkbox(pkgname, value):
+        retval='<input type="checkbox" name="%s"' % pkgname
+        if value:
+            retval=retval+' checked="yes"'
+        retval=retval+'>'
+        return retval
+
+    if 'action' in options and options['action'] in actions:
+        pass
+    else:
+        wwwiface.addcontent('<form action="index.py" method="post"><table border="1">')
+        wwwiface.addcontent('<tr><td>Mv</td><td>Rm</td><td>Package</td></tr>')
+        for pkg in ftpio.gettree():
+            wwwiface.addcontent('<tr><td>%s</td><td>%s</td><td>%s</td></tr>' % (chkbox(pkg[0], pkg[1]), chkbox(pkg[0], pkg[2]), pkg[0]))
+        wwwiface.addcontent('<tr><td>&nbsp;</td><td>&nbsp;</td><td><input type="submit" value="Commit"></td></tr>')
+        wwwiface.addcontent('</table></form>')
+
+
+actions={}
+
diff --git a/modules/wwwiface.py b/modules/wwwiface.py
new file mode 100644 (file)
index 0000000..bc215ae
--- /dev/null
@@ -0,0 +1,77 @@
+# vi: encoding=utf-8 ts=8 sts=4 sw=4 et
+
+import cgi, Cookie, os
+
+menu=[]
+content=[]
+header=[]
+
+def getfile(file):
+    f=open("../html/" + file + ".html", 'r')
+    s=f.read()
+    f.close()
+    return s
+
+def catfile(file):
+    f=open("../html/" + file + ".html", 'r')
+    print f.read()
+    f.close()
+
+def sendhttpheaders():
+    print "Content-Type: text/html\n"
+
+def getopts():
+    form = cgi.FieldStorage()
+    opts = {}
+    for key in form.keys():
+        opts[key] = form[key].value
+
+    cookies = {}
+
+    if os.environ.has_key('HTTP_COOKIE'):
+        c = Cookie.SimpleCookie()
+        c.load(os.environ['HTTP_COOKIE'])
+        for key in c.keys():
+            cookies[key] = c[key].value
+
+    return (opts, cookies)
+
+def sendhtml():
+    catfile('header')
+
+    print '<div id="Header">'
+    for i in header:
+        print i
+    print '</div>'
+
+    print '<div id="Menu">'
+    for i in menu:
+        print i
+    catfile('menufooter')
+    print '</div>'
+
+    print '<div id="Content">'
+    for i in content:
+        print i
+    print '</div>'
+   
+    catfile('footer')
+
+def addmenu(text=None, file=None):
+    if text:
+        menu.append(text)
+    else:
+        menu.append(getfile(file))
+
+def addheader(text=None, file=None):
+    if text:
+        header.append(text)
+    else:
+        header.append(getfile(file))
+
+def addcontent(text=None, file=None):
+    if text:
+        content.append(text)
+    else:
+        content.append(getfile(file))
+
diff --git a/repodata/comps.xml b/repodata/comps.xml
new file mode 100644 (file)
index 0000000..d437ad9
--- /dev/null
@@ -0,0 +1,2134 @@
+<?xml version="1.0"?>
+<!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
+<comps>
+       <group>
+               <id>base</id>
+               <name>Base packages</name>
+               <default>true</default>
+               <uservisible>true</uservisible>
+               <description>Base packages</description>
+               <packagelist>
+                       <packagereq type="default">ntp-client</packagereq>
+                       <packagereq type="mandatory">FHS</packagereq>
+                       <packagereq type="mandatory">SysVinit</packagereq>
+                       <packagereq type="mandatory">basesystem</packagereq>
+                       <packagereq type="mandatory">chkconfig</packagereq>
+                       <packagereq type="mandatory">cpio</packagereq>
+                       <packagereq type="default">cracklib</packagereq>
+                       <packagereq type="default">cracklib-dicts</packagereq>
+                       <packagereq type="default">dhcp-client</packagereq>
+                       <packagereq type="default">findutils</packagereq>
+                       <packagereq type="mandatory">fix-info-dir</packagereq>
+                       <packagereq type="default">geninitrd</packagereq>
+                       <packagereq type="default">genromfs</packagereq>
+                       <packagereq type="default">gettext</packagereq>
+                       <packagereq type="mandatory">glibc</packagereq>
+                       <packagereq type="default">glibc-localedb-all</packagereq>
+                       <packagereq type="default">grep</packagereq>
+                       <packagereq type="default">grubby</packagereq>
+                       <packagereq type="default">gzip</packagereq>
+                       <packagereq type="default">iproute2</packagereq>
+                       <packagereq type="mandatory">issue</packagereq>
+                       <packagereq type="default">kbd</packagereq>
+                       <packagereq type="default">kernel</packagereq>
+                       <packagereq type="optional">libstdc++</packagereq>
+                       <packagereq type="default">login</packagereq>
+                       <packagereq type="mandatory">logrotate</packagereq>
+                       <packagereq type="optional">losetup</packagereq>
+                       <packagereq type="optional">mawk</packagereq>
+                       <packagereq type="mandatory">mingetty</packagereq>
+                       <packagereq type="mandatory">mktemp</packagereq>
+                       <packagereq type="mandatory">module-init-tools</packagereq>
+                       <packagereq type="mandatory">mount</packagereq>
+                       <packagereq type="default">net-tools</packagereq>
+                       <packagereq type="mandatory">pam</packagereq>
+                       <packagereq type="mandatory">pciutils</packagereq>
+                       <packagereq type="mandatory">pdksh</packagereq>
+                       <packagereq type="default">poldek</packagereq>
+                       <packagereq type="default">procps</packagereq>
+                       <packagereq type="mandatory">rc-scripts</packagereq>
+                       <packagereq type="default">rpm</packagereq>
+                       <packagereq type="default">sed</packagereq>
+                       <packagereq type="optional">setserial</packagereq>
+                       <packagereq type="mandatory">setup</packagereq>
+                       <packagereq type="default">syslog-ng</packagereq>
+                       <packagereq type="mandatory">pwdutils</packagereq>
+                       <packagereq type="optional">tar</packagereq>
+                       <packagereq type="mandatory">tzdata</packagereq>
+                       <packagereq type="optional">tzdata-zoneinfo_right</packagereq>
+                       <packagereq type="mandatory">udev</packagereq>
+                       <packagereq type="default">utempter</packagereq>
+                       <packagereq type="mandatory">util-linux-ng</packagereq>
+                       <packagereq type="optional">vim-static</packagereq>
+                       <packagereq type="default">vixie-cron</packagereq>
+                       <packagereq type="default">which</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>basic</id>
+               <name>Programs you're unlikely not to need.</name>
+               <default>true</default>
+               <uservisible>true</uservisible>
+               <description>Programs you're unlikely not to need.</description>
+               <packagelist>
+                       <packagereq type="default">mailx</packagereq>
+                       <packagereq type="default">nc</packagereq>
+                       <packagereq type="default">psmisc</packagereq>
+                       <packagereq type="default">terminfo</packagereq>
+                       <packagereq type="optional">rpm-utils</packagereq>
+                       <packagereq type="default">telnet</packagereq>
+                       <packagereq type="default">tcp_wrappers</packagereq>
+                       <packagereq type="optional">sharutils</packagereq>
+                       <packagereq type="default">file</packagereq>
+                       <packagereq type="optional">ftp</packagereq>
+                       <packagereq type="default">less</packagereq>
+                       <packagereq type="optional">time</packagereq>
+                       <packagereq type="optional">iputils</packagereq>
+                       <packagereq type="default">iputils-ping</packagereq>
+                       <packagereq type="default">mtr</packagereq>
+                       <packagereq type="default">bash</packagereq>
+                       <packagereq type="default">bzip2</packagereq>
+                       <packagereq type="default">vim</packagereq>
+                       <packagereq type="default">vim-rt</packagereq>
+                       <packagereq type="default">openssh-clients</packagereq>
+                       <packagereq type="default">tmpwatch</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>backup_tools</id>
+               <name>Backup tools</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Backup tools</description>
+               <packagelist>
+                       <packagereq type="default">afio</packagereq>
+                       <packagereq type="default">amanda-client</packagereq>
+                       <packagereq type="default">amanda-server</packagereq>
+                       <packagereq type="default">mt-st</packagereq>
+                       <packagereq type="default">dump</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>extras1</id>
+               <name>Not essential, but *very* often needed.</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Not essential, but *very* often needed.</description>
+               <packagelist>
+                       <packagereq type="optional">dhcpcd</packagereq>
+                       <packagereq type="optional">pump</packagereq>
+                       <packagereq type="default">MAKEDEV</packagereq>
+                       <packagereq type="default">bind-utils</packagereq>
+                       <packagereq type="default">hexedit</packagereq>
+                       <packagereq type="default">info</packagereq>
+                       <packagereq type="default">lftp</packagereq>
+                       <packagereq type="default">ltrace</packagereq>
+                       <packagereq type="default">lsof</packagereq>
+                       <packagereq type="default">lynx</packagereq>
+                       <packagereq type="default">man</packagereq>
+                       <packagereq type="default">man-pages</packagereq>
+                       <packagereq type="default">mc</packagereq>
+                       <packagereq type="optional">mutt</packagereq>
+                       <packagereq type="default">ncftp</packagereq>
+                       <packagereq type="optional">ncompress</packagereq>
+                       <packagereq type="optional">nmap</packagereq>
+                       <packagereq type="default">openssh-server</packagereq>
+                       <packagereq type="default">pcregrep</packagereq>
+                       <packagereq type="default">perl</packagereq>
+                       <packagereq type="default">pinfo</packagereq>
+                       <packagereq type="optional">pine</packagereq>
+                       <packagereq type="optional">acpid</packagereq>
+                       <packagereq type="optional">quota</packagereq>
+                       <packagereq type="optional">rsync</packagereq>
+                       <packagereq type="default">slocate</packagereq>
+                       <packagereq type="default">strace</packagereq>
+                       <packagereq type="default">tcpdump</packagereq>
+                       <packagereq type="default">unzip</packagereq>
+                       <packagereq type="default">wget</packagereq>
+                       <packagereq type="optional">zsh</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>nfs</id>
+               <name>Network File Systems stuff</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Network File Systems stuff</description>
+               <packagelist>
+                       <packagereq type="default">portmap</packagereq>
+                       <packagereq type="default">nfs-utils</packagereq>
+                       <packagereq type="default">nfs-utils-clients</packagereq>
+                       <packagereq type="default">nfs-utils-lock</packagereq>
+                       <packagereq type="optional">samba</packagereq>
+                       <packagereq type="optional">samba-client</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>servers</id>
+               <name>Various servers</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Various servers</description>
+               <packagelist>
+                       <packagereq type="optional">proftpd-standalone</packagereq>
+                       <packagereq type="optional">proftpd-inetd</packagereq>
+                       <packagereq type="optional">pure-ftpd</packagereq>
+                       <packagereq type="optional">ftpd-BSD</packagereq>
+                       <packagereq type="optional">exim</packagereq>
+                       <packagereq type="optional">postfix</packagereq>
+                       <packagereq type="optional">sendmail</packagereq>
+                       <packagereq type="optional">omta</packagereq>
+                       <packagereq type="optional">cfingerd</packagereq>
+                       <packagereq type="optional">efingerd</packagereq>
+                       <packagereq type="optional">ffingerd</packagereq>
+                       <packagereq type="optional">bsd-fingerd</packagereq>
+                       <packagereq type="optional">xinetd</packagereq>
+                       <packagereq type="optional">rlinetd</packagereq>
+                       <packagereq type="optional">inetd</packagereq>
+                       <packagereq type="optional">telnetd</packagereq>
+                       <packagereq type="optional">cvs-pserver</packagereq>
+                       <packagereq type="optional">rsyncd-standalone</packagereq>
+                       <packagereq type="optional">rsyncd-inetd</packagereq>
+                       <packagereq type="optional">cups</packagereq>
+                       <packagereq type="optional">dhcp</packagereq>
+                       <packagereq type="optional">bircd</packagereq>
+                       <packagereq type="optional">ircd</packagereq>
+                       <packagereq type="optional">ircd-hybrid</packagereq>
+                       <packagereq type="optional">ircd-ptlink</packagereq>
+                       <packagereq type="optional">inn</packagereq>
+                       <packagereq type="optional">imap</packagereq>
+                       <packagereq type="optional">courier-imap</packagereq>
+                       <packagereq type="optional">cyrus-imapd</packagereq>
+                       <packagereq type="optional">imap-pop3</packagereq>
+                       <packagereq type="optional">courier-imap-pop3</packagereq>
+                       <packagereq type="optional">solid-pop3d</packagereq>
+                       <packagereq type="optional">tpop3d</packagereq>
+                       <packagereq type="optional">vm-pop3d-standalone</packagereq>
+                       <packagereq type="optional">bind</packagereq>
+                       <packagereq type="optional">pdns</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>irc</id>
+               <name>Client packages for IRC</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Client packages for IRC</description>
+               <packagelist>
+                       <packagereq type="optional">ircii</packagereq>
+                       <packagereq type="optional">irssi</packagereq>
+                       <packagereq type="optional">BitchX</packagereq>
+                       <packagereq type="optional">epic4</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>console</id>
+               <name>Utilites usable at Linux' console</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Utilites usable at Linux' console</description>
+               <packagelist>
+                       <packagereq type="optional">open</packagereq>
+                       <packagereq type="optional">SVGATextMode</packagereq>
+                       <packagereq type="optional">fbset</packagereq>
+                       <packagereq type="default">gpm</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>mp3</id>
+               <name>MPEG II Layer 3 encoding/decoding</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>MPEG II Layer 3 encoding/decoding</description>
+               <packagelist>
+                       <packagereq type="optional">mpg123</packagereq>
+                       <packagereq type="optional">bladeenc</packagereq>
+                       <packagereq type="optional">lame</packagereq>
+                       <packagereq type="optional">cdparanoia-III</packagereq>
+                       <packagereq type="optional">cdrtools-cdda2wav</packagereq>
+                       <packagereq type="optional">mp3blaster</packagereq>
+                       <packagereq type="optional">ripenc</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>apache_base</id>
+               <name>Apache 2.2.x web server base packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Apache 2.2.x web server base packages</description>
+               <packagelist>
+                       <packagereq type="default">apache</packagereq>
+                       <packagereq type="default">apache-errordocs</packagereq>
+                       <packagereq type="default">apache-index</packagereq>
+                       <packagereq type="default">apache-mod_alias</packagereq>
+                       <packagereq type="default">apache-mod_auth</packagereq>
+                       <packagereq type="default">apache-mod_auth_basic</packagereq>
+                       <packagereq type="default">apache-mod_authn_default</packagereq>
+                       <packagereq type="default">apache-mod_authn_file</packagereq>
+                       <packagereq type="default">apache-mod_authz_default</packagereq>
+                       <packagereq type="default">apache-mod_authz_groupfile</packagereq>
+                       <packagereq type="default">apache-mod_authz_host</packagereq>
+                       <packagereq type="default">apache-mod_authz_owner</packagereq>
+                       <packagereq type="default">apache-mod_authz_user</packagereq>
+                       <packagereq type="default">apache-mod_autoindex</packagereq>
+                       <packagereq type="default">apache-mod_deflate</packagereq>
+                       <packagereq type="default">apache-mod_dir</packagereq>
+                       <packagereq type="default">apache-mod_env</packagereq>
+                       <packagereq type="default">apache-mod_include</packagereq>
+                       <packagereq type="default">apache-mod_log_config</packagereq>
+                       <packagereq type="default">apache-mod_mime</packagereq>
+                       <packagereq type="default">apache-mod_mime_magic</packagereq>
+                       <packagereq type="default">apache-mod_negotiation</packagereq>
+                       <packagereq type="default">apache-mod_rewrite</packagereq>
+                       <packagereq type="default">apache-mod_setenvif</packagereq>
+                       <packagereq type="default">apache-mod_speling</packagereq>
+                       <packagereq type="default">apache-mod_userdir</packagereq>
+                       <packagereq type="default">apache-mod_version</packagereq>
+                       <packagereq type="default">apache-mod_vhost_alias</packagereq>
+                       <packagereq type="default">apache-tools</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>apache_mod</id>
+               <name>Apache 2.2.x additional packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Apache 2.2.x additional packages</description>
+               <packagelist>
+                       <packagereq type="optional">apache-apxs</packagereq>
+                       <packagereq type="optional">apache-cgi_test</packagereq>
+                       <packagereq type="optional">apache-dbmtools</packagereq>
+                       <packagereq type="optional">apache-doc</packagereq>
+                       <packagereq type="optional">apache-mod_actions</packagereq>
+                       <packagereq type="optional">apache-mod_cern_meta</packagereq>
+                       <packagereq type="optional">apache-mod_asis</packagereq>
+                       <packagereq type="optional">apache-mod_auth_dbm</packagereq>
+                       <packagereq type="optional">apache-mod_auth_digest</packagereq>
+                       <packagereq type="optional">apache-mod_authn_alias</packagereq>
+                       <packagereq type="optional">apache-mod_authn_anon</packagereq>
+                       <packagereq type="optional">apache-mod_authn_dbd</packagereq>
+                       <packagereq type="optional">apache-mod_authn_dbm</packagereq>
+                       <packagereq type="optional">apache-mod_authnz_ldap</packagereq>
+                       <packagereq type="optional">apache-mod_authz_dbm</packagereq>
+                       <packagereq type="optional">apache-mod_cache</packagereq>
+                       <packagereq type="optional">apache-mod_case_filter</packagereq>
+                       <packagereq type="optional">apache-mod_case_filter_in</packagereq>
+                       <packagereq type="optional">apache-mod_cgi</packagereq>
+                       <packagereq type="optional">apache-mod_cgid</packagereq>
+                       <packagereq type="optional">apache-mod_charset_lite</packagereq>
+                       <packagereq type="optional">apache-mod_dav</packagereq>
+                       <packagereq type="optional">apache-mod_dbd</packagereq>
+                       <packagereq type="optional">apache-mod_dumpio</packagereq>
+                       <packagereq type="optional">apache-mod_echo</packagereq>
+                       <packagereq type="optional">apache-mod_expires</packagereq>
+                       <packagereq type="optional">apache-mod_ext_filter</packagereq>
+                       <packagereq type="optional">apache-mod_file_cache</packagereq>
+                       <packagereq type="optional">apache-mod_filter</packagereq>
+                       <packagereq type="optional">apache-mod_headers</packagereq>
+                       <packagereq type="optional">apache-mod_ident</packagereq>
+                       <packagereq type="optional">apache-mod_imagemap</packagereq>
+                       <packagereq type="optional">apache-mod_info</packagereq>
+                       <packagereq type="optional">apache-mod_ldap</packagereq>
+                       <packagereq type="optional">apache-mod_log_forensic</packagereq>
+                       <packagereq type="optional">apache-mod_logio</packagereq>
+                       <packagereq type="optional">apache-mod_proxy</packagereq>
+                       <packagereq type="optional">apache-mod_ssl</packagereq>
+                       <packagereq type="optional">apache-mod_status</packagereq>
+                       <packagereq type="optional">apache-mod_unique_id</packagereq>
+                       <packagereq type="optional">apache-mod_usertrack</packagereq>
+                       <packagereq type="optional">apache-suexec</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>apache1_base</id>
+               <name>Apache 1.3.x web server base packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Apache 1.3.x web server base packages</description>
+               <packagelist>
+                       <packagereq type="default">apache1</packagereq>
+                       <packagereq type="default">apache1-errordocs</packagereq>
+                       <packagereq type="default">apache1-index</packagereq>
+                       <packagereq type="default">apache1-mod_access</packagereq>
+                       <packagereq type="default">apache1-mod_asis</packagereq>
+                       <packagereq type="default">apache1-mod_auth</packagereq>
+                       <packagereq type="default">apache1-mod_autoindex</packagereq>
+                       <packagereq type="default">apache1-mod_cern_meta</packagereq>
+                       <packagereq type="default">apache1-mod_cgi</packagereq>
+                       <packagereq type="default">apache1-mod_dir</packagereq>
+                       <packagereq type="default">apache1-mod_env</packagereq>
+                       <packagereq type="default">apache1-mod_log_config</packagereq>
+                       <packagereq type="default">apache1-mod_mime</packagereq>
+                       <packagereq type="default">apache1-mod_mime_magic</packagereq>
+                       <packagereq type="default">apache1-mod_negotiation</packagereq>
+                       <packagereq type="default">apache1-mod_rewrite</packagereq>
+                       <packagereq type="default">apache1-mod_setenvif</packagereq>
+                       <packagereq type="default">apache1-mod_speling</packagereq>
+                       <packagereq type="default">apache1-mod_ssl</packagereq>
+                       <packagereq type="default">apache1-mod_userdir</packagereq>
+                       <packagereq type="default">apache1-mod_vhost_alias</packagereq>
+                       <packagereq type="default">apache1-tools</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>apache1_mod</id>
+               <name>Apache 1.3.x additional packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Apache 1.3.x additional packages</description>
+               <packagelist>
+                       <packagereq type="optional">apache1-apxs</packagereq>
+                       <packagereq type="optional">apache1-doc</packagereq>
+                       <packagereq type="optional">apache1-mod_actions</packagereq>
+                       <packagereq type="optional">apache1-mod_alias</packagereq>
+                       <packagereq type="optional">apache1-mod_auth_anon</packagereq>
+                       <packagereq type="optional">apache1-mod_auth_db</packagereq>
+                       <packagereq type="optional">apache1-mod_auth_digest</packagereq>
+                       <packagereq type="optional">apache1-mod_define</packagereq>
+                       <packagereq type="optional">apache1-mod_digest</packagereq>
+                       <packagereq type="optional">apache1-mod_expires</packagereq>
+                       <packagereq type="optional">apache1-mod_headers</packagereq>
+                       <packagereq type="optional">apache1-mod_imap</packagereq>
+                       <packagereq type="optional">apache1-mod_include</packagereq>
+                       <packagereq type="optional">apache1-mod_info</packagereq>
+                       <packagereq type="optional">apache1-mod_log_agent</packagereq>
+                       <packagereq type="optional">apache1-mod_log_forensic</packagereq>
+                       <packagereq type="optional">apache1-mod_log_referer</packagereq>
+                       <packagereq type="optional">apache1-mod_mmap_static</packagereq>
+                       <packagereq type="optional">apache1-mod_proxy</packagereq>
+                       <packagereq type="optional">apache1-mod_status</packagereq>
+                       <packagereq type="optional">apache1-mod_sxnet</packagereq>
+                       <packagereq type="optional">apache1-mod_unique_id</packagereq>
+                       <packagereq type="optional">apache1-mod_usertrack</packagereq>
+                       <packagereq type="optional">apache1-suexec</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>php_base</id>
+               <name>PHP 5.x, base packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>PHP 5.x, base packages</description>
+               <packagelist>
+                       <packagereq type="optional">apache-mod_php</packagereq>
+                       <packagereq type="optional">apache1-mod_php</packagereq>
+                       <packagereq type="optional">php-cgi</packagereq>
+                       <packagereq type="optional">php-cli</packagereq>
+                       <packagereq type="default">php-common</packagereq>
+                       <packagereq type="optional">php-curl</packagereq>
+                       <packagereq type="optional">php-gettext</packagereq>
+                       <packagereq type="optional">php-iconv</packagereq>
+                       <packagereq type="optional">php-mbstring</packagereq>
+                       <packagereq type="optional">php-mime_magic</packagereq>
+                       <packagereq type="optional">php-msession</packagereq>
+                       <packagereq type="optional">php-openssl</packagereq>
+                       <packagereq type="optional">php-readline</packagereq>
+                       <packagereq type="optional">php-sysvmsg</packagereq>
+                       <packagereq type="optional">php-sysvsem</packagereq>
+                       <packagereq type="optional">php-sysvshm</packagereq>
+                       <packagereq type="optional">php-tokenizer</packagereq>
+                       <packagereq type="optional">php-zlib</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>php_add</id>
+               <name>PHP 5.x, additional packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>PHP 5.x, additional packages</description>
+               <packagelist>
+                       <packagereq type="optional">php-bcmath</packagereq>
+                       <packagereq type="optional">php-bzip2</packagereq>
+                       <packagereq type="optional">php-calendar</packagereq>
+                       <packagereq type="optional">php-ctype</packagereq>
+                       <packagereq type="optional">php-dba</packagereq>
+                       <packagereq type="optional">php-dbase</packagereq>
+                       <packagereq type="optional">php-dom</packagereq>
+                       <packagereq type="optional">php-exif</packagereq>
+                       <packagereq type="optional">php-fcgi</packagereq>
+                       <packagereq type="optional">php-filepro</packagereq>
+                       <packagereq type="optional">php-ftp</packagereq>
+                       <packagereq type="optional">php-gd</packagereq>
+                       <packagereq type="optional">php-gmp</packagereq>
+                       <packagereq type="optional">php-imap</packagereq>
+                       <packagereq type="optional">php-interbase</packagereq>
+                       <packagereq type="optional">php-ldap</packagereq>
+                       <packagereq type="optional">php-mcrypt</packagereq>
+                       <packagereq type="optional">php-mhash</packagereq>
+                       <packagereq type="optional">php-ming</packagereq>
+                       <packagereq type="optional">php-mssql</packagereq>
+                       <packagereq type="optional">php-mysql</packagereq>
+                       <packagereq type="optional">php-mysqli</packagereq>
+                       <packagereq type="optional">php-ncurses</packagereq>
+                       <packagereq type="optional">php-odbc</packagereq>
+                       <packagereq type="optional">php-pcntl</packagereq>
+                       <packagereq type="optional">php-pdo</packagereq>
+                       <packagereq type="optional">php-pdo-dblib</packagereq>
+                       <packagereq type="optional">php-pdo-firebird</packagereq>
+                       <packagereq type="optional">php-pdo-mysql</packagereq>
+                       <packagereq type="optional">php-pdo-odbc</packagereq>
+                       <packagereq type="optional">php-pdo-pgsql</packagereq>
+                       <packagereq type="optional">php-pdo-sqlite</packagereq>
+                       <packagereq type="optional">php-pgsql</packagereq>
+                       <packagereq type="optional">php-posix</packagereq>
+                       <packagereq type="optional">php-program</packagereq>
+                       <packagereq type="optional">php-pspell</packagereq>
+                       <packagereq type="optional">php-recode</packagereq>
+                       <packagereq type="optional">php-shmop</packagereq>
+                       <packagereq type="optional">php-snmp</packagereq>
+                       <packagereq type="optional">php-soap</packagereq>
+                       <packagereq type="optional">php-sockets</packagereq>
+                       <packagereq type="optional">php-sqlite</packagereq>
+                       <packagereq type="optional">php-sybase-ct</packagereq>
+                       <packagereq type="optional">php-sybase</packagereq>
+                       <packagereq type="optional">php-tidy</packagereq>
+                       <packagereq type="optional">php-wddx</packagereq>
+                       <packagereq type="optional">php-xml</packagereq>
+                       <packagereq type="optional">php-xmlreader</packagereq>
+                       <packagereq type="optional">php-xmlrpc</packagereq>
+                       <packagereq type="optional">php-xsl</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>mysql</id>
+               <name>MySQL database server</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>MySQL database server</description>
+               <packagelist>
+                       <packagereq type="default">mysql</packagereq>
+                       <packagereq type="default">mysql-client</packagereq>
+                       <packagereq type="default">mysql-libs</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>mysql_extras</id>
+               <name>MySQL extras</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>MySQL extras</description>
+               <packagelist>
+                       <packagereq type="optional">mysql-bench</packagereq>
+                       <packagereq type="optional">mysql-extras</packagereq>
+                       <packagereq type="optional">mysql-extras-perl</packagereq>
+                       <packagereq type="optional">mysql-ndb</packagereq>
+                       <packagereq type="optional">mysql-ndb-client</packagereq>
+                       <packagereq type="optional">mysql-ndb-cpc</packagereq>
+                       <packagereq type="optional">mysql-ndb-mgm</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>pgsql</id>
+               <name>PostgreSQL database server</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>PostgreSQL database server</description>
+               <packagelist>
+                       <packagereq type="default">postgresql</packagereq>
+                       <packagereq type="default">postgresql-clients</packagereq>
+                       <packagereq type="default">postgresql-libs</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>pgsql_extras</id>
+               <name>PostgreSQL extras</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>PostgreSQL extras</description>
+               <packagelist>
+                       <packagereq type="optional">postgresql-doc</packagereq>
+                       <packagereq type="optional">postgresql-ecpg</packagereq>
+                       <packagereq type="optional">postgresql-module-lo</packagereq>
+                       <packagereq type="optional">postgresql-module-pgcrypto</packagereq>
+                       <packagereq type="optional">postgresql-module-plperl</packagereq>
+                       <packagereq type="optional">postgresql-module-plpgsql</packagereq>
+                       <packagereq type="optional">postgresql-module-plpython</packagereq>
+                       <packagereq type="optional">postgresql-module-pltcl</packagereq>
+                       <packagereq type="optional">postgresql-module-tsearch2</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>crunchers</id>
+               <name>Packages for (de)compressing using strange formats</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Packages for (de)compressing using strange formats</description>
+               <packagelist>
+                       <packagereq type="default">zip</packagereq>
+                       <packagereq type="optional">unzip</packagereq>
+                       <packagereq type="default">unace</packagereq>
+                       <packagereq type="default">unrar</packagereq>
+                       <packagereq type="default">unarj</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>xemacs</id>
+               <name>XEmacs editor/environment</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>XEmacs editor/environment</description>
+               <packagelist>
+                       <packagereq type="default">xemacs</packagereq>
+                       <packagereq type="optional">xemacs-Sun-pkg</packagereq>
+                       <packagereq type="default">xemacs-auctex-pkg</packagereq>
+                       <packagereq type="default">xemacs-cc-mode-pkg</packagereq>
+                       <packagereq type="default">xemacs-common</packagereq>
+                       <packagereq type="default">xemacs-debug-pkg</packagereq>
+                       <packagereq type="default">xemacs-dired-pkg</packagereq>
+                       <packagereq type="default">xemacs-edit-utils-pkg</packagereq>
+                       <packagereq type="default">xemacs-eterm-pkg</packagereq>
+                       <packagereq type="default">xemacs-extras</packagereq>
+                       <packagereq type="default">xemacs-fsf-compat-pkg</packagereq>
+                       <packagereq type="default">xemacs-games-pkg</packagereq>
+                       <packagereq type="default">xemacs-ispell-pkg</packagereq>
+                       <packagereq type="default">xemacs-jde-pkg</packagereq>
+                       <packagereq type="default">xemacs-mail-lib-pkg</packagereq>
+                       <packagereq type="default">xemacs-mailcrypt-pkg</packagereq>
+                       <packagereq type="default">xemacs-mh-e-pkg</packagereq>
+                       <packagereq type="optional">xemacs-nox</packagereq>
+                       <packagereq type="default">xemacs-parigp-mode-pkg</packagereq>
+                       <packagereq type="default">xemacs-pc-pkg</packagereq>
+                       <packagereq type="default">xemacs-psgml-pkg</packagereq>
+                       <packagereq type="default">xemacs-sgml-pkg</packagereq>
+                       <packagereq type="default">xemacs-spec-mode-pkg</packagereq>
+                       <packagereq type="default">xemacs-speedbar-pkg</packagereq>
+                       <packagereq type="default">xemacs-text-modes-pkg</packagereq>
+                       <packagereq type="default">xemacs-vc-pkg</packagereq>
+                       <packagereq type="default">xemacs-w3-pkg</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>devel</id>
+               <name>Basic programs needed for compilation.</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Basic programs needed for compilation.</description>
+               <packagelist>
+                       <packagereq type="default">glibc-devel</packagereq>
+                       <packagereq type="default">autoconf</packagereq>
+                       <packagereq type="default">automake</packagereq>
+                       <packagereq type="default">bison</packagereq>
+                       <packagereq type="default">cpp</packagereq>
+                       <packagereq type="optional">cproto</packagereq>
+                       <packagereq type="default">ctags</packagereq>
+                       <packagereq type="default">cvs</packagereq>
+                       <packagereq type="default">gcc</packagereq>
+                       <packagereq type="default">make</packagereq>
+                       <packagereq type="default">binutils</packagereq>
+                       <packagereq type="default">flex</packagereq>
+                       <packagereq type="default">kernel-headers</packagereq>
+                       <packagereq type="optional">gcc-c++</packagereq>
+                       <packagereq type="optional">objc</packagereq>
+                       <packagereq type="optional">gcc-objc</packagereq>
+                       <packagereq type="default">patch</packagereq>
+                       <packagereq type="default">diffutils</packagereq>
+                       <packagereq type="default">m4</packagereq>
+                       <packagereq type="default">perl-devel</packagereq>
+                       <packagereq type="optional">rpm-build</packagereq>
+                       <packagereq type="default">ncurses-devel</packagereq>
+                       <packagereq type="default">bin86</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>devel_extras</id>
+               <name>Packages needed for real development.</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Packages needed for real development.</description>
+               <packagelist>
+                       <packagereq type="default">gdb</packagereq>
+                       <packagereq type="optional">lcc</packagereq>
+                       <packagereq type="default">ElectricFence</packagereq>
+                       <packagereq type="optional">ddd</packagereq>
+                       <packagereq type="default">diffstat</packagereq>
+                       <packagereq type="default">doxygen</packagereq>
+                       <packagereq type="default">indent</packagereq>
+                       <packagereq type="default">nasm</packagereq>
+                       <packagereq type="optional">ocaml</packagereq>
+                       <packagereq type="optional">python</packagereq>
+                       <packagereq type="optional">python-devel</packagereq>
+                       <packagereq type="optional">python-doc</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>java</id>
+               <name>Packages needed for JAVA development.</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Packages needed for JAVA development.</description>
+               <packagelist>
+                       <packagereq type="default">kaffe</packagereq>
+                       <packagereq type="default">jikes</packagereq>
+                       <packagereq type="optional">gcc-java</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>xwindow</id>
+               <name>X Window System</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>X Window System</description>
+               <packagelist>
+                       <packagereq type="default">xinitrc-ng</packagereq>
+                       <packagereq type="default">xorg-app-appres</packagereq>
+                       <packagereq type="default">xorg-app-bitmap</packagereq>
+                       <packagereq type="default">xorg-app-editres</packagereq>
+                       <packagereq type="default">xorg-app-iceauth</packagereq>
+                       <packagereq type="default">xorg-app-lbxproxy</packagereq>
+                       <packagereq type="default">xorg-app-luit</packagereq>
+                       <packagereq type="default">xorg-app-mkfontdir</packagereq>
+                       <packagereq type="default">xorg-app-mkfontscale</packagereq>
+                       <packagereq type="default">xorg-app-proxymngr</packagereq>
+                       <packagereq type="default">xorg-app-rgb</packagereq>
+                       <packagereq type="default">xorg-app-rstart</packagereq>
+                       <packagereq type="default">xorg-app-scripts</packagereq>
+                       <packagereq type="default">xorg-app-setxkbmap</packagereq>
+                       <packagereq type="default">xorg-app-smproxy</packagereq>
+                       <packagereq type="default">xorg-app-x11perf</packagereq>
+                       <packagereq type="default">xorg-app-xclipboard</packagereq>
+                       <packagereq type="default">xorg-app-xcmsdb</packagereq>
+                       <packagereq type="default">xorg-app-xconsole</packagereq>
+                       <packagereq type="default">xorg-app-xcursorgen</packagereq>
+                       <packagereq type="default">xorg-app-xditview</packagereq>
+                       <packagereq type="default">xorg-app-xdpyinfo</packagereq>
+                       <packagereq type="default">xorg-app-xf86dga</packagereq>
+                       <packagereq type="default">xorg-app-xfindproxy</packagereq>
+                       <packagereq type="default">xorg-app-xfwp</packagereq>
+                       <packagereq type="default">xorg-app-xgamma</packagereq>
+                       <packagereq type="default">xorg-app-xhost</packagereq>
+                       <packagereq type="default">xorg-app-xinit</packagereq>
+                       <packagereq type="default">xorg-app-xkbevd</packagereq>
+                       <packagereq type="default">xorg-app-xkbprint</packagereq>
+                       <packagereq type="default">xorg-app-xkbutils</packagereq>
+                       <packagereq type="default">xorg-app-xlsatoms</packagereq>
+                       <packagereq type="default">xorg-app-xlsclients</packagereq>
+                       <packagereq type="default">xorg-app-xlsfonts</packagereq>
+                       <packagereq type="default">xorg-app-xmh</packagereq>
+                       <packagereq type="default">xorg-app-xmodmap</packagereq>
+                       <packagereq type="default">xorg-app-xprop</packagereq>
+                       <packagereq type="default">xorg-app-xrandr</packagereq>
+                       <packagereq type="default">xorg-app-xrdb</packagereq>
+                       <packagereq type="default">xorg-app-xrefresh</packagereq>
+                       <packagereq type="default">xorg-app-xrx</packagereq>
+                       <packagereq type="default">xorg-app-xset</packagereq>
+                       <packagereq type="default">xorg-app-xsetmode</packagereq>
+                       <packagereq type="default">xorg-app-xsetpointer</packagereq>
+                       <packagereq type="default">xorg-app-xsetroot</packagereq>
+                       <packagereq type="default">xorg-app-xsm</packagereq>
+                       <packagereq type="default">xorg-app-xstdcmap</packagereq>
+                       <packagereq type="default">xorg-app-xvidtune</packagereq>
+                       <packagereq type="default">xorg-app-xvinfo</packagereq>
+                       <packagereq type="default">xorg-app-xwd</packagereq>
+                       <packagereq type="default">xorg-app-xwud</packagereq>
+                       <packagereq type="default">xorg-data-xbitmaps</packagereq>
+                       <packagereq type="default">xorg-docs</packagereq>
+                       <packagereq type="default">xorg-lib-libXpm-utils</packagereq>
+                       <packagereq type="default">xorg-util-imake</packagereq>
+                       <packagereq type="default">xorg-util-lndir</packagereq>
+                       <packagereq type="default">xorg-xserver-Xprt</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">Mesa-libGL</packagereq>
+                       <packagereq type="default">Mesa-libGLU</packagereq>
+                       <packagereq type="default">Mesa-utils</packagereq>
+                       <packagereq type="default">Mesa-libGL-devel</packagereq>
+                       <packagereq type="default">Mesa-libGL-static</packagereq>
+                       <packagereq type="default">xorg-xserver-Xnest</packagereq>
+                       <packagereq type="default">xorg-xserver-Xprt</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">xorg-xserver-Xvfb</packagereq>
+                       <packagereq type="default">xorg-app-bdftopcf</packagereq>
+                       <packagereq type="default">xorg-lib-libFS-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libICE-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libSM-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libX11-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXau-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXaw-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXcomposite-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXcursor-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXdamage-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXdmcp-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXevie-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXfixes-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXfont-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXft-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXmu-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXpm-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXprintAppUtil-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXprintUtil-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXrandr-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXrender-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXres-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXt-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXv-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libXvMC-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libfontenc-devel</packagereq>
+                       <packagereq type="default">xorg-lib-liboldX-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libxkbfile-devel</packagereq>
+                       <packagereq type="default">xorg-lib-libxkbui-devel</packagereq>
+                       <packagereq type="default">xorg-proto-applewmproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-bigreqsproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-compositeproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-damageproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-dmxproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-evieext-devel</packagereq>
+                       <packagereq type="default">xorg-proto-fixesproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-fontcacheproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-fontsproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-inputproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-kbproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-printproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-randrproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-recordproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-renderproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-resourceproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-scrnsaverproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-trapproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-videoproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-windowswmproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xcmiscproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xextproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xf86bigfontproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xf86dgaproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xf86miscproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xf86rushproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xf86vidmodeproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xineramaproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xproto-devel</packagereq>
+                       <packagereq type="default">xorg-proto-xproxymanagementprotocol-devel</packagereq>
+                       <packagereq type="default">xorg-xserver-server-devel</packagereq>
+                       <packagereq type="default">xorg-driver-video-apm</packagereq>
+                       <packagereq type="default">xorg-driver-video-ark</packagereq>
+                       <packagereq type="default">xorg-driver-video-ati</packagereq>
+                       <packagereq type="default">xorg-driver-video-ati</packagereq>
+                       <packagereq type="default">xorg-driver-video-ati</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-rage128</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-radeon-R100</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-radeon-R200</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-radeon-R300</packagereq>
+                       <packagereq type="default">xorg-driver-video-chips</packagereq>
+                       <packagereq type="default">xorg-driver-video-cirrus</packagereq>
+                       <packagereq type="default">xorg-driver-video-cyrix</packagereq>
+                       <packagereq type="default">xorg-driver-video-fbdev</packagereq>
+                       <packagereq type="default">xorg-driver-video-voodoo</packagereq>
+                       <packagereq type="default">xorg-driver-video-glint</packagereq>
+                       <packagereq type="default">xorg-driver-video-i128</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">xorg-driver-video-i740</packagereq>
+                       <packagereq type="default">xorg-driver-video-i810</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-intel-i810</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-intel-i915</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-intel-i965</packagereq>
+                       <packagereq type="default">xorg-driver-video-mga</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-matrox</packagereq>
+                       <packagereq type="default">xorg-driver-video-neomagic</packagereq>
+                       <packagereq type="default">xorg-driver-video-newport</packagereq>
+                       <packagereq type="default">xorg-driver-video-nsc</packagereq>
+                       <packagereq type="default">xorg-driver-video-nv</packagereq>
+                       <packagereq type="default">xorg-driver-video-rendition</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-s3virge</packagereq>
+                       <packagereq type="default">xorg-driver-video-s3virge</packagereq>
+                       <packagereq type="default">xorg-driver-video-s3</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-savage</packagereq>
+                       <packagereq type="default">xorg-driver-video-savage</packagereq>
+                       <packagereq type="default">xorg-driver-video-siliconmotion</packagereq>
+                       <packagereq type="default">xorg-driver-video-sis</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-sis</packagereq>
+                       <packagereq type="default">xorg-driver-video-sisusb</packagereq>
+                       <packagereq type="default">xorg-driver-video-sunbw2</packagereq>
+                       <packagereq type="default">xorg-driver-video-suncg14</packagereq>
+                       <packagereq type="default">xorg-driver-video-suncg3</packagereq>
+                       <packagereq type="default">xorg-driver-video-suncg6</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ffb</packagereq>
+                       <packagereq type="default">xorg-driver-video-sunffb</packagereq>
+                       <packagereq type="default">xorg-driver-video-sunleo</packagereq>
+                       <packagereq type="default">xorg-driver-video-suntcx</packagereq>
+                       <packagereq type="default">xorg-driver-video-tdfx</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-tdfx</packagereq>
+                       <packagereq type="default">xorg-driver-video-tga</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-trident</packagereq>
+                       <packagereq type="default">xorg-driver-video-trident</packagereq>
+                       <packagereq type="default">xorg-driver-video-tseng</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-via-unichrome</packagereq>
+                       <packagereq type="default">xorg-driver-video-via</packagereq>
+                       <packagereq type="default">xorg-driver-video-vmware</packagereq>
+                       <packagereq type="default">xorg-app-bitmap</packagereq>
+                       <packagereq type="default">xorg-app-xditview</packagereq>
+                       <packagereq type="default">xorg-app-xmh</packagereq>
+                       <packagereq type="default">xorg-data-xbitmaps</packagereq>
+                       <packagereq type="default">xorg-app-xkbcomp</packagereq>
+                       <packagereq type="default">xorg-driver-input-keyboard</packagereq>
+                       <packagereq type="default">xorg-driver-input-mouse</packagereq>
+                       <packagereq type="default">xorg-driver-video-v4l</packagereq>
+                       <packagereq type="default">xorg-driver-video-vesa</packagereq>
+                       <packagereq type="default">xorg-driver-video-vga</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">xorg-lib-libFS-static</packagereq>
+                       <packagereq type="default">xorg-lib-libICE-static</packagereq>
+                       <packagereq type="default">xorg-lib-libSM-static</packagereq>
+                       <packagereq type="default">xorg-lib-libX11-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXScrnSaver-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXTrap-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXau-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXcomposite-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXcursor-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXdamage-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXdmcp-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXevie-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXext-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXfixes-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXfont-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXfontcache-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXft-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXi-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXinerama-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXmu-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXp-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXpm-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXrandr-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXrender-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXres-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXt-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXtst-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXv-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXvMC-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXxf86dga-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXxf86misc-static</packagereq>
+                       <packagereq type="default">xorg-lib-libXxf86vm-static</packagereq>
+                       <packagereq type="default">xorg-lib-libfontenc-static</packagereq>
+                       <packagereq type="default">xorg-lib-libxkbfile-static</packagereq>
+                       <packagereq type="default">xorg-lib-libxkbui-static</packagereq>
+                       <packagereq type="default">xorg-app-beforelight</packagereq>
+                       <packagereq type="default">xorg-app-ico</packagereq>
+                       <packagereq type="default">xorg-app-listres</packagereq>
+                       <packagereq type="default">xorg-app-oclock</packagereq>
+                       <packagereq type="default">xorg-app-showfont</packagereq>
+                       <packagereq type="default">xorg-app-viewres</packagereq>
+                       <packagereq type="default">xorg-app-x11perf</packagereq>
+                       <packagereq type="default">xorg-app-xbiff</packagereq>
+                       <packagereq type="default">xorg-app-xcalc</packagereq>
+                       <packagereq type="default">xorg-app-xclipboard</packagereq>
+                       <packagereq type="default">xorg-app-xclock</packagereq>
+                       <packagereq type="default">xorg-app-xdbedizzy</packagereq>
+                       <packagereq type="default">xorg-app-xditview</packagereq>
+                       <packagereq type="default">xorg-app-xdriinfo</packagereq>
+                       <packagereq type="default">xorg-app-xedit</packagereq>
+                       <packagereq type="default">xorg-app-xev</packagereq>
+                       <packagereq type="default">xorg-app-xeyes</packagereq>
+                       <packagereq type="default">xorg-app-xfd</packagereq>
+                       <packagereq type="default">xorg-app-xfontsel</packagereq>
+                       <packagereq type="default">xorg-app-xgc</packagereq>
+                       <packagereq type="default">xorg-app-xkill</packagereq>
+                       <packagereq type="default">xorg-app-xload</packagereq>
+                       <packagereq type="default">xorg-app-xlogo</packagereq>
+                       <packagereq type="default">xorg-app-xmag</packagereq>
+                       <packagereq type="default">xorg-app-xman</packagereq>
+                       <packagereq type="default">xorg-app-xmessage</packagereq>
+                       <packagereq type="default">xorg-app-xmh</packagereq>
+                       <packagereq type="default">xorg-app-xmore</packagereq>
+                       <packagereq type="default">xorg-app-xphelloworld</packagereq>
+                       <packagereq type="default">xorg-app-xplsprinters</packagereq>
+                       <packagereq type="default">xorg-app-xpr</packagereq>
+                       <packagereq type="default">xorg-app-xprehashprinterlist</packagereq>
+                       <packagereq type="default">xorg-app-xprop</packagereq>
+                       <packagereq type="default">xorg-app-xtrap</packagereq>
+                       <packagereq type="default">xorg-app-xwininfo</packagereq>
+                       <packagereq type="default">xorg-util-gccmakedep</packagereq>
+                       <packagereq type="default">xorg-util-imake</packagereq>
+                       <packagereq type="default">xorg-app-sessreg</packagereq>
+                       <packagereq type="default">xorg-app-twm</packagereq>
+                       <packagereq type="default">xorg-app-xauth</packagereq>
+                       <packagereq type="default">xorg-app-xdm</packagereq>
+                       <packagereq type="default">xorg-app-xfs</packagereq>
+                       <packagereq type="default">xorg-font-encodings</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-arabic-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-ttf</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-daewoo-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-dec-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-ibm-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-isas-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-micro-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-ethiopic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-meltho</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-mutt-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-schumacher-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-sony-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-sun-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-xfree86-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-util</packagereq>
+                       <packagereq type="default">xorg-font-font-cursor-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-base</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-1</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-2</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-3</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-4</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-5</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-7</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-8</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-9</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-10</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-11</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-13</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-14</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-15</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-16</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-lucidatypewriter-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-lucidatypewriter-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-jis-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-cronyx-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-screen-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-winitzki-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-ethiopic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-meltho</packagereq>
+                       <packagereq type="default">X11-DPS</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">Mesa-libGL</packagereq>
+                       <packagereq type="default">Mesa-libGLU</packagereq>
+                       <packagereq type="default">Mesa-utils</packagereq>
+                       <packagereq type="default">xorg-xserver-Xnest</packagereq>
+                       <packagereq type="default">xorg-xserver-Xprt</packagereq>
+                       <packagereq type="default">xorg-xserver-Xprt</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">xorg-xserver-server-devel</packagereq>
+                       <packagereq type="default">xorg-xserver-Xvfb</packagereq>
+                       <packagereq type="default">X11-common</packagereq>
+                       <packagereq type="default">xorg-driver-video-apm</packagereq>
+                       <packagereq type="default">xorg-driver-video-ark</packagereq>
+                       <packagereq type="default">xorg-driver-video-ati</packagereq>
+                       <packagereq type="default">xorg-driver-video-chips</packagereq>
+                       <packagereq type="default">xorg-driver-video-cirrus</packagereq>
+                       <packagereq type="default">xorg-driver-video-cyrix</packagereq>
+                       <packagereq type="default">X11-driver-evdev</packagereq>
+                       <packagereq type="default">xorg-driver-video-fbdev</packagereq>
+                       <packagereq type="default">xorg-driver-video-voodoo</packagereq>
+                       <packagereq type="default">xorg-driver-video-glint</packagereq>
+                       <packagereq type="default">xorg-driver-video-i128</packagereq>
+                       <packagereq type="default">xorg-driver-video-i740</packagereq>
+                       <packagereq type="default">xorg-driver-video-i810</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-intel-i810</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-intel-i915</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-intel-i965</packagereq>
+                       <packagereq type="default">xorg-driver-video-mga</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-matrox</packagereq>
+                       <packagereq type="default">xorg-driver-video-neomagic</packagereq>
+                       <packagereq type="default">xorg-driver-video-nsc</packagereq>
+                       <packagereq type="default">xorg-driver-video-nv</packagereq>
+                       <packagereq type="default">xorg-driver-video-ati</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-rage128</packagereq>
+                       <packagereq type="default">xorg-driver-video-ati</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-radeon-R100</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-radeon-R200</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-ati-radeon-R300</packagereq>
+                       <packagereq type="default">xorg-driver-video-rendition</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-s3virge</packagereq>
+                       <packagereq type="default">xorg-driver-video-s3virge</packagereq>
+                       <packagereq type="default">xorg-driver-video-s3</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-s3virge</packagereq>
+                       <packagereq type="default">xorg-driver-video-s3virge</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-savage</packagereq>
+                       <packagereq type="default">xorg-driver-video-savage</packagereq>
+                       <packagereq type="default">xorg-driver-video-siliconmotion</packagereq>
+                       <packagereq type="default">xorg-driver-video-sis</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-sis</packagereq>
+                       <packagereq type="default">xorg-driver-video-sisusb</packagereq>
+                       <packagereq type="default">xorg-driver-video-tdfx</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-tdfx</packagereq>
+                       <packagereq type="default">xorg-driver-video-tga</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-trident</packagereq>
+                       <packagereq type="default">xorg-driver-video-trident</packagereq>
+                       <packagereq type="default">xorg-driver-video-tseng</packagereq>
+                       <packagereq type="default">Mesa-dri-driver-via-unichrome</packagereq>
+                       <packagereq type="default">xorg-driver-video-via</packagereq>
+                       <packagereq type="default">xorg-driver-video-vmware</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-lucidatypewriter-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-100dpi</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-1</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-10</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-13</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-14</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-15</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-2</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-3</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-4</packagereq>
+                       <packagereq type="default">X11-fonts-100dpi-ISO8859-9</packagereq>
+                       <packagereq type="default">xorg-font-encodings</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-arabic-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-ttf</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-daewoo-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-dec-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-ibm-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-isas-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-micro-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-ethiopic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-meltho</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-mutt-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-schumacher-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-sony-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-sun-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-xfree86-type1</packagereq>
+                       <packagereq type="default">xorg-font-font-util</packagereq>
+                       <packagereq type="default">xorg-font-font-cursor-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-base</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-1</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-2</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-3</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-4</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-5</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-7</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-8</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-9</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-10</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-11</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-13</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-14</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-15</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-16</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-lucidatypewriter-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-lucidatypewriter-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-100dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-jis-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-cronyx-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-screen-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-winitzki-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-ethiopic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-meltho</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-adobe-utopia-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bh-lucidatypewriter-75dpi</packagereq>
+                       <packagereq type="default">xorg-font-font-bitstream-75dpi</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-1</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-10</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-13</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-14</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-15</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-2</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-3</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-4</packagereq>
+                       <packagereq type="default">X11-fonts-75dpi-ISO8859-9</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-ethiopic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-1</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-10</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-11</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-13</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-14</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-15</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-16</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-10</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-11</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-13</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-14</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-15</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-16</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-2</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-3</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-4</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-5</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-7</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-8</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-ISO8859-9</packagereq>
+                       <packagereq type="default">xorg-font-font-jis-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-cronyx-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-screen-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-winitzki-cyrillic</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-meltho</packagereq>
+                       <packagereq type="default">xorg-font-font-cursor-misc</packagereq>
+                       <packagereq type="default">xorg-font-font-misc-misc-base</packagereq>
+                       <packagereq type="default">xorg-font-font-util</packagereq>
+                       <packagereq type="default">xorg-util-gccmakedep</packagereq>
+                       <packagereq type="default">xorg-util-imake</packagereq>
+                       <packagereq type="default">X11-input-synaptics</packagereq>
+                       <packagereq type="default">xorg-app-bitmap</packagereq>
+                       <packagereq type="default">xorg-app-xditview</packagereq>
+                       <packagereq type="default">xorg-app-xmh</packagereq>
+                       <packagereq type="default">xorg-data-xbitmaps</packagereq>
+                       <packagereq type="default">xorg-app-xkbcomp</packagereq>
+                       <packagereq type="default">xorg-driver-input-keyboard</packagereq>
+                       <packagereq type="default">xorg-driver-input-mouse</packagereq>
+                       <packagereq type="default">xorg-driver-video-v4l</packagereq>
+                       <packagereq type="default">xorg-driver-video-vesa</packagereq>
+                       <packagereq type="default">xorg-driver-video-vga</packagereq>
+                       <packagereq type="default">xorg-app-sessreg</packagereq>
+                       <packagereq type="default">xorg-xserver-server</packagereq>
+                       <packagereq type="default">X11-synaptics</packagereq>
+                       <packagereq type="default">xorg-app-beforelight</packagereq>
+                       <packagereq type="default">xorg-app-ico</packagereq>
+                       <packagereq type="default">xorg-app-listres</packagereq>
+                       <packagereq type="default">xorg-app-oclock</packagereq>
+                       <packagereq type="default">xorg-app-showfont</packagereq>
+                       <packagereq type="default">xorg-app-viewres</packagereq>
+                       <packagereq type="default">xorg-app-x11perf</packagereq>
+                       <packagereq type="default">xorg-app-xbiff</packagereq>
+                       <packagereq type="default">xorg-app-xcalc</packagereq>
+                       <packagereq type="default">xorg-app-xclipboard</packagereq>
+                       <packagereq type="default">xorg-app-xclock</packagereq>
+                       <packagereq type="default">xorg-app-xdbedizzy</packagereq>
+                       <packagereq type="default">xorg-app-xditview</packagereq>
+                       <packagereq type="default">xorg-app-xdriinfo</packagereq>
+                       <packagereq type="default">xorg-app-xedit</packagereq>
+                       <packagereq type="default">xorg-app-xev</packagereq>
+                       <packagereq type="default">xorg-app-xeyes</packagereq>
+                       <packagereq type="default">xorg-app-xfd</packagereq>
+                       <packagereq type="default">xorg-app-xfontsel</packagereq>
+                       <packagereq type="default">xorg-app-xgc</packagereq>
+                       <packagereq type="default">xorg-app-xkill</packagereq>
+                       <packagereq type="default">xorg-app-xload</packagereq>
+                       <packagereq type="default">xorg-app-xlogo</packagereq>
+                       <packagereq type="default">xorg-app-xmag</packagereq>
+                       <packagereq type="default">xorg-app-xman</packagereq>
+                       <packagereq type="default">xorg-app-xmessage</packagereq>
+                       <packagereq type="default">xorg-app-xmh</packagereq>
+                       <packagereq type="default">xorg-app-xmore</packagereq>
+                       <packagereq type="default">xorg-app-xphelloworld</packagereq>
+                       <packagereq type="default">xorg-app-xplsprinters</packagereq>
+                       <packagereq type="default">xorg-app-xpr</packagereq>
+                       <packagereq type="default">xorg-app-xprehashprinterlist</packagereq>
+                       <packagereq type="default">xorg-app-xprop</packagereq>
+                       <packagereq type="default">xorg-app-xtrap</packagereq>
+                       <packagereq type="default">xorg-app-xwininfo</packagereq>
+                       <packagereq type="default">xorg-app-twm</packagereq>
+                       <packagereq type="default">xorg-app-xauth</packagereq>
+                       <packagereq type="default">xorg-app-xfs</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>gnome</id>
+               <name>Very basic GNOME Desktop part</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Very basic GNOME Desktop part</description>
+               <packagelist>
+                       <packagereq type="default">gnome-control-center</packagereq>
+                       <packagereq type="default">gedit2</packagereq>
+                       <packagereq type="default">gnome-menus</packagereq>
+                       <packagereq type="default">gnome-panel</packagereq>
+                       <packagereq type="default">gnome-session</packagereq>
+                       <packagereq type="default">gnome-desktop</packagereq>
+                       <packagereq type="default">gnome-terminal</packagereq>
+                       <packagereq type="default">gnome-themes-Clearlooks</packagereq>
+                       <packagereq type="default">metacity</packagereq>
+                       <packagereq type="default">nautilus</packagereq>
+                       <packagereq type="default">hal</packagereq>
+                       <packagereq type="default">dbus</packagereq>
+                       <packagereq type="default">gdm</packagereq>
+                       <packagereq type="default">dbus-init</packagereq>
+
+               </packagelist>
+       </group>
+
+       <group>
+               <id>gnome_complete</id>
+               <name>More complete GNOME Desktop part</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>More complete GNOME Desktop part</description>
+               <packagelist>
+                       <packagereq type="default">bug-buddy</packagereq>
+                       <packagereq type="default">eog</packagereq>
+                       <packagereq type="default">epiphany</packagereq>
+                       <packagereq type="default">epiphany-extensions</packagereq>
+                       <packagereq type="default">evince</packagereq>
+                       <packagereq type="default">file-roller</packagereq>
+                       <packagereq type="default">gcalctool</packagereq>
+                       <packagereq type="default">gconf-editor</packagereq>
+                       <packagereq type="default">gnome-applets-accessx-status</packagereq>
+                       <packagereq type="default">gnome-applets-battstat</packagereq>
+                       <packagereq type="default">gnome-applets-charpicker</packagereq>
+                       <packagereq type="default">gnome-applets-cpufreq</packagereq>
+                       <packagereq type="optional">gnome-applets-drivemount</packagereq>
+                       <packagereq type="optional">gnome-applets-geyes</packagereq>
+                       <packagereq type="optional">gnome-applets-gtik</packagereq>
+                       <packagereq type="default">gnome-applets-gweather</packagereq>
+                       <packagereq type="default">gnome-applets-keyboard</packagereq>
+                       <packagereq type="optional">gnome-applets-minicommander</packagereq>
+                       <packagereq type="default">gnome-applets-mixer</packagereq>
+                       <packagereq type="optional">gnome-applets-modemlights</packagereq>
+                       <packagereq type="optional">gnome-applets-multiload</packagereq>
+                       <packagereq type="optional">gnome-applets-stickynotes</packagereq>
+                       <packagereq type="default">gnome-applets-trash</packagereq>
+                       <packagereq type="default">gnome-backgrounds</packagereq>
+                       <packagereq type="default">gnome-cups-manager</packagereq>
+                       <packagereq type="default">gnome-keyring-manager</packagereq>
+                       <packagereq type="default">gnome-mail-notification</packagereq>
+                       <packagereq type="optional">gnome-media-cd</packagereq>
+                       <packagereq type="optional">gnome-media-cddb</packagereq>
+                       <packagereq type="default">gnome-media-sound-recorder</packagereq>
+                       <packagereq type="default">gnome-media-volume-control</packagereq>
+                       <packagereq type="optional">gnome-media-vumeter</packagereq>
+                       <packagereq type="default">gnome-menus-editor</packagereq>
+                       <packagereq type="default">gnome-netstatus</packagereq>
+                       <packagereq type="default">gnome-nettool</packagereq>
+                       <packagereq type="default">gnome-system-monitor</packagereq>
+                       <packagereq type="default">gnome-utils-dict</packagereq>
+                       <packagereq type="optional">gnome-utils-floppy</packagereq>
+                       <packagereq type="optional">gnome-utils-logview</packagereq>
+                       <packagereq type="default">gnome-utils-screenshot</packagereq>
+                       <packagereq type="default">gnome-utils-search-tool</packagereq>
+                       <packagereq type="default">gnome-volume-manager</packagereq>
+                       <packagereq type="default">nautilus-cd-burner</packagereq>
+                       <packagereq type="default">rhythmbox</packagereq>
+                       <packagereq type="default">sound-juicer</packagereq>
+                       <packagereq type="default">totem</packagereq>
+                       <packagereq type="default">cheese</packagereq>
+                       <packagereq type="default">xscreensaver-gnome2</packagereq>
+                       <packagereq type="default">yelp</packagereq>
+                       <packagereq type="default">gnome-packagekit</packagereq>
+                       <packagereq type="default">NetworkManager-applet</packagereq>
+                       <packagereq type="default">gstreamer</packagereq>
+                       <packagereq type="default">gstreamer-plugins-base</packagereq>
+                       <packagereq type="default">gstreamer-plugins-good</packagereq>
+                       <packagereq type="default">gstreamer-plugins-bad</packagereq>
+                       <packagereq type="default">gstreamer-plugins-ugly</packagereq>
+                       <packagereq type="default">gstreamer-audiosink-alsa</packagereq>
+                       <packagereq type="default">gstreamer-imagesink-xv</packagereq>
+                       <packagereq type="default">gstreamer-audio-formats</packagereq>
+                       <packagereq type="default">gstreamer-cdaudio</packagereq>
+                       <packagereq type="default">gstreamer-flac</packagereq>
+                       <packagereq type="default">gstreamer-GConf</packagereq>
+                       <packagereq type="default">gstreamer-cdio</packagereq>
+                       <packagereq type="default">gstreamer-lame</packagereq>
+                       <packagereq type="default">gstreamer-mad</packagereq>
+                       <packagereq type="default">gstreamer-mpeg</packagereq>
+                       <packagereq type="default">gstreamer-musicbrainz</packagereq>
+                       <packagereq type="default">gstreamer-soup</packagereq>
+                       <packagereq type="default">gstreamer-taglib</packagereq>
+                       <packagereq type="default">gstreamer-theora</packagereq>
+                       <packagereq type="default">gstreamer-vorbis</packagereq>
+                       <packagereq type="default">gstreamer-video-effects</packagereq>
+                       <packagereq type="default">gstreamer-visualisation</packagereq>
+                       <packagereq type="default">gstreamer-wavpack</packagereq>
+                       <packagereq type="default">gstreamer-x264</packagereq>
+                       <packagereq type="default">gstreamer-ximagesrc</packagereq>
+                       <packagereq type="default">gstreamer-xvid</packagereq>
+                       <packagereq type="default">gstreamer-dvdread</packagereq>
+                       <packagereq type="default">gstreamer-ffmpeg</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>gnome_games</id>
+               <name>GNOME Games</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>GNOME Games</description>
+               <packagelist>
+                       <packagereq type="optional">gnome-games-blackjack</packagereq>
+                       <packagereq type="optional">gnome-games-extra-data-glines</packagereq>
+                       <packagereq type="optional">gnome-games-extra-data-gnobots2</packagereq>
+                       <packagereq type="optional">gnome-games-extra-data-gnometris</packagereq>
+                       <packagereq type="optional">gnome-games-extra-data-iagno</packagereq>
+                       <packagereq type="optional">gnome-games-extra-data-mahjongg</packagereq>
+                       <packagereq type="optional">gnome-games-extra-data-same-gnome</packagereq>
+                       <packagereq type="optional">gnome-games-gataxx</packagereq>
+                       <packagereq type="optional">gnome-games-glines</packagereq>
+                       <packagereq type="optional">gnome-games-gnect</packagereq>
+                       <packagereq type="optional">gnome-games-gnibbles</packagereq>
+                       <packagereq type="optional">gnome-games-gnobots2</packagereq>
+                       <packagereq type="optional">gnome-games-gnometris</packagereq>
+                       <packagereq type="optional">gnome-games-gnomine</packagereq>
+                       <packagereq type="optional">gnome-games-gnotravex</packagereq>
+                       <packagereq type="optional">gnome-games-gnotski</packagereq>
+                       <packagereq type="optional">gnome-games-gtal</packagereq>
+                       <packagereq type="optional">gnome-games-iagno</packagereq>
+                       <packagereq type="optional">gnome-games-mahjongg</packagereq>
+                       <packagereq type="optional">gnome-games-same-gnome</packagereq>
+                       <packagereq type="optional">gnome-games-sol</packagereq>
+                       <packagereq type="optional">gnome-games-stones</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>gnome_themes</id>
+               <name>GNOME Themes and Icons</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>GNOME Themes and Icons</description>
+               <packagelist>
+                       <packagereq type="optional">gnome-theme-aqua</packagereq>
+                       <packagereq type="optional">gnome-theme-Bluecurve</packagereq>
+                       <packagereq type="optional">gnome-theme-Industrial</packagereq>
+                       <packagereq type="optional">gnome-themes-Crux</packagereq>
+                       <packagereq type="optional">gnome-themes-Flat-Blue</packagereq>
+                       <packagereq type="optional">gnome-themes-Glider</packagereq>
+                       <packagereq type="optional">gnome-themes-Grand-Canyon</packagereq>
+                       <packagereq type="optional">gnome-themes-HighContrast</packagereq>
+                       <packagereq type="optional">gnome-themes-HighContrastInverse</packagereq>
+                       <packagereq type="optional">gnome-themes-HighContrastLargePrint</packagereq>
+                       <packagereq type="optional">gnome-themes-HighContrastLargePrintInverse</packagereq>
+                       <packagereq type="optional">gnome-themes-LargePrint</packagereq>
+                       <packagereq type="optional">gnome-themes-LowContrast</packagereq>
+                       <packagereq type="optional">gnome-themes-LowContrastLargePrint</packagereq>
+                       <packagereq type="optional">gnome-themes-Mist</packagereq>
+                       <packagereq type="optional">gnome-themes-Ocean-Dream</packagereq>
+                       <packagereq type="optional">gnome-themes-Sandwish</packagereq>
+                       <packagereq type="optional">gnome-themes-Sandy</packagereq>
+                       <packagereq type="optional">gnome-themes-Simple</packagereq>
+                       <packagereq type="optional">gnome-themes-Smokey</packagereq>
+                       <packagereq type="optional">gnome-themes-Smokey-Blue</packagereq>
+                       <packagereq type="optional">gnome-themes-Smokey-Red</packagereq>
+                       <packagereq type="optional">gnome-themes-Traditional</packagereq>
+                       <packagereq type="optional">gnome-themes-extras-Amaranth</packagereq>
+                       <packagereq type="optional">gnome-themes-extras-Gorilla</packagereq>
+                       <packagereq type="optional">gnome-themes-extras-Lush</packagereq>
+                       <packagereq type="optional">gnome-themes-extras-Nuvola</packagereq>
+                       <packagereq type="optional">gnome-themes-extras-Wasp</packagereq>
+                       <packagereq type="optional">gnome-icons-gartoon</packagereq>
+                       <packagereq type="optional">gnome-icons-gperfection2</packagereq>
+                       <packagereq type="optional">gnome-icons-graphite</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>gnome_office</id>
+               <name>GNOME Office Packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>GNOME Office Packages</description>
+               <packagelist>
+                       <packagereq type="default">abiword</packagereq>
+                       <packagereq type="optional">abiword-clipart</packagereq>
+                       <packagereq type="optional">abiword-plugin-aiksaurus</packagereq>
+                       <packagereq type="optional">abiword-plugin-applix</packagereq>
+                       <packagereq type="optional">abiword-plugin-babelfish</packagereq>
+                       <packagereq type="optional">abiword-plugin-bmp</packagereq>
+                       <packagereq type="optional">abiword-plugin-bz2</packagereq>
+                       <packagereq type="optional">abiword-plugin-capi</packagereq>
+                       <packagereq type="optional">abiword-plugin-clarisworks</packagereq>
+                       <packagereq type="optional">abiword-plugin-collab</packagereq>
+                       <packagereq type="optional">abiword-plugin-command</packagereq>
+                       <packagereq type="optional">abiword-plugin-dash</packagereq>
+                       <packagereq type="optional">abiword-plugin-docbook</packagereq>
+                       <packagereq type="optional">abiword-plugin-eml</packagereq>
+                       <packagereq type="optional">abiword-plugin-freetranslation</packagereq>
+                       <packagereq type="optional">abiword-plugin-gda</packagereq>
+                       <packagereq type="optional">abiword-plugin-gdict</packagereq>
+                       <packagereq type="optional">abiword-plugin-gimp</packagereq>
+                       <packagereq type="optional">abiword-plugin-goffice</packagereq>
+                       <packagereq type="optional">abiword-plugin-google</packagereq>
+                       <packagereq type="optional">abiword-plugin-hancom</packagereq>
+                       <packagereq type="optional">abiword-plugin-hrtext</packagereq>
+                       <packagereq type="optional">abiword-plugin-iscii</packagereq>
+                       <packagereq type="optional">abiword-plugin-jpeg</packagereq>
+                       <packagereq type="optional">abiword-plugin-kword</packagereq>
+                       <packagereq type="optional">abiword-plugin-latex</packagereq>
+                       <packagereq type="optional">abiword-plugin-link-grammar</packagereq>
+                       <packagereq type="optional">abiword-plugin-mathview</packagereq>
+                       <packagereq type="optional">abiword-plugin-mif</packagereq>
+                       <packagereq type="optional">abiword-plugin-mswrite</packagereq>
+                       <packagereq type="optional">abiword-plugin-nroff</packagereq>
+                       <packagereq type="optional">abiword-plugin-opendocument</packagereq>
+                       <packagereq type="optional">abiword-plugin-openwritter</packagereq>
+                       <packagereq type="optional">abiword-plugin-ots</packagereq>
+                       <packagereq type="optional">abiword-plugin-palmdoc</packagereq>
+                       <packagereq type="optional">abiword-plugin-passepartout</packagereq>
+                       <packagereq type="optional">abiword-plugin-pdf</packagereq>
+                       <packagereq type="optional">abiword-plugin-psion</packagereq>
+                       <packagereq type="optional">abiword-plugin-rsvg</packagereq>
+                       <packagereq type="optional">abiword-plugin-scripthappy</packagereq>
+                       <packagereq type="optional">abiword-plugin-sdw</packagereq>
+                       <packagereq type="optional">abiword-plugin-t602</packagereq>
+                       <packagereq type="optional">abiword-plugin-urldict</packagereq>
+                       <packagereq type="optional">abiword-plugin-wikipedia</packagereq>
+                       <packagereq type="optional">abiword-plugin-wmf</packagereq>
+                       <packagereq type="optional">abiword-plugin-wml</packagereq>
+                       <packagereq type="optional">abiword-plugin-wordperfect</packagereq>
+                       <packagereq type="optional">abiword-plugin-xhtml</packagereq>
+                       <packagereq type="optional">abiword-plugin-xslfo</packagereq>
+                       <packagereq type="default">dia</packagereq>
+                       <packagereq type="default">evolution</packagereq>
+                       <packagereq type="default">gimp</packagereq>
+                       <packagereq type="default">glabels</packagereq>
+                       <packagereq type="default">gnome-spell</packagereq>
+                       <packagereq type="default">gnucash</packagereq>
+                       <packagereq type="default">gnumeric</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-applix</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-dif</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-excel</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-gdaif</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-gnomedb</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-gnuoleo</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-html</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-lotus123</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-openoffice</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-paradox</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-perl</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-planperfect</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-psiconv</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-python</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-qpro</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-sample</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-sc</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-sylk</packagereq>
+                       <packagereq type="optional">gnumeric-plugin-xbase</packagereq>
+                       <packagereq type="default">gthumb</packagereq>
+                       <packagereq type="default">Guppi</packagereq>
+                       <packagereq type="default">inkscape</packagereq>
+                       <packagereq type="default">ToutDoux</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>wmaker</id>
+               <name>WindowMaker</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>WindowMaker</description>
+               <packagelist>
+                       <packagereq type="default">WindowMaker</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde</id>
+               <name>Basic KDE part</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Basic KDE part</description>
+               <packagelist>
+                       <packagereq type="default">konqueror</packagereq>
+                       <packagereq type="default">kdelibs</packagereq>
+                       <packagereq type="default">kdebase-core</packagereq>
+                       <packagereq type="default">kdebase-desktop</packagereq>
+                       <packagereq type="default">kdebase-desktop-libs</packagereq>
+                       <packagereq type="default">kdebase-konsole</packagereq>
+                       <packagereq type="default">kdebase-kpager</packagereq>
+                       <packagereq type="default">kdebase-kwrite</packagereq>
+                       <packagereq type="default">kdebase-screensavers</packagereq>
+                       <packagereq type="default">kdebase-common-konsole</packagereq>
+                       <packagereq type="default">kdebase-common-filemanagement</packagereq>
+                       <packagereq type="default">kdebase-colorschemes</packagereq>
+                       <packagereq type="default">kde-logoutpic-default</packagereq>
+                       <packagereq type="default">kde-splash-Default</packagereq>
+                       <packagereq type="optional">kdm</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_extras1</id>
+               <name>KDE additional packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE additional packages</description>
+               <packagelist>
+                       <packagereq type="default">kdeaddons-ark</packagereq>
+                       <packagereq type="default">kdeaddons-atlantikdesigner</packagereq>
+                       <packagereq type="default">kdeaddons-fsview</packagereq>
+                       <packagereq type="default">kdeaddons-kaddressbook-plugins</packagereq>
+                       <packagereq type="default">kdeaddons-kate</packagereq>
+                       <packagereq type="default">kdeaddons-kicker</packagereq>
+                       <packagereq type="default">kdeaddons-knewsticker</packagereq>
+                       <packagereq type="default">kdeaddons-konqueror</packagereq>
+                       <packagereq type="default">kdeaddons-ksig</packagereq>
+                       <packagereq type="default">kdeaddons-lnkforward</packagereq>
+                       <packagereq type="default">kdeaddons-noatun</packagereq>
+                       <packagereq type="default">kdeartwork-kworldclock-3.5.0-1</packagereq>
+                       <packagereq type="default">kdeartwork-screensavers-3.5.0-1</packagereq>
+                       <packagereq type="default">kdeartwork-sounds-3.5.0-1</packagereq>
+                       <packagereq type="default">kdeartwork-wallpapers-3.5.0-1</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_extras2</id>
+               <name>KDE additional packages2</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE additional packages2</description>
+               <packagelist>
+                       <packagereq type="default">kdeutils-ark</packagereq>
+                       <packagereq type="default">kdeutils-kcalc</packagereq>
+                       <packagereq type="default">kdeutils-kcharselect</packagereq>
+                       <packagereq type="default">kdeutils-kdelirc</packagereq>
+                       <packagereq type="default">kdeutils-kdessh</packagereq>
+                       <packagereq type="default">kdeutils-kdf</packagereq>
+                       <packagereq type="default">kdeutils-kedit</packagereq>
+                       <packagereq type="default">kdeutils-kfloppy</packagereq>
+                       <packagereq type="default">kdeutils-kgpg</packagereq>
+                       <packagereq type="default">kdeutils-khexedit</packagereq>
+                       <packagereq type="default">kdeutils-kjots</packagereq>
+                       <packagereq type="default">kdeutils-klaptopdaemon</packagereq>
+                       <packagereq type="default">kdeutils-kmilo</packagereq>
+                       <packagereq type="default">kdeutils-kmilo-thinkpad</packagereq>
+                       <packagereq type="default">kdeutils-kregexpeditor</packagereq>
+                       <packagereq type="default">kdeutils-ksim</packagereq>
+                       <packagereq type="default">kdeutils-ktimer</packagereq>
+                       <packagereq type="default">kdeutils-kwalletmanager</packagereq>
+                       <packagereq type="default">kdeutils-superkaramba</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_kdepim</id>
+               <name>KDE PIM Packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE PIM Packages</description>
+               <packagelist>
+                       <packagereq type="default">kdepim</packagereq>
+                       <packagereq type="default">kdepim-apidocs</packagereq>
+                       <packagereq type="default">kdepim-kaddressbook</packagereq>
+                       <packagereq type="default">kdepim-kalarm</packagereq>
+                       <packagereq type="default">kdepim-kandy</packagereq>
+                       <packagereq type="default">kdepim-karm</packagereq>
+                       <packagereq type="default">kdepim-kmail</packagereq>
+                       <packagereq type="default">kdepim-knode</packagereq>
+                       <packagereq type="default">kdepim-knotes</packagereq>
+                       <packagereq type="default">kdepim-konsolekalendar</packagereq>
+                       <packagereq type="default">kdepim-korn</packagereq>
+                       <packagereq type="default">kdepim-kpilot</packagereq>
+                       <packagereq type="default">kdepim-libs</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_kdeedu</id>
+               <name>KDE Edu Packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE Edu Packages</description>
+               <packagelist>
+                       <packagereq type="default">kdeedu-blinken</packagereq>
+                       <packagereq type="default">kdeedu-kalzium</packagereq>
+                       <packagereq type="default">kdeedu-kanagram</packagereq>
+                       <packagereq type="default">kdeedu-kbruch</packagereq>
+                       <packagereq type="default">kdeedu-keduca</packagereq>
+                       <packagereq type="default">kdeedu-kgeography</packagereq>
+                       <packagereq type="default">kdeedu-khangman</packagereq>
+                       <packagereq type="default">kdeedu-kig</packagereq>
+                       <packagereq type="default">kdeedu-kiten</packagereq>
+                       <packagereq type="default">kdeedu-klatin</packagereq>
+                       <packagereq type="default">kdeedu-klettres</packagereq>
+                       <packagereq type="default">kdeedu-kmplot</packagereq>
+                       <packagereq type="default">kdeedu-kpercentage</packagereq>
+                       <packagereq type="default">kdeedu-kstars</packagereq>
+                       <packagereq type="default">kdeedu-ktouch</packagereq>
+                       <packagereq type="default">kdeedu-kturtle</packagereq>
+                       <packagereq type="default">kdeedu-kverbos</packagereq>
+                       <packagereq type="default">kdeedu-kvoctrain</packagereq>
+                       <packagereq type="default">kdeedu-kwordquiz</packagereq>
+                       <packagereq type="default">kdeedu-libextdate</packagereq>
+                       <packagereq type="default">kdeedu-libkdeeducore</packagereq>
+                       <packagereq type="default">kdeedu-libkdeeduplot</packagereq>
+                       <packagereq type="default">kdeedu-libkdeeduui</packagereq>
+                       <packagereq type="default">kdeedu-libkiten</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_multimedia</id>
+               <name>KDE Multimedia Packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE Multimedia Packages</description>
+               <packagelist>
+                       <packagereq type="default">kdemultimedia-akode</packagereq>
+                       <packagereq type="default">kdemultimedia-arts</packagereq>
+                       <packagereq type="default">kdemultimedia-artsbuilder</packagereq>
+                       <packagereq type="default">kdemultimedia-artscontrol</packagereq>
+                       <packagereq type="default">kdemultimedia-artsplugin-xine</packagereq>
+                       <packagereq type="default">kdemultimedia-audiocd</packagereq>
+                       <packagereq type="default">kdemultimedia-cddb</packagereq>
+                       <packagereq type="default">kdemultimedia-juk</packagereq>
+                       <packagereq type="default">kdemultimedia-kaboodle</packagereq>
+                       <packagereq type="default">kdemultimedia-kappfinder</packagereq>
+                       <packagereq type="default">kdemultimedia-kaudiocreator</packagereq>
+                       <packagereq type="default">kdemultimedia-kfile</packagereq>
+                       <packagereq type="default">kdemultimedia-kmid</packagereq>
+                       <packagereq type="default">kdemultimedia-kmix</packagereq>
+                       <packagereq type="default">kdemultimedia-krec</packagereq>
+                       <packagereq type="default">kdemultimedia-kscd</packagereq>
+                       <packagereq type="default">kdemultimedia-libkcddb</packagereq>
+                       <packagereq type="default">kdemultimedia-mpeglib</packagereq>
+                       <packagereq type="default">kdemultimedia-mpeglib-examples</packagereq>
+                       <packagereq type="default">kdemultimedia-noatun</packagereq>
+                       <packagereq type="default">kdemultimedia-noatun-libs</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_koffice</id>
+               <name>KOffice Packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KOffice Packages</description>
+               <packagelist>
+                       <packagereq type="default">koffice-apidocs</packagereq>
+                       <packagereq type="default">koffice-common</packagereq>
+                       <packagereq type="default">koffice-karbon</packagereq>
+                       <packagereq type="default">koffice-kchart</packagereq>
+                       <packagereq type="default">koffice-kexi</packagereq>
+                       <packagereq type="default">koffice-kformula</packagereq>
+                       <packagereq type="default">koffice-kivio</packagereq>
+                       <packagereq type="default">koffice-kpresenter</packagereq>
+                       <packagereq type="default">koffice-krita</packagereq>
+                       <packagereq type="default">koffice-kspread</packagereq>
+                       <packagereq type="default">koffice-kugar</packagereq>
+                       <packagereq type="default">koffice-kword</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_network</id>
+               <name>KDE Network Packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE Network Packages</description>
+               <packagelist>
+                       <packagereq type="default">kdenetwork-filesharing</packagereq>
+                       <packagereq type="default">kdenetwork-kdict</packagereq>
+                       <packagereq type="default">kdenetwork-kdnssd</packagereq>
+                       <packagereq type="default">kdenetwork-kfile-torrent</packagereq>
+                       <packagereq type="default">kdenetwork-kget</packagereq>
+                       <packagereq type="default">kdenetwork-kinetd</packagereq>
+                       <packagereq type="default">kdenetwork-knewsticker</packagereq>
+                       <packagereq type="default">kdenetwork-kopete</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-aim</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-gg</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-groupwise</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-icq</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-irc</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-jabber</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-meanwhile</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-msn</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-sms</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-winpopup</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-protocol-yahoo</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-alias</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-autoreplace</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-avdeviceconfig</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-conectionstatus</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-contactnotes</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-cryptography</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-highlight</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-history</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-latex</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-nowlistening</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-smpppdcs</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-texteffect</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-translator</packagereq>
+                       <packagereq type="default">kdenetwork-kopete-tool-webpresence</packagereq>
+                       <packagereq type="default">kdenetwork-kpf</packagereq>
+                       <packagereq type="default">kdenetwork-kppp</packagereq>
+                       <packagereq type="default">kdenetwork-krfb</packagereq>
+                       <packagereq type="default">kdenetwork-ksirc</packagereq>
+                       <packagereq type="default">kdenetwork-ktalkd</packagereq>
+                       <packagereq type="default">kdenetwork-kwifimanager</packagereq>
+                       <packagereq type="default">kdenetwork-lanbrowser</packagereq>
+                       <packagereq type="default">kdenetwork-libkopete</packagereq>
+                       <packagereq type="default">kdenetwork-libkopete_msn</packagereq>
+                       <packagereq type="default">kdenetwork-libkopete_oscar</packagereq>
+                       <packagereq type="default">kdenetwork-libkopete_videodevice</packagereq>
+                       <packagereq type="default">kdenetwork-librss</packagereq>
+                       <packagereq type="default">kdenetwork-rss</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_graphics</id>
+               <name>KDE Graphics</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE Graphics</description>
+               <packagelist>
+                       <packagereq type="default">kdegraphics-daemonwatcher</packagereq>
+                       <packagereq type="default">kdegraphics-kamera</packagereq>
+                       <packagereq type="default">kdegraphics-kcolorchooser</packagereq>
+                       <packagereq type="default">kdegraphics-kcoloredit</packagereq>
+                       <packagereq type="default">kdegraphics-kdvi</packagereq>
+                       <packagereq type="default">kdegraphics-kfax</packagereq>
+                       <packagereq type="default">kdegraphics-kfile</packagereq>
+                       <packagereq type="default">kdegraphics-kgamma</packagereq>
+                       <packagereq type="default">kdegraphics-kghostview</packagereq>
+                       <packagereq type="default">kdegraphics-kiconedit</packagereq>
+                       <packagereq type="default">kdegraphics-kmrml</packagereq>
+                       <packagereq type="default">kdegraphics-kolourpaint</packagereq>
+                       <packagereq type="default">kdegraphics-kooka</packagereq>
+                       <packagereq type="default">kdegraphics-kpdf</packagereq>
+                       <packagereq type="default">kdegraphics-kpovmodeler</packagereq>
+                       <packagereq type="default">kdegraphics-kruler</packagereq>
+                       <packagereq type="default">kdegraphics-ksnapshot</packagereq>
+                       <packagereq type="default">kdegraphics-ksvg</packagereq>
+                       <packagereq type="default">kdegraphics-kuickshow</packagereq>
+                       <packagereq type="default">kdegraphics-kview</packagereq>
+                       <packagereq type="default">kdegraphics-kviewshell</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_admin</id>
+               <name>KDE Admin packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE Admin packages</description>
+               <packagelist>
+                       <packagereq type="default">kdeadmin-kcmlilo</packagereq>
+                       <packagereq type="default">kdeadmin-kcron</packagereq>
+                       <packagereq type="default">kdeadmin-kdat</packagereq>
+                       <packagereq type="default">kdeadmin-knetworkconf</packagereq>
+                       <packagereq type="default">kdeadmin-kpackage</packagereq>
+                       <packagereq type="default">kdeadmin-ksysv</packagereq>
+                       <packagereq type="default">kdeadmin-kuser</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_games</id>
+               <name>Games for KDE</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Games for KDE</description>
+               <packagelist>
+                       <packagereq type="default">kdegames</packagereq>
+                       <packagereq type="optional">kdegames-apidocs</packagereq>
+                       <packagereq type="optional">kdegames-atlantik</packagereq>
+                       <packagereq type="optional">kdegames-carddecks</packagereq>
+                       <packagereq type="optional">kdegames-kasteroids</packagereq>
+                       <packagereq type="optional">kdegames-katomic</packagereq>
+                       <packagereq type="optional">kdegames-kbackgammon</packagereq>
+                       <packagereq type="optional">kdegames-kbattleship</packagereq>
+                       <packagereq type="optional">kdegames-kblackbox</packagereq>
+                       <packagereq type="optional">kdegames-kbounce</packagereq>
+                       <packagereq type="optional">kdegames-kenolaba</packagereq>
+                       <packagereq type="optional">kdegames-kfouleggs</packagereq>
+                       <packagereq type="optional">kdegames-kgoldrunner</packagereq>
+                       <packagereq type="optional">kdegames-kjumpingcube</packagereq>
+                       <packagereq type="optional">kdegames-klickety</packagereq>
+                       <packagereq type="optional">kdegames-klines</packagereq>
+                       <packagereq type="optional">kdegames-kmahjongg</packagereq>
+                       <packagereq type="optional">kdegames-kmines</packagereq>
+                       <packagereq type="optional">kdegames-knetwalk</packagereq>
+                       <packagereq type="optional">kdegames-kolf</packagereq>
+                       <packagereq type="optional">kdegames-konquest</packagereq>
+                       <packagereq type="optional">kdegames-kpat</packagereq>
+                       <packagereq type="optional">kdegames-kpoker</packagereq>
+                       <packagereq type="optional">kdegames-kreversi</packagereq>
+                       <packagereq type="optional">kdegames-ksame</packagereq>
+                       <packagereq type="optional">kdegames-kshisen</packagereq>
+                       <packagereq type="optional">kdegames-ksirtet</packagereq>
+                       <packagereq type="optional">kdegames-ksmiletris</packagereq>
+                       <packagereq type="optional">kdegames-ksnake</packagereq>
+                       <packagereq type="optional">kdegames-ksokoban</packagereq>
+                       <packagereq type="optional">kdegames-kspaceduel</packagereq>
+                       <packagereq type="optional">kdegames-ktron</packagereq>
+                       <packagereq type="optional">kdegames-ktuberling</packagereq>
+                       <packagereq type="optional">kdegames-kwin4</packagereq>
+                       <packagereq type="optional">kdegames-lskat</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_accessibility</id>
+               <name>KDE Accessibility packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE Accessibility packages</description>
+               <packagelist>
+                       <packagereq type="default">kdeaccessibility-kbstateapplet</packagereq>
+                       <packagereq type="default">kdeaccessibility-kmag</packagereq>
+                       <packagereq type="default">kdeaccessibility-kmousetool</packagereq>
+                       <packagereq type="default">kdeaccessibility-kmouth</packagereq>
+                       <packagereq type="default">kdeaccessibility-ksayit</packagereq>
+                       <packagereq type="default">kdeaccessibility-kttsd</packagereq>
+                       <packagereq type="default">kdeaccessibility-kttsd-akode</packagereq>
+                       <packagereq type="default">kdeaccessibility-kttsd-gstreamer</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_look</id>
+               <name>Packages for changing KDE look</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Packages for changing KDE look</description>
+               <packagelist>
+                       <packagereq type="default">kde-decoration-ActiveHeart</packagereq>
+                       <packagereq type="default">kde-decoration-alloy</packagereq>
+                       <packagereq type="default">kde-decoration-aqua</packagereq>
+                       <packagereq type="default">kde-decoration-b2</packagereq>
+                       <packagereq type="default">kde-decoration-baghira</packagereq>
+                       <packagereq type="default">kde-decoration-cde</packagereq>
+                       <packagereq type="default">kde-decoration-crystal</packagereq>
+                       <packagereq type="default">kde-decoration-dotcurve</packagereq>
+                       <packagereq type="default">kde-decoration-fahrenheit</packagereq>
+                       <packagereq type="default">kde-decoration-glow</packagereq>
+                       <packagereq type="default">kde-decoration-gorilla</packagereq>
+                       <packagereq type="default">kde-decoration-icewm</packagereq>
+                       <packagereq type="default">kde-decoration-icewm-Suedstern</packagereq>
+                       <packagereq type="default">kde-decoration-icewm-Wasp</packagereq>
+                       <packagereq type="default">kde-decoration-icewm-xp</packagereq>
+                       <packagereq type="default">kde-decoration-kde1</packagereq>
+                       <packagereq type="default">kde-decoration-keramik</packagereq>
+                       <packagereq type="default">kde-decoration-kstep</packagereq>
+                       <packagereq type="default">kde-decoration-laptop</packagereq>
+                       <packagereq type="default">kde-decoration-modernsys</packagereq>
+                       <packagereq type="default">kde-decoration-nvidia</packagereq>
+                       <packagereq type="default">kde-decoration-openlook</packagereq>
+                       <packagereq type="default">kde-decoration-quartz</packagereq>
+                       <packagereq type="default">kde-decoration-redmond</packagereq>
+                       <packagereq type="default">kde-decoration-ridge</packagereq>
+                       <packagereq type="default">kde-decoration-riscos</packagereq>
+                       <packagereq type="default">kde-decoration-smoothblend</packagereq>
+                       <packagereq type="default">kde-decoration-system</packagereq>
+                       <packagereq type="default">kde-decoration-thinkeramik</packagereq>
+                       <packagereq type="default">kde-decoration-web</packagereq>
+                       <packagereq type="default">kde-colorscheme-ActiveHeart</packagereq>
+                       <packagereq type="default">kde-colorscheme-alloy</packagereq>
+                       <packagereq type="default">kde-colorscheme-baghira</packagereq>
+                       <packagereq type="default">kde-colorscheme-gorilla</packagereq>
+                       <packagereq type="default">kde-colorscheme-konx</packagereq>
+                       <packagereq type="default">kde-colorscheme-nvidia</packagereq>
+                       <packagereq type="default">kde-colorscheme-ridge</packagereq>
+                       <packagereq type="default">kde-colorscheme-Suedstern</packagereq>
+                       <packagereq type="default">kde-colorscheme-thinkeramik</packagereq>
+                       <packagereq type="default">kde-colorscheme-Wasp</packagereq>
+                       <packagereq type="default">kde-colorscheme-Wasp-activeheart</packagereq>
+                       <packagereq type="default">kde-colorscheme-Wasp-thinkeramik</packagereq>
+                       <packagereq type="default">kde-icons-amaranth</packagereq>
+                       <packagereq type="default">kde-icons-amaranth-althaea</packagereq>
+                       <packagereq type="default">kde-icons-aqua</packagereq>
+                       <packagereq type="default">kde-icons-aquafusion</packagereq>
+                       <packagereq type="default">kde-icons-gorilla</packagereq>
+                       <packagereq type="default">kde-icons-ikons</packagereq>
+                       <packagereq type="default">kde-icons-kdeclassic</packagereq>
+                       <packagereq type="default">kde-icons-kids</packagereq>
+                       <packagereq type="default">kde-icons-Locolor</packagereq>
+                       <packagereq type="default">kde-icons-mono</packagereq>
+                       <packagereq type="default">kde-icons-noia</packagereq>
+                       <packagereq type="default">kde-icons-Nuvola</packagereq>
+                       <packagereq type="default">kde-icons-outline</packagereq>
+                       <packagereq type="default">kde-icons-Realistic</packagereq>
+                       <packagereq type="default">kde-icons-slick</packagereq>
+                       <packagereq type="default">kde-icons-sparkling</packagereq>
+                       <packagereq type="default">kde-icons-Suedstern</packagereq>
+                       <packagereq type="default">kde-icons-umicons</packagereq>
+                       <packagereq type="default">kde-icons-Wasp</packagereq>
+                       <packagereq type="default">kde-icons-xp</packagereq>
+                       <packagereq type="default">kde-splash-ac-chemician</packagereq>
+                       <packagereq type="default">kde-splash-ac-oiler</packagereq>
+                       <packagereq type="default">kde-splash-blue-bend</packagereq>
+                       <packagereq type="default">kde-splash-BlueTribe</packagereq>
+                       <packagereq type="default">kde-splash-Clock_K</packagereq>
+                       <packagereq type="default">kde-splash-Gears</packagereq>
+                       <packagereq type="default">kde-splash-gears_white</packagereq>
+                       <packagereq type="default">kde-splash-GNU_CooL</packagereq>
+                       <packagereq type="default">kde-splash-GNU_cool2</packagereq>
+                       <packagereq type="default">kde-splash-gorilla</packagereq>
+                       <packagereq type="default">kde-splash-heregear</packagereq>
+                       <packagereq type="default">kde-splash-heregear-moodin</packagereq>
+                       <packagereq type="default">kde-splash-KDEGirl</packagereq>
+                       <packagereq type="default">kde-splash-keramik</packagereq>
+                       <packagereq type="default">kde-splash-Lila</packagereq>
+                       <packagereq type="default">kde-splash-login-scan-splash</packagereq>
+                       <packagereq type="default">kde-splash-moodin</packagereq>
+                       <packagereq type="default">kde-splash-MoonKDE</packagereq>
+                       <packagereq type="default">kde-splash-NWN</packagereq>
+                       <packagereq type="default">kde-splash-oldtimesplash</packagereq>
+                       <packagereq type="default">kde-splash-PhyNet</packagereq>
+                       <packagereq type="default">kde-splash-Rambo-tux</packagereq>
+                       <packagereq type="default">kde-splash-safari</packagereq>
+                       <packagereq type="default">kde-splash-Suedstern</packagereq>
+                       <packagereq type="default">kde-splash-think-linux</packagereq>
+                       <packagereq type="default">kde-splash-Wasp</packagereq>
+                       <packagereq type="default">kde-style-ActiveHeart</packagereq>
+                       <packagereq type="default">kde-style-alloy</packagereq>
+                       <packagereq type="default">kde-style-aqua</packagereq>
+                       <packagereq type="default">kde-style-baghira</packagereq>
+                       <packagereq type="default">kde-style-comix</packagereq>
+                       <packagereq type="default">kde-style-dotnet</packagereq>
+                       <packagereq type="default">kde-style-konx</packagereq>
+                       <packagereq type="default">kde-style-phase</packagereq>
+                       <packagereq type="default">kde-style-thinkeramik</packagereq>
+                       <packagereq type="default">kde-style-xp</packagereq>
+                       <packagereq type="default">kde-theme-ActiveHeart</packagereq>
+                       <packagereq type="default">kde-theme-alloy</packagereq>
+                       <packagereq type="default">kde-theme-aqua</packagereq>
+                       <packagereq type="default">kde-theme-baghira</packagereq>
+                       <packagereq type="default">kde-theme-Bluecurve</packagereq>
+                       <packagereq type="default">kde-theme-gorilla</packagereq>
+                       <packagereq type="default">kde-theme-konx</packagereq>
+                       <packagereq type="default">kde-theme-Suedstern</packagereq>
+                       <packagereq type="default">kde-theme-thinkeramik</packagereq>
+                       <packagereq type="default">kde-theme-umicons</packagereq>
+                       <packagereq type="default">kde-theme-Wasp</packagereq>
+                       <packagereq type="default">kde-theme-xp</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>kde_devel</id>
+               <name>KDE development tools</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>KDE development tools</description>
+               <packagelist>
+                       <packagereq type="default">kdevelop</packagereq>
+                       <packagereq type="default">kdesdk-cervisia</packagereq>
+                       <packagereq type="default">kdesdk-completions-bash</packagereq>
+                       <packagereq type="default">kdesdk-completions-zsh</packagereq>
+                       <packagereq type="default">kdesdk-emacs</packagereq>
+                       <packagereq type="default">kdesdk-kapptemplate</packagereq>
+                       <packagereq type="default">kdesdk-kbabel</packagereq>
+                       <packagereq type="default">kdesdk-kbugbuster</packagereq>
+                       <packagereq type="default">kdesdk-kcachegrind</packagereq>
+                       <packagereq type="default">kdesdk-kde-resource-bugzilla</packagereq>
+                       <packagereq type="default">kdesdk-kde-resource-kdeaccounts</packagereq>
+                       <packagereq type="default">kdesdk-kfile</packagereq>
+                       <packagereq type="default">kdesdk-kmtrace</packagereq>
+                       <packagereq type="default">kdesdk-kompare</packagereq>
+                       <packagereq type="default">kdesdk-kprofilemethod</packagereq>
+                       <packagereq type="default">kdesdk-kspy</packagereq>
+                       <packagereq type="default">kdesdk-kstartperf</packagereq>
+                       <packagereq type="default">kdesdk-kuiviewer</packagereq>
+                       <packagereq type="default">kdesdk-kunittest</packagereq>
+                       <packagereq type="default">kdesdk-libcvsservice</packagereq>
+                       <packagereq type="default">kdesdk-pallette-gimp</packagereq>
+                       <packagereq type="default">kdesdk-pallette-xpaint</packagereq>
+                       <packagereq type="default">kdesdk-po2xml</packagereq>
+                       <packagereq type="default">kdesdk-scheck</packagereq>
+                       <packagereq type="default">kdesdk-scripts-cvs</packagereq>
+                       <packagereq type="default">kdesdk-scripts-doc</packagereq>
+                       <packagereq type="default">kdesdk-scripts-kdekillall</packagereq>
+                       <packagereq type="default">kdesdk-umbrello</packagereq>
+                       <packagereq type="default">kdesdk-xemacs</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>icewm</id>
+               <name>Basic IceWM</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Basic IceWM</description>
+               <packagelist>
+                       <packagereq type="default">icewm</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>science_apps</id>
+               <name>Scientific applications</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Scientific applications</description>
+               <packagelist>
+                       <packagereq type="default">parigp</packagereq>
+                       <packagereq type="default">calc</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>science_libs</id>
+               <name>Scientific libraries</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Scientific libraries</description>
+               <packagelist>
+                       <packagereq type="default">gmp</packagereq>
+                       <packagereq type="default">gsl</packagereq>
+                       <packagereq type="default">fftw</packagereq>
+                       <packagereq type="default">pari</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>science_devel</id>
+               <name>Scientific development packages</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Scientific development packages</description>
+               <packagelist>
+                       <packagereq type="default">gmp-devel</packagereq>
+                       <packagereq type="default">gsl-devel</packagereq>
+                       <packagereq type="default">gsl-progs</packagereq>
+                       <packagereq type="default">fftw-devel</packagereq>
+                       <packagereq type="default">pari-devel</packagereq>
+                       <packagereq type="default">parigp-gp2c</packagereq>
+                       <packagereq type="default">gcc-g77</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>games</id>
+               <name>Textmode games</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Textmode games</description>
+               <packagelist>
+                       <packagereq type="default">bsd-games</packagereq>
+                       <packagereq type="default">fortune-mod</packagereq>
+                       <packagereq type="default">fortune-mod-bofh-excuses</packagereq>
+               </packagelist>
+       </group>
+
+       <group>
+               <id>games_x</id>
+               <name>Games for X.</name>
+               <default>false</default>
+               <uservisible>true</uservisible>
+               <description>Games for X.</description>
+               <packagelist>
+                       <packagereq type="default">xbill</packagereq>
+                       <packagereq type="default">prboom</packagereq>
+                       <packagereq type="default">xpenguins</packagereq>
+                       <packagereq type="default">xsnow</packagereq>
+                       <packagereq type="default">bzflag</packagereq>
+                       <packagereq type="default">tuxkart</packagereq>
+                       <packagereq type="default">tuxracer</packagereq>
+                       <packagereq type="default">xmoto</packagereq>
+               </packagelist>
+       </group>
+
+</comps>
diff --git a/shell/bash-completion.sh b/shell/bash-completion.sh
new file mode 100644 (file)
index 0000000..ca05c32
--- /dev/null
@@ -0,0 +1,125 @@
+# various completions for pld-ftp-admin tools
+# Author: Elan Ruusamäe <glen@pld-linux.org>
+
+# return list of pld ftp trees
+_pfa_tree() {
+    local cur="$1"
+    if [ -z "$PFA_TREES" ]; then
+        local d
+        local -a trees
+        for d in  ~/ftp/*/; do
+            d=${d%/}
+            d=${d##*/}
+            PFA_TREES+=" $d"
+        done
+        # kill leading space
+        PFA_TREES=${PFA_TREES# }
+    fi
+
+    COMPREPLY=( $( compgen -W "$PFA_TREES" -- "$cur" ) )
+}
+
+# return list of files in a tree
+_pfa_tree_files() {
+    local tree="$1" cur="$2" file i
+    local dir=~/ftp/$tree/SRPMS/.metadata
+
+    # generate reply from dir
+    COMPREPLY=( $( compgen -f -X "*.(src.rpm.info)" -- "$dir/$cur" ) )
+    # filter out dirname from results
+    for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+        file=${COMPREPLY[$i]##*/}
+        COMPREPLY[$i]=$file
+    done
+}
+
+have pfa-genindex &&
+_pfa-genindex()
+{
+    local cur
+
+    COMPREPLY=()
+    _get_comp_words_by_ref cur
+
+    if [[ "$cur" == -* ]]; then
+        COMPREPLY=( $( compgen -W '\
+            -q --quiet --index \
+            --nopoldek --noyum --norpmrepo \
+            --poldek --yum --rpmrepo \
+            ' \
+            -- "$cur" ) )
+    else
+        _pfa_tree "$cur"
+    fi
+} &&
+complete -F _pfa-genindex pfa-genindex
+
+have pfa-lintpkg &&
+_pfa-lintpkg()
+{
+    local cur arg
+
+    COMPREPLY=()
+    _get_comp_words_by_ref cur
+
+    if [[ "$cur" == -* ]]; then
+        COMPREPLY=( $( compgen -W '-q -s --quiet' -- "$cur" ) )
+    else
+        # The first argument is an tree; the rest are files in a dir
+        _count_args :
+
+        if [[ $args == 1 ]]; then
+            _pfa_tree "$cur"
+        else
+            _get_first_arg
+            _pfa_tree_files "$arg" "$cur"
+        fi
+    fi
+} &&
+complete -F _pfa-lintpkg pfa-lintpkg
+
+have pfa-signpkg &&
+_pfa-signpkg()
+{
+    local cur
+
+    COMPREPLY=()
+    _get_comp_words_by_ref cur
+
+    # The first argument is an tree; the rest are files in a dir
+    _count_args :
+
+    if [[ $args == 1 ]]; then
+        _pfa_tree "$cur"
+    else
+        _pfa_tree_files "${COMP_WORDS[1]}" "$cur"
+    fi
+} &&
+complete -F _pfa-signpkg pfa-signpkg pfa-rmpkg
+
+have pfa-mvpkg &&
+_pfa-mvpkg()
+{
+    local cur
+
+    COMPREPLY=()
+    _get_comp_words_by_ref cur
+
+    # The first two arguments are tree names; the rest are files in a dir
+    _count_args :
+
+    if [[ $args == 1 ]] || [[ $args == 2 ]]; then
+        _pfa_tree "$cur"
+    else
+        _pfa_tree_files "${COMP_WORDS[1]}" "$cur"
+    fi
+} &&
+complete -F _pfa-mvpkg pfa-mvpkg pfa-testmvpkg
+
+# Local variables:
+# mode: shell-script
+# sh-basic-offset: 4
+# sh-indent-comment: t
+# indent-tabs-mode: nil
+# End:
+# ex: ts=4 sw=4 et filetype=sh
diff --git a/shell/bash_profile b/shell/bash_profile
new file mode 100644 (file)
index 0000000..8221875
--- /dev/null
@@ -0,0 +1,46 @@
+# .bash_profile - file executed when logging in
+
+# identify via remote addr
+case "${SSH_CLIENT%% *}" in
+195.222.9.201) # glen ;)
+       export FTPADM=glen
+       ;;
+193.0.96.*|2001:6a0:5001:*) # baggins
+       export FTPADM=baggins
+       ;;
+esac
+
+# identify via terminal last login (su, sudo)
+if [ -z "$FTPADM" ]; then
+       LAST_TTY=$(tty)
+       LAST_LOGIN=$(last -if /var/run/utmp | awk -vtty="${LAST_TTY#/dev/}" '$2 == tty && $0 ~ /still logged in/ { print $1; exit; }')
+       export FTPADM=$LAST_LOGIN
+       unset LAST_TTY LAST_LOGIN
+fi
+
+# per-admin defaults
+case "$FTPADM" in
+glen)
+       export TZ=EET
+       case $(id -un) in
+       pldth)
+               echo -ne "\033kth@ftp\033\\"
+               ;;
+       fpldac|pldac)
+               echo -ne "\033kac@ftp\033\\"
+               ;;
+       esac
+       ;;
+'')
+       export FTPADM=$USER
+       ;;
+esac
+
+# let each ftp admin have own history file
+if [ "$FTPADM" ]; then
+       export HISTFILE=$HOME/.history-$FTPADM
+       export CDHISTFILE=$HOME/.cd_history-$FTPADM
+fi
+export EDITOR=vim
+
+PATH=$PATH:~/pld-ftp-admin/bin
diff --git a/shell/bashrc b/shell/bashrc
new file mode 100644 (file)
index 0000000..09ca3f5
--- /dev/null
@@ -0,0 +1,37 @@
+# .bashrc - file executed when executing bash
+
+# ftp admin aliases
+alias rmpkg=~/pld-ftp-admin/bin/pfa-rmpkg
+alias gen-indexes=~/pld-ftp-admin/bin/pfa-genindex
+alias dump-locks=~/pld-ftp-admin/bin/pfa-dump-locks
+alias signpkg=~/pld-ftp-admin/bin/pfa-signpkg
+alias mvpkg=~/pld-ftp-admin/bin/pfa-mvpkg
+alias testmvpkg=~/pld-ftp-admin/bin/pfa-testmvpkg
+alias lintpkg=~/pld-ftp-admin/bin/pfa-lintpkg
+
+# usual aliases to make your terminal usable
+alias ls='ls --color=auto -BFN --show-control-chars'
+alias l='ls -lh'
+alias la='ls -la'
+alias du='du -h'
+alias df='df -Th'
+alias vi='vim'
+alias h='history $(($LINES-6))'
+
+# glen ;)
+if [ "$FTPADM" = "glen" ]; then
+       [ -f /usr/share/okas/bashrc ] && . /usr/share/okas/bashrc
+fi
+
+unlocktree() {
+       for tree in "$@"; do
+       (
+       cd ~/pld-ftp-admin/modules
+       python -c "
+import ftpio
+ftpio.connect()
+ftpio.unlock('$tree')
+"
+       )
+       done
+}
diff --git a/ucred/make.sh b/ucred/make.sh
new file mode 100755 (executable)
index 0000000..74c5f6f
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+python setup.py build --build-lib ../modules
+
diff --git a/ucred/setup.py b/ucred/setup.py
new file mode 100644 (file)
index 0000000..1730c01
--- /dev/null
@@ -0,0 +1,4 @@
+from distutils.core import setup, Extension
+setup(name="ucred",#version="1.0",
+      ext_modules=[Extension("ucred", ["ucred.c"])])
+
diff --git a/ucred/ucred.c b/ucred/ucred.c
new file mode 100644 (file)
index 0000000..2428240
--- /dev/null
@@ -0,0 +1,162 @@
+#include <Python.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <signal.h>
+
+PyObject *g_socketerror; // socket.error
+
+static PyObject *ucred_sendcreds(PyObject *self, PyObject *args, PyObject *keywds);
+static PyObject *ucred_recvcreds(PyObject *self, PyObject *args, PyObject *keywds);
+
+static PyMethodDef ucredMethods[] = {
+    {"sendcreds", (PyCFunction)ucred_sendcreds, METH_VARARGS|METH_KEYWORDS, NULL},
+    {"recvcreds", (PyCFunction)ucred_recvcreds, METH_VARARGS|METH_KEYWORDS, NULL},
+    {NULL, NULL, 0, NULL}
+};
+
+void initucred(void) {
+    PyObject *module;
+    module = Py_InitModule("ucred", ucredMethods);
+
+       if(-1 == PyModule_AddIntConstant(module, "SO_PASSCRED", SO_PASSCRED)) {
+               return;
+       }
+
+    module = PyImport_ImportModule("socket");
+    if(!module) {
+        return;
+    }
+    g_socketerror = PyObject_GetAttrString(module, "error");
+    if(!g_socketerror) {
+        return;
+    }
+}
+
+static PyObject *ucred_sendcreds(PyObject *self, PyObject *args, PyObject *keywds) {
+    int fd;
+    int ret;
+    int pid=-1, uid=-1, gid=-1;
+    struct msghdr msg;
+    struct iovec iov[1];
+       struct ucred uc;
+       unsigned char control[CMSG_SPACE(sizeof(struct ucred))];
+       struct cmsghdr *cur;
+
+    static char *kwlist[] = {"fd",
+                                                        "pid",
+                                                        "uid",
+                                                        "gid",
+                                                        NULL};
+
+//    kill(0, SIGTRAP);
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|iii", kwlist, 
+                               &fd,
+                               &pid,
+                               &uid,
+                               &gid)) {
+        return NULL;
+    }
+//    kill(0, SIGTRAP);
+
+       if(pid!=-1)
+               uc.pid=pid;
+       else
+               uc.pid=getpid();
+       if(uid!=-1)
+               uc.uid=uid;
+       else
+               uc.uid=getuid();
+       if(gid!=-1)
+               uc.gid=gid;
+       else
+               uc.gid=getgid();
+
+       iov[0].iov_base="SCM_CREDENTIALS";
+       iov[0].iov_len=15;
+
+       memset(&msg, 0, sizeof(msg));
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+       msg.msg_control = &control;
+       msg.msg_controllen = sizeof(control);
+
+       cur = CMSG_FIRSTHDR(&msg);
+       cur->cmsg_level = SOL_SOCKET;
+       cur->cmsg_type = SCM_CREDENTIALS;
+       cur->cmsg_len = CMSG_SPACE(sizeof(struct ucred));
+       memcpy(CMSG_DATA(cur), &uc, sizeof(struct ucred));
+
+    ret = sendmsg(fd, &msg, 0);
+    if(ret < 0) {
+        PyErr_SetFromErrno(g_socketerror);
+        return NULL;
+    }
+
+    return Py_BuildValue("i", ret);
+}
+
+static PyObject *ucred_recvcreds(PyObject *self, PyObject *args, PyObject *keywds) {
+    int fd;
+    int flags=0;
+    size_t maxsize=1024;
+       char control[CMSG_SPACE(sizeof(struct ucred))];
+    int ret;
+       int pid=-1, uid=-1, gid=-1;
+    struct msghdr msg;
+    struct iovec iov[1];
+       struct cmsghdr *cur;
+       struct ucred *uc;
+
+
+    static char *kwlist[] = {"fd", "flags", "maxsize", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|ii", kwlist,
+            &fd, &flags, &maxsize)) {
+        return NULL;
+    }
+
+       memset(&msg, 0, sizeof(msg));
+
+    iov[0].iov_len = maxsize;
+    iov[0].iov_base = malloc(maxsize);
+    if (!iov[0].iov_base) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    msg.msg_control = &control;
+    msg.msg_controllen = sizeof(control);
+
+    ret = recvmsg(fd, &msg, flags);
+    if (ret < 0) {
+        PyErr_SetFromErrno(g_socketerror);
+        free(iov[0].iov_base);
+        return NULL;
+    }
+
+       cur=CMSG_FIRSTHDR(&msg);
+
+       if(cur && cur->cmsg_type==SCM_CREDENTIALS && cur->cmsg_level==SOL_SOCKET)
+       {
+               assert(cur->cmsg_len >= sizeof(struct cmsghdr));
+                                               // no null ancillary data messages?
+
+               uc=(struct ucred*)CMSG_DATA(cur);
+               pid=uc->pid;
+               uid=uc->uid;
+               gid=uc->gid;
+       }
+
+    {
+        PyObject *r;
+        r = Py_BuildValue("s#iii",
+                iov[0].iov_base, ret,
+                pid, uid, gid);
+        free(iov[0].iov_base);
+        return r;
+    }
+}
+
+
diff --git a/var/.keep b/var/.keep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/wwwbin/ac-th-diff.py b/wwwbin/ac-th-diff.py
new file mode 100755 (executable)
index 0000000..b8a518d
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+import os
+import re
+import struct
+
+acdir = "/home/ftp/pub/Linux/PLD/dists/ac/PLD/SRPMS/SRPMS"
+thdir = "/home/ftp/pub/Linux/PLD/dists/th/PLD/SRPMS/RPMS/"
+
+thpkg = []
+acpkg = []
+
+ign = '^(xorg-.*|X11-.*|XcursorTheme-.*)$'
+re_c = re.compile(ign)
+
+re_n = re.compile('^(.*)-([^-]*)-([^-]*)$')
+
+def getname(file):
+       #f = os.popen('rpm --nomd5 --nodigest --nosignature -qp --queryformat "%{NAME}" ' + file, "r")
+       #name = f.read()
+       #f.close()
+       #f = open(file, 'rb')
+       #rpmlead = f.read(96)
+       #f.close()
+       #data = struct.unpack("6B2h66s2h16s", rpmlead)
+       #name = data[8].strip()
+       #print name
+       m = re_n.match(file)
+       name =  m.group(1).strip()
+       return name
+
+for rpm in os.listdir(acdir):
+       if re_c.match(rpm):
+               continue
+       acpkg.append(getname(rpm))
+
+for rpm in os.listdir(thdir):
+       if re_c.match(rpm):
+               continue
+       thpkg.append(getname(rpm))
+
+thpkg.sort()
+acpkg.sort()
+
+print "*****************************************************"
+print "Packages in AC repo that are not in TH repo:"
+for pkg in acpkg:
+       if pkg not in thpkg:
+               print pkg
+
+print
+print
+print "*****************************************************"
+print "Packages in TH repo that are not in AC repo:"
+for pkg in thpkg:
+       if pkg not in acpkg:
+               print pkg
diff --git a/wwwbin/by-group.sh b/wwwbin/by-group.sh
new file mode 100755 (executable)
index 0000000..f00c93b
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+dir="$1"
+cd $dir || exit 1
+
+for f in *.rpm ; do
+       g=$(rpm --qf '%{GROUP}' -qp $f)
+       level=".."
+       a="$g"
+       b=
+       while [ "$a" != "$b" ]; do
+               b="$a"
+               a=${a#*/}
+               level="${level}/.."
+       done
+       [ -d "../by-group/$g" ] || mkdir -p "../by-group/$g"
+       ln -sf "$level/RPMS/$f" "../by-group/$g/$f"
+done
+
+# remove dangling symlinks...
+symlinks -dr $dir/../by-group
+# and empty directories
+find $dir/../by-group -depth -type d -empty -exec rmdir {} \;
diff --git a/wwwbin/checkrepo.sh b/wwwbin/checkrepo.sh
new file mode 100755 (executable)
index 0000000..db2cff1
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+# stat(1) each file in .info to see if they exist
+
+REPODIR=${1:-/home/pld/admins/th/ftp/test}
+
+# expand files from .info file
+expand_info() {
+       awk -F ':' -vD="$REPODIR/" '
+               /file:/ {
+                       if (/-debuginfo-/) {
+                               print D $2 "/debuginfo/" $3
+                       } else {
+                               print D $2 "/RPMS/" $3
+                       }
+               }
+       ' "$@"
+}
+
+i=0
+nn=
+ls -1 $REPODIR/SRPMS/RPMS | sed -rne "s/^((.+)-[^-]+-[^-]+\.src\.rpm)$/\1.info/p" | \
+while read pkg; do
+       for f in $(expand_info $REPODIR/SRPMS/.metadata/$pkg); do
+               if ! stat $f >/dev/null; then
+                       echo "!!!: $pkg : $f" >&2
+               fi
+       done
+done
diff --git a/wwwbin/clean-dups-archive.py b/wwwbin/clean-dups-archive.py
new file mode 100755 (executable)
index 0000000..4961c39
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+# arekm, 2008
+# remove 
+
+import os
+import re
+import time
+import rpm
+import sys
+
+re_rpm = re.compile(r'.*\.rpm$')
+re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.rpm$')
+dir = '/home/pld/admins/th/ftp/.archive/PLD/SRPMS/RPMS'
+
+ts = rpm.TransactionSet("", (rpm.RPMVSF_NOHDRCHK or rpm.RPMVSF_NEEDPAYLOAD))
+
+def compare(f1, f2):
+       try:
+               fd1 = os.open(os.path.join(dir, f1), os.O_RDONLY)
+       except Exception, e:
+               print e
+               # ignore non-files
+               return 0
+       try:
+               fd2 = os.open(os.path.join(dir, f2), os.O_RDONLY)
+       except Exception, e:
+               print e
+               # ignore non-files
+               return 0
+       h1 = ts.hdrFromFdno(fd1)
+       h2 = ts.hdrFromFdno(fd2)
+       os.close(fd1)
+       os.close(fd2)
+
+       l1 = rpm.versionCompare(h1, h2)
+       l2 = rpm.versionCompare(h2, h1)
+
+       if l1 > 0 and l2 > 0:
+               return 0
+
+       return -l1
+
+
+def find_old(files):
+       return sorted(files, compare)
+
+files = {}
+dupes = {}
+
+for file in os.listdir(dir):
+       if not re_rpm.match(file):
+               continue
+
+       m = re_nvr.match(file)
+       if not m:
+               print "problem with: %s" % file
+               sys.exit(1)
+
+       if len(sys.argv) == 0:
+               p = os.path.join(dir, file)
+               mtime = os.stat(p).st_mtime
+               if mtime > time.time() - 3*86400:
+                       continue
+
+       name = m.group(1)
+
+       if files.has_key(name):
+               if dupes.has_key(name):
+                       dupes[name].append(file)
+               else:
+                       dupes[name] = [ files[name] ]
+                       dupes[name].append(file)
+       else:
+               files[name] = file
+
+for i in dupes.iterkeys():
+       for old in find_old(dupes[i])[1:]:
+               os.system("/home/pld/admins/th/pld-ftp-admin/scripts/remove.py .archive/PLD %s" % old)
diff --git a/wwwbin/clean-dups-old.py b/wwwbin/clean-dups-old.py
new file mode 100755 (executable)
index 0000000..e0ba2a0
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+# arekm, 2008
+# remove 
+
+import os
+import re
+import time
+import rpm
+import sys
+
+re_info = re.compile(r'.*\.info$')
+re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.info$')
+dir = '/home/pld/admins/th/ftp/test/SRPMS/.metadata'
+
+def compare(f1, f2):
+       m1 = re_nvr.match(f1)
+       n1 = m1.group(1)
+       v1 = m1.group(2)
+       r1 = m1.group(3)
+
+       m2 = re_nvr.match(f2)
+       n2 = m2.group(1)
+       v2 = m2.group(2)
+       r2 = m2.group(3)
+
+       l1 = rpm.labelCompare((n1, v1, r1), (n2, v2, r2))
+       l2 = rpm.labelCompare((n2, v2, r2), (n1, v1, r1))
+
+       if l1 > 0 and l2 > 0:
+               return 0
+
+       return -l1
+
+
+def find_old(files):
+       return sorted(files, compare)
+
+files = {}
+dupes = {}
+
+for file in os.listdir(dir):
+       if not re_info.match(file):
+               continue
+
+       m = re_nvr.match(file)
+       if not m:
+               print "problem with: %s" % file
+               sys.exit(1)
+
+       if len(sys.argv) == 0:
+               p = os.path.join(dir, file)
+               mtime = os.stat(p).st_mtime
+               if mtime > time.time() - 3*86400:
+                       continue
+
+       name = m.group(1)
+
+       if files.has_key(name):
+               if dupes.has_key(name):
+                       dupes[name].append(file)
+               else:
+                       dupes[name] = [ files[name] ]
+                       dupes[name].append(file)
+       else:
+               files[name] = file
+
+for i in dupes.iterkeys():
+       for old in find_old(dupes[i])[1:]:
+               os.system("/home/pld/admins/th/pld-ftp-admin/scripts/remove.py test %s" % old)
diff --git a/wwwbin/clean-dups.py b/wwwbin/clean-dups.py
new file mode 100755 (executable)
index 0000000..003b538
--- /dev/null
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+# arekm, 2008
+# remove 
+
+import os
+import re
+import time
+import rpm
+import sys
+
+re_rpm = re.compile(r'.*\.rpm$')
+re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.rpm$')
+dir = '/home/pld/admins/th/ftp/test/SRPMS/RPMS'
+
+ignore = re.compile('^(kernel-.*)$')
+#|\
+#crash-.*|\
+#dahdi-linux-.*|\
+#e1000e-.*|\
+#igb-.*|\
+#ipset-.*|\
+#iscsitarget-.*|\
+#ixgbe-.*|\
+#kernel-net-wl-.*|\
+#lin_tape-.*|\
+#linux-fusion-.*|\
+#linuxrdac-.*|\
+#lirc-.*|\
+#lttng-modules-.*|\
+#madwifi-ng-.*|\
+#nvidiabl-.*|\
+#open-vm-tools-.*|\
+#openvswitch-.*|\
+#r8168-.*|\
+#spl-.*|\
+#tpm_emulator-.*|\
+#VirtualBox-.*|\
+#vpb-driver-.*|\
+#xorg-driver-video-fglrx-.*|\
+#xorg-driver-video-fglrx-legacy-.*|\
+#xorg-driver-video-nvidia-.*|\
+#xorg-driver-video-nvidia-legacy3-.*|\
+#xorg-driver-video-nvidia-legacy-304xx-.*|\
+#xtables-addons-.*)$')
+
+ts = rpm.TransactionSet("", (rpm.RPMVSF_NOHDRCHK or rpm.RPMVSF_NEEDPAYLOAD))
+
+def compare(f1, f2):
+       try:
+               fd1 = os.open(os.path.join(dir, f1), os.O_RDONLY)
+       except Exception, e:
+               print e
+               # ignore non-files
+               return 0
+       try:
+               fd2 = os.open(os.path.join(dir, f2), os.O_RDONLY)
+       except Exception, e:
+               print e
+               # ignore non-files
+               return 0
+       try:
+               h1 = ts.hdrFromFdno(fd1)
+       except Exception, e:
+               print "hdrFromFdno for %s failed: %s" % (f1, e)
+               os.close(fd1)
+               os.close(fd2)
+               return 0
+
+       try:
+               h2 = ts.hdrFromFdno(fd2)
+       except Exception, e:
+               print "hdrFromFdno for %s failed: %s" % (f2, e)
+               os.close(fd1)
+               os.close(fd2)
+               return 0
+
+       os.close(fd1)
+       os.close(fd2)
+
+       l1 = rpm.versionCompare(h1, h2)
+       l2 = rpm.versionCompare(h2, h1)
+
+       if l1 > 0 and l2 > 0:
+               return 0
+
+       return -l1
+
+
+def find_old(files):
+       return sorted(files, compare)
+
+files = {}
+dupes = {}
+
+for file in os.listdir(dir):
+       if not re_rpm.match(file):
+               continue
+
+       if ignore.match(file):
+               continue
+
+       m = re_nvr.match(file)
+       if not m:
+               print "problem with: %s" % file
+               sys.exit(1)
+
+       if len(sys.argv) == 0:
+               p = os.path.join(dir, file)
+               mtime = os.stat(p).st_mtime
+               if mtime > time.time() - 3*86400:
+                       continue
+
+       name = m.group(1)
+
+       if files.has_key(name):
+               if dupes.has_key(name):
+                       dupes[name].append(file)
+               else:
+                       dupes[name] = [ files[name] ]
+                       dupes[name].append(file)
+       else:
+               files[name] = file
+
+for i in dupes.iterkeys():
+       for old in find_old(dupes[i])[1:]:
+               print "removing: %s" % old
+               os.system("/home/pld/admins/th/pld-ftp-admin/scripts/remove.py test %s" % old)
diff --git a/wwwbin/clean-test.sh b/wwwbin/clean-test.sh
new file mode 100755 (executable)
index 0000000..475f84f
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+LC_ALL=C; export LC_ALL
+cd /home/pld/admins/th/ftp/test/SRPMS/.metadata || exit 1
+#for file in `find . -name '*.info' -print`; do
+for file in `find . -name '*.info' -mtime +1 -print`; do
+       dfile=$(basename $file | sed -e 's#^\./##g')
+       if (~/pld-ftp-admin/scripts/test-move.py test ready "$dfile" | grep -q "has only src.rpm built"); then
+               echo "Removing $dfile..."
+       ~/pld-ftp-admin/scripts/remove.py test "$dfile"
+       fi
+done
+for file in `find . -name '*.info' -mtime +100`; do
+       dfile=$(basename $file | sed -e 's#^\./##g')
+       echo "Removing $dfile..."
+       ~/pld-ftp-admin/scripts/remove.py test "$dfile"
+done
diff --git a/wwwbin/consistency-check.sh b/wwwbin/consistency-check.sh
new file mode 100755 (executable)
index 0000000..25ed2c3
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+export LC_ALL=C
+
+filter_deps() {
+       if [ $1 = "x32" ]; then
+         grep -Ev "(uname\(release\)|-multilib-|\/eclipse\/|statifier|kernel-nopae-source-|kernel-tools-perf-vdso|libreoffice|iceape|iceweasel|icedove|nodejs)"
+       else
+         grep -Ev "(uname\(release\)|-multilib-|\/eclipse\/|statifier|kernel-nopae-source-|kernel-tools-perf-vdso)"
+    fi
+}
+
+# group errors by same error kind
+group_deps() {
+       local t=$(mktemp)
+       cat > $t
+       sed -ne 's/.*req \(.*\) not found.*/\1/p' $t | sort -u | while read dep; do
+               grep -F "req $dep not found" $t
+       done
+       rm -f $t
+}
+
+# convert pkg name to src.rpm name
+# uses poldek
+pkg2src() {
+       local t=$(mktemp)
+       local t3=$(mktemp)
+
+       # save input
+       cat > $t
+
+       # create list of N-V-R.A.rpm -> src-N pairs from error report
+       # error: arcconf-7.0.18786-1: req libstdc++.so.5()(64bit) not found
+       sed -ne 's/error: \(.*\): req .* not found/\1/p' $t | sort -u | \
+               xargs -d '\n' poldek --noignore -Q "$@" --cmd ls -q -s | \
+               sed -rne "s/^([^\t ]+)[\t ]+(.+)-[^-]+-[^-]+\.src\.rpm$/\1\t\2/p" >$t3
+
+       local pkg error message srpm
+       while read error pkg message; do
+               # error: arcconf-7.0.18786-1: req libstdc++.so.5()(64bit) not found
+               srpm=$(awk -vpkg="${pkg%:}" '$1 == pkg {printf("%s.spec", $2)}' $t3)
+               if [ -z "$srpm" ]; then
+                       echo >&2 "srpms: No match for [${pkg%:}]"
+               fi
+               echo "$error [$srpm] $pkg $message"
+       done < $t
+
+       rm -f $t $t3
+}
+
+gen_list() {
+       arch=$1
+       shift
+       date
+       poldek -O "auto directory dependencies = yes" \
+       --ignore "*-debuginfo-*" \
+       --ignore "opera-plugin32-*" \
+       --ignore "nspluginwrapper-*" \
+       --ignore "mbrola-voice-*" \
+       --ignore "pysql-*" \
+       --ignore "yasql-*" \
+       --ignore "kde4-kdenetwork-kopete-protocol-skype-*.x86_64" \
+       --ignore "libpurple-protocol-skype-*.x86_64" \
+       --ignore "nagios-plugin-check_cciss-*" \
+       --ignore "libpng1*" \
+       --verify=deps -Q "$@" | filter_deps $arch | group_deps | pkg2src "$@" | sort
+}
+
+gen_list_uniq() {
+       arch=$1
+       shift
+       gen_list $arch -O"unique package names = yes" "$@"
+}
+
+t=$(mktemp)
+ftpdir=$HOME/ftp
+for arch in x86_64 i686 x32 ; do
+       if [ "$arch" = x86_64 ]; then
+               outfext=.txt
+       else
+               outfext=-$arch.txt
+       fi
+       gen_list      $arch -s $ftpdir/PLD/$arch/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ > $t && cat $t > $HOME/www/main$outfext
+       gen_list_uniq $arch -s $ftpdir/PLD/$arch/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ -s $ftpdir/ready/$arch/RPMS/ -s $ftpdir/ready/noarch/RPMS/ > $t && cat $t > $HOME/www/main-ready$outfext
+       gen_list_uniq $arch -s $ftpdir/PLD/$arch/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ -s $ftpdir/ready/$arch/RPMS/ -s $ftpdir/ready/noarch/RPMS/ -s $ftpdir/test/$arch/RPMS/ -s $ftpdir/test/noarch/RPMS/ > $t && cat $t > $HOME/www/main-ready-test$outfext
+done
+
+chmod 644 $HOME/www/*.txt
+rm -f $t
diff --git a/wwwbin/dump-packagenames.py b/wwwbin/dump-packagenames.py
new file mode 100755 (executable)
index 0000000..59f9e89
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+import os
+import re
+
+import rpm
+
+dirs = ['/home/services/ftp/pld/dists/3.0/PLD/x32/RPMS',
+               '/home/services/ftp/pld/dists/3.0/PLD/i686/RPMS',
+               '/home/services/ftp/pld/dists/3.0/PLD/x86_64/RPMS',
+               '/home/services/ftp/pld/dists/3.0/ready/x32/RPMS',
+               '/home/services/ftp/pld/dists/3.0/ready/i686/RPMS',
+               '/home/services/ftp/pld/dists/3.0/ready/x86_64/RPMS',
+               '/home/services/ftp/pld/dists/3.0/test/x32/RPMS',
+               '/home/services/ftp/pld/dists/3.0/test/i686/RPMS',
+               '/home/services/ftp/pld/dists/3.0/test/x86_64/RPMS']
+
+#dirs = ['/home/services/ftp/pld/dists/3.0/test/x86_64/RPMS']
+
+outname = "/home/pld/admins/th/www/name-srcname.txt"
+
+re_rpm = re.compile(r'.*\.rpm$')
+re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.rpm$')
+
+ts = rpm.ts()
+ts.setVSFlags(-1)
+pkgs = {}
+for dir in dirs:
+       for file in os.listdir(dir):
+               if not re_rpm.match(file):
+                       continue
+
+               rpm_file = os.path.join(dir, file)
+
+               fdno = os.open(rpm_file, os.O_RDONLY)
+               try:
+                       hdr = ts.hdrFromFdno(fdno)
+               except Exception, e:
+                       print "hdrFromFdno: %s: %s" % (rpm_file, e)
+                       os.close(fdno)
+                       continue
+               os.close(fdno)
+
+               name = hdr[rpm.RPMTAG_NAME]
+               sourcerpm = hdr[rpm.RPMTAG_SOURCERPM]
+               m = re_nvr.match(sourcerpm)
+               if not m:
+                       print "%s: doesn't match src.rpm file name" % (sourcerpm)
+                       continue
+               srcname = m.group(1)
+               pkgs[name] = srcname
+
+f = open(outname + ".tmp", "w")
+for (pkg, spkg) in pkgs.iteritems():
+       f.write("%s:%s\n" % (pkg, spkg))
+f.close()
+os.chmod(outname + ".tmp", 0644)
+os.rename(outname + ".tmp", outname)
+
diff --git a/wwwbin/freshness.sh b/wwwbin/freshness.sh
new file mode 100755 (executable)
index 0000000..4646650
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+date > $HOME/www/freshness.txt.new
+/usr/bin/time -f '\nElapsed time: %E' $HOME/bin/ftp-freshness.py >> $HOME/www/freshness.txt.new 2>&1
+chmod 644 $HOME/www/freshness.txt.new
+mv $HOME/www/freshness.txt.new $HOME/www/freshness.txt
+
diff --git a/wwwbin/ftp-freshness.py b/wwwbin/ftp-freshness.py
new file mode 100755 (executable)
index 0000000..aa27b37
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+# arekm, 2007
+
+import os
+import re
+import struct
+import rpm
+import subprocess
+
+# earlier == more important
+dirs = [ "/home/ftp/pub/Linux/PLD/dists/th/test/SRPMS/RPMS/",
+                "/home/ftp/pub/Linux/PLD/dists/th/ready/SRPMS/RPMS/",
+                "/home/ftp/pub/Linux/PLD/dists/th/PLD/SRPMS/RPMS/" ]
+#dirs = [ "/home/pld/admins/th/1" ]
+specsdir = "/home/pld/admins/th/SPECS"
+
+if os.path.isdir(specsdir):
+    os.chdir(specsdir)
+    cmd = ['git', 'pull']
+else:
+    cmd = ['git', 'clone', '--depth=1', 'git://git.pld-linux.org/SPECS', specsdir]
+
+p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+(out, err) = p.communicate(None)
+
+
+os.chdir(specsdir)
+class Pkgs(object):
+       def __init__(self):
+               self.pkg = {}
+               self.cvs = {}
+               self.cvs_new_nvr = []
+               self.cvs_new_nv = []
+               self.re_n = re.compile('^(.*)-([^-]*)-([^-]*)\.src\.rpm$')
+
+       def __get_from_rpm(self, file_name):
+               m = self.re_n.match(file_name)
+               if not m:
+                       return False
+               name =  m.group(1).strip()
+               version = m.group(2).strip()
+               release = m.group(3).strip()
+               return (name, version, release)
+
+       def __get_from_cvs(self, name):
+               f = os.popen('rpm --specfile -q --queryformat "%{name}\n%{version}\n%{release}\n" ' + specsdir + '/' + name + '.spec 2> /dev/null', 'r')
+               name = f.readline().strip()
+               version = f.readline().strip()
+               release = f.readline().strip()
+               f.close()
+               return (name, version, release)
+
+       def __update_cvs(self, name):
+               if not self.cvs.has_key(name):
+                       self.cvs[name] = self.__get_from_cvs(name)
+
+       def __update_new(self, name):
+               cvs_nvr = self.cvs[name]
+               pkg_nvr = self.pkg[name]
+
+               cvs_rpm_vr = rpm.labelCompare((cvs_nvr[0], cvs_nvr[1], cvs_nvr[2]), (pkg_nvr[0], pkg_nvr[1], pkg_nvr[2]))
+               cvs_rpm_v = rpm.labelCompare((cvs_nvr[0], cvs_nvr[1], ""), (pkg_nvr[0], pkg_nvr[1], ""))
+               rpm_cvs_vr = rpm.labelCompare((pkg_nvr[0], pkg_nvr[1], pkg_nvr[2]), (cvs_nvr[0], cvs_nvr[1], cvs_nvr[2]))
+               rpm_cvs_v = rpm.labelCompare((pkg_nvr[0], pkg_nvr[1], ""), (cvs_nvr[0], cvs_nvr[1], ""))
+
+               if rpm_cvs_v < 0 and cvs_rpm_v > 0:
+                       self.cvs_new_nv.append(name)
+               else:
+                       if rpm_cvs_vr < 0 and cvs_rpm_vr > 0 and cvs_nvr[1] == pkg_nvr[1]:
+                               self.cvs_new_nvr.append(name)
+
+       def     prepare(self):
+               self.cvs_new_nvr.sort()
+               self.cvs_new_nvr = list(set(self.cvs_new_nvr))
+               self.cvs_new_nv.sort()
+               self.cvs_new_nv = list(set(self.cvs_new_nv))
+
+               pkgs = list(self.pkg) + list(self.cvs)
+
+               for name in list(set(pkgs)):
+                       self.__update_new(name)
+
+       def add_rpm(self, file_name):
+               nvr = self.__get_from_rpm(file_name)
+               if not nvr:
+                       return False
+               name = nvr[0]
+               if self.pkg.has_key(name):
+                       if rpm.labelCompare(nvr, self.pkg[name]) > 0:
+                               del self.pkg[name]
+                               self.pkg[name] = nvr
+               else:
+                       self.pkg[name] = nvr
+               self.__update_cvs(name)
+
+       def print_nvr(self):
+               print "*** VERSION-RELEASE COMPARE FOR THE SAME VERSIONS ONLY ***"
+               for name in self.cvs_new_nvr:
+                       self.cvs_new_nvr.sort()
+                       cvs_nvr = self.cvs[name]
+                       pkg_nvr = self.pkg[name]
+                       print "GIT: [%s.spec] %s-%s-%s vs FTP: %s-%s-%s" % (name, cvs_nvr[0], cvs_nvr[1], cvs_nvr[2], pkg_nvr[0], pkg_nvr[1], pkg_nvr[2])
+
+       def print_nv(self):
+               print "*** VERSION COMPARE ONLY ***"
+               self.cvs_new_nv.sort()
+               for name in self.cvs_new_nv:
+                       cvs_nvr = self.cvs[name]
+                       pkg_nvr = self.pkg[name]
+                       print "GIT: [%s.spec] %s-%s-%s vs FTP: %s-%s-%s" % (name, cvs_nvr[0], cvs_nvr[1], cvs_nvr[2], pkg_nvr[0], pkg_nvr[1], pkg_nvr[2])
+
+p = Pkgs()
+
+for d in dirs:
+       for rpm_file_name in os.listdir(d):
+               p.add_rpm(rpm_file_name)
+
+p.prepare()
+
+p.print_nv()
+print "\n\n"
+p.print_nvr()
diff --git a/wwwbin/obsolete-check.sh b/wwwbin/obsolete-check.sh
new file mode 100755 (executable)
index 0000000..52a9f81
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+# check for packages on ftp whose .spec does not exist (anymore)
+# this is extremely heavy on cvs server, so you run it once per month or so
+# Author: Elan Ruusamäe <glen@pld-linux.org>
+
+export LC_ALL=C
+ftpdir=$HOME/ftp
+wwwdir=$HOME/www
+CVSROOT=:pserver:cvs@cvs.pld-linux.org:/cvsroot
+d=$-
+
+orphaned_pkgs() {
+       set -$d
+       [ -s $t/pkgs.desc ] || /usr/bin/poldek --skip-installed "$@" --cmd "desc *" > $t/pkgs.desc
+       [ -s $t/pkgs.lst ] || sed -n 's/^Source package: \(.*\)-[^-]\+-[^-]\+$/\1/p' $t/pkgs.desc | sort -u > $t/pkgs.lst
+       # {w32codec,acroread,...}-installer pkgs
+       sed -i -e 's,-installer$,,' $t/pkgs.lst
+       for pkg in $(cat $t/pkgs.lst); do
+               # use awk to match package without any regexp fuzz
+               awk -vpkg=$pkg.spec -vm=1 '$1 == pkg{m=0} END{exit m}' $t/cvs.lst || echo Obsolete: $pkg
+       done
+}
+
+# generate list of .specs on ftp. needs cvsnt client
+cvs_pkgs() {
+       set -$d
+       [ -s $t/cvs.raw ] || cvs -d $CVSROOT -Q ls -e packages > $t/cvs.raw 2>/dev/null
+       [ -s $t/cvs.dirs ] || awk -F/ '$1 == "D" { print $2 } ' $t/cvs.raw > $t/cvs.dirs
+       [ -s $t/cvs.specs ] || {
+               while read pkg; do
+                       cvs -d $CVSROOT -Q ls -e packages/$pkg/$pkg.spec 2>/dev/null
+               done < $t/cvs.dirs > $t/cvs.lst.tmp && mv $t/cvs.lst.tmp $t/cvs.specs
+       }
+       [ -s $t/cvs.lst ] || awk -F/ '$1 == "" { print $2 } ' $t/cvs.specs > $t/cvs.lst
+}
+
+
+set -e
+t=$(mktemp -d)
+#t=/home/pld/admins/th/tmp/tmp.KOCrX7BtOy
+
+cvs_pkgs
+orphaned_pkgs -s $ftpdir/PLD/i686/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ > $t/orphaned.txt && cat $t/orphaned.txt > $wwwdir/main-orphaned4.txt
+
+chmod 644 $wwwdir/*.txt
+#rm -rf $t
diff --git a/wwwbin/rpmcheck.sh b/wwwbin/rpmcheck.sh
new file mode 100755 (executable)
index 0000000..35f4373
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+# check for packages on local system with PLD-doc/PLD-update-TODO
+# Author: Elan Ruusamäe <glen@pld-linux.org>
+# Date: 2012-04-05
+
+export LC_ALL=C
+CVSROOT=:pserver:cvs@cvs.pld-linux.org:/cvsroot
+d=$-
+
+# generate list of package basenames from rpmdb
+rpm_pkgs() {
+       set -$d
+
+       [ -s $t/rpm.db ] || rpm -qa --qf '%{SOURCERPM} %{VERSION}\n' > $t/rpm.db
+
+       # translate pkg names to basenames
+       [ -s $t/rpm.basenames ] || sed -re 's,^(.+)-[^-]+-[^-]+ ,\1 ,' $t/rpm.db > $t/rpm.basenames
+       # uniq
+       [ -s $t/rpm.lst ] || sort -u $t/rpm.basenames > $t/rpm.lst
+}
+
+# fetch PLD-update-TODO
+cvs_todo() {
+       set -$d
+
+       [ -s $t/PLD-doc/PLD-update-TODO ] || (cd $t; cvs -d $CVSROOT -Q co PLD-doc/PLD-update-TODO)
+
+       # reformat for easier parsing
+       # amaya(13) [OLD] 9.54 [NEW] 11.0
+       # vim [OLD] 7.3.401 [NEW] 7.3.515
+       [ -s $t/rpm.todo ] || sed -rne 's,^([^( ]+).*?\[NEW\] (.+)$,\1 \2,p' $t/PLD-doc/PLD-update-TODO > $t/rpm.todo
+
+       # TODO: should uniq as well:
+       # $ grep links2 PLD-doc/PLD-update-TODO 
+       # links2(22) [OLD] 2.2 [NEW] 2.6
+       # links2(25) [OLD] 2.1pre28 [NEW] 2.6
+}
+
+rpm_diff() {
+#      set -$d
+
+       rpm_pkgs
+       cvs_todo
+
+       [ -s $t/rpm.diffs ] || {
+               set +e
+               while read pkg v2; do
+                       v1=$(awk -vpkg=$pkg '$1 == pkg {print $2}' $t/rpm.lst)
+                       [ "$v1" ] || continue
+
+                       cmp=$(rpmvercmp $v1 $v2)
+                       if [ $? -eq 2 ]; then
+                               echo "$pkg $cmp"
+                       fi
+               done < $t/rpm.todo
+               set -e
+       }
+}
+
+set -e
+t=$(mktemp -d)
+#t=.
+
+rpm_diff
+
+rm -rf $t