#!/usr/bin/python2.4
#
# This file is part of the Falcon repository manager
# Copyright (C) 2005-2008 Dennis Kaarsemaker
# See the file named COPYING in the root of the source tree for license details
#
# falcon-import - Import packages from other repositories 
#                 (aka apt-get source on steroids)

import gettext
from gettext import gettext as _
gettext.bindtextdomain('falcon','/usr/share/locale')
gettext.textdomain('falcon')

import falcon
import optparse, os, apt_pkg, urllib2, sys
from email import FeedParser

usage = _("""%prog [options] <url> [release] <component> <package>
%prog [options] <url of dscfile>
It will download the source package files from the specified repository and
place them inside the falcon-managed repository in your configuration or the
current working directory.
""")

parser = optparse.OptionParser(usage=usage)
parser.error = lambda string: falcon.util.error(string)
parser.add_option('-r','--root', dest="rootdir",
                  help=_("Specify the rootdir"), metavar=_("DIR"))
parser.add_option('-v','--verbose', action="store_true", dest="verbose", default=False,
                  help=_("Give verbose output"))
parser.add_option('-P', '--pocket', dest="pocket", default=None,
                  help=_("Install into this pocket"))
parser.add_option('-C', '--component', dest="component", default=None,
                  help=_("Install into this component"))
parser.add_option('-x', '--extract', action="store_true", dest="extract", default=False,
                  help=_("Extract the downloaded source"))
parser.add_option('-d', '--builddeps', action="store_true", dest="builddeps", default=False,
                  help=_("Download build dependencies"))
parser.add_option('-b', '--build', action="store_true", dest="build", default=False,
                  help=_("Build the downloaded source"))
parser.add_option('-p', '--proxy', dest="proxy", default=None, metavar=_("PROXY"),
                  help=_("Use a proxy server"))
parser.add_option('-D', '--delete', action="store_true", dest="delete", default=False,
                  help=_("Delete .dsc and .diff.gz after extracting")) 
parser.add_option('-V', '--version', action="store_true", dest="dump_version", default=False,
                  help=_("Show version and exit"))

options, args = parser.parse_args()
options.application = 'falcon-import'

if options.dump_version:
    print falcon.conf.falcon_version
    sys.exit(0)

print _("Falcon repository manager %s (C)2005-2008 %s") % (falcon.conf.falcon_version, falcon.conf.falcon_author)
if os.getuid() == 0:
    falcon.util.error(_("Falcon should not be run as root"))
dldir = os.getcwd()
falcon.conf.set_options(options, False)
if falcon.conf.pocket and falcon.conf.component:
    dldir = os.path.join(falcon.conf.rootdir, 'pool', falcon.conf.pocket, falcon.conf.component)

res = falcon.plugin.run_plugins('pre_action', 'import', args)
if not res:
    error(_("Unable to proceed"))


known_urls = {
  'debian': 'http://ftp.debian.org/',
  'ubuntu': 'http://archive.ubuntu.com/ubuntu',
  'seveas': 'http://mirror3.ubuntulinux.nl',
  'ubuntustudio': 'http://archive.ubuntustudio.org/ubuntustudio'
}

if falcon.conf.proxy:
    urllib2.install_opener(urllib2.build_opener((urllib2.ProxyHandler({'http': falcon.conf.proxy}),)))
elif 'http_proxy' in os.environ:
    urllib2.install_opener(urllib2.build_opener((urllib2.ProxyHandler({'http': os.environ['http_proxy']}),)))
elif 'HTTP_PROXY' in os.environ:
    urllib2.install_opener(urllib2.build_opener((urllib2.ProxyHandler({'http': os.environ['HTTP_PROXY']}),)))

source, sourcefile, filesize, checksum = '', None, 0, 0
if len(args) == 1 and (args[0].startswith('http://') or args[0].startwith('https://')) and args[0].endswith('.dsc'):
    sourcefile = args[0]
elif len(args) == 3: # Simple repo
    repo, pocket, component = args[0], args[1], None
    if repo in known_urls:
        repo = known_urls[repo]
    if repo.endswith('/'):
        repo = repo[:-1]
    source = '%s/%s/Sources' % (repo, pocket)
    release = None
elif len(args) == 4: # Good repo
    repo, pocket, component = args[0:3]
    if repo in known_urls:
        repo = known_urls[repo]
    if repo.endswith('/'):
        repo = repo[:-1]
    source = '%s/dists/%s/%s/source/Sources' % (repo, pocket, component)
    release = '%s/dists/%s/Release' % (repo, pocket)
else:
    parser.print_help()
    sys.exit(1)
package = args[-1]

if not sourcefile:
    cachedir = os.path.join(os.getenv('HOME'),'.falcon','listcache')
    if not os.path.exists(cachedir):
        os.makedirs(cachedir)
    
    # Grab a release file first
    if release:
        try:
            cache = os.path.join(cachedir, falcon.util.escapeurl(release))
            rel = falcon.util.download(release, cache)
            gpg = falcon.util.download(release + '.gpg', cache + '.gpg')
        except:
            falcon.util.error(_("Couldn't download release file for %s") % pocket)
        # Run gpgv (exit 1: badsig, exit 2: nopubkey)
        try:
            falcon.util.run(['gpgv','--keyring','/etc/apt/trusted.gpg',cache + '.gpg',cache])
        except RuntimeError, e:
            if e.code == 1:
                falcon.util.error(_("Bad gpg signature in the release file for %s" % pocket))
            else:
                falcon.util.warning(e.stderr)
        # Grab checksum from release file
        f = FeedParser.FeedParser()
        f.feed(rel.read())
        rel = f.close()
        for csum in (('SHA256',apt_pkg.sha256sum),('SHA1',apt_pkg.sha1sum),('MD5SUM',apt_pkg.md5sum)):
            if rel[csum[0]]:
                sums = rel[csum[0]]
                method = csum[1]
                path = ' %s/source/Sources' % component
                for line in sums.split('\n'):
                    if line.endswith(path):
                        (checksum, filesize, dummy) = line.split()
                        filesize = int(filesize)
                        break
                else:
                    falcon.util.error(_("Can't find component %s in the releasefile") % component)
                break
    
    # Find a Sources file
    sources = None
    cache = os.path.join(cachedir, falcon.util.escapeurl(source))
    for ext in ('.bz2', '.gz', ''):
        try:
            sources = falcon.util.download(source + ext, cache)
            break
        except:
            falcon.util.debug(_("Downloading %s failed") % source + ext)
    
    if not sources:
        falcon.util.error(_("Unable to download Sources{.bz2,.gz,} for %s") % ' '.join(args[:-1]))
    
    if release:
        if os.path.getsize(cache) != filesize:
            falcon.util.error(_("Error downloading Sources file, size mismatch"))
        if method(sources) != checksum:
            falcon.util.error(_("Error downloading Sources file, bad checksum"))
        sources.seek(0)
    
    # Find the package
    list = [x.strip() for x in sources.read().split("\n\n")]
    for p in list:
        if not p.strip():
            continue
        f = FeedParser.FeedParser()
        f.feed(p.strip())
        p = f.close()
        bins = [x.strip() for x in p['Binary'].split(',')]
        # Found it! Now download
        if p['Package'] == package or package in bins:
            sourcefile = ''
            for f in p['Files'].split("\n"):
                checksum, filesize, f = f.split()
                filesize = int(filesize)
                if f.endswith('.dsc'):
                    sourcefile = '%s/%s/%s' % (repo,p['Directory'],f)
                    break
            else:
                continue
            break
    else:
        falcon.util.error(_('Could not locate package %s on %s') % (package, ' '.join(args[:1])))

dscfile = sourcefile[sourcefile.rfind('/')+1:]
if os.path.exists(os.path.join(dldir,dscfile)):
    falcon.util.output(_('File %s already exists in %s, not redownloading') % (dscfile, dldir))
else:
    fd = falcon.util.download(sourcefile, store=os.path.join(dldir, dscfile))
    if filesize and (os.path.getsize(os.path.join(dldir, dscfile)) != filesize):
        falcon.util.error(_("Error downloading %s, size mismatch") % sourcefile)
    if checksum and (apt_pkg.md5sum(fd) != checksum):
        falcon.util.error(_("Error downloading %s file, bad checksum") % sourcefile)

# Now that we have a .dsc, grab the other files
dsc, controlfields, files = falcon.package.parse_dsc(os.path.join(dldir,dscfile))
for f in files:
    if f.type == '.dsc':
        continue
    sf = sourcefile[:sourcefile.rfind('/')+1] + f.name
    if os.path.exists(os.path.join(dldir,f.name)):
        falcon.util.output(_('File %s already exists in %s, not redownloading') % (f.name, dldir))
    else:
        fd = falcon.util.download(sf, store=os.path.join(dldir, f.name))
        if os.path.getsize(os.path.join(dldir, f.name)) != f.size:
            falcon.util.error(_("Error downloading %s, size mismatch") % sf)
        if apt_pkg.md5sum(fd) != f.sum:
            falcon.util.error(_("Error downloading %s file, bad checksum") % sf)

if falcon.conf.extract:
    falcon.util.run(['dpkg-source', '-x', dscfile], wd=dldir, buffer=False)
if falcon.conf.builddeps:
    if os.path.exists('/usr/bin/gdebi'):
        falcon.util.run(['sudo', '/usr/lib/pbuilder/pbuilder-satisfydepends-gdebi', '--chroot', '/', '--control', dscfile], wd=dldir, buffer=False)
    else:
        falcon.util.run(['sudo', '/usr/lib/pbuilder/pbuilder-satisfydepends', '--control', dscfile], wd=dldir, buffer=False)
if falcon.conf.build:
    raise NotImplementedError
    #falcon.util.run(build % sourcefile, wd=dir, buffer=False)
if falcon.conf.delete:
    for f in files:
        if f.type == '.dsc' or f.type == '.diff.gz':
            os.unlink(os.path.join(dldir, f.name))

falcon.plugin.run_plugins('post_action', 'import', args)
