# This file is a portion of the Red Hat Network Panel Applet
#
# Copyright (C) 1999-2003 Red Hat, Inc. All Rights Reserved.
# Distributed under GPL version 2.
#
# This handle the yum repository package sources
#
# Author: Daniel Veillard
#
# $Id: rhn_applet_yum.py,v 1.9 2004/01/09 15:54:29 veillard Exp $

import string
import os
import time
import pwd
import time
import stat
import rpm

import rhn_utils

from rhn_utils import \
     rhnAppletException, \
     rhnAppletRPCFault, \
     rhnAppletNetworkException
from rhn_applet_source import rhnAppletSource
import rhn_applet_protocols
import rhn_applet_version

def split_nvrae(line):
    """Parse one line of a yum header.info file, and extract the
       (name, version, release, arch, epoch) from it."""
    if line == '' or line[0] == '#':
        return None
    try:
	(e, lin) = string.split(line, ':', 1)
	(package, lin) = string.split(lin, '=', 1)
	idx = string.rfind(package, '.')
	nvr = package[0:idx]
	a = package[idx+1:]
	idx = string.rfind(nvr, '-')
	r = nvr[idx+1:]
	nv = nvr[0:idx]
	idx = string.rfind(nv, '-')
	v = nv[idx+1:]
	n = nv[0:idx]
    except:
	rhn_utils.log_debug("failed to parse yum line: %s" % (line))
	return None
    return (n,v,r,a,e)
	
class rhnAppletYum(rhnAppletSource):
    """Class for handling updates from a Yum repository. Tries to reuse
       most of the HTTP/HTTPS/Proxy code from rhnlib. Handles 304 HTTP
       code to not reload or recompute for non-modified lists."""

    def __init__(self, url, refresh_callback, label, release, arch,
                 proxy_url, proxy_username, proxy_password):
	rhnAppletSource.__init__(self, url=url, release=release, arch=arch)
        self.__arch__ = arch
        self.__release__ = release
        
        self.__server_url__ = url
        self.__label__ = label
        self.__refresh_callback__ = refresh_callback
        
	rhn_applet_protocols.init_connections(proxy_url, proxy_username,
	                                      proxy_password)
        self.__latest_packages__ = []
        self.__latest_packages_mtime__ = 0
	self.__last_modified__ = None
	self.__data__ = None
	self.__nb_errors = 0

        self.__get_data_store()

    def name(self):
        return "%s @ %s" % (self.__label__, self.__server_url__)

    def in_error(self):
        return self.__nb_errors != 0

    def set_proxy(self, proxy_url, proxy_username, proxy_password):
        rhn_applet_protocols.init_connections(proxy_url, proxy_username,
	                                      proxy_password)
        
    def _load_header_info(self, url, lastmodified = None):
        """load the headers. Does not change self so that it can be 
	   extracted easilly. returns the data and the new lastmodified
	   string or NULL if no update has been made"""

	if url[-1] != '/':
	    url = url + '/headers/header.info'
	else:
	    url = url + 'headers/header.info'

	return rhn_applet_protocols.request(url, lastmodified)


    def _do_refresh(self):
        # print "__do_refresh %s" % (self.__label__)
        try:
            # let's not change this object's state unless we fully
            # succeed in our checkin
	    # do not check more than every 4 hours
            
            last_checkin = time.time()
            next_checkin = time.time() + 4 * 60 * 60;

            if self.__data__ == None:
	        self.__last_modified__ = None
            fetch = self._load_header_info(self.__server_url__,
	                                   self.__last_modified__)
	    if fetch != None:
		(data, last_modified) = fetch
        except:
	    self.__nb_errors = self.__nb_errors + 1;
	    if self.__nb_errors > 5:
	        self.__last_checkin__ = last_checkin
		self.set_next_checking(next_checkin)
		import sys
		rhn_utils.log_debug("failed to load url: %s : %s" %
		                    (sys.exc_type, sys.exc_value));
	    return


        self.__nb_errors = 0
        self.__last_checkin__ = last_checkin
        self.set_next_checking(next_checkin)

	if fetch == None:
	    rhn_utils.log_debug("not updated, use cached copy")
	    return;

	pkglist = self.__build_package_list(data)
	self.__data__ = data
	self.__last_modified__ = last_modified
	self.__latest_packages__ = pkglist
	self.inc_change_number()
	self.__save_data_store()
        # print "__do_refresh %s suceeded" % (self.__label__)
            
    def latest_packages(self):
	if self.__data__ == None:
	    self._do_refresh()
        return self.__latest_packages__

    def __check_architecture(self, n, a):
        score = rpm.archscore(a)
        if score <= 0:
	    return 0
	if n == 'kernel' or n == 'kernel-smp' or n == 'kernel-uml' or \
	   n == 'kernel-unsupported' or n == 'glibc':
	     if score != 1:
	         return 0
        return 1

    def __build_package_list(self, data):
	pkglist = []
	for line in string.split(data, '\n'):
	    try:
	        line = line.strip()
		(n, v, r, a, e) = split_nvrae(line)
		if not self.__check_architecture(n, a):
		    continue
		nvr = n + '-' + v + '-' + r
		if e == None or e == '0':
		    nevr = nvr + ':'
		else:
		    nevr = nvr + ':' + e
		pkglist.append({'name': n, 'version': v, 'release': r,
				'epoch': e, 'nvr': nvr, 'nevr': nevr,
				'errata_id': '', 'errata_synopsis' : '',
				'errata_advisory': ''})
	    except:
		pass
        return pkglist

    def __get_data_store(self):
        # print "__get_data_store %s" % (self.__label__)
        file = pwd.getpwuid(os.getuid())[5]
        file = file + "/.rhn-applet"
	#
	# Ensure the .rhn-applet directory is present
	#
	try:
	    statinfo = os.stat(file)
	except:
	    try:
		os.mkdir(file, 0700)
		statinfo = os.stat(file)
	    except:
		rhn_utils.log_debug("Failed to create dir %s" % (file))
		return
	if not stat.S_ISDIR(statinfo[stat.ST_MODE]):
	    rhn_utils.log_debug("File %s not a directory" % (file))
	    try:
		os.unlink(file)
		os.mkdir(file, 0700)
            except:
		rhn_utils.log_debug("Failed to create dir %s" % (file))
		return

	file = file + "/" + self.__label__ + ".yum"
        try:
	    statinfo = os.stat(file)
	except:
	    rhn_utils.log_debug("Cache %s not available" % (file))
	    return

        try:
            fd = open(file, "r")
	    store = fd.readline().strip()
	    url = fd.readline().strip()
	    if store != "Yum channel cache for %s" % self.__label__ or \
	       url != self.__server_url__:
		rhn_utils.log_debug("File %s corrupted or wrong channel" %
		                    (file))
		try:
		    os.unlink(file)
		except:
		    rhn_utils.log_debug("Failed to remove %s" % (file))
		return

	    last_modified = fd.readline().strip()
	    data = fd.read()
	    fd.close()
	    pkglist = self.__build_package_list(data)
	    if last_modified == '':
	        self.__last_modified__ = None
	    else:
		self.__last_modified__ = last_modified
	    self.__data__ = data
	    self.__latest_packages__ = pkglist
	    rhn_utils.log_debug("found %d packages for %s" %
				(len(pkglist), self.__label__))
	    rhn_utils.log_debug("last_modified : %s" % (last_modified))
        except Exception, e:
            rhn_utils.log_debug("can't open for reading %s: %s, trying to remove it..." % (file, str(e)))
            try:
                os.unlink(file)
            except:
                pass

        # print "__get_data_store %s suceeeded" % (self.__label__)

    def __save_data_store(self):
        # print "__save_data_store %s" % (self.__label__)
        if self.__data__ == None:
	    return

        file = pwd.getpwuid(os.getuid())[5]
        file = file + "/.rhn-applet"
	#
	# Ensure the .rhn-applet directory is present
	#
	try:
	    statinfo = os.stat(file)
	except:
	    try:
		os.mkdir(file, 0700)
		statinfo = os.stat(file)
	    except:
		rhn_utils.log_debug("Failed to create dir %s" % (file))
		return
	if not stat.S_ISDIR(statinfo[stat.ST_MODE]):
	    rhn_utils.log_debug("File %s not a directory" % (file))
	    try:
		os.unlink(file)
		os.mkdir(file, 0700)
            except:
		rhn_utils.log_debug("Failed to create dir %s" % (file))
		return
        
	file = file + "/" + self.__label__ + ".yum"
	try:
	    fd = open(file, "w")
	    fd.write("Yum channel cache for %s\n%s\n" % (self.__label__, self.__server_url__))
	    if self.__last_modified__ == None:
	        fd.write("\n")
	    else:
	        fd.write("%s\n" % self.__last_modified__)
	    fd.write(self.__data__)
	    fd.close()
        except Exception, e:
            rhn_utils.log_debug("Cannot write to %s: %s, trying to remove it..." % (file, str(e)))
            try:
                os.unlink(file)
            except:
                pass
        # print "__save_data_store %s suceeeded" % (self.__label__)
        return
        
