#
# urlinstall.py - URL based install source method
#
# Erik Troan <[email protected]>
#
# 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 self.isUpdateRPM(h):
#               path = "/RedHat/Updates/"
               path = "/" + product.productSite + "/Updates/"
           else:
#               path = "/RedHat/RPMS/"
               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