# Part of the A-A-P recipe executive: Access files which may be remote

# Copyright (C) 2002 Stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here: http://www.a-a-p.org/COPYING

#
# Access files by their URL.
# If they are remote, may download or upload the file.
# Uses the Cache to avoid up/downloading too often.
#

import os.path
import shutil
import time
from urlparse import urlparse
from urllib import urlretrieve, urlcleanup, urlopen

from Util import *
from Message import *

def is_url(name):
    """Return non-zero when "name" is a URL, zero when it's a local file."""
    scheme, mach, path, parm, query, frag = urlparse(name, '', 0)
    return scheme != ''


def url_split3(name):
    """Split a URL into scheme, machine and path."""
    scheme, mach, path, parm, query, frag = urlparse(name, '', 0)
    if scheme != '' and mach == '' and path[:2] == '//':
	# urlparse doesn't handle scp://machine/path correctly
	mach = path[2:]
	i = string.find(mach, '/')
	if i > 0:
	    path = mach[i + 1:]
	    mach = mach[:i]
    return scheme, mach, path + parm + query + frag


def url_time(globals, name):
    """Obtain the timestamp in seconds (in GMT if possible) for the URL "name".
       Returns zero (very old) if the timestamp can't be obtained."""
    if is_url(name):
	from Cache import cache_lookup
	c = cache_lookup(globals, name)
	if c:
	    # use timestamp for cached file.
	    t = c.timestamp()
	else:
	    # obtain timestamp for remote files.
	    t = remote_time(name)

    else:
	try:
	    t = os.path.getmtime(name)
	except (IOError, OSError):
	    t = 0
    return t


def remote_time(name):
    """Get the timestamp of a remote file."""
    try:
	msg_info(_('getting timestamp for "%s"') % name)
	up = urlopen(name)
	t = get_header_date(up.info())
	up.close()
    except:
	t = 0
    return t


def get_header_date(headers):
    """Get the date from a MIME header.  Returns zero when not available."""
    from rfc822 import parsedate

    if headers.has_key("Last-Modified"):
	return time.mktime(parsedate(headers["Last-Modified"]))
    if headers.has_key("Date"):
	return time.mktime(parsedate(headers["Date"]))
    return 0


def url_download(url, fname):
    """Attempt downloading file "url" to file "fname".
       Overwrite "fname" if it already exists.
       When "fname" is empty, use a temporary file.  The caller has to use
       "url_cleanup()" when done with it.
       Returns a tuple of the filename and the timestamp of the remote file
       when possible.
       Throws an IOError if downloading failed."""
    msg_info(_('Attempting download of "%s"' % url))
    rtime = 0

    fscheme, fmach, fpath = url_split3(url)

    if fscheme == 'scp':
	if fname == '':
	    resfile = tempfname()
	else:
	    resfile = fname
	logged_system('scp -C %s:%s %s' % (fmach, fpath, resfile))
    else:
	if fname == '':
	    # read to temporary file
	    resfile, h = urlretrieve(url)
	else:
	    resfile, h = urlretrieve(url, fname)
	    if resfile != fname:
		# Using a cached file, need to make a copy.
		shutil.copy2(resfile, fname)
		resfile = fname
	    urlcleanup()
	if h:
	    rtime = get_header_date(h)

    if fname == '':
	msg_info(_('Downloaded "%s"' % url))
    else:
	msg_info(_('Downloaded "%s" to "%s"' % (url, fname)))

    return resfile, rtime


def url_cleanup(scheme):
    """Cleanup after using url_download with scheme "scheme"."""
    if scheme != 'scp':
	urlcleanup()	# remove any cached file from urlretrieve()


def download_file(globals, url_dl, node, use_cache):
    """Download a file according to "url_dl" and copy it over "node.name".
       Use the cache when "use_cache" is non-zero, otherwise obtain a fresh
       copy.
       Return non-zero for success."""
    from Cache import local_name

    if not use_cache:
	cu = "0 sec"
    elif url_dl.has_key("cache_update"):
	cu = url_dl["cache_update"]
    else:
	cu = None
    # TODO: handle attributes (e.g., login and password)
    fname = local_name(globals, url_dl["name"], cu)

    if fname and os.path.exists(fname):
	# copy the downloaded file over the original one.
	try:
	    import shutil
	    shutil.copyfile(fname, node.absname)
	except IOError, e:
	    raise UserError, (_('Cannot copy "%s" to "%s"')
						 % (fname, node.name) + str(e))
	msg_info(_('Copied file from cache: "%s"') % node.short_name())
	return 1
    return 0


# vim: set sw=4 sts=4 tw=79 fo+=l:
