#
# urlinstall.py - URL based install source method
#
# Erik Troan <ewt@redhat.com>
#
# Copyright 1999-2002 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

from hdrlist import groupSetFromCompsFile, HeaderList
from installmethod import InstallMethod, FileCopyException
import os
import rpm
import time
import urllib2
import string
import struct
import socket

# we import these explicitly because urllib loads them dynamically, which
# stinks -- and we need to have them imported for the --traceonly option
import ftplib
import httplib
import StringIO

from rhpl.log import log
import product

FILENAME = 1000000
DISCNUM  = 1000002

def urlretrieve(location, file):
    """Downloads from location and saves to file."""

    try:
        url = urllib2.urlopen(location)
    except urllib2.HTTPError, e:
        raise IOError(e.code, e.msg)
    except urllib2.URLError, e:
	raise IOError(-1, e.reason)
	
    f = open(file, 'w+')
    f.write(url.read())
    f.close()
    url.close()
    

class UrlInstallMethod(InstallMethod):
    def readCompsViaMethod(self, hdlist):
	fname = self.findBestFileMatch(None, 'comps.xml')
	# if not local then assume its on host
	if fname is None:
	    if (product.productDefault == product.productSite) :
	      fname = self.baseUrl + "/" + product.productDefault + "/base/comps.xml"
	    else:
	      fname = self.baseUrl + "/" + product.productSite + "/base/comps.xml"
	    log("Comps not in update dirs, using %s",fname)
        return groupSetFromCompsFile(fname, hdlist)

    def getFilename(self, h, timer):
        tmppath = self.getTempPath()
        
	# h doubles as a filename -- gross
	if type("/") == type(h):
	    fullPath = self.baseUrl + "/" + h
	else:
	    if self.multiDiscs:
		base = "%s/disc%d" % (self.pkgUrl, h[DISCNUM])
	    else:
		base = self.pkgUrl

            if h[1000005] is not None:
              path = "/" + product.productSite + "/Updates/"
            else:
              path = "/" + product.productDefault + "/RPMS/"

	    fullPath = base + path + h[FILENAME]

	file = tmppath + os.path.basename(fullPath)

        tries = 0
        while tries < 5:
            try:
                urlretrieve(fullPath, file)
            except IOError, (errnum, msg):
		log("IOError %s occurred getting %s: %s"
                    %(errnum, fullPath, str(msg)))
                time.sleep(5)
            else:
                break
            tries = tries + 1

        if tries >= 5:
            raise FileCopyException
                
	return file

    def copyFileToTemp(self, filename):
        tmppath = self.getTempPath()

        if self.multiDiscs:
            base = "%s/disc1" % (self.pkgUrl,)
        else:
            base = self.pkgUrl
	    
        fullPath = base + "/" + filename

        file = tmppath + "/" + os.path.basename(fullPath)

        tries = 0
        while tries < 5:
            try:
                urlretrieve(fullPath, file)
            except IOError, (errnum, msg):
		log("IOError %s occurred getting %s: %s",
			errnum, fullPath, str(msg))
                time.sleep(5)
            else:
                break
            tries = tries + 1

        if tries >= 5:
            raise FileCopyException
	return file

    def unlinkFilename(self, fullName):
	os.remove(fullName)

    def readHeaders(self):
        tries = 0

        while tries < 5:
	    if (product.productDefault == product.productSite) :
	      hdurl = self.baseUrl + "/" + product.productDefault + "/base/hdlist"
	    else:
	      log("product.productSiteDir is %s", product.productSiteDir)
	      log("product.productSite is %s ", product.productSite)
	      hdurl = self.baseUrl + "/" + product.productSite + "/base/hdlist"
            try:
                url = urllib2.urlopen(hdurl)
	    except urllib2.HTTPError, e:
		log("HTTPError: %s occurred getting %s", hdurl, e)
	    except urllib2.URLError, e:
		log("URLError: %s occurred getting %s", hdurl, e)
            except IOError, (errnum, msg):
		log("IOError %s occurred getting %s: %s",
			errnum, hdurl, msg)
	    else:
		break

	    time.sleep(5)
            tries = tries + 1

        if tries >= 5:
            raise FileCopyException
                
	raw = url.read(16)
	if raw is None or len(raw) < 1:
	    raise TypeError, "header list is empty!"
	
	hl = []
	while (raw and len(raw)>0):
	    info = struct.unpack("iiii", raw)
	    magic1 = socket.ntohl(info[0]) & 0xffffffff
	    if (magic1 != 0x8eade801 or info[1]):
		raise TypeError, "bad magic in header"

	    il = socket.ntohl(info[2])
	    dl = socket.ntohl(info[3])
	    totalSize = il * 16 + dl;
	    hdrString = raw[8:] + url.read(totalSize)
	    hdr = rpm.headerLoad(hdrString)
	    hl.append(hdr)

	    raw = url.read(16)

	return HeaderList(hl)

    def mergeFullHeaders(self, hdlist):
	if (product.productDefault == product.productSite) :
	  fn = self.getFilename("/" + product.productDefault + "/base/hdlist2", None)
	else:
	  fn = self.getFilename("/" + product.productSite + "/base/hdlist2", None)
	hdlist.mergeFullHeaders(fn)
	os.unlink(fn)

    def __init__(self, url, rootPath):
	InstallMethod.__init__(self, rootPath)

        if url.startswith("ftp"):
            isFtp = 1
        else:
            isFtp = 0

        # build up the url.  this is tricky so that we can replace
        # the first instance of // with /%3F to do absolute URLs right
        i = string.index(url, '://') + 3
        self.baseUrl = url[:i]
        rem = url[i:]

        i = string.index(rem, '/') + 1
        self.baseUrl = self.baseUrl + rem[:i]
        rem = rem[i:]
        
        # encoding fun so that we can handle absolute paths
        if rem.startswith("/") and isFtp:
            rem = "%2F" + rem[1:]

        self.baseUrl = self.baseUrl + rem

        if self.baseUrl[-1] == "/":
            self.baseUrl = self.baseUrl[:-1]

	# self.baseUrl points at the path which contains the 'RedHat'
	# directory with the hdlist.

	if self.baseUrl[-6:] == "/disc1":
	    self.multiDiscs = 1
	    self.pkgUrl = self.baseUrl[:-6]
	else:
	    self.multiDiscs = 0
	    self.pkgUrl = self.baseUrl