#!/usr/bin/python
#
# Copyright (C) 2007-2008  Canonical Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

import httplib

from cStringIO import StringIO
from mod_python import apache
from pysqlite2 import dbapi2 as sqlite
from xml.dom.xmlbuilder import *
from xml.dom.minidom import *
import os
import time
import thread
import threading
import sys
import plugindata

# TestURL: http://localhost/~asac/cgi-bin/PluginFinderService.php?mimetype=%PLUGIN_MIMETYPE%&appID=%APP_ID%&appVersion=%APP_VERSION%&clientOS=%CLIENT_OS%&chromeLocale=%CHROME_LOCALE%

counter=0
docroot=""
CACHE_TIME_SEC=3600
DB_DIR="/home/asac/pfsdb/"
ENABLE_MOZILLA_PFS_RESULTS=None

arch_map=dict()
cache_con_map=dict()

arch_map['x86_64']="amd64"
arch_map['i686']="i386"

def connect_db(docroot):
	apt_con = sqlite.connect(DB_DIR+"apt-plugins.sqlite")
	return apt_con

def get_db(req):
	apt_con = connect_db(req.document_root())
	return apt_con

def retrieve_proxied_result(req):
	builder = DOMBuilder()
	result_doc = None
	if ENABLE_MOZILLA_PFS_RESULTS:
		hc = httplib.HTTPSConnection("pfs.mozilla.org")
		hc.request("GET", "/plugins/PluginFinderService.php?" + req.args)
		res = hc.getresponse()
		source = DOMInputSource()
		source.byteStream = StringIO(res.read())
		result_doc = builder.parse(source)
	else:
		result_doc = Document()
		root = result_doc.createElement("RDF:RDF")
		root.setAttribute("xmlns:RDF", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
		root.setAttribute("xmlns:pfs", "http://www.mozilla.org/2004/pfs-rdf#")
		result_doc.appendChild(root)

	return result_doc


def parse_http_parameters (req):
	parameters = dict()
	for parameterblock in req.args.split("&"):
		keyvaluepair = parameterblock.split("=")
		if len(keyvaluepair) < 2:
			continue
		parameters[url_unquote(keyvaluepair[0])] = url_unquote(keyvaluepair[1])
	return parameters


def retrieve_filehint_name_map(req, parameters):
	res = ""
	apt_con = get_db(req)
	
        distribution = "7.10"
        if parameters.has_key('distributionID') and parameters['distributionID'] is not None:
		distribution = parameters['distributionID']
	cur = apt_con.cursor()
	cur.execute("SELECT distinct filehint, name FROM package " \
			+ "WHERE filehint!=\"\"") 
	for row in cur:
		res = res + "filehint: ";
		res = res + row[0];
		res = res + "\n";
		res = res + "name: ";
		res = res + row[1];
		res = res + "\n";
	return res

def retrieve_descriptions(req, parameters):
	ret = []
	apt_con = get_db(req)

	mimetype = parameters['mimetype']
	architecture = None
	if not parameters['clientOS'] is None:
		tmp = parameters['clientOS'].split(" ")
		architecture = arch_map[tmp[1]]
		if architecture is None:
			architecture = tmp[1]
	else:
		return ret # nothing to do for no arch

        distribution = "7.10"
        if parameters.has_key('distributionID') and parameters['distributionID'] is not None:
		distribution = parameters['distributionID']

	appid = parameters['appID']

	cur = apt_con.cursor()
	cur.execute("SELECT name, mimetype, pkgname, description, section, filehint FROM package " \
			+ "WHERE mimetype=? AND architecture=? AND appid=? " \
			+ "AND distribution=? order by package.weight desc", (mimetype, architecture, appid, distribution))
	for row in cur:
		apturl = "apt:" + row[2] + "?section=" + row[4]
		if row[4] == "multiverse":
			apturl = apturl + "?section=universe"
		desc = plugindata.PluginDescription ( 		\
				row[0], 	\
				row[1], 	\
				None, 		\
				None,		\
				None,		\
				apturl,	\
				None,	\
				None,	\
				None,		\
				"false", \
				row [5], \
				row [3] )
		ret.append(desc)

	cur.close()

	return ret		

def retrieve_ubuntu_results(req, http_parameters):
	description_results = []
	description_results = retrieve_descriptions(req, http_parameters)
	return description_results

def url_unquote(s):
	res = s.split('%')
	for i in xrange(1, len(res)):
		item = res[i]
		try:
			res[i] = unichr(int(item[:2], 16)) + item[2:]
		except KeyError:
			res[i] = '%' + item
		except ValueError:
			res[i] = '%' + item

	return "".join(res)

def get_cache_con():
	if not cache_con_map.has_key(threading._get_ident()):
		cache_con = sqlite.connect(":memory:")
		cur = cache_con.cursor()
		cur.execute("create table plugin_result_cache(url string, timestamp long, content string)")
		cur.close()
		cache_con_map[threading._get_ident()] = cache_con
	con = cache_con_map[threading._get_ident()]
	return con

def inject_ubuntu_descriptions_in_upstream_result (resxml, ubuntu_descriptions, mimetype):
	result_list = None

	node = resxml.documentElement.firstChild
	seq_r = None
	while not node is None:
		nextNode = node.nextSibling
		if node.nodeType == node.ELEMENT_NODE:
			about = node.getAttribute("about")
			if not about is None and about.rfind("urn:mozilla:plugin-results:") == 0:
				item = node.getElementsByTagName("RDF:Seq").item(0)
				seq_r = item
				result_list = node
				break
		node = nextNode

	if not result_list:
		result_list = resxml.createElement("RDF:Description")

	if seq_r is None:
		result_list.setAttribute("about", "urn:mozilla:plugin-results:"+mimetype)
		pluginsElement = resxml.createElement("pfs:plugins")
		result_list.appendChild(pluginsElement)
		seq_r = resxml.createElement("RDF:Seq")
		pluginsElement.appendChild(seq_r)
		resxml.documentElement.appendChild(result_list)

	for description in ubuntu_descriptions:
		result_list.setAttribute("about", "urn:mozilla:plugin-results:"+description.requestedMimetype)
		update_list = resxml.createElement("RDF:Description")
		update_list.setAttribute("about", "urn:mozilla:plugin:"+ description.guid)
		resxml.documentElement.appendChild(update_list)
		updatesElement = resxml.createElement("pfs:updates")
		update_list.appendChild(updatesElement)
		seq_u = resxml.createElement("RDF:Seq")
		updatesElement.appendChild(seq_u)

		main_element = description.to_element(resxml)
		resxml.documentElement.appendChild(main_element);
		li_r = resxml.createElement("RDF:li")
		li_u = resxml.createElement("RDF:li")
		li_r.setAttribute("resource", "urn:mozilla:plugin:" + description.guid)
		li_u.setAttribute("resource", description.id)
		seq_r.appendChild(li_r)
		seq_u.appendChild(li_u)

	node = seq_r.lastChild
	while not node is None:
		nextNode = node.previousSibling
		if node.nodeType == node.ELEMENT_NODE and node.getAttribute("resource") == "urn:mozilla:plugin:-1":
			seq_r.removeChild(node)
		
		node = nextNode



def get_cached_and_cache(req, http_params):
	global counter
	counter= (counter + 1) % 2
	req.content_type = 'text/xml'

	if counter == 0:
		now = int(time.time() * 1000)
		low_time = now - CACHE_TIME_SEC * 1000
		cur = get_cache_con().cursor()
		cur.execute("delete from plugin_result_cache where timestamp <= ?", (low_time,))
		cur.close()

	key = req.args
	found = False
	result = None
	cur = get_cache_con().cursor()
	cur.execute("select timestamp, content from plugin_result_cache where url = ?", (key,))
	for row in cur:
		found = True
		result = row[1]

	cur.close()

	if found:
		print "found!"
		return result

	resxml = retrieve_proxied_result(req)
	ubuntu_descriptions = retrieve_ubuntu_results(req, http_params)

	inject_ubuntu_descriptions_in_upstream_result(resxml, ubuntu_descriptions, http_params['mimetype'])

	result_string = resxml.toxml()

	now = int(time.time() * 1000)
	cur = get_cache_con().cursor()
	cur.execute("insert into plugin_result_cache (timestamp, url, content) values (?, ?, ?)", (now, key, result_string))
	return result_string

def get_filehint_and_name(req, http_params):
	req.content_type = 'text/plain'
	res = retrieve_filehint_name_map( req, http_params)
	return res

apt_con_lock = thread.allocate_lock()
apt_plugin_db_updater=None
error_txt=""

def handler(req):
	global apt_plugin_db_updater
	pfsdbdir =  req.document_root() + "/pfsdb/"
	http_params = parse_http_parameters(req)
	op = None
	if http_params.has_key('op'):
		op = http_params['op']

	if op == "filehint2name":
		res = get_filehint_and_name (req, http_params)
	else:	
		res = get_cached_and_cache(req, http_params)
	req.write(res)
	return apache.OK

