#!/usr/share/ucs-test/runner python
## desc: Checks if App packages are available as i386
## tags: [appcenter]
## exposure: safe
## packages:
##   - univention-directory-manager-tools
##   - univention-management-console-module-appcenter
#  Bug [31792]

import apt
import re
import univention.management.console.modules.appcenter.app_center as app
import univention.lib.package_manager as PM
import sys
from urllib import urlopen
from gzip import GzipFile
from StringIO import StringIO
from urlparse import urljoin

import univention.config_registry
ucr = univention.config_registry.ConfigRegistry()
ucr.load()


def get_installed_default_packages():
	c = apt.Cache()
	c.open()
	pm = PM.PackageManager()
	apps = app.Application.all()
	installed_apps = {}

	for application in apps:
		if (application.is_installed(pm)):
			supported_architectures = application.get('Architecture')
			if (supported_architectures == '') or (supported_architectures == 'all') or (( 'amd64' in supported_architectures) and ('i386' in supported_architectures)) :
				for idefault_package in application.get('defaultpackages'):
					pkgs = installed_apps.setdefault(application.name, [])
					pkgs.append(idefault_package)
				if application.get('defaultpackagesmaster'):
					for idefault_package_master in application.get('defaultpackagesmaster'):
						pkgs = installed_apps.setdefault(application.name, [])
						pkgs.append(idefault_package_master)

	packages = {}
	for application in installed_apps:
		for package in installed_apps[application]:
			pack = c[package]
			arch = pack.installed.architecture
			if (arch == 'amd64' or arch == 'i386') and pack.installed:
				packages[package] = pack.installed.version, pack.installed.uris[0], application
	return packages


def change_architecture(package_repository):
	other_arch_packages_with_repository= {}
	for (ipackage_name, (ipackage_version, iuri, _application)) in package_repository.iteritems():
		if get_local_architecture() == 'amd64':
			other_arch_packages_with_repository[ipackage_name] = (ipackage_version, iuri.replace('amd64','i386'))
		else:
			other_arch_packages_with_repository[ipackage_name] = (ipackage_version, iuri.replace('i386','amd64'))

	return other_arch_packages_with_repository


def check_packages_available(local_arch_packages_with_repository):
	other_arch_packages_with_repository = change_architecture(local_arch_packages_with_repository)
	repository_packages = {}
	for (_name, (_version, uri)) in other_arch_packages_with_repository.iteritems():
		repository_index_url = urljoin(uri ,'Packages.gz')
		stream = urlopen(repository_index_url)
		zipdata = StringIO(stream.read())
		data = GzipFile(fileobj=zipdata)
		repository_packages.update(filter_packages_and_versions(data))

	not_found_packages = compare_versions(repository_packages, other_arch_packages_with_repository)
	if not_found_packages:
		main_repository_packages = get_main_repository_packages()
		not_found_packages = compare_versions(main_repository_packages, other_arch_packages_with_repository)
	show_result(not_found_packages, local_arch_packages_with_repository)


def compare_versions(repository_packages, not_found_packages):
	not_found_packages_temp_dict = not_found_packages.copy()
	for key in repository_packages:
		if key in not_found_packages_temp_dict:
			package_version = not_found_packages_temp_dict[key][0]
			if package_version in repository_packages[key]:
				del not_found_packages_temp_dict[key]
	return not_found_packages_temp_dict


def get_main_repository_packages():
	main_repository_packages = {}
	arch = get_local_architecture(True)
	packages_files = (
		'http://%(repository/online/server)s/%(version/version)s/maintained/%(version/version)s-%(version/patchlevel)s/{0}/Packages.gz'.format(arch) % ucr,
		'http://%(repository/online/server)s/%(version/version)s/unmaintained/%(version/version)s-%(version/patchlevel)s/{0}/Packages.gz'.format(arch) % ucr,
	)
	for iurl in packages_files:
		stream = urlopen(iurl)
		zipdata = StringIO(stream.read())
		data = GzipFile(fileobj=zipdata)
		main_repository_packages.update(filter_packages_and_versions(data))
	return main_repository_packages


def filter_packages_and_versions(temp_file):
	repository_packages = {}
	re_repository_version = re.compile(r"Version:\s+(?P<version>\S+)")
	re_package = re.compile(r"Package:\s+(?P<package>\S+)")
	for line in temp_file: 
		match = re_package.match(line)
		if match:
			package_match = match.group("package")
			continue
		match = re_repository_version.match(line)
		if match:
			if not package_match in repository_packages:
				repository_packages[package_match] = [match.group("version")]
			else:
				repository_packages[package_match].append(match.group("version"))
	return repository_packages


def show_result(not_found_packages, local_arch_packages):
	if len(not_found_packages) > 0:
		diff_arch = get_local_architecture(True)
		print
		print ("Error!")
		print ("Following package(s) could not be found for architecture " + diff_arch + ":")
		for ipackage in not_found_packages:
			print local_arch_packages[ipackage][1]
		print
		sys.exit("Test Failed: Packages not found")
	else:
		print
		print ("All packages found in repository")
		print


def get_local_architecture(reversed=False):
	if sys.maxsize > 2**32:
		if reversed:
			arch = 'i386'
		else:
			arch = 'amd64'
	else:
		if reversed:
			arch = 'amd64'
		else:
			arch = 'i386'
	return arch


if __name__ == "__main__":
	single_architecture_packages = get_installed_default_packages()

	arch = get_local_architecture()
	print
	print ("Packages with " + arch + "-only architecture :")
	for (ipackage, (_version, _uri, application)) in single_architecture_packages.iteritems():
		print (ipackage + " from App '" + application + "'")

	check_packages_available(single_architecture_packages)
