From 9b49c4bb488ce375972001fca03b39eb0c76a8b9 Mon Sep 17 00:00:00 2001
From: mattpiwik <matthieu.aubry@gmail.com>
Date: Mon, 6 Aug 2007 19:59:48 +0000
Subject: [PATCH] Finished main algorithm for the Logging script. Still todo:
 the "Ip to provider" plugin.

git-svn-id: http://dev.piwik.org/svn/trunk@28 59fd770c-687e-43c8-a1e3-f5a4ff64c105
---
 config/config.ini.php               |    9 +-
 misc/stress.sh                      |    5 +
 modules/Common.php                  |   53 +-
 modules/DataFiles/SearchEngines.php | 1040 +++++++++++++++++++
 modules/LogStats.php                | 1454 +++++++++++++++++++++++++++
 modules/LogStats/Plugins.php        |   52 +
 modules/Piwik.php                   |   74 +-
 modules/PluginManager.php           |  158 +++
 piwik.php                           | 1285 +----------------------
 tests/modules/Common.test.php       |   95 ++
 10 files changed, 2908 insertions(+), 1317 deletions(-)
 create mode 100755 misc/stress.sh
 create mode 100644 modules/DataFiles/SearchEngines.php
 create mode 100644 modules/LogStats.php
 create mode 100644 modules/LogStats/Plugins.php
 create mode 100644 modules/PluginManager.php

diff --git a/config/config.ini.php b/config/config.ini.php
index b73cbcd7ee..bc4aeef7d5 100755
--- a/config/config.ini.php
+++ b/config/config.ini.php
@@ -18,11 +18,18 @@ tables_prefix	= piwiktests_
 [LogStats]
 ; set to 0 if you want to stop tracking the visitors. Useful if you need to stop all the connections on the DB.
 record_statistics			= 1
+
 default_action_name 		= index
-default_time_one_page_visit = 20
+default_time_one_page_visit = 10
 download_url_var_name 		= download
 outlink_url_var_name		= link
 download_outlink_name_var   = name
+newsletter_var_name			= piwik_nl
+partner_var_name			= piwik_partner
+campaign_var_name			= piwik_campaign
+campaign_keyword_var_name	= piwik_kwd
+
+cookie_name	= piwik_visitor
 
 [log]
 
diff --git a/misc/stress.sh b/misc/stress.sh
new file mode 100755
index 0000000000..22a1c10ce7
--- /dev/null
+++ b/misc/stress.sh
@@ -0,0 +1,5 @@
+echo "
+Stress testing piwik.php
+========================
+"
+ab -n500 -c50 "http://localhost/dev/piwiktrunk/piwik.php"
diff --git a/modules/Common.php b/modules/Common.php
index f007568685..dfbca20a56 100644
--- a/modules/Common.php
+++ b/modules/Common.php
@@ -9,6 +9,15 @@
  */
 class Piwik_Common 
 {
+	const REFERER_TYPE_DIRECT_ENTRY		= 1;
+	const REFERER_TYPE_SEARCH_ENGINE	= 2;
+	const REFERER_TYPE_WEBSITE			= 3;
+	const REFERER_TYPE_PARTNER			= 4;
+	const REFERER_TYPE_NEWSLETTER		= 5;
+	const REFERER_TYPE_CAMPAIGN			= 6;
+	
+	const HTML_ENCODING_QUOTE_STYLE		= ENT_COMPAT;
+	
 	/**
 	 * Returns the variable after cleaning operations.
 	 * NB: The variable still has to be escaped before going into a SQL Query!
@@ -50,7 +59,7 @@ class Piwik_Common
 		}
 		elseif(is_string($value))
 		{
-			$value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
+			$value = htmlspecialchars($value, Piwik_Common::HTML_ENCODING_QUOTE_STYLE, 'UTF-8');
 
 			/* Undo the damage caused by magic_quotes */
 			if (get_magic_quotes_gpc()) 
@@ -68,6 +77,12 @@ class Piwik_Common
 		return $value;
     }
 
+
+	static public function getDatetimeFromTimestamp($timestamp)
+	{
+		return date("Y-m-d H:i:s",$timestamp);
+	}
+	
 	/**
 	 * Returns a variable from the $_REQUEST superglobal.
 	 * If the variable doesn't have a value or an empty value, returns the defaultValue if specified.
@@ -388,7 +403,7 @@ class Piwik_Common
 	*/
 	function getContinent($country)
 	{
-		require_once PIWIK_INCLUDE_PATH . "/modules/DataFiles/Countries.php";
+		require_once PIWIK_DATAFILES_INCLUDE_PATH . "/Countries.php";
 		
 		$countryList = $GLOBALS['Piwik_CountryList'];
 		
@@ -411,7 +426,7 @@ class Piwik_Common
 	*/
 	function getCountry( $lang )
 	{
-		require_once PIWIK_INCLUDE_PATH . "/modules/DataFiles/Countries.php";
+		require_once PIWIK_DATAFILES_INCLUDE_PATH . "/Countries.php";
 		
 		$countryList = $GLOBALS['Piwik_CountryList'];
 		
@@ -507,7 +522,37 @@ class Piwik_Common
 		// at this point we really can't guess the country
 		return 'xx';
 	}
-		
+	
+	/**
+	* Returns the value of a GET parameter $parameter in an URL query $urlQuery
+	* 
+	* @param string $urlQuery result of parse_url()['query'] and htmlentitied (& is &amp;)
+	* @param string $param
+	* 
+	* @return string|bool Parameter value if found (can be the empty string!), false if not found
+	*/
+	static public function getParameterFromQueryString( $urlQuery, $parameter)
+	{	
+		$refererQuery = '&amp;'.trim(str_replace(array('%20'), ' ', '&amp;'.$urlQuery));
+		$word = '&amp;'.$parameter.'=';
+				
+		if( $off = strrpos($refererQuery, $word))
+		{
+			$off += strlen($word); // &amp;q=
+			$str = substr($refererQuery, $off);
+			$len = strpos($str, '&amp;');
+			if($len === false)
+			{
+				$len = strlen($str);
+			}
+			$toReturn = substr($refererQuery, $off, $len);
+			return $toReturn;
+		}
+		else
+		{
+			return false;
+		}
+	}
 	
 }
 ?>
diff --git a/modules/DataFiles/SearchEngines.php b/modules/DataFiles/SearchEngines.php
new file mode 100644
index 0000000000..cec3cc33b0
--- /dev/null
+++ b/modules/DataFiles/SearchEngines.php
@@ -0,0 +1,1040 @@
+<?php
+
+if(!isset($GLOBALS['Piwik_SearchEngines'] ))
+{
+	$GLOBALS['Piwik_SearchEngines'] = array(
+	
+		//" "		=> array(" ", " " [, " "]),
+		
+		// 1
+		"1.cz" 						=> array("1.cz", "q", "iso-8859-2"),
+		"www.1.cz" 					=> array("1.cz", "q", "iso-8859-2"),
+		
+		// 1und1
+		"portal.1und1.de"			=> array("1und1", "search"),
+		
+		// 3271
+		"nmsearch.3721.com"			=> array("3271", "p"),
+		"seek.3721.com"				=> array("3271", "p"),
+		
+		// A9
+		"www.a9.com"				=> array("A9", ""),
+		"a9.com"					=> array("A9", ""),
+		
+		// Abacho
+		"search.abacho.com"		=> array("Abacho", "q"),
+		
+		// about
+		"search.about.com"		=> array("About", "terms"),
+		
+		//Acoon
+		"www.acoon.de"			=> array("Acoon", "begriff"),
+		
+		//Acont
+		"acont.de"			=> array("Acont", "query"),
+		
+		//Alexa
+		"www.alexa.com"		        => array("Alexa", "q"),
+		"alexa.com"		        => array("Alexa", "q"),
+		
+		//Alice Adsl
+		"rechercher.aliceadsl.fr"	=> array("Alice Adsl", "qs"),
+		"search.alice.it"		      => array("Alice (Virgilio)", "qt"),
+		
+		//Allesklar
+		"www.allesklar.de"		=> array("Allesklar", "words"),
+		
+		// AllTheWeb 
+		"www.alltheweb.com"		    => array("AllTheWeb", "q"),
+		
+		// all.by
+		"all.by"			=> array("All.by", "query"),
+		
+		// Altavista
+		"listings.altavista.com"        => array("AltaVista", "q"),
+		"www.altavista.de"		=> array("AltaVista", "q"),
+		"altavista.fr"			=> array("AltaVista", "q"),
+		"de.altavista.com"		=> array("AltaVista", "q"),
+		"fr.altavista.com"		=> array("AltaVista", "q"),
+		"es.altavista.com"		=> array("AltaVista", "q"),
+		"www.altavista.fr"		=> array("AltaVista", "q"),
+		"search.altavista.com"		=> array("AltaVista", "q"),
+		"search.fr.altavista.com"	=> array("AltaVista", "q"),
+		"se.altavista.com"		=> array("AltaVista", "q"),
+		"be-nl.altavista.com" 		=> array("AltaVista", "q"),
+		"be-fr.altavista.com" 		=> array("AltaVista", "q"),
+		"it.altavista.com" 		=> array("AltaVista", "q"),
+		"us.altavista.com" 		=> array("AltaVista", "q"),
+		"nl.altavista.com" 		=> array("Altavista", "q"),
+		"ch.altavista.com" 		=> array("AltaVista", "q"),
+		"www.altavista.com"		=> array("AltaVista", "q"),
+		
+		// APOLLO7
+		"www.apollo7.de"		=> array("Apollo7", "query"),
+		"apollo7.de"			=> array("Apollo7", "query"),
+		
+		// AOL
+		"www.aolrecherche.aol.fr"	=> array("AOL", "q"),
+		"www.aolrecherches.aol.fr" 	=> array("AOL", "query"),
+		"www.aolimages.aol.fr"   	=> array("AOL", "query"),
+		"www.recherche.aol.fr"		=> array("AOL", "q"),
+		"aolsearch.aol.com"		=> array("AOL", "query"),
+		"aolsearcht.aol.com"		=> array("AOL", "query"),
+		"find.web.aol.com"		=> array("AOL", "query"),
+		"recherche.aol.ca"		=> array("AOL", "query"),
+		"aolsearch.aol.co.uk"		=> array("AOL", "query"),
+		"search.aol.co.uk"		=> array("AOL", "query"),
+		"aolrecherche.aol.fr"		=> array("AOL", "q"),
+		"sucheaol.aol.de"		=> array("AOL", "q"),
+		"suche.aol.de"			=> array("AOL", "q"),
+		
+		"aolbusqueda.aol.com.mx"	=> array("AOL", "query"),
+		"search.aol.com"		=> array("AOL", "query"),
+		
+		// Aport
+		"sm.aport.ru"			=> array("Aport", "r"),
+		
+		// Arcor
+		"www.arcor.de"			=> array("Arcor", "Keywords"),
+		
+		// Arianna (Libero.it)
+		"arianna.libero.it" 		=> array("Arianna", "query"),
+		
+		// Ask
+		"web.ask.com"			=> array("Ask", "ask"),
+		"www.ask.co.uk"			=> array("Ask", "q"),
+		"uk.ask.com"			=> array("Ask", "q"),
+		"fr.ask.com"			=> array("Ask", "q"),
+		"de.ask.com"			=> array("Ask", "q"),
+		"es.ask.com"			=> array("Ask", "q"),
+		"it.ask.com"			=> array("Ask", "q"),
+		"nl.ask.com"			=> array("Ask", "q"),
+		"ask.jp"			=> array("Ask", "q"),
+		"www.ask.com"			=> array("Ask", "ask"),
+		
+		// Atlas
+		"search.atlas.cz" 		=> array("Atlas", "q", "windows-1250"),
+		
+		// Austronaut
+		"www2.austronaut.at"		=> array("Austronaut", "begriff"),
+		
+		// Baidu
+		"www1.baidu.com"		=> array("Baidu", "wd"),
+		"www.baidu.com"			=> array("Baidu", "wd"),
+		
+		// BBC
+		"search.bbc.co.uk"	        => array("BBC", "q"),
+		
+		// Bellnet
+		"www.suchmaschine.com"		 => array("Bellnet", "suchstr"),
+		
+		// Biglobe
+		"cgi.search.biglobe.ne.jp"	=> array("Biglobe", "q"),
+		
+		// Bild
+		"www.bild.t-online.de"	        => array("Bild.de (enhanced by Google)", "query"),
+		
+		//Blogdigger
+		"www.blogdigger.com"		=> array("Blogdigger","q"),
+		
+		//Bloglines
+		"www.bloglines.com"		=> array("Bloglines","q"),
+		
+		//Blogpulse
+		"www.blogpulse.com"		=> array("Blogpulse","query"),
+		
+		//Bluewin
+		"search.bluewin.ch"		=> array("Bluewin","query"),
+		
+		// Caloweb
+		"www.caloweb.de"		=> array("Caloweb", "q"),
+		
+		// Cegetel (Google)
+		"www.cegetel.net" 		=> array("Cegetel (Google)", "q"),
+		
+		// Centrum
+		"fulltext.centrum.cz" 		=> array("Centrum", "q", "windows-1250"),
+		"morfeo.centrum.cz" 		=> array("Centrum", "q", "windows-1250"),
+		"search.centrum.cz" 		=> array("Centrum", "q", "windows-1250"),
+		
+		// Chello
+		"www.chello.fr"		 	=> array("Chello", "q1"),
+		
+		// Club Internet
+		"recherche.club-internet.fr"    => array("Club Internet", "q"),
+		
+		// Comcast
+		"www.comcast.net" 		=> array("Comcast", "query"),
+		
+		// Comet systems
+		"search.cometsystems.com"	=> array("CometSystems", "q"),
+		
+		// Compuserve
+		"suche.compuserve.de"	        => array("Compuserve.de (Powered by Google)", "q"),
+		"websearch.cs.com"		     => array("Compuserve.com (Enhanced by Google)", "query"),
+		
+		// Copernic
+		"metaresults.copernic.com"	=> array("Copernic", " "),
+		
+		// DasOertliche
+		"www.dasoertliche.de"	        => array("DasOertliche", "kw"),
+		
+		// DasTelefonbuch
+		"www.4call.dastelefonbuch.de"	=> array("DasTelefonbuch", "kw"),
+		
+		// Defind.de
+		"suche.defind.de"	        => array("Defind.de", "search"),
+		
+		// Deskfeeds
+		"www.deskfeeds.com"	        => array("Deskfeeds", "sx"),
+		
+		// Dino
+		"www.dino-online.de"		=> array("Dino", "query"),
+		
+		// dir.com
+		"fr.dir.com" 			=> array("dir.com", "req"),
+		
+		// dmoz
+		"editors.dmoz.org"		=> array("dmoz", "search"),
+		"search.dmoz.org"		=> array("dmoz", "search"),
+		"www.dmoz.org"			=> array("dmoz", "search"),
+		"dmoz.org"			=> array("dmoz", "search"),
+		
+		// Dogpile
+		"search.dogpile.com"		=> array("Dogpile", "q"),
+		"nbci.dogpile.com"		=> array("Dogpile", "q"),
+		
+		// earthlink
+		"search.earthlink.net"		=> array("Earthlink", "q"),
+		
+		// Eniro
+		"www.eniro.se" 			=> array("Eniro", "q"),
+		
+		// Espotting 
+		"affiliate.espotting.fr"	=> array("Espotting", "keyword"),
+		
+		// Eudip
+		"www.eudip.com"			=> array("Eudip", " "),
+		
+		// Eurip
+		"www.eurip.com"			=> array("Eurip", "q"),
+		
+		// Euroseek
+		"www.euroseek.com"		=> array("Euroseek", "string"),
+		
+		// Excite
+		"www.excite.it" 		=> array("Excite", "q"),
+		"msxml.excite.com"		=> array("Excite", "qkw"),
+		"www.excite.fr"			=> array("Excite", "search"),
+		
+		// Exalead
+		"www.exalead.fr"		=> array("Exalead", "q"),
+		"www.exalead.com"		=> array("Exalead", "q"),
+		
+		// eo
+		"eo.st"				=> array("eo", "q"),
+		
+		// Feedminer
+		"www.feedminer.com"		=> array("Feedminer", "q"),
+		
+		// Feedster
+		"www.feedster.com"		=> array("Feedster", ""),
+		
+		// Francite
+		"antisearch.francite.com"	=> array("Francite", "KEYWORDS"),
+		"recherche.francite.com"	=> array("Francite", "name"),
+		
+		// Fireball
+		"suche.fireball.de"		=> array("Fireball", "query"),
+		
+		
+		// Firstfind
+		"www.firstsfind.com"		=> array("Firstsfind", "qry"),
+		
+		// Fixsuche
+		"www.fixsuche.de"		=> array("Fixsuche", "q"),
+		
+		// Flix
+		"www.flix.de"			=> array("Flix.de", "keyword"),
+		
+		// Free
+		"search1-2.free.fr"		=> array("Free", "q"),
+		"search1-1.free.fr"		=> array("Free", "q"),
+		"search.free.fr"		=> array("Free", "q"),
+		
+		// Freenet
+		"suche.freenet.de"		=> array("Freenet", "query"),
+		
+		//Froogle
+		"froogle.google.de" 		=> array("Google (Froogle)", "q"),
+		"froogle.google.com" 		=> array("Google (Froogle)", "q"),
+		"froogle.google.co.uk" 		=> array("Google (Froogle)", "q"),
+		
+		//GAIS
+		"gais.cs.ccu.edu.tw" 		=> array("GAIS)", "query"),
+		
+		// Gigablast
+		"www.gigablast.com" 		=> array("Gigablast", "q"),
+		"blogs.gigablast.com" 		=> array("Gigablast (Blogs)", "q"),
+		"travel.gigablast.com" 		=> array("Gigablast (Travel)", "q"),
+		"dir.gigablast.com" 		=> array("Gigablast (Directory)", "q"),
+		"gov.gigablast.com" 		=> array("Gigablast (Gov)", "q"),
+		
+		// GMX
+		"suche.gmx.net"			=> array("GMX", "su"),
+		"www.gmx.net"			=> array("GMX", "su"),
+		
+		// goo
+		"search.goo.ne.jp"		=> array("goo", "mt"),
+		"ocnsearch.goo.ne.jp"		=> array("goo", "mt"),
+		
+		
+		// Powered by Google (add or not?)
+		"www.charter.net" 		=> array("Google", "q"),
+		"brisbane.t-online.de" 	        => array("Google", "q"),
+		"www.eniro.se" 			=> array("Google", "q"),
+		"www.eniro.no" 			=> array("Google", "q"),
+		"miportal.bellsouth.net"        => array("Google", "string"),
+		"home.bellsouth.net"	        => array("Google", "string"),
+		"pesquisa.clix.pt" 		=> array("Google", "q"),
+		"google.startsiden.no" 	        => array("Google", "q"),
+		"arianna.libero.it" 	        => array("Google", "query"),
+		"google.startpagina.nl"		=> array("Google", "q"),
+		"search.peoplepc.com" 	        => array("Google", "q"),
+		"www.google.interia.pl"		=> array("Google", "q"),
+		"buscador.terra.es" 	        => array("Google", "query"),
+		"buscador.terra.cl" 	        => array("Google", "query"),
+		"buscador.terra.com.br"		=> array("Google", "query"),
+		"www.icq.com" 			=> array("Google", "q"),
+		"www.adelphia.net" 		=> array("Google", "q"),
+		"www.comcast.net" 		=> array("Google", "query"),
+		"so.qq.com" 			=> array("Google", "word"),
+		"misc.skynet.be" 		=> array("Google", "keywords"),
+		"www.start.no" 			=> array("Google", "q"),
+		"verden.abcsok.no"		=> array("Google", "q"),
+		"search.sweetim.com"	        => array("Google", "q"),
+		
+		// Google
+		"gogole.fr"				=> array("Google", "q"),
+		"www.gogole.fr"			=> array("Google", "q"),
+		"wwwgoogle.fr"			=> array("Google", "q"),
+		"ww.google.fr"			=> array("Google", "q"),
+		"w.google.fr"			=> array("Google", "q"),
+		"www.google.fr"			=> array("Google", "q"),
+		"www.google.fr."		=> array("Google", "q"),
+		"google.fr"				=> array("Google", "q"),
+		"www2.google.com"		=> array("Google", "q"),
+		"w.google.com"			=> array("Google", "q"),
+		"ww.google.com"			=> array("Google", "q"),
+		"wwwgoogle.com"		    => array("Google", "q"),
+		"www.gogole.com"		=> array("Google", "q"),
+		"www.gppgle.com"		=> array("Google", "q"),
+		"go.google.com"			=> array("Google", "q"),
+		"www.google.ae"			=> array("Google", "q"),
+		"www.google.as"			=> array("Google", "q"),
+		"www.google.at"			=> array("Google", "q"),
+		"wwwgoogle.at"			=> array("Google", "q"),
+		"ww.google.at"			=> array("Google", "q"),
+		"w.google.at"			=> array("Google", "q"),
+		"www.google.az"			=> array("Google", "q"),
+		"www.google.be"			=> array("Google", "q"),
+		"www.google.bg"			=> array("Google", "q"),
+		"www.google.ba"			=> array("Google", "q"),
+		"google.bg"				=> array("Google", "q"),
+		"www.google.bi"			=> array("Google", "q"),
+		"www.google.ca"			=> array("Google", "q"),
+		"ww.google.ca"			=> array("Google", "q"),
+		"w.google.ca"			=> array("Google", "q"),
+		"www.google.cc"			=> array("Google", "q"),
+		"www.google.cd"			=> array("Google", "q"),
+		"www.google.cg"			=> array("Google", "q"),
+		"www.google.ch"			=> array("Google", "q"),
+		"ww.google.ch"			=> array("Google", "q"),
+		"w.google.ch"			=> array("Google", "q"),
+		"www.google.ci"			=> array("Google", "q"),
+		"www.google.cl"			=> array("Google", "q"),
+		"www.google.cn"			=> array("Google", "q"),
+		"www.google.co"			=> array("Google", "q"),
+		"www.google.cz"			=> array("Google", "q"),
+		"wwwgoogle.cz"			=> array("Google", "q"),
+		"www.google.de"			=> array("Google", "q"),
+		"ww.google.de"			=> array("Google", "q"),
+		"w.google.de"			=> array("Google", "q"),
+		"wwwgoogle.de" 			=> array("Google", "q"),
+		"www.googleearth.de" 	=> array("Google", "q"),
+		"googleearth.de"		=> array("Google", "q"),
+		"google.gr"				=> array("Google", "q"),
+		"google.hr"				=> array("Google", "q"),
+		"www.google.dj"			=> array("Google", "q"),
+		"www.google.dk"			=> array("Google", "q"),
+		"www.google.es"			=> array("Google", "q"),
+		"www.google.fi"			=> array("Google", "q"),
+		"www.google.fm"			=> array("Google", "q"),
+		"www.google.gg"			=> array("Google", "q"),
+		"www.googel.fi"			=> array("Google", "q"),
+		"www.googleearth.fr"	=> array("Google", "q"),
+		"www.google.gl"			=> array("Google", "q"),
+		"www.google.gm"			=> array("Google", "q"),
+		"www.google.gr"			=> array("Google", "q"),
+		"www.google.hn"			=> array("Google", "q"),
+		"www.google.hr"			=> array("Google", "q"),
+		"www.google.hu"			=> array("Google", "q"),
+		"www.google.ie"			=> array("Google", "q"),
+		"www.google.is"			=> array("Google", "q"),
+		"www.google.it"			=> array("Google", "q"),
+		"www.google.jo"			=> array("Google", "q"),
+		"www.google.kz"			=> array("Google", "q"),
+		"www.google.li"			=> array("Google", "q"),
+		"www.google.lt"			=> array("Google", "q"),
+		"www.google.lu"			=> array("Google", "q"),
+		"www.google.lv"			=> array("Google", "q"),
+		"www.google.ms"			=> array("Google", "q"),
+		"www.google.mu"			=> array("Google", "q"),
+		"www.google.mw"			=> array("Google", "q"),
+		"www.google.md"			=> array("Google", "q"),
+		"www.google.nl"			=> array("Google", "q"),
+		"www.google.no"			=> array("Google", "q"),
+		"www.google.pl"			=> array("Google", "q"),
+		"www.google.sk" 		=> array("Google", "q"),
+		"www.google.pn"			=> array("Google", "q"),
+		"www.google.pt"			=> array("Google", "q"),
+		"www.google.dk"			=> array("Google", "q"),
+		"www.google.ro"			=> array("Google", "q"),
+		"www.google.ru"			=> array("Google", "q"),
+		"www.google.rw"			=> array("Google", "q"),
+		"www.google.se"			=> array("Google", "q"),
+		"www.google.sn"			=> array("Google", "q"),
+		"www.google.sh"			=> array("Google", "q"),
+		"www.google.si"			=> array("Google", "q"),
+		"www.google.sm" 		=> array("Google", "q"),
+		"www.google.td"			=> array("Google", "q"),
+		"www.google.tt"			=> array("Google", "q"),
+		"www.google.uz"			=> array("Google", "q"),
+		"www.google.vg"			=> array("Google", "q"),
+		"www.google.com.ar"		=> array("Google", "q"),
+		"www.google.com.au"		=> array("Google", "q"),
+		"www.google.com.bo"		=> array("Google", "q"),
+		"www.google.com.br"		=> array("Google", "q"),
+		"www.google.com.co"		=> array("Google", "q"),
+		"www.google.com.cu"		=> array("Google", "q"),
+		"www.google.com.ec"		=> array("Google", "q"),
+		"www.google.com.eg"		=> array("Google", "q"),
+		"www.google.com.do"		=> array("Google", "q"),
+		"www.google.com.fj"		=> array("Google", "q"),
+		"www.google.com.gr" 	=> array("Google", "q"),
+		"www.google.com.gt" 	=> array("Google", "q"),
+		"www.google.com.hk"		=> array("Google", "q"),
+		"www.google.com.ly"		=> array("Google", "q"),
+		"www.google.com.mt"		=> array("Google", "q"),
+		"www.google.com.mx"		=> array("Google", "q"),
+		"www.google.com.my"		=> array("Google", "q"),
+		"www.google.com.nf"		=> array("Google", "q"),
+		"www.google.com.ni"		=> array("Google", "q"),
+		"www.google.com.np"		=> array("Google", "q"),
+		"www.google.com.pa"		=> array("Google", "q"),
+		"www.google.com.pe" 	=> array("Google", "q"),
+		"www.google.com.ph"		=> array("Google", "q"),
+		"www.google.com.pk"		=> array("Google", "q"),
+		"www.google.com.pl"		=> array("Google", "q"),
+		"www.google.com.pr"		=> array("Google", "q"),
+		"www.google.com.py"		=> array("Google", "q"),
+		"www.google.com.qa"		=> array("Google", "q"),
+		"www.google.com.om"		=> array("Google", "q"),
+		"www.google.com.ru"		=> array("Google", "q"),
+		"www.google.com.sg"		=> array("Google", "q"),
+		"www.google.com.sa"		=> array("Google", "q"),
+		"www.google.com.sv"		=> array("Google", "q"),
+		"www.google.com.tr"		=> array("Google", "q"),
+		"www.google.com.tw"		=> array("Google", "q"),
+		"www.google.com.ua"		=> array("Google", "q"),
+		"www.google.com.uy"		=> array("Google", "q"),
+		"www.google.com.vc"		=> array("Google", "q"),
+		"www.google.com.vn"		=> array("Google", "q"),
+		"www.google.co.cr"		=> array("Google", "q"),
+		"www.google.co.gg"		=> array("Google", "q"),
+		"www.google.co.hu"		=> array("Google", "q"),
+		"www.google.co.id"		=> array("Google", "q"),
+		"www.google.co.il"		=> array("Google", "q"),
+		"www.google.co.in" 		=> array("Google", "q"),
+		"www.google.co.je"		=> array("Google", "q"),
+		"www.google.co.jp"		=> array("Google", "q"),
+		"www.google.co.ls"		=> array("Google", "q"),
+		"www.google.co.ke" 		=> array("Google", "q"),
+		"www.google.co.kr"		=> array("Google", "q"),
+		"www.google.co.nz"		=> array("Google", "q"),
+		"www.google.co.th"		=> array("Google", "q"),
+		"www.google.co.uk"		=> array("Google", "q"),
+		"www.google.co.ve"		=> array("Google", "q"),
+		"www.google.co.za" 		=> array("Google", "q"),
+		"www.google.co.ma"		=> array("Google", "q"),
+		"www.goggle.com"		=> array("Google", "q"),
+		"www.google.com"		=> array("Google", "q"),
+		
+		//Google Blogsearch
+		"blogsearch.google.de"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.fr"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.co.uk"	=> array("Google Blogsearch", "q"),
+		"blogsearch.google.it"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.net"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.es"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.ru"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.be"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.nl"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.at"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.ch"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.pl"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.com"		=> array("Google Blogsearch", "q"),
+		
+		
+		// Google translation
+		"translate.google.com"		=> array("Google Translations", "q"),
+		
+		// Google Directory
+		"directory.google.com"		=> array("Google Directory", " "),
+		
+		// Google Images
+		"images.google.fr"		=> array("Google Images", "q"),
+		"images.google.be" 		=> array("Google Images", "q"),
+		"images.google.ca" 		=> array("Google Images", "q"),
+		"images.google.co.uk"		=> array("Google Images", "q"),
+		"images.google.de" 		=> array("Google Images", "q"),
+		"images.google.be" 		=> array("Google Images", "q"),
+		"images.google.ca" 		=> array("Google Images", "q"),
+		"images.google.it"    		=> array("Google Images", "q"),
+		"images.google.at"		=> array("Google Images", "q"),
+		"images.google.bg"		=> array("Google Images", "q"),
+		"images.google.ch"		=> array("Google Images", "q"),
+		"images.google.ci"		=> array("Google Images", "q"),
+		"images.google.com.au"		=> array("Google Images", "q"),
+		"images.google.com.cu"		=> array("Google Images", "q"),
+		"images.google.co.id"		=> array("Google Images", "q"),
+		"images.google.co.il"		=> array("Google Images", "q"),
+		"images.google.co.in"		=> array("Google Images", "q"),
+		"images.google.co.jp"		=> array("Google Images", "q"),
+		"images.google.co.hu"		=> array("Google Images", "q"),
+		"images.google.co.kr"		=> array("Google Images", "q"),
+		"images.google.co.nz"		=> array("Google Images", "q"),
+		"images.google.co.th"		=> array("Google Images", "q"),
+		"images.google.co.tw"		=> array("Google Images", "q"),
+		"images.google.co.ve"		=> array("Google Images", "q"),
+		"images.google.com.ar"		=> array("Google Images", "q"),
+		"images.google.com.br"		=> array("Google Images", "q"),
+		"images.google.com.cu"		=> array("Google Images", "q"),
+		"images.google.com.do"		=> array("Google Images", "q"),
+		"images.google.com.gr"		=> array("Google Images", "q"),
+		"images.google.com.hk"		=> array("Google Images", "q"),
+		"images.google.com.mx"		=> array("Google Images", "q"),
+		"images.google.com.my"		=> array("Google Images", "q"),
+		"images.google.com.pe"		=> array("Google Images", "q"),
+		"images.google.com.tr"		=> array("Google Images", "q"),
+		"images.google.com.tw"		=> array("Google Images", "q"),
+		"images.google.com.ua"		=> array("Google Images", "q"),
+		"images.google.com.vn"		=> array("Google Images", "q"),
+		"images.google.dk"		=> array("Google Images", "q"),
+		"images.google.es"		=> array("Google Images", "q"),
+		"images.google.fi"		=> array("Google Images", "q"),
+		"images.google.gg"		=> array("Google Images", "q"),
+		"images.google.gr"		=> array("Google Images", "q"),
+		"images.google.it"		=> array("Google Images", "q"),
+		"images.google.ms"		=> array("Google Images", "q"),
+		"images.google.nl"		=> array("Google Images", "q"),
+		"images.google.no"		=> array("Google Images", "q"),
+		"images.google.pl"		=> array("Google Images", "q"),
+		"images.google.pt"		=> array("Google Images", "q"),
+		"images.google.ro"		=> array("Google Images", "q"),
+		"images.google.ru"		=> array("Google Images", "q"),
+		"images.google.se"		=> array("Google Images", "q"),
+		"images.google.sk"		=> array("Google Images", "q"),
+		"images.google.com"		=> array("Google Images", "q"),
+		
+		// Google News
+		"news.google.se" 		=> array("Google News", "q"),
+		"news.google.com" 		=> array("Google News", "q"),
+		"news.google.es" 		=> array("Google News", "q"),
+		"news.google.ch" 		=> array("Google News", "q"),
+		"news.google.lt" 		=> array("Google News", "q"),
+		"news.google.ie" 		=> array("Google News", "q"),
+		"news.google.de" 		=> array("Google News", "q"),
+		"news.google.cl" 		=> array("Google News", "q"),
+		"news.google.com.ar" 		=> array("Google News", "q"),
+		"news.google.fr" 		=> array("Google News", "q"),
+		"news.google.ca" 		=> array("Google News", "q"),
+		"news.google.co.uk" 		=> array("Google News", "q"),
+		"news.google.co.jp" 		=> array("Google News", "q"),
+		"news.google.com.pe" 		=> array("Google News", "q"),
+		"news.google.com.au" 		=> array("Google News", "q"),
+		"news.google.com.mx" 		=> array("Google News", "q"),
+		"news.google.com.hk" 		=> array("Google News", "q"),
+		"news.google.co.in" 		=> array("Google News", "q"),
+		"news.google.at" 		=> array("Google News", "q"),
+		"news.google.com.tw" 		=> array("Google News", "q"),
+		"news.google.com.co" 		=> array("Google News", "q"),
+		"news.google.co.ve" 		=> array("Google News", "q"),
+		"news.google.lu" 		=> array("Google News", "q"),
+		"news.google.com.ly" 		=> array("Google News", "q"),
+		"news.google.it" 		=> array("Google News", "q"),
+		"news.google.sm" 		=> array("Google News", "q"),
+		"news.google.com" 		=> array("Google News", "q"),
+		
+		// Goyellow.de
+		"www.goyellow.de"	        => array("GoYellow.de", "MDN"),
+		
+		// HighBeam
+		"www.highbeam.com"	        => array("HighBeam", "Q"),
+		
+		// Hit-Parade
+		"recherche.hit-parade.com"	=> array("Hit-Parade", "p7"),
+		"class.hit-parade.com"		=> array("Hit-Parade", "p7"),
+		
+		// Hotbot via Lycos
+		"hotbot.lycos.com"		=> array("Hotbot (Lycos)", "query"),
+		"search.hotbot.de"		=> array("Hotbot", "query"),
+		"search.hotbot.fr"		=> array("Hotbot", "query"),
+		"www.hotbot.com"		=> array("Hotbot", "query"),
+		
+		// 1stekeuze
+		"zoek.1stekeuze.nl" 		=> array("1stekeuze", "terms"),
+		
+		// Infoseek
+		"search.www.infoseek.co.jp"     => array("Infoseek", "qt"),
+		
+		// Icerocket
+		"blogs.icerocket.com"		  => array("Icerocket", "qt"),
+		
+		// ICQ
+		"www.icq.com"			=> array("ICQ", "q"),
+		
+		// Ilse
+		"spsearch.ilse.nl" 		=> array("Startpagina", "search_for"),
+		"be.ilse.nl" 			=> array("Ilse BE", "query"),
+		"search.ilse.nl" 		=> array("Ilse NL", "search_for"),
+		
+		// Iwon
+		"search.iwon.com"		=> array("Iwon", "searchfor"),
+		
+		// Ixquick
+		"ixquick.com"			=> array("Ixquick", "query"),
+		"www.eu.ixquick.com"		=> array("Ixquick", "query"),
+		"us.ixquick.com"		=> array("Ixquick", "query"),
+		"s1.us.ixquick.com"		=> array("Ixquick", "query"),
+		"s2.us.ixquick.com"		=> array("Ixquick", "query"),
+		"s3.us.ixquick.com"		=> array("Ixquick", "query"),
+		"s4.us.ixquick.com"		=> array("Ixquick", "query"),
+		"s5.us.ixquick.com"		=> array("Ixquick", "query"),
+		"eu.ixquick.com" 		=> array("Ixquick","query"),
+		
+		// Jyxo
+		"jyxo.cz" 			=> array("Jyxo", "q"),
+		
+		// Jungle Spider
+		"www.jungle-spider.de"		=> array("Jungle Spider", "qry"),
+		
+		// Kartoo
+		"kartoo.com"			=> array("Kartoo", ""),
+		"kartoo.de"			=> array("Kartoo", ""),
+		"kartoo.fr"			=> array("Kartoo", ""),
+		
+		
+		// Kataweb
+		"www.kataweb.it" 		=> array("Kataweb", "q"),
+		
+		// Klug suchen
+		"www.klug-suchen.de"		   => array("Klug suchen!", "query"),
+		
+		// La Toile Du Québec via Google
+		"google.canoe.com"		=> array("La Toile Du Québec (Google)", "q"),
+		"www.toile.com"			=> array("La Toile Du Québec (Google)", "q"),	
+		"web.toile.com"			=> array("La Toile Du Québec (Google)", "q"),
+		
+		// La Toile Du Québec 
+		"recherche.toile.qc.ca"		=> array("La Toile Du Québec", "query"),
+		
+		// Live.com
+		"www.live.com"			=> array("Live", "q"),
+		"beta.search.live.com"		=> array("Live", "q"),
+		"search.live.com"		=> array("Live", "q"),
+		"g.msn.com"		        => array("Live", " "),
+		
+		// Looksmart
+		"www.looksmart.com"		=> array("Looksmart", "key"),
+		
+		// Lycos
+		"search.lycos.com"		=> array("Lycos", "query"),
+		"vachercher.lycos.fr"		=> array("Lycos", "query"),
+		"www.lycos.fr"			=> array("Lycos", "query"),
+		"suche.lycos.de"		=> array("Lycos", "query"),
+		"search.lycos.de"		=> array("Lycos", "query"),
+		"sidesearch.lycos.com"		=> array("Lycos", "query"),
+		"www.multimania.lycos.fr" 	=> array("Lycos", "query"),
+		"buscador.lycos.es" 	=> array("Lycos", "query"),
+		
+		// Mail.ru
+		"go.mail.ru"			=> array("Mailru", "q"),
+		
+		// Mamma
+		"mamma.com"			=> array("Mamma", "query"),
+		"mamma75.mamma.com"		=> array("Mamma", "query"),
+		"www.mamma.com"			=> array("Mamma", "query"),
+		
+		// Meceoo
+		"www.meceoo.fr" 		=> array("Meceoo", "kw"),
+		
+		// Mediaset
+		"servizi.mediaset.it" 		=> array("Mediaset", "searchword"),
+		
+		// Metacrawler
+		"search.metacrawler.com"	=> array("Metacrawler", "general"),
+		
+		// Metager
+		"mserv.rrzn.uni-hannover.de"	=> array("Metager", "eingabe"),
+		
+		// Metager2
+		"www.metager2.de"	        => array("Metager2", "q"),
+		"metager2.de"			       => array("Metager2", "q"),
+		
+		// Meinestadt
+		"www.meinestadt.de"	        => array("Meinestadt.de", "words"),
+		
+		// Monstercrawler
+		"www.monstercrawler.com" 	=> array("Monstercrawler", "qry"),
+		
+		// Mozbot
+		"www.mozbot.fr"			=> array("mozbot", "q"),
+		"www.mozbot.co.uk" 		=> array("mozbot", "q"),
+		"www.mozbot.com"		=> array("mozbot", "q"),
+		
+		// MSN
+		"beta.search.msn.fr"		=> array("MSN", "q"),
+		"search.msn.fr"			=> array("MSN", "q"),
+		"search.msn.es"			=> array("MSN", "q"),
+		"search.msn.se"			=> array("MSN", "q"),
+		"search.latam.msn.com"		=> array("MSN", "q"),
+		"search.msn.nl" 		=> array("MSN", "q"),
+		"leguide.fr.msn.com"		=> array("MSN", "s"),
+		"leguide.msn.fr"		=> array("MSN", "s"),
+		"search.msn.co.jp"		=> array("MSN", "q"),
+		"search.msn.no"			=> array("MSN", "q"),
+		"search.msn.at"			=> array("MSN", "q"),
+		"search.msn.com.hk"		=> array("MSN", "q"),
+		"search.t1msn.com.mx"		=> array("MSN", "q"),
+		"fr.ca.search.msn.com"		=> array("MSN", "q"),
+		"search.msn.be" 		=> array("MSN", "q"),
+		"search.fr.msn.be" 		=> array("MSN", "q"),
+		"search.msn.it" 		=> array("MSN", "q"),
+		"sea.search.msn.it" 		=> array("MSN", "q"),
+		"sea.search.msn.fr" 		=> array("MSN", "q"),
+		"sea.search.msn.de" 		=> array("MSN", "q"),
+		"sea.search.msn.com" 		=> array("MSN", "q"),
+		"sea.search.fr.msn.be" 		=> array("MSN", "q"),
+		"search.msn.com.tw" 		=> array("MSN", "q"),
+		"search.msn.de" 		=> array("MSN", "q"),
+		"search.msn.co.uk" 		=> array("MSN", "q"),
+		"search.msn.co.za"		=> array("MSN", "q"),
+		"search.msn.ch" 		=> array("MSN", "q"),
+		"search.msn.es" 		=> array("MSN", "q"),
+		"search.msn.com.br"		=> array("MSN", "q"),
+		"search.ninemsn.com.au"		=> array("MSN", "q"),
+		"search.msn.dk"			=> array("MSN", "q"),
+		"search.arabia.msn.com"		=> array("MSN", "q"),
+		"search.msn.com"		=> array("MSN", "q"),
+		"search.prodigy.msn.com"	=> array("MSN", "q"),
+		
+		// El Mundo
+		"ariadna.elmundo.es" 	=> array("El Mundo", "q"),
+		
+		// MyWebSearch
+		"kf.mysearch.myway.com" 	=> array("MyWebSearch", "searchfor"),
+		"ms114.mysearch.com" 		=> array("MyWebSearch", "searchfor"),
+		"ms146.mysearch.com"	 	=> array("MyWebSearch", "searchfor"),
+		"mysearch.myway.com"		=> array("MyWebSearch", "searchfor"),
+		"searchfr.myway.com"		=> array("MyWebSearch", "searchfor"),
+		"ki.mysearch.myway.com" 	=> array("MyWebSearch", "searchfor"),
+		"search.mywebsearch.com"	=> array("MyWebSearch", "searchfor"),
+		"www.mywebsearch.com"		=> array("MyWebSearch", "searchfor"),
+		
+		// Najdi
+		"www.najdi.si" 			=> array("Najdi.si", "q"),
+		
+		// Needtofind
+		"ko.search.need2find.com"	=> array("Needtofind", "searchfor"),
+		
+		// Netster
+		"www.netster.com"		=> array("Netster", "keywords"),
+		
+		// Netscape
+		"search-intl.netscape.com"	=> array("Netscape", "search"),
+		"www.netscape.fr"		=> array("Netscape", "q"),
+		"suche.netscape.de"		=> array("Netscape", "q"),
+		"search.netscape.com"		=> array("Netscape", "query"),
+		
+		// Nomade
+		"ie4.nomade.fr"			=> array("Nomade", "s"),
+		"rechercher.nomade.aliceadsl.fr"=> array("Nomade (AliceADSL)", "s"),
+		"rechercher.nomade.fr"		=> array("Nomade", "s"),
+		
+		// Northern Light
+		"www.northernlight.com"		=> array("Northern Light", "qr"),
+		
+		// Numéricable
+		"www.numericable.fr" 		=> array("Numéricable", "query"),
+		
+		// Onet
+		"szukaj.onet.pl" 		=> array("Onet.pl", "qt"),
+		
+		// Opera
+		"search.opera.com" 		=> array("Opera", "search"),
+		
+		// Openfind
+		"wps.openfind.com.tw" 		=> array("Openfind (Websearch)", "query"),
+		"bbs2.openfind.com.tw" 		=> array("Openfind (BBS)", "query"),
+		"news.openfind.com.tw" 		=> array("Openfind (News)", "query"),
+		
+		// Overture
+		"www.overture.com"		=> array("Overture", "Keywords"),
+		"www.fr.overture.com"		=> array("Overture", "Keywords"),
+		
+		// Paperball
+		"suche.paperball.de" 		=> array("Paperball", "query"),
+		
+		// Picsearch
+		"www.picsearch.com" 		=> array("Picsearch", "q"),
+		
+		// Plazoo
+		"www.plazoo.com" 		=> array("Plazoo", "q"),
+		
+		// Postami
+		"www.postami.com" 		=> array("Postami", "query"),
+		
+		// Quick searches
+		"data.quicksearches.net"	=> array("QuickSearches", "q"),
+		
+		// Qualigo
+		"www.qualigo.de"	        => array("Qualigo", "q"),
+		"www.qualigo.ch"	        => array("Qualigo", "q"),
+		"www.qualigo.at"	        => array("Qualigo", "q"),
+		"www.qualigo.nl"	        => array("Qualigo", "q"),
+		
+		// Rambler
+		"search.rambler.ru" 		=> array("Rambler", "words"),
+		
+		// Reacteur.com
+		"www.reacteur.com"		=> array("Reacteur", "kw"),
+		
+		// Sapo
+		"pesquisa.sapo.pt" 		=> array("Sapo","q"),
+		
+		// Search.com
+		"www.search.com"		=> array("Search.com", "q"),
+		
+		// Search.ch
+		"www.search.ch"			=> array("Search.ch", "q"),
+		
+		// Search a lot
+		"www.searchalot.com"		=> array("Searchalot", "query"),
+		
+		// Seek
+		"www.seek.fr"			=> array("Searchalot", "qry_str"),
+		
+		// Seekport
+		"www.seekport.de"		=> array("Seekport", "query"),
+		"www.seekport.co.uk"		=> array("Seekport", "query"),
+		"www.seekport.fr"		=> array("Seekport", "query"),
+		"www.seekport.at"		=> array("Seekport", "query"),
+		"www.seekport.es"		=> array("Seekport", "query"),
+		"www.seekport.it"		=> array("Seekport", "query"),
+		
+		// Seekport (blogs)
+		"blogs.seekport.de"		=> array("Seekport (Blogs)", "query"),
+		"blogs.seekport.co.uk"		=> array("Seekport (Blogs)", "query"),
+		"blogs.seekport.fr"		=> array("Seekport (Blogs)", "query"),
+		"blogs.seekport.at"		=> array("Seekport (Blogs)", "query"),
+		"blogs.seekport.es"		=> array("Seekport (Blogs)", "query"),
+		"blogs.seekport.it"		=> array("Seekport (Blogs)", "query"),
+		
+		// Seekport (news)
+		"news.seekport.de"		=> array("Seekport (News)", "query"),
+		"news.seekport.co.uk"		=> array("Seekport (News)", "query"),
+		"news.seekport.fr"		=> array("Seekport (News)", "query"),
+		"news.seekport.at"		=> array("Seekport (News)", "query"),
+		"news.seekport.es"		=> array("Seekport (News)", "query"),
+		"news.seekport.it"		=> array("Seekport (News)", "query"),
+		
+		// Searchscout
+		"www.searchscout.com"		=> array("Search Scout", "gt_keywords"),
+		
+		// Searchy
+		"www.searchy.co.uk"		=> array("Searchy", "search_term"),
+		
+		// Seznam
+		"search1.seznam.cz" 		=> array("Seznam", "w"),
+		"search2.seznam.cz" 		=> array("Seznam", "w"),
+		"search.seznam.cz" 		=> array("Seznam", "w"),
+		
+		// Sharelook
+		"www.sharelook.fr"		=> array("Sharelook", "keyword"),
+		"www.sharelook.de"		=> array("Sharelook", "keyword"),
+		
+		// Skynet
+		"search.skynet.be" 		=> array("Skynet", "keywords"),
+		
+		// Sphere
+		"www.sphere.com" 		=> array("Sphere", "q"),
+		
+		// Startpagina
+		"startgoogle.startpagina.nl" 	=> array("Startpagina (Google)", "q"),
+		
+		// Suchnase
+		"www.suchnase.de" 		=> array("Suchnase", "qkw"),
+		
+		// Supereva
+		"search.supereva.com" 		=> array("Supereva", "q"),
+		
+		// Sympatico
+		"search.sli.sympatico.ca"       => array("Sympatico", "q"),
+		"search.fr.sympatico.msn.ca"    => array("Sympatico", "q"),
+		"sea.search.fr.sympatico.msn.ca"=> array("Sympatico", "q"),
+		"search.sympatico.msn.ca"	=> array("Sympatico", "q"),
+		
+		// Suchmaschine.com
+		"www.suchmaschine.com"		=> array("Suchmaschine.com", "suchstr"),
+		
+		//Technorati
+		"www.technorati.com"		=> array("Technorati", " "),
+		
+		// Teoma
+		"www.teoma.com"			=> array("Teoma", "t"),
+		
+		// Tiscali
+		"rechercher.nomade.tiscali.fr"  => array("Tiscali", "s"),
+		"search-dyn.tiscali.it" 	=> array("Tiscali", "key"),
+		"www.tiscali.co.uk"		=> array("Tiscali", "query"),
+		"search-dyn.tiscali.de"		=> array("Tiscali", "key"),
+		"hledani.tiscali.cz" 		=> array("Tiscali", "query", "windows-1250"),
+		
+		// T-Online
+		"suche.t-online.de"		=> array("T-Online", "q"),
+		
+		// Trouvez.com
+		"www.trouvez.com"		=> array("Trouvez.com", "query"),
+		
+		// Trusted-Search
+		
+		"www.trusted--search.com"       => array("Trusted Search", "w"),
+		 
+		// Vinden
+		"zoek.vinden.nl" 		=> array("Vinden", "query"),
+		
+		// Vindex
+		"www.vindex.nl" 		=> array("Vindex","search_for"),
+		
+		// Virgilio
+		"search.virgilio.it"		=> array("Virgilio", "qs"),
+		
+		// Voila
+		"search.ke.voila.fr"		=> array("Voila", "rdata"),
+		"moteur.voila.fr"		=> array("Voila", "kw"),
+		"search.voila.fr"		=> array("Voila", "kw"),
+		"beta.voila.fr"			=> array("Voila", "kw"),
+		"search.voila.com"		=> array("Voila", "kw"),
+		
+		// Volny
+		"web.volny.cz" 			=> array("Volny", "search", "windows-1250"),
+		
+		// Wanadoo
+		"search.ke.wanadoo.fr"		=> array("Wanadoo", "kw"),
+		"busca.wanadoo.es"		=> array("Wanadoo", "buscar"),
+		
+		// Web.de
+		"suche.web.de"			=> array("Web.de (Websuche)", "su"),
+		"dir.web.de"			=> array("Web.de (Directory)", "su"),
+		
+		// Webtip
+		"www.webtip.de"			=> array("Webtip", "keyword"),
+		
+		// X-recherche
+		"www.x-recherche.com" 		=> array("X-Recherche", "mots"),
+		
+		// Yahoo
+		"ink.yahoo.com"			=> array("Yahoo !", "p"),
+		"ink.yahoo.fr"			=> array("Yahoo !", "p"),
+		"fr.ink.yahoo.com"		=> array("Yahoo !", "p"),
+		"search.yahoo.co.jp" 		=> array("Yahoo !", "p"),
+		"search.yahoo.fr"		=> array("Yahoo !", "p"),
+		"ar.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"br.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"de.search.yahoo.com"		=> array("Yahoo !", "p"),
+		"ca.search.yahoo.com"		=> array("Yahoo !", "p"),
+		"cf.search.yahoo.com"		=> array("Yahoo !", "p"),
+		"fr.search.yahoo.com"		=> array("Yahoo !", "p"),
+		"espanol.search.yahoo.com"	=> array("Yahoo !", "p"),
+		"es.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"id.search.yahoo.com"		  => array("Yahoo !", "p"),
+		"it.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"kr.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"mx.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"nl.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"uk.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"cade.search.yahoo.com"		=> array("Yahoo !", "p"),
+		"tw.search.yahoo.com" 		=> array("Yahoo !", "p"),
+		"www.yahoo.com.cn" 		=> array("Yahoo !", "p"),
+		"search.yahoo.com"		=> array("Yahoo !", "p"),
+		
+		"de.dir.yahoo.com"		     => array("Yahoo ! Webverzeichnis", ""),   
+		"cf.dir.yahoo.com"		=> array("Yahoo ! Répertoires", ""),
+		"fr.dir.yahoo.com"		=> array("Yahoo ! Répertoires", ""),
+		
+		// Yandex
+		"www.yandex.ru" 		=> array("Yandex", "text"),
+		"yandex.ru" 			=> array("Yandex", "text"),
+		"search.yaca.yandex.ru" 	=> array("Yandex", "text"),
+		"ya.ru" 			=> array("Yandex", "text"),
+		"www.ya.ru" 			=> array("Yandex", "text"),
+		"images.yandex.ru"		=> array("Yandex Images","text"),
+		
+		//Yellowmap
+		
+		"www.yellowmap.de"	        => array("Yellowmap", " "),
+		"yellowmap.de"			       => array("Yellowmap", " "),
+		
+		// Wanadoo
+		"search.ke.wanadoo.fr"		=> array("Wanadoo", "kw"),
+		"busca.wanadoo.es"		=> array("Wanadoo", "buscar"),
+		
+		// Wedoo
+		"fr.wedoo.com"			=> array("Wedoo", "keyword"),
+		
+		// Web.nl
+		"www.web.nl" 			=> array("Web.nl","query"),
+		
+		// Weborama
+		"www.weborama.fr"		=> array("weborama", "query"),
+		
+		// WebSearch
+		"is1.websearch.com"		=> array("WebSearch", "qkw"),
+		"www.websearch.com"		=> array("WebSearch", "qkw"),
+		"websearch.cs.com"		=> array("WebSearch", "query"),
+		
+		// Witch
+		"www.witch.de"		        => array("Witch", "search"),
+		
+		// WXS
+		"wxsl.nl" 			=> array("Planet Internet","q"),
+		
+		// Zoek
+		"www3.zoek.nl" 			=> array("Zoek","q"),
+		
+		// Zhongsou
+		"p.zhongsou.com" 		=> array("Zhongsou","w"),
+		
+		// Zoeken
+		"www.zoeken.nl" 		=> array("Zoeken","query"),
+		
+		// Zoohoo
+		"zoohoo.cz" 			=> array("Zoohoo", "q", "windows-1250"),
+		"www.zoohoo.cz" 		=> array("Zoohoo", "q", "windows-1250"),
+		
+		// Zoznam
+		"www.zoznam.sk" 		=> array("Zoznam", "s"),
+	);
+}
+?>
diff --git a/modules/LogStats.php b/modules/LogStats.php
new file mode 100644
index 0000000000..753e70dbef
--- /dev/null
+++ b/modules/LogStats.php
@@ -0,0 +1,1454 @@
+<?php
+
+/**
+ * Simple database PDO wrapper
+ * 
+ */
+class Piwik_LogStats_Db 
+{
+	private $connection;
+	private $username;
+	private $password;
+	
+	public function __construct( $host, $username, $password, $dbname) 
+	{
+		$this->dsn = "mysql:dbname=$dbname;host=$host";
+		$this->username = $username;
+		$this->password = $password;
+	}
+
+	public function connect() 
+	{
+		try {
+			$pdoConnect = new PDO($this->dsn, $this->username, $this->password);
+			$pdoConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+			$this->connection = $pdoConnect;
+		} catch (PDOException $e) {
+			throw new Exception("Error connecting database: ".$e->getMessage());
+		}
+	}
+
+	public function prefixTable( $suffix )
+	{
+		$prefix = Piwik_LogStats_Config::getInstance()->database['tables_prefix'];
+		
+		return $prefix . $suffix;
+	}
+	
+	public function fetchAll( $query, $parameters )
+	{
+		try {
+			$sth = $this->query( $query, $parameters );
+			return $sth->fetchAll(PDO::FETCH_ASSOC);
+		} catch (PDOException $e) {
+			throw new Exception("Error query: ".$e->getMessage());
+		}
+	}
+	
+	public function fetch( $query, $parameters )
+	{
+		try {
+			$sth = $this->query( $query, $parameters );
+			return $sth->fetch(PDO::FETCH_ASSOC);
+		} catch (PDOException $e) {
+			throw new Exception("Error query: ".$e->getMessage());
+		}
+	}
+	
+	public function query($query, $parameters = array()) 
+	{
+		try {
+			$sth = $this->connection->prepare($query);
+			$sth->execute( $parameters );
+			return $sth;
+		} catch (PDOException $e) {
+			throw new Exception("Error query: ".$e->getMessage());
+		}
+	}
+	
+	public function lastInsertId()
+	{
+		return  $this->connection->lastInsertId();
+	}
+}
+
+/**
+ * Simple class to access the configuration file
+ */
+class Piwik_LogStats_Config
+{
+	static private $instance = null;
+	
+	static public function getInstance()
+	{
+		if (self::$instance == null)
+		{			
+			$c = __CLASS__;
+			self::$instance = new $c();
+		}
+		return self::$instance;
+	}
+	
+	public $config = array();
+	
+	private function __construct()
+	{
+		$pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
+		$this->config = parse_ini_file($pathIniFile, true);
+	}
+	
+	public function __get( $name )
+	{
+		if(isset($this->config[$name]))
+		{
+			return $this->config[$name];
+		}
+		else
+		{
+			throw new Exception("The config element $name is not available in the configuration (check the configuration file).");
+		}
+	}
+}
+
+
+/**
+ * To maximise the performance of the logging module, we use different techniques.
+ * 
+ * On the PHP-only side:
+ * - minimize the number of external files included. 
+ * 	 Ideally only one (the configuration file) in all the normal cases.
+ *   We load the Loggers only when an error occurs ; this error is logged in the DB/File/etc
+ *   depending on the loggers settings in the configuration file.
+ * - we may have to include external classes but we try to include only very 
+ *   simple code without any dependency, so that we could simply write a script
+ *   that would merge all this simple code into a big piwik.php file.
+ * 
+ * On the Database-related side:
+ * - write all the SQL queries without using any DB abstraction layer.
+ * 	 Of course we carefully filter all input values.
+ * - minimize the number of SQL queries necessary to complete the algorithm.
+ * - carefully index the tables used
+ * - try to have fixed length rows
+ * 
+ * [ - use a partitionning by date for the tables ]
+ *   
+ * - handle the timezone settings??
+ * 
+ * [ - country detection plugin => ip lookup ]
+ * [ - precise country detection plugin ]
+ * 
+ * We could also imagine a batch system that would read a log file every 5min,
+ * and which prepares the file containg the rows to insert, then we load DATA INFILE 
+ * 
+ */
+
+/**
+ * Configuration options for the statsLogEngine module:
+ * - use_cookie  ; defines if we try to get/set a cookie to help recognize a unique visitor
+ */
+
+/**
+ * Simple class to handle the cookies.
+ * Its features are:
+ * 
+ * - read a cookie values
+ * - edit an existing cookie and save it
+ * - create a new cookie, set values, expiration date, etc. and save it
+ * 
+ * The cookie content is saved in an optimized way.
+ */
+class Piwik_LogStats_Cookie
+{
+	/**
+	 * The name of the cookie 
+	 */
+	protected $name = null;
+	
+	/**
+	 * The expire time for the cookie (expressed in UNIX Timestamp)
+	 */
+	protected $expire = null;
+	
+	/**
+	 * The content of the cookie
+	 */
+	protected $value = array();
+	
+	const VALUE_SEPARATOR = ':';
+	
+	public function __construct( $cookieName, $expire = null)
+	{
+		$this->name = $cookieName;
+		
+		if(is_null($expire)
+			|| !is_numeric($expire)
+			|| $expire <= 0)
+		{
+			$this->expire = $this->getDefaultExpire();
+		}
+		
+		if($this->isCookieFound())
+		{
+			$this->loadContentFromCookie();
+		}
+	}
+	
+	public function isCookieFound()
+	{
+		return isset($_COOKIE[$this->name]);
+	}
+	
+	protected function getDefaultExpire()
+	{
+		return time() + 86400*365*10;
+	}	
+	
+	/**
+	 * taken from http://usphp.com/manual/en/function.setcookie.php
+	 * fix expires bug for IE users (should i say expected to fix the bug in 2.3 b2)
+	 * TODO setCookie: use the other parameters of the function
+	 */
+	protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', $Secure = false, $HTTPOnly = false)
+	{
+		if (!empty($Domain))
+		{	
+			// Fix the domain to accept domains with and without 'www.'.
+			if (strtolower(substr($Domain, 0, 4)) == 'www.')  $Domain = substr($Domain, 4);
+			
+			$Domain = '.' . $Domain;
+			
+			// Remove port information.
+			$Port = strpos($Domain, ':');
+			if ($Port !== false)  $Domain = substr($Domain, 0, $Port);
+		}
+		
+		$header = 'Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value)
+					 . (empty($Expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $Expires) . ' GMT')
+					 . (empty($Path) ? '' : '; path=' . $Path)
+					 . (empty($Domain) ? '' : '; domain=' . $Domain)
+					 . (!$Secure ? '' : '; secure')
+					 . (!$HTTPOnly ? '' : '; HttpOnly');
+		 
+		 header($header, false);
+	}
+	
+	protected function setP3PHeader()
+	{
+		header("P3P: CP='OTI DSP COR NID STP UNI OTPa OUR'");
+	}
+	
+	public function deleteCookie()
+	{
+		$this->setP3PHeader();
+		setcookie($this->name, false, time() - 86400);
+	}
+	
+	public function save()
+	{
+		$this->setP3PHeader();
+		$this->setCookie( $this->name, $this->generateContentString(), $this->expire);
+	}
+	
+	/**
+	 * Load the cookie content into a php array 
+	 */
+	protected function loadContentFromCookie()
+	{
+		$cookieStr = $_COOKIE[$this->name];
+		
+		$values = explode( self::VALUE_SEPARATOR, $cookieStr);
+		foreach($values as $nameValue)
+		{
+			$equalPos = strpos($nameValue, '=');
+			$varName = substr($nameValue,0,$equalPos);
+			$varValue = substr($nameValue,$equalPos+1);
+			
+			// no numeric value are base64 encoded so we need to decode them
+			if(!is_numeric($varValue))
+			{
+				$varValue = base64_decode($varValue);
+				
+				// some of the values may be serialized array so we try to unserialize it
+				if( ($arrayValue = @unserialize($varValue)) !== false
+					// we set the unserialized version only for arrays as you can have set a serialized string on purpose
+					&& is_array($arrayValue) 
+					)
+				{
+					$varValue = $arrayValue;
+				}
+			}
+			
+			$this->set($varName, $varValue);
+		}
+	}
+	
+	/**
+	 * Returns the string to save in the cookie frpm the $this->value array of values
+	 * 
+	 */
+	public function generateContentString()
+	{
+		$cookieStr = '';
+		foreach($this->value as $name=>$value)
+		{
+			if(is_array($value))
+			{
+				$value = base64_encode(serialize($value));
+			}
+			elseif(is_string($value))
+			{
+				$value = base64_encode($value);
+			}
+			
+			$cookieStr .= "$name=$value" . self::VALUE_SEPARATOR;
+		}
+		$cookieStr = substr($cookieStr, 0, strlen($cookieStr)-1);
+		return $cookieStr;
+	}
+	
+	/**
+	 * Registers a new name => value association in the cookie.
+	 * 
+	 * Registering new values is optimal if the value is a numeric value.
+	 * If the value is a string, it will be saved as a base64 encoded string.
+	 * If the value is an array, it will be saved as a serialized and base64 encoded 
+	 * string which is not very good in terms of bytes usage. 
+	 * You should save arrays only when you are sure about their maximum data size.
+	 * 
+	 * @param string Name of the value to save; the name will be used to retrieve this value
+	 * @param string|array|numeric Value to save
+	 * 
+ 	 */
+	public function set( $name, $value )
+	{
+		$name = self::escapeValue($name);
+		$this->value[$name] = $value;
+	}
+	
+	/**
+	 * Returns the value defined by $name from the cookie.
+	 * 
+	 * @param string|integer Index name of the value to return
+	 * @return mixed The value if found, false if the value is not found
+	 */
+	public function get( $name )
+	{
+		$name = self::escapeValue($name);
+		return isset($this->value[$name]) ? self::escapeValue($this->value[$name]) : false;
+	}
+	
+	public function __toString()
+	{
+		$str = "<-- Content of the cookie '{$this->name}' <br>\n";
+		foreach($this->value as $name => $value )
+		{
+			$str .= $name . " = " . var_export($this->get($name), true) . "<br>\n";
+		}
+		$str .= "--> <br>\n";
+		return $str;
+	}
+	
+	static protected function escapeValue( $value )
+	{
+		return Piwik_Common::sanitizeInputValues($value);
+	}	
+}
+
+//
+//$c = new Piwik_LogStats_Cookie( 'piwik_logstats', 86400);
+//echo $c;
+//$c->set(1,1);
+//$c->set('test',1);
+//$c->set('test2','test=432:gea785');
+//$c->set('test3',array('test=432:gea785'));
+//$c->set('test4',array(array(0=>1),1=>'test'));
+//echo $c;
+//echo "<br>";
+//echo $c->generateContentString();
+//echo "<br>";
+//$v=$c->get('more!');
+//if(empty($v)) $c->set('more!',1);
+//$c->set('more!', array($c->get('more!')));
+//$c->save();
+//$c->deleteCookie();
+
+class Piwik_LogStats_Action
+{
+	
+	 /*
+	  * Specifications
+	  *  
+	  * - External file tracking
+	  * 
+	  *    * MANUAL Download tracking 
+	  *      download = http://piwik.org/hellokity.zip
+	  * 	(name = dir1/file alias name)
+	  *
+	  *    * AUTOMATIC Download tracking for a known list of file extensions. 
+	  *    Make a hit to the piwik.php with the parameter: 
+	  *      download = http://piwik.org/hellokity.zip
+	  *  
+	  *   When 'name' is not specified, 
+	  * 	if AUTOMATIC and if anchor not empty => name = link title anchor
+	  * 	else name = path+query of the URL
+	  *   Ex: myfiles/beta.zip
+	  *
+	  * - External link tracking
+	  * 
+	  *    * MANUAL External link tracking
+	  * 	 outlink = http://amazon.org/test
+	  * 	(name = the big partners / amazon)
+	  * 
+	  *    * AUTOMATIC External link tracking
+	  *      When a link is not detected as being part of the same website 
+	  *     AND when the url extension is not detected as being a file download
+	  * 	 outlink = http://amazon.org/test
+	  * 
+	  *  When 'name' is not specified, 
+	  * 	if AUTOMATIC and if anchor not empty => name = link title anchor
+	  * 	else name = URL
+	  *   Ex: http://amazon.org/test
+	  */
+	private $actionName;
+	private $url;
+	private $defaultActionName;
+	private $nameDownloadOutlink;
+	
+	const TYPE_ACTION   = 1;
+	const TYPE_DOWNLOAD = 3;
+	const TYPE_OUTLINK  = 2;
+	
+	function __construct( $db )
+	{
+		$this->actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string');
+		
+		$downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
+		$this->downloadUrl = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
+		
+		$outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
+		$this->outlinkUrl = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
+		
+		$nameVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_outlink_name_var'];
+		$this->nameDownloadOutlink = Piwik_Common::getRequestVar( $nameVariableName, '', 'string');
+		
+		$this->url = Piwik_Common::getRequestVar( 'url', '', 'string');
+		$this->db = $db;
+		$this->defaultActionName = Piwik_LogStats_Config::getInstance()->LogStats['default_action_name'];
+	}
+	
+	/**
+	 * About the Action concept:
+	 * 
+	 * - An action is defined by a name.
+	 * - The name can be specified in the JS Code in the variable 'action_name'
+	 * - Handling UTF8 in the action name
+	 * PLUGIN_IDEA - An action is associated to URLs and link to the URL from the interface
+	 * PLUGIN_IDEA - An action hit by a visitor is associated to the HTML title of the page that triggered the action
+	 * 
+	 * + If the name is not specified, we use the URL(path+query) to build a default name.
+	 *   For example for "http://piwik.org/test/my_page/test.html" 
+	 *   the name would be "test/my_page/test.html"
+	 * 
+	 * We make sure it is clean and displayable.
+	 * If the name is empty we set it to a default name.
+	 * 
+	 * TODO UTF8 handling to test
+	 * 
+	 */
+	private function generateInfo()
+	{
+		if(!empty($this->downloadUrl))
+		{
+			$this->actionType = self::TYPE_DOWNLOAD;
+			$url = $this->downloadUrl;
+			$actionName = $this->nameDownloadOutlink;
+		}
+		elseif(!empty($this->outlinkUrl))
+		{
+			$this->actionType = self::TYPE_OUTLINK;
+			$url = $this->outlinkUrl;
+			$actionName = $this->nameDownloadOutlink;
+			if( empty($actionName) )
+			{
+				$actionName = $url;
+			}
+		}
+		else
+		{
+			$this->actionType = self::TYPE_ACTION;
+			$url = $this->url;
+			$actionName = $this->actionName;
+		}		
+		
+		// the ActionName wasn't specified
+		if( empty($actionName) )
+		{
+			$parsedUrl = parse_url( $url );
+			
+			$actionName = '';
+			
+			if(isset($parsedUrl['path']))
+			{
+				$actionName .= substr($parsedUrl['path'], 1);
+			}
+			
+			if(isset($parsedUrl['query']))
+			{
+				$actionName .= '?'.$parsedUrl['query'];
+			}
+		}
+		
+		// clean the name
+		$actionName = str_replace(array("\n", "\r"), '', $actionName);
+		
+		if(empty($actionName))
+		{
+			$actionName = $this->defaultActionName;
+		}
+		
+		$this->finalActionName = $actionName;
+	}
+	
+	/**
+	 * Returns the idaction of the current action name.
+	 * This idaction is used in the visitor logging table to link the visit information 
+	 * (entry action, exit action) to the actions.
+	 * This idaction is also used in the table that links the visits and their actions.
+	 * 
+	 * The methods takes care of creating a new record in the action table if the existing 
+	 * action name doesn't exist yet.
+	 * 
+	 * @return int Id action
+	 */
+	function getActionId()
+	{
+		$this->loadActionId();
+		return $this->idAction;
+	}
+	
+	/**
+	 * @see getActionId()
+	 */
+	private function loadActionId()
+	{		
+		$this->generateInfo();
+		
+		$name = $this->finalActionName;
+		$type = $this->actionType;
+		
+		$idAction = $this->db->fetch("	SELECT idaction 
+							FROM ".$this->db->prefixTable('log_action')
+						."  WHERE name = ? AND type = ?", array($name, $type) );
+		
+		// the action name has not been found, create it
+		if($idAction === false)
+		{
+			$this->db->query("INSERT INTO ". $this->db->prefixTable('log_action'). "( name, type ) 
+								VALUES (?,?)",array($name,$type) );
+			$idAction = $this->db->lastInsertId();
+		}
+		else
+		{
+			$idAction = $idAction['idaction'];
+		}
+		
+		$this->idAction = $idAction;
+	}
+	
+	/**
+	 * Records in the DB the association between the visit and this action.
+	 */
+	 public function record( $idVisit, $idRefererAction, $timeSpentRefererAction)
+	 {
+	 	$this->db->query("INSERT INTO ".$this->db->prefixTable('log_link_visit_action')
+						." (idvisit, idaction, idaction_ref, time_spent_ref_action) VALUES (?,?,?,?)",
+					array($idVisit, $this->idAction, $idRefererAction, $timeSpentRefererAction)
+					);
+	 }
+	 
+}
+
+class Piwik_LogStats_Visit
+{
+	private $cookieLog = null;
+	private $visitorInfo = array();
+	private $userSettingsInformation = null;
+	
+	function __construct( $db )
+	{
+		$this->db = $db;
+				
+		$idsite = Piwik_Common::getRequestVar('idsite', 0, 'int');
+		if($idsite <= 0)
+		{
+			throw new Exception("The 'idsite' in the request is invalide.");
+		}
+		
+		$this->idsite = $idsite;
+	}
+	
+	// test if the visitor is excluded because of
+	// - IP
+	// - cookie
+	// - configuration option?
+	private function isExcluded()
+	{
+		$excluded = 0;
+		
+		if($excluded)
+		{
+			printDebug("Visitor excluded.");
+			return true;
+		}
+		
+		return false;
+	}
+	
+	private function getCookieName()
+	{
+		return Piwik_LogStats_Config::getInstance()->LogStats['cookie_name'] . $this->idsite;
+	}
+	
+	/**
+	 * This methods tries to see if the visitor has visited the website before.
+	 * 
+	 * We have to split the visitor into one of the category 
+	 * - Known visitor
+	 * - New visitor
+	 * 
+	 * A known visitor is a visitor that has already visited the website in the current month.
+	 * We define a known visitor using the algorithm:
+	 * 
+	 * 1) Checking if a cookie contains
+	 * 		// a unique id for the visitor
+	 * 		- id_visitor 
+	 * 
+	 * 		// the timestamp of the last action in the most recent visit
+	 * 		- timestamp_last_action 
+	 * 
+ 	 *  	// the timestamp of the first action in the most recent visit
+	 * 		- timestamp_first_action
+	 * 
+	 * 		// the ID of the most recent visit (which could be in the past or the current visit)
+	 * 		- id_visit 
+	 * 
+	 * 		// the ID of the most recent action
+	 * 		- id_last_action
+	 * 
+	 * 2) If the visitor doesn't have a cookie, we try to look for a similar visitor configuration.
+	 * 	  We search for a visitor with the same plugins/OS/Browser/Resolution for today for this website.
+	 */
+	private function recognizeTheVisitor()
+	{
+		$this->visitorKnown = false;
+		
+		$this->cookieLog = new Piwik_LogStats_Cookie( $this->getCookieName() );
+		/*
+		 * Case the visitor has the piwik cookie.
+		 * We make sure all the data that should saved in the cookie is available.
+		 */
+		
+		if( false !== ($idVisitor = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_IDVISITOR )) )
+		{
+			$timestampLastAction = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_LAST_ACTION );
+			$timestampFirstAction = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION );
+			$idVisit = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_ID_VISIT );
+			$idLastAction = $this->cookieLog->get( Piwik_LogStats::COOKIE_INDEX_ID_LAST_ACTION );
+			
+			if(		$timestampLastAction !== false && is_numeric($timestampLastAction)
+				&& 	$timestampFirstAction !== false && is_numeric($timestampFirstAction)
+				&& 	$idVisit !== false && is_numeric($idVisit)
+				&& 	$idLastAction !== false && is_numeric($idLastAction)
+			)
+			{
+				$this->visitorInfo['visitor_idcookie'] = $idVisitor;
+				$this->visitorInfo['visit_last_action_time'] = $timestampLastAction;
+				$this->visitorInfo['visit_first_action_time'] = $timestampFirstAction;
+				$this->visitorInfo['idvisit'] = $idVisit;
+				$this->visitorInfo['visit_exit_idaction'] = $idLastAction;
+				
+				$this->visitorKnown = true;								
+				
+				printDebug("The visitor is known because he has the piwik cookie (idcookie = {$this->visitorInfo['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") ");
+			}
+		}		
+		
+		/*
+		 * If the visitor doesn't have the piwik cookie, we look for a visitor that has exactly the same configuration
+		 * and that visited the website today.
+		 */
+		if( !$this->visitorKnown )
+		{
+			$userInfo = $this->getUserSettingsInformation();
+			$md5Config = $userInfo['config_md5config'];
+			
+			$visitRow = $this->db->fetch( 
+										" SELECT  	visitor_idcookie, 
+													UNIX_TIMESTAMP(visit_last_action_time) as visit_last_action_time,
+													UNIX_TIMESTAMP(visit_first_action_time) as visit_first_action_time,
+													idvisit,
+													visit_exit_idaction 
+										FROM ".$this->db->prefixTable('log_visit').
+										" WHERE visit_server_date = ?
+											AND idsite = ?
+											AND config_md5config = ?
+										ORDER BY visit_last_action_time DESC
+										LIMIT 1",
+										array( date("Y-m-d"), $this->idsite, $md5Config));
+			if($visitRow 
+				&& count($visitRow) > 0)
+			{
+				$this->visitorInfo['visitor_idcookie'] = $visitRow['visitor_idcookie'];
+				$this->visitorInfo['visit_last_action_time'] = $visitRow['visit_last_action_time'];
+				$this->visitorInfo['visit_first_action_time'] = $visitRow['visit_first_action_time'];
+				$this->visitorInfo['idvisit'] = $visitRow['idvisit'];
+				$this->visitorInfo['visit_exit_idaction'] = $visitRow['visit_exit_idaction'];
+				
+				$this->visitorKnown = true;
+				
+				printDebug("The visitor is known because of his userSettings+IP (idcookie = {$visitRow['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") ");
+			}
+		}
+	}
+	
+	private function getUserSettingsInformation()
+	{
+		// we already called this method before, simply returns the result
+		if(is_array($this->userSettingsInformation))
+		{
+			return $this->userSettingsInformation;
+		}
+		
+		
+		$plugin_Flash 			= Piwik_Common::getRequestVar( 'fla', 0, 'int');
+		$plugin_Director 		= Piwik_Common::getRequestVar( 'dir', 0, 'int');
+		$plugin_Quicktime		= Piwik_Common::getRequestVar( 'qt', 0, 'int');
+		$plugin_RealPlayer 		= Piwik_Common::getRequestVar( 'realp', 0, 'int');
+		$plugin_Pdf 			= Piwik_Common::getRequestVar( 'pdf', 0, 'int');
+		$plugin_WindowsMedia 	= Piwik_Common::getRequestVar( 'wma', 0, 'int');
+		$plugin_Java 			= Piwik_Common::getRequestVar( 'java', 0, 'int');
+		$plugin_Cookie 			= Piwik_Common::getRequestVar( 'cookie', 0, 'int');
+		
+		$userAgent		= Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_USER_AGENT']);
+		$aBrowserInfo	= Piwik_Common::getBrowserInfo($userAgent);
+		$browserName	= $aBrowserInfo['name'];
+		$browserVersion	= $aBrowserInfo['version'];
+		
+		$os				= Piwik_Common::getOs($userAgent);
+		
+		$resolution		= Piwik_Common::getRequestVar('res', 'unknown', 'string');
+		$colorDepth		= Piwik_Common::getRequestVar('col', 32, 'numeric');
+		
+
+		$ip				= Piwik_Common::getIp();
+		$ip 			= ip2long($ip);
+
+		$browserLang	= Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
+		if(is_null($browserLang))
+		{
+			$browserLang = '';
+		}
+		
+
+		$configurationHash = $this->getConfigHash( 
+												$os,
+												$browserName,
+												$browserVersion,
+												$resolution,
+												$colorDepth,
+												$plugin_Flash,
+												$plugin_Director,
+												$plugin_RealPlayer,
+												$plugin_Pdf,
+												$plugin_WindowsMedia,
+												$plugin_Java,
+												$plugin_Cookie,
+												$ip,
+												$browserLang);
+												
+		$this->userSettingsInformation = array(
+			'config_md5config' => $configurationHash,
+			'config_os' 			=> $os,
+			'config_browser_name' 	=> $browserName,
+			'config_browser_version' => $browserVersion,
+			'config_resolution' 	=> $resolution,
+			'config_color_depth' 	=> $colorDepth,
+			'config_pdf' 			=> $plugin_Pdf,
+			'config_flash' 			=> $plugin_Flash,
+			'config_java' 			=> $plugin_Java,
+			'config_director' 		=> $plugin_Director,
+			'config_quicktime' 		=> $plugin_Quicktime,
+			'config_realplayer' 	=> $plugin_RealPlayer,
+			'config_windowsmedia' 	=> $plugin_WindowsMedia,
+			'config_cookie' 		=> $plugin_RealPlayer,
+			'location_ip' 			=> $ip,
+			'location_browser_lang' => $browserLang,			
+		);
+		
+		return $this->userSettingsInformation;
+	}
+	
+	/**
+	 * Returns true if the last action was done during the last 30 minutes
+	 */
+	private function isLastActionInTheSameVisit()
+	{
+		return $this->visitorInfo['visit_last_action_time'] >= time() - Piwik_LogStats::VISIT_STANDARD_LENGTH;
+	}
+
+	private function isVisitorKnown()
+	{
+		return $this->visitorKnown === true;
+	}
+	
+	/**
+	 * Once we have the visitor information, we have to define if the visit is a new or a known visit.
+	 * 
+	 * 1) When the last action was done more than 30min ago, 
+	 * 	  or if the visitor is new, then this is a new visit.
+	 *	
+	 * 2) If the last action is less than 30min ago, then the same visit is going on. 
+	 *	Because the visit goes on, we can get the time spent during the last action.
+	 *
+	 * NB:
+	 *  - In the case of a new visit, then the time spent 
+	 *	during the last action of the previous visit is unknown.
+	 * 
+	 *	- In the case of a new visit but with a known visitor, 
+	 *	we can set the 'returning visitor' flag.
+	 *
+	 */
+	 
+	/**
+	 * In all the cases we set a cookie to the visitor with the new information.
+	 */
+	public function handle()
+	{
+		if(!$this->isExcluded())
+		{
+			$this->recognizeTheVisitor();
+			
+			// known visitor
+			if($this->isVisitorKnown())
+			{
+				if($this->isLastActionInTheSameVisit())
+				{
+					$this->handleKnownVisit();
+				}
+				else
+				{
+					$this->handleNewVisit();
+				}
+			}
+			// new visitor
+			else
+			{
+				$this->handleNewVisit();
+			}
+			
+			$this->updateCookie();
+			
+		}
+	}
+	
+	private function updateCookie()
+	{
+		printDebug("We manage the cookie...");
+		
+		// idcookie has been generated in handleNewVisit or we simply propagate the old value
+		$this->cookieLog->set( 	Piwik_LogStats::COOKIE_INDEX_IDVISITOR, 
+								$this->visitorInfo['visitor_idcookie'] );
+		
+		// the last action timestamp is the current timestamp
+		$this->cookieLog->set( 	Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_LAST_ACTION, 	
+								$this->visitorInfo['visit_last_action_time'] );
+		
+		// the first action timestamp is the timestamp of the first action of the current visit
+		$this->cookieLog->set( 	Piwik_LogStats::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION, 	
+								$this->visitorInfo['visit_first_action_time'] );
+		
+		// the idvisit has been generated by mysql in handleNewVisit or simply propagated here
+		$this->cookieLog->set( 	Piwik_LogStats::COOKIE_INDEX_ID_VISIT, 	
+								$this->visitorInfo['idvisit'] );
+		
+		// the last action ID is the current exit idaction
+		$this->cookieLog->set( 	Piwik_LogStats::COOKIE_INDEX_ID_LAST_ACTION, 	
+								$this->visitorInfo['visit_exit_idaction'] );
+								
+		$this->cookieLog->save();
+	}
+	
+	/**
+	 * In the case of a known visit, we have to do the following actions:
+	 * 
+	 * 1) Insert the new action
+	 * 
+	 * 2) Update the visit information
+	 */
+	private function handleKnownVisit()
+	{
+		printDebug("Visit known.");		
+		
+		/**
+		 * Init the action
+		 */
+		$action = new Piwik_LogStats_Action( $this->db );
+		
+		$actionId = $action->getActionId();
+		
+		printDebug("idAction = $actionId");
+				
+		$serverTime 	= time();
+		$datetimeServer = Piwik_Common::getDatetimeFromTimestamp($serverTime);
+	
+		$this->db->query("UPDATE ". $this->db->prefixTable('log_visit')." 
+							SET visit_last_action_time = ?,
+								visit_exit_idaction = ?,
+								visit_total_actions = visit_total_actions + 1,
+								visit_total_time = UNIX_TIMESTAMP(visit_last_action_time) - UNIX_TIMESTAMP(visit_first_action_time)
+							WHERE idvisit = ?
+							LIMIT 1",
+							array( 	$datetimeServer,
+									$actionId,
+									$this->visitorInfo['idvisit'] )
+				);
+		/**
+		 * Save the action
+		 */
+		$timespentLastAction = $serverTime - $this->visitorInfo['visit_last_action_time'];
+		
+		$action->record( 	$this->visitorInfo['idvisit'], 
+							$this->visitorInfo['visit_exit_idaction'],
+							$timespentLastAction
+			);
+		
+		
+		/**
+		 * Cookie fields to be updated
+		 */
+		$this->visitorInfo['visit_last_action_time'] = $serverTime;
+		$this->visitorInfo['visit_exit_idaction'] = $actionId;
+		
+
+	}
+	
+	/**
+	 * In the case of a new visit, we have to do the following actions:
+	 * 
+	 * 1) Insert the new action
+	 * 
+	 * 2) Insert the visit information
+	 */
+	private function handleNewVisit()
+	{
+		printDebug("New Visit.");
+		
+		/**
+		 * Get the variables from the REQUEST 
+		 */
+
+		// Configuration settings
+		$userInfo = $this->getUserSettingsInformation();
+
+		// General information
+		$localTime				= Piwik_Common::getRequestVar( 'h', date("H"), 'numeric')
+							.':'. Piwik_Common::getRequestVar( 'm', date("i"), 'numeric')
+							.':'. Piwik_Common::getRequestVar( 's', date("s"), 'numeric');
+		$serverDate 	= date("Y-m-d");
+		$serverTime 	= time();		
+		
+		if($this->isVisitorKnown())
+		{
+			$idcookie = $this->visitorInfo['visitor_idcookie'];
+			$returningVisitor = 1;
+		}
+		else
+		{
+			$idcookie = $this->getVisitorUniqueId();			
+			$returningVisitor = 0;
+		}
+		
+		$defaultTimeOnePageVisit = Piwik_LogStats_Config::getInstance()->LogStats['default_time_one_page_visit'];
+		
+		// Location information
+		$country 		= Piwik_Common::getCountry($userInfo['location_browser_lang']);				
+		$continent		= Piwik_Common::getContinent( $country );
+														
+		//Referer information
+		$refererInfo = $this->getRefererInformation();
+		
+		/**
+		 * Init the action
+		 */
+		$action = new Piwik_LogStats_Action( $this->db );
+		
+		$actionId = $action->getActionId();
+		
+		printDebug("idAction = $actionId");		
+		
+		
+		/**
+		 * Save the visitor
+		 */
+		$informationToSave = array(
+			//'idvisit' => ,
+			'idsite' 				=> $this->idsite,
+			'visitor_localtime' 	=> $localTime,
+			'visitor_idcookie' 		=> $idcookie,
+			'visitor_returning' 	=> $returningVisitor,
+			'visit_first_action_time' => Piwik_Common::getDatetimeFromTimestamp($serverTime),
+			'visit_last_action_time' =>  Piwik_Common::getDatetimeFromTimestamp($serverTime),
+			'visit_server_date' 	=> $serverDate,
+			'visit_entry_idaction' 	=> $actionId,
+			'visit_exit_idaction' 	=> $actionId,
+			'visit_total_actions' 	=> 1,
+			'visit_total_time' 		=> $defaultTimeOnePageVisit,
+			'referer_type' 			=> $refererInfo['referer_type'],
+			'referer_name' 			=> $refererInfo['referer_name'],
+			'referer_url' 			=> $refererInfo['referer_url'],
+			'referer_keyword' 		=> $refererInfo['referer_keyword'],
+			'config_md5config' 		=> $userInfo['config_md5config'],
+			'config_os' 			=> $userInfo['config_os'],
+			'config_browser_name' 	=> $userInfo['config_browser_name'],
+			'config_browser_version' => $userInfo['config_browser_version'],
+			'config_resolution' 	=> $userInfo['config_resolution'],
+			'config_color_depth' 	=> $userInfo['config_color_depth'],
+			'config_pdf' 			=> $userInfo['config_pdf'],
+			'config_flash' 			=> $userInfo['config_flash'],
+			'config_java' 			=> $userInfo['config_java'],
+			'config_director' 		=> $userInfo['config_director'],
+			'config_quicktime' 		=> $userInfo['config_quicktime'],
+			'config_realplayer' 	=> $userInfo['config_realplayer'],
+			'config_windowsmedia' 	=> $userInfo['config_windowsmedia'],
+			'config_cookie' 		=> $userInfo['config_cookie'],
+			'location_ip' 			=> $userInfo['location_ip'],
+			'location_browser_lang' => $userInfo['location_browser_lang'],
+			'location_country' 		=> $country,
+			'location_continent' 	=> $continent,
+		);
+		
+		
+		$fields = implode(", ", array_keys($informationToSave));
+		$values = substr(str_repeat( "?,",count($informationToSave)),0,-1);
+		
+		$this->db->query( "INSERT INTO ".$this->db->prefixTable('log_visit').
+						" ($fields) VALUES ($values)", array_values($informationToSave));
+						
+		$idVisit = $this->db->lastInsertId();
+		
+		// Update the visitor information attribute with this information array
+		$this->visitorInfo = $informationToSave;
+		$this->visitorInfo['idvisit'] = $idVisit;
+
+		// we have to save timestamp in the object properties, whereas mysql eats some other datetime format
+		$this->visitorInfo['visit_first_action_time'] = $serverTime;
+		$this->visitorInfo['visit_last_action_time'] = $serverTime;
+		
+		/**
+		 * Save the action
+		 */
+		$action->record( $idVisit, 0, 0 );
+		
+	}
+	
+	/**
+	 * Returns an array containing the following information:
+	 * - referer_type
+	 *		- direct			-- absence of referer URL OR referer URL has the same host
+	 *		- site				-- based on the referer URL
+	 *		- search_engine		-- based on the referer URL
+	 *		- campaign			-- based on campaign URL parameter
+	 *		- newsletter		-- based on newsletter URL parameter
+	 *		- partner			-- based on partner URL parameter
+	 *
+	 * - referer_name
+	 * 		- ()
+	 * 		- piwik.net			-- site host name
+	 * 		- google.fr			-- search engine host name
+	 * 		- adwords-search	-- campaign name
+	 * 		- beta-release		-- newsletter name
+	 * 		- my-nice-partner	-- partner name
+	 * 		
+	 * - referer_keyword
+	 * 		- ()
+	 * 		- ()
+	 * 		- my keyword
+	 * 		- my paid keyword
+	 * 		- ()
+	 * 		- ()
+	 *  
+	 * - referer_url : the same for all the referer types
+	 * 
+	 */
+	private function getRefererInformation()
+	{	
+		// bool that says if the referer detection is done
+		$refererAnalyzed = false;
+		$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+		$nameRefererAnalyzed = '';
+		$keywordRefererAnalyzed = '';	
+		
+		$refererUrl	= Piwik_Common::getRequestVar( 'urlref', '', 'string');
+		$currentUrl	= Piwik_Common::getRequestVar( 'url', '', 'string');
+
+		$refererUrlParse = @parse_url($refererUrl);
+		$currentUrlParse = @parse_url($currentUrl);
+
+		if(isset($refererUrlParse['host'])
+			&& !empty($refererUrlParse['host']))
+		{
+			
+			$refererHost = $refererUrlParse['host'];
+			$refererSH = $refererUrlParse['scheme'].'://'.$refererUrlParse['host'];
+			
+			/*
+			 * Search engine detection
+			 */
+			if( !$refererAnalyzed )
+			{
+				/*
+				 * A referer is a search engine if the URL's host is in the SearchEngines array
+				 * and if we found the keyword in the URL.
+				 * 
+				 * For example if someone comes from http://www.google.com/partners.html this will not
+				 * be counted as a search engines, but as a website referer from google.com (because the
+				 * keyword couldn't be found in the URL) 
+				 */
+				require_once PIWIK_DATAFILES_INCLUDE_PATH . "/SearchEngines.php";
+				
+				if(array_key_exists($refererHost, $GLOBALS['Piwik_SearchEngines']))
+				{
+					// which search engine ?
+					$searchEngineName = $GLOBALS['Piwik_SearchEngines'][$refererHost][0];
+					$variableName = $GLOBALS['Piwik_SearchEngines'][$refererHost][1];
+					
+					// if there is a query, there may be a keyword...
+					if(isset($refererUrlParse['query']))
+					{
+						$query = $refererUrlParse['query'];
+						
+						//TODO: change the search engine file and use REGEXP; performance downside?
+						//TODO: port the phpmyvisites google-images hack here
+	
+						// search for keywords now &vname=keyword
+						$key = strtolower(Piwik_Common::getParameterFromQueryString($query, $variableName));
+	
+						//TODO test the search engine non-utf8 support
+						// for search engines that don't use utf-8
+						if((function_exists('iconv')) 
+							&& (isset($GLOBALS['Piwik_SearchEngines'][$refererHost][2])))
+						{
+							$charset = trim($GLOBALS['searchEngines'][$refererHost][2]);
+							
+							if(!empty($charset)) 
+							{
+								$key = htmlspecialchars(
+											@iconv(	$charset, 
+													'utf-8//TRANSLIT', 
+													htmlspecialchars_decode($key, Piwik_Common::HTML_ENCODING_QUOTE_STYLE))
+											, Piwik_Common::HTML_ENCODING_QUOTE_STYLE);
+							}
+						}
+						
+						
+						if(!empty($key))
+						{
+							$refererAnalyzed = true;
+							$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_SEARCH_ENGINE;
+							$nameRefererAnalyzed = $searchEngineName;
+							$keywordRefererAnalyzed = $key;
+						}
+					}
+				}
+			}
+			
+			/*
+			 * Newsletter analysis
+			 */
+			if( !$refererAnalyzed )
+			{
+				if(isset($currentUrlParse['query']))
+				{
+					$newsletterVariableName = Piwik_LogStats_Config::getInstance()->LogStats['newsletter_var_name'];
+					$newsletterVar = Piwik_Common::getParameterFromQueryString( $currentUrlParse['query'], $newsletterVariableName);
+		
+					if($newsletterVar !== false && !empty($newsletterVar))
+					{
+						$refererAnalyzed = true;
+						$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_NEWSLETTER;
+						$nameRefererAnalyzed = $newsletterVar;
+					}
+				}
+			}
+			
+			/*
+			 * Partner analysis
+			 */
+			 //TODO handle partner from a list of known partner URLs
+			if( !$refererAnalyzed )
+			{				
+				if(isset($currentUrlParse['query']))
+				{		
+					$partnerVariableName = Piwik_LogStats_Config::getInstance()->LogStats['partner_var_name'];
+					$partnerVar = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $partnerVariableName);
+									
+					if($partnerVar !== false && !empty($partnerVar))
+					{
+						$refererAnalyzed = true;
+						$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_PARTNER;
+						$nameRefererAnalyzed = $partnerVar;
+					}
+				}
+			}
+			
+			/*
+			 * Campaign analysis
+			 */
+			if( !$refererAnalyzed )
+			{				
+				if(isset($currentUrlParse['query']))
+				{		
+					$campaignVariableName = Piwik_LogStats_Config::getInstance()->LogStats['campaign_var_name'];
+					$campaignName = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $campaignVariableName);
+					
+					if( $campaignName !== false && !empty($campaignName))
+					{
+						$campaignKeywordVariableName = Piwik_LogStats_Config::getInstance()->LogStats['campaign_keyword_var_name'];
+						$campaignKeyword = Piwik_Common::getParameterFromQueryString($currentUrlParse['query'], $campaignKeywordVariableName);
+	
+						$refererAnalyzed = true;
+						$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_CAMPAIGN;
+						$nameRefererAnalyzed = $campaignName;
+					
+						if(!empty($campaignKeyword))
+						{
+							$keywordRefererAnalyzed = $campaignKeyword;
+						}
+					}
+				}
+			}
+			
+			/*
+			 * Direct entry (referer host is similar to current host)
+			 * And we have previously tried to detect the newsletter/partner/campaign variables in the URL 
+			 * so it can only be a direct access
+			 */
+			if( !$refererAnalyzed )
+			{
+				$currentUrlParse = @parse_url($currentUrl);
+		
+				if(isset($currentUrlParse['host']))
+				{
+					$currentHost = $currentUrlParse['host'];
+					$currentSH = $currentUrlParse['scheme'].'://'.$currentUrlParse['host'];
+				
+					if($currentHost == $refererHost)
+					{
+						$refererAnalyzed = true;
+						$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+					}
+				}
+				
+			}
+
+			/*
+			 * Normal website referer
+			 */
+			if( !$refererAnalyzed )
+			{
+				$refererAnalyzed = true;
+				$typeRefererAnalyzed = Piwik_Common::REFERER_TYPE_WEBSITE;
+				$nameRefererAnalyzed = $refererHost;
+			}
+		}
+
+
+		$refererInformation = array(
+			'referer_type' 		=> $typeRefererAnalyzed,
+			'referer_name' 		=> $nameRefererAnalyzed,
+			'referer_keyword' 	=> $keywordRefererAnalyzed,
+			'referer_url' 		=> $refererUrl,
+		);
+		
+		return $refererInformation;
+	}
+	
+	private function getConfigHash( $os, $browserName, $browserVersion, $resolution, $colorDepth, $plugin_Flash, $plugin_Director, $plugin_RealPlayer, $plugin_Pdf, $plugin_WindowsMedia, $plugin_Java, $plugin_Cookie, $ip, $browserLang)
+	{
+		return md5( $os . $browserName . $browserVersion . $resolution . $colorDepth . $plugin_Flash . $plugin_Director . $plugin_RealPlayer . $plugin_Pdf . $plugin_WindowsMedia . $plugin_Java . $plugin_Cookie . $ip . $browserLang );
+	}
+	
+	private function getVisitorUniqueId()
+	{
+		if($this->isVisitorKnown())
+		{
+			return -1;
+		}
+		else
+		{
+			return Piwik_Common::generateUniqId();
+		}
+	}
+		
+}
+
+class Piwik_LogStats
+{	
+	private $stateValid;
+	
+	private $urlToRedirect;
+	
+	private $db = null;
+	
+	const STATE_NOTHING_TO_NOTICE = 1;
+	const STATE_TO_REDIRECT_URL = 2;
+	const STATE_LOGGING_DISABLE = 10;
+	const STATE_NO_GET_VARIABLE = 11;
+		
+	const COOKIE_INDEX_IDVISITOR 				= 1;
+	const COOKIE_INDEX_TIMESTAMP_LAST_ACTION 	= 2;
+	const COOKIE_INDEX_TIMESTAMP_FIRST_ACTION 	= 3;
+	const COOKIE_INDEX_ID_VISIT 				= 4;
+	const COOKIE_INDEX_ID_LAST_ACTION 			= 5;
+	
+	const VISIT_STANDARD_LENGTH = 1800;
+	
+	public function __construct()
+	{
+		$this->stateValid = self::STATE_NOTHING_TO_NOTICE;
+	}
+	
+	// create the database object
+	function connectDatabase()
+	{
+		$configDb = Piwik_LogStats_Config::getInstance()->database;
+		$this->db = new Piwik_LogStats_Db( 	$configDb['host'], 
+										$configDb['username'], 
+										$configDb['password'], 
+										$configDb['dbname']
+							);  
+		$this->db->connect();
+	}
+	
+	private function initProcess()
+	{
+		$saveStats = Piwik_LogStats_Config::getInstance()->LogStats['record_statistics'];
+		
+		if($saveStats == 0)
+		{
+			$this->setState(self::STATE_LOGGING_DISABLE);
+		}
+		
+		if( count($_GET) == 0)
+		{
+			$this->setState(self::STATE_NO_GET_VARIABLE);			
+		}
+		
+		$downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
+		$urlDownload = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
+		
+		if( !empty($urlDownload) )
+		{
+			$this->setState( self::STATE_TO_REDIRECT_URL );
+			$this->setUrlToRedirect ( $urlDownload);
+		}
+		
+		$outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
+		$urlOutlink = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
+		
+		if( !empty($urlOutlink) )
+		{
+			$this->setState( self::STATE_TO_REDIRECT_URL );
+			$this->setUrlToRedirect ( $urlOutlink);
+		}
+	}
+	
+	private function processVisit()
+	{
+		return $this->stateValid !== self::STATE_LOGGING_DISABLE
+				&&  $this->stateValid !== self::STATE_NO_GET_VARIABLE;
+	}
+	private function getState()
+	{
+		return $this->stateValid;
+	}
+	
+	private function setUrlToRedirect( $url )
+	{
+		$this->urlToRedirect = $url;
+	}
+	private function getUrlToRedirect()
+	{
+		return $this->urlToRedirect;
+	}
+	private function setState( $value )
+	{
+		$this->stateValid = $value;
+	}
+	
+	// main algorithm 
+	// => input : variables filtered
+	// => action : read cookie, read database, database logging, cookie writing
+	function main()
+	{
+		$this->initProcess();
+		
+		if( $this->processVisit() )
+		{
+			$this->connectDatabase();
+			$visit = new Piwik_LogStats_Visit( $this->db );
+			$visit->handle();
+		}
+		$this->endProcess();
+	}	
+
+	// display the logo or pixel 1*1 GIF
+	// or a marketing page if no parameters in the url
+	// or redirect to a url (transmit the cookie as well)
+	// or load a URL (rss feed) (transmit the cookie as well)
+	private function endProcess()
+	{
+		switch($this->getState())
+		{
+			case self::STATE_LOGGING_DISABLE:
+				printDebug("Logging disabled, display transparent logo");
+			break;
+			
+			case self::STATE_NO_GET_VARIABLE:
+				printDebug("No get variables => piwik page");
+			break;
+			
+			
+			case self::STATE_TO_REDIRECT_URL:
+				header('Location: ' . $this->getUrlToRedirect());
+			break;
+			
+			
+			case self::STATE_NOTHING_TO_NOTICE:
+			default:
+				printDebug("Nothing to notice => default behaviour");
+			break;
+		}
+		printDebug("End of the page.");
+	}
+}
+
+
+
+function printDebug( $info = '' )
+{
+	if(isset($GLOBALS['DEBUGPIWIK']) && $GLOBALS['DEBUGPIWIK'])
+	{
+		if(is_array($info))
+		{
+			print("<PRE>");
+			print(var_export($info,true));
+			print("</PRE>");
+		}
+		else
+		{
+			print($info . "<br>\n");
+		}
+	}
+}
+?>
diff --git a/modules/LogStats/Plugins.php b/modules/LogStats/Plugins.php
new file mode 100644
index 0000000000..8e5610a40a
--- /dev/null
+++ b/modules/LogStats/Plugins.php
@@ -0,0 +1,52 @@
+<?php
+
+class Piwik_Plugin_LogStats_Provider extends Piwik_Plugin
+{	
+	public function __construct()
+	{
+	}
+
+	public function getInformation()
+	{
+		$info = array(
+			'name' => 'LogProvider',
+			'description' => 'Log in the DB the hostname looked up from the IP',
+			'author' => 'Piwik',
+			'homepage' => 'http://piwik.org/plugins/LogProvider',
+			'version' => '0.1',
+		);
+		
+		return $info;
+	}
+	
+	function install()
+	{
+		// add column hostname / hostname ext in the visit table
+	}
+	
+	function uninstall()
+	{
+		// add column hostname / hostname ext in the visit table
+	}
+	
+	function getListHooksRegistered()
+	{
+		$hooks = array(
+			'LogsStats.NewVisitor' => 'detectHostname'
+		);
+		return $hooks;
+	}
+	
+	function detectHostname( $notification )
+	{
+		$object = $notification->getNotificationObject();
+		printDebug();
+	}
+}
+/*
+class Piwik_Plugin_LogStats_UserSettings extends Piwik_Plugin
+{
+	
+}*/
+
+?>
diff --git a/modules/Piwik.php b/modules/Piwik.php
index a99b3c8744..80c0ca1015 100755
--- a/modules/Piwik.php
+++ b/modules/Piwik.php
@@ -106,43 +106,43 @@ class Piwik
 			",
 			
 			'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
-								  idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
-								  idsite INTEGER(10) UNSIGNED NOT NULL,
-								  visitor_localtime TIME NOT NULL,
-								  visitor_idcookie CHAR(32) NOT NULL,
-								  visitor_returning TINYINT(1) NOT NULL,
-								  visitor_last_visit_time TIME NOT NULL,
-								  visit_server_date DATE NOT NULL,
-								  visit_server_time TIME NOT NULL,
-								  visit_exit_idaction INTEGER(11) NOT NULL,
-								  visit_entry_idaction INTEGER(11) NOT NULL,
-								  visit_total_actions SMALLINT(5) UNSIGNED NOT NULL,
-								  visit_total_time SMALLINT(5) UNSIGNED NOT NULL,
-								  referer_type INTEGER UNSIGNED NULL,
-								  referer_name VARCHAR(70) NULL,
-								  referer_url TEXT NOT NULL,
-								  referer_keyword VARCHAR(255) NULL,
-								  config_md5config CHAR(32) NOT NULL,
-								  config_os CHAR(3) NOT NULL,
-								  config_browser_name VARCHAR(10) NOT NULL,
-								  config_browser_version VARCHAR(20) NOT NULL,
-								  config_resolution VARCHAR(9) NOT NULL,
-								  config_color_depth TINYINT(2) UNSIGNED NOT NULL,
-								  config_pdf TINYINT(1) NOT NULL,
-								  config_flash TINYINT(1) NOT NULL,
-								  config_java TINYINT(1) NOT NULL,
-								  config_javascript TINYINT(1) NOT NULL,
-								  config_director TINYINT(1) NOT NULL,
-								  config_quicktime TINYINT(1) NOT NULL,
-								  config_realplayer TINYINT(1) NOT NULL,
-								  config_windowsmedia TINYINT(1) NOT NULL,
-								  config_cookie TINYINT(1) NOT NULL,
-								  location_ip BIGINT(11) NOT NULL,
-								  location_browser_lang VARCHAR(20) NOT NULL,
-								  location_country CHAR(3) NOT NULL,
-								  location_continent CHAR(3) NOT NULL,
-								  PRIMARY KEY(idvisit)
-								)
+  idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  idsite INTEGER(10) UNSIGNED NOT NULL,
+  visitor_localtime TIME NOT NULL,
+  visitor_idcookie CHAR(32) NOT NULL,
+  visitor_returning TINYINT(1) NOT NULL,
+  visit_first_action_time DATETIME NOT NULL,
+  visit_last_action_time DATETIME NOT NULL,
+  visit_server_date DATE NOT NULL,
+  visit_exit_idaction INTEGER(11) NOT NULL,
+  visit_entry_idaction INTEGER(11) NOT NULL,
+  visit_total_actions SMALLINT(5) UNSIGNED NOT NULL,
+  visit_total_time SMALLINT(5) UNSIGNED NOT NULL,
+  referer_type INTEGER UNSIGNED NULL,
+  referer_name VARCHAR(70) NULL,
+  referer_url TEXT NOT NULL,
+  referer_keyword VARCHAR(255) NULL,
+  config_md5config CHAR(32) NOT NULL,
+  config_os CHAR(3) NOT NULL,
+  config_browser_name VARCHAR(10) NOT NULL,
+  config_browser_version VARCHAR(20) NOT NULL,
+  config_resolution VARCHAR(9) NOT NULL,
+  config_color_depth TINYINT(2) UNSIGNED NOT NULL,
+  config_pdf TINYINT(1) NOT NULL,
+  config_flash TINYINT(1) NOT NULL,
+  config_java TINYINT(1) NOT NULL,
+  config_javascript TINYINT(1) NOT NULL,
+  config_director TINYINT(1) NOT NULL,
+  config_quicktime TINYINT(1) NOT NULL,
+  config_realplayer TINYINT(1) NOT NULL,
+  config_windowsmedia TINYINT(1) NOT NULL,
+  config_cookie TINYINT(1) NOT NULL,
+  location_ip BIGINT(11) NOT NULL,
+  location_browser_lang VARCHAR(20) NOT NULL,
+  location_country CHAR(3) NOT NULL,
+  location_continent CHAR(3) NOT NULL,
+  PRIMARY KEY(idvisit)
+)
 			",
 			
 			'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
diff --git a/modules/PluginManager.php b/modules/PluginManager.php
new file mode 100644
index 0000000000..b589ec1674
--- /dev/null
+++ b/modules/PluginManager.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * Plugin specification for a statistics logging plugin
+ * 
+ * A plugin that display data in the Piwik Interface is very different from a plugin 
+ * that will save additional data in the database during the statistics logging. 
+ * These two types of plugins don't have the same requirements at all. Therefore a plugin
+ * that saves additional data in the database during the stats logging process will have a different
+ * structure.
+ * 
+ * A plugin for logging data has to focus on performance and therefore has to stay as simple as possible.
+ * For input data, it is strongly advised to use the Piwik methods available in Piwik_Common 
+ *
+ * Things that can be done with such a plugin:
+ * - having a dependency with a list of other plugins
+ * - have an install step that would prepare the plugin environment
+ * 		- install could add columns to the tables
+ * 		- install could create tables 
+ * - register to hooks at several points in the logging process
+ * - register to hooks in other plugins
+ * - generally a plugin method can modify data (filter) and add/remove data 
+ * 
+ * 
+ */ 
+class Piwik_PluginsManager
+{
+	public $dispatcher;
+	private $pluginsPath;
+	
+	static private $instance = null;
+	
+	static public function getInstance()
+	{
+		if (self::$instance == null)
+		{			
+			$c = __CLASS__;
+			self::$instance = new $c();
+		}
+		return self::$instance;
+	}
+	
+	private function __construct()
+	{
+		$this->pluginsPath = 'plugins/';
+		$this->pluginsCategory = 'LogsStats/';
+		
+		$this->dispatcher = Event_Dispatcher::getInstance();
+		$this->loadPlugins();
+	}
+	
+	/**
+	 * Load the plugins classes installed.
+	 * Register the observers for every plugin.
+	 * 
+	 */
+	public function loadPlugins()
+	{
+		$defaultPlugins = array(
+			array( 'fileName' => 'Provider', 'className' => 'Piwik_Plugin_LogStats_Provider' ),
+		//	'Piwik_Plugin_LogStats_UserSettings',
+		);
+		
+		foreach($defaultPlugins as $pluginInfo)
+		{
+			$pluginFileName = $pluginInfo['fileName'];
+			$pluginClassName = $pluginInfo['className'];
+			/*
+			// TODO make sure the plugin name is secure
+			// make sure thepluigin is a child of Piwik_Plugin
+			$path = PIWIK_INCLUDE_PATH 
+					. $this->pluginsPath 
+					. $this->pluginsCategory
+					. $pluginFileName . ".php";
+			
+			if(is_file($path))
+			{
+				throw new Exception("The plugin file $path couldn't be found.");
+			}
+			
+			require_once $path;
+			*/
+			
+			$newPlugin = new $pluginClassName;
+			
+			$this->addPluginObservers( $newPlugin );
+		}
+	}
+	
+	/**
+	 * For the given plugin, add all the observers of this plugin.
+	 */
+	private function addPluginObservers( Piwik_Plugin $plugin )
+	{
+		$hooks = $plugin->getListHooksRegistered();
+		
+		foreach($hooks as $hookName => $methodToCall)
+		{
+			$this->dispatcher->addObserver( array( $plugin, $methodToCall) );
+		}
+	}
+	
+}
+
+/**
+ * Post an event to the dispatcher which will notice the observers
+ */
+function Piwik_PostEvent( $eventName, $object = null, $info = array() )
+{
+	printDebug("Dispatching event $eventName...");
+	Piwik_PluginsManager::getInstance()->dispatcher->post( $object, $eventName, $info, false, false );
+}
+
+/**
+ * Abstract class to define a Piwik_Plugin.
+ * Any plugin has to at least implement the abstract methods of this class.
+ */
+abstract class Piwik_Plugin
+{
+	/**
+	 * Returns the plugin details
+	 */
+	abstract function getInformation();
+	
+	/**
+	 * Returns the list of hooks registered with the methods names
+	 */
+	abstract function getListHooksRegistered();
+	
+	/**
+	 * Returns the names of the required plugins
+	 */
+	public function getListRequiredPlugins()
+	{
+		return array();
+	}
+	 
+	/**
+	 * Install the plugin
+	 * - create tables
+	 * - update existing tables
+	 * - etc.
+	*/
+	public function install()
+	{
+		return;
+	}
+	  
+	/**
+	 * Remove the created resources during the install
+	 */
+	public function uninstall()
+	{
+		return;
+	}
+}
+
+?>
diff --git a/piwik.php b/piwik.php
index d73b33039c..91a2f02a8d 100644
--- a/piwik.php
+++ b/piwik.php
@@ -9,6 +9,7 @@
  */
 error_reporting(E_ALL|E_NOTICE);
 define('PIWIK_INCLUDE_PATH', '.');
+define('PIWIK_DATAFILES_INCLUDE_PATH', PIWIK_INCLUDE_PATH . "/modules/DataFiles");
 
 @ignore_user_abort(true);
 @set_time_limit(0);
@@ -22,22 +23,11 @@ set_include_path(PIWIK_INCLUDE_PATH
 
 require_once "Event/Dispatcher.php";
 require_once "Common.php";
+require_once "LogStats.php";
+require_once "PluginManager.php";
+require_once "LogStats/Plugins.php";
 
-function printDebug( $info = '' )
-{
-	if(is_array($info))
-	{
-		print("<PRE>");
-		print(var_export($info,true));
-		print("</PRE>");
-	}
-	else
-	{
-		print($info . "<br>\n");
-	}
-}
-
-ob_start();
+$GLOBALS['DEBUGPIWIK'] = false;
 
 /*
  * Some benchmarks
@@ -45,1276 +35,21 @@ ob_start();
  * - with the config parsing + db connection
  * Requests per second:    471.91 [#/sec] (mean)
  * 
+ * - with the main algorithm working + one visitor requesting 5000 times
+ * Requests per second:    155.00 [#/sec] (mean)
  * 
  */
 
-/**
- * Simple database PDO wrapper
- * 
- */
-class Piwik_LogStats_Db 
-{
-	private $connection;
-	private $username;
-	private $password;
-	
-	public function __construct( $host, $username, $password, $dbname) 
-	{
-		$this->dsn = "mysql:dbname=$dbname;host=$host";
-		$this->username = $username;
-		$this->password = $password;
-	}
-
-	public function connect() 
-	{
-		try {
-			$pdoConnect = new PDO($this->dsn, $this->username, $this->password);
-			$pdoConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-			$this->connection = $pdoConnect;
-		} catch (PDOException $e) {
-			throw new Exception("Error connecting database: ".$e->getMessage());
-		}
-	}
-
-	public function prefixTable( $suffix )
-	{
-		$prefix = Piwik_LogStats_Config::getInstance()->database['tables_prefix'];
-		
-		return $prefix . $suffix;
-	}
-	
-	public function fetchAll( $query, $parameters )
-	{
-		try {
-			$sth = $this->query( $query, $parameters );
-			return $sth->fetchAll(PDO::FETCH_ASSOC);
-		} catch (PDOException $e) {
-			throw new Exception("Error query: ".$e->getMessage());
-		}
-	}
-	public function fetch( $query, $parameters )
-	{
-		try {
-			$sth = $this->query( $query, $parameters );
-			return $sth->fetch(PDO::FETCH_ASSOC);
-		} catch (PDOException $e) {
-			throw new Exception("Error query: ".$e->getMessage());
-		}
-	}
-	
-	public function query($query, $parameters = array()) 
-	{
-		try {
-			$sth = $this->connection->prepare($query);
-			$sth->execute( $parameters );
-			return $sth;
-		} catch (PDOException $e) {
-			throw new Exception("Error query: ".$e->getMessage());
-		}
-	}
-	
-	public function lastInsertId()
-	{
-		return  $this->connection->lastInsertId();
-	}
-}
-
-/**
- * Simple class to access the configuration file
- */
-class Piwik_LogStats_Config
-{
-	static private $instance = null;
-	
-	static public function getInstance()
-	{
-		if (self::$instance == null)
-		{			
-			$c = __CLASS__;
-			self::$instance = new $c();
-		}
-		return self::$instance;
-	}
-	
-	public $config = array();
-	
-	private function __construct()
-	{
-		$pathIniFile = PIWIK_INCLUDE_PATH . '/config/config.ini.php';
-		$this->config = parse_ini_file($pathIniFile, true);
-	}
-	
-	public function __get( $name )
-	{
-		if(isset($this->config[$name]))
-		{
-			return $this->config[$name];
-		}
-		else
-		{
-			throw new Exception("The config element $name is not available in the configuration (check the configuration file).");
-		}
-	}
-}
-
-
-/**
- * Plugin specification for a statistics logging plugin
- * 
- * A plugin that display data in the Piwik Interface is very different from a plugin 
- * that will save additional data in the database during the statistics logging. 
- * These two types of plugins don't have the same requirements at all. Therefore a plugin
- * that saves additional data in the database during the stats logging process will have a different
- * structure.
- * 
- * A plugin for logging data has to focus on performance and therefore has to stay as simple as possible.
- * For input data, it is strongly advised to use the Piwik methods available in Piwik_Common 
- *
- * Things that can be done with such a plugin:
- * - having a dependency with a list of other plugins
- * - have an install step that would prepare the plugin environment
- * 		- install could add columns to the tables
- * 		- install could create tables 
- * - register to hooks at several points in the logging process
- * - register to hooks in other plugins
- * - generally a plugin method can modify data (filter) and add/remove data 
- * 
- * 
- */ 
-class Piwik_PluginsManager
-{
-	public $dispatcher;
-	private $pluginsPath;
-	
-	static private $instance = null;
-	
-	static public function getInstance()
-	{
-		if (self::$instance == null)
-		{			
-			$c = __CLASS__;
-			self::$instance = new $c();
-		}
-		return self::$instance;
-	}
-	
-	private function __construct()
-	{
-		$this->pluginsPath = 'plugins/';
-		$this->pluginsCategory = 'LogsStats/';
-		
-		$this->dispatcher = Event_Dispatcher::getInstance();
-		$this->loadPlugins();
-	}
-	
-	/**
-	 * Load the plugins classes installed.
-	 * Register the observers for every plugin.
-	 * 
-	 */
-	public function loadPlugins()
-	{
-		$defaultPlugins = array(
-			array( 'fileName' => 'Provider', 'className' => 'Piwik_Plugin_LogStats_Provider' ),
-		//	'Piwik_Plugin_LogStats_UserSettings',
-		);
-		
-		foreach($defaultPlugins as $pluginInfo)
-		{
-			$pluginFileName = $pluginInfo['fileName'];
-			$pluginClassName = $pluginInfo['className'];
-			/*
-			// TODO make sure the plugin name is secure
-			// make sure thepluigin is a child of Piwik_Plugin
-			$path = PIWIK_INCLUDE_PATH 
-					. $this->pluginsPath 
-					. $this->pluginsCategory
-					. $pluginFileName . ".php";
-			
-			if(is_file($path))
-			{
-				throw new Exception("The plugin file $path couldn't be found.");
-			}
-			
-			require_once $path;
-			*/
-			
-			$newPlugin = new $pluginClassName;
-			
-			$this->addPluginObservers( $newPlugin );
-		}
-	}
-	
-	/**
-	 * For the given plugin, add all the observers of this plugin.
-	 */
-	private function addPluginObservers( Piwik_Plugin $plugin )
-	{
-		$hooks = $plugin->getListHooksRegistered();
-		
-		foreach($hooks as $hookName => $methodToCall)
-		{
-			$this->dispatcher->addObserver( array( $plugin, $methodToCall) );
-		}
-	}
-	
-}
-
-/**
- * Post an event to the dispatcher which will notice the observers
- */
-function Piwik_PostEvent( $eventName, $object = null, $info = array() )
-{
-	printDebug("Dispatching event $eventName...");
-	Piwik_PluginsManager::getInstance()->dispatcher->post( $object, $eventName, $info, false, false );
-}
-
-/**
- * Abstract class to define a Piwik_Plugin.
- * Any plugin has to at least implement the abstract methods of this class.
- */
-abstract class Piwik_Plugin
-{
-	/**
-	 * Returns the plugin details
-	 */
-	abstract function getInformation();
-	
-	/**
-	 * Returns the list of hooks registered with the methods names
-	 */
-	abstract function getListHooksRegistered();
-	
-	/**
-	 * Returns the names of the required plugins
-	 */
-	public function getListRequiredPlugins()
-	{
-		return array();
-	}
-	 
-	/**
-	 * Install the plugin
-	 * - create tables
-	 * - update existing tables
-	 * - etc.
-	*/
-	public function install()
-	{
-		return;
-	}
-	  
-	/**
-	 * Remove the created resources during the install
-	 */
-	public function uninstall()
-	{
-		return;
-	}
-}
-
-
-
-class Piwik_Plugin_LogStats_Provider extends Piwik_Plugin
-{	
-	public function __construct()
-	{
-	}
-
-	public function getInformation()
-	{
-		$info = array(
-			'name' => 'LogProvider',
-			'description' => 'Log in the DB the hostname looked up from the IP',
-			'author' => 'Piwik',
-			'homepage' => 'http://piwik.org/plugins/LogProvider',
-			'version' => '0.1',
-		);
-		
-		return $info;
-	}
-	
-	function install()
-	{
-		// add column hostname / hostname ext in the visit table
-	}
-	
-	function uninstall()
-	{
-		// add column hostname / hostname ext in the visit table
-	}
-	
-	function getListHooksRegistered()
-	{
-		$hooks = array(
-			'LogsStats.NewVisitor' => 'detectHostname'
-		);
-		return $hooks;
-	}
-	
-	function detectHostname( $notification )
-	{
-		$object = $notification->getNotificationObject();
-		var_dump($object);printDebug();
-	}
-}
-/*
-class Piwik_Plugin_LogStats_UserSettings extends Piwik_Plugin
-{
-	
-}*/
-
-Piwik_PostEvent( 'LogsStats.NewVisitor' );
-
-/**
- * To maximise the performance of the logging module, we use different techniques.
- * 
- * On the PHP-only side:
- * - minimize the number of external files included. 
- * 	 Ideally only one (the configuration file) in all the normal cases.
- *   We load the Loggers only when an error occurs ; this error is logged in the DB/File/etc
- *   depending on the loggers settings in the configuration file.
- * - we may have to include external classes but we try to include only very 
- *   simple code without any dependency, so that we could simply write a script
- *   that would merge all this simple code into a big piwik.php file.
- * 
- * On the Database-related side:
- * - write all the SQL queries without using any DB abstraction layer.
- * 	 Of course we carefully filter all input values.
- * - minimize the number of SQL queries necessary to complete the algorithm.
- * - carefully index the tables used
- * - try to have fixed length rows
- * 
- * [ - use a partitionning by date for the tables ]
- *   
- * - handle the timezone settings??
- * 
- * [ - country detection plugin => ip lookup ]
- * [ - precise country detection plugin ]
- * 
- * We could also imagine a batch system that would read a log file every 5min,
- * and which prepares the file containg the rows to insert, then we load DATA INFILE 
- * 
- */
-
-/**
- * Configuration options for the statsLogEngine module:
- * - use_cookie  ; defines if we try to get/set a cookie to help recognize a unique visitor
- */
-
-/**
- * Simple class to handle the cookies.
- * Its features are:
- * 
- * - read a cookie values
- * - edit an existing cookie and save it
- * - create a new cookie, set values, expiration date, etc. and save it
- * 
- * The cookie content is saved in an optimized way.
- */
-class Piwik_LogStats_Cookie
-{
-	/**
-	 * The name of the cookie 
-	 */
-	protected $name = null;
-	
-	/**
-	 * The expire time for the cookie (expressed in UNIX Timestamp)
-	 */
-	protected $expire = null;
-	
-	/**
-	 * The content of the cookie
-	 */
-	protected $value = array();
-	
-	const VALUE_SEPARATOR = ':';
-	
-	public function __construct( $cookieName, $expire = null)
-	{
-		$this->name = $cookieName;
-		
-		if(is_null($expire)
-			|| !is_numeric($expire)
-			|| $expire <= 0)
-		{
-			$this->expire = $this->getDefaultExpire();
-		}
-		
-		if($this->isCookieFound())
-		{
-			$this->loadContentFromCookie();
-		}
-	}
-	
-	public function isCookieFound()
-	{
-		return isset($_COOKIE[$this->name]);
-	}
-	
-	protected function getDefaultExpire()
-	{
-		return 86400*365*10;
-	}	
-	
-	/**
-	 * taken from http://usphp.com/manual/en/function.setcookie.php
-	 * fix expires bug for IE users (should i say expected to fix the bug in 2.3 b2)
-	 * TODO use the other parameters of the function
-	 */
-	protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', $Secure = false, $HTTPOnly = false)
-	{
-		if (!empty($Domain))
-		{	
-			// Fix the domain to accept domains with and without 'www.'.
-			if (strtolower(substr($Domain, 0, 4)) == 'www.')  $Domain = substr($Domain, 4);
-			
-			$Domain = '.' . $Domain;
-			
-			// Remove port information.
-			$Port = strpos($Domain, ':');
-			if ($Port !== false)  $Domain = substr($Domain, 0, $Port);
-		}
-		
-		header('Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value)
-		 . (empty($Expires) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $Expires) . ' GMT')
-		 . (empty($Path) ? '' : '; path=' . $Path)
-		 . (empty($Domain) ? '' : '; domain=' . $Domain)
-		 . (!$Secure ? '' : '; secure')
-		 . (!$HTTPOnly ? '' : '; HttpOnly'), false);
-	}
-	
-	protected function setP3PHeader()
-	{
-		header("P3P: CP='OTI DSP COR NID STP UNI OTPa OUR'");
-	}
-	
-	public function deleteCookie()
-	{
-		$this->setP3PHeader();
-		setcookie($this->name, false, time() - 86400);
-	}
-	
-	public function save()
-	{
-		$this->setP3PHeader();
-		$this->setCookie( $this->name, $this->generateContentString(), $this->expire);
-	}
-	
-	/**
-	 * Load the cookie content into a php array 
-	 */
-	protected function loadContentFromCookie()
-	{
-		$cookieStr = $_COOKIE[$this->name];
-		
-		$values = explode( self::VALUE_SEPARATOR, $cookieStr);
-		foreach($values as $nameValue)
-		{
-			$equalPos = strpos($nameValue, '=');
-			$varName = substr($nameValue,0,$equalPos);
-			$varValue = substr($nameValue,$equalPos+1);
-			
-			// no numeric value are base64 encoded so we need to decode them
-			if(!is_numeric($varValue))
-			{
-				$varValue = base64_decode($varValue);
-				
-				// some of the values may be serialized array so we try to unserialize it
-				if( ($arrayValue = @unserialize($varValue)) !== false
-					// we set the unserialized version only for arrays as you can have set a serialized string on purpose
-					&& is_array($arrayValue) 
-					)
-				{
-					$varValue = $arrayValue;
-				}
-			}
-			
-			$this->set($varName, $varValue);
-		}
-	}
-	
-	/**
-	 * Returns the string to save in the cookie frpm the $this->value array of values
-	 * 
-	 */
-	public function generateContentString()
-	{
-		$cookieStr = '';
-		foreach($this->value as $name=>$value)
-		{
-			if(is_array($value))
-			{
-				$value = base64_encode(serialize($value));
-			}
-			elseif(is_string($value))
-			{
-				$value = base64_encode($value);
-			}
-			
-			$cookieStr .= "$name=$value" . self::VALUE_SEPARATOR;
-		}
-		$cookieStr = substr($cookieStr, 0, strlen($cookieStr)-1);
-		return $cookieStr;
-	}
-	
-	/**
-	 * Registers a new name => value association in the cookie.
-	 * 
-	 * Registering new values is optimal if the value is a numeric value.
-	 * If the value is a string, it will be saved as a base64 encoded string.
-	 * If the value is an array, it will be saved as a serialized and base64 encoded 
-	 * string which is not very good in terms of bytes usage. 
-	 * You should save arrays only when you are sure about their maximum data size.
-	 * 
-	 * @param string Name of the value to save; the name will be used to retrieve this value
-	 * @param string|array|numeric Value to save
-	 * 
- 	 */
-	public function set( $name, $value )
-	{
-		$name = self::escapeValue($name);
-		$this->value[$name] = $value;
-	}
-	
-	/**
-	 * Returns the value defined by $name from the cookie.
-	 * 
-	 * @param string|integer Index name of the value to return
-	 * @return mixed The value if found, false if the value is not found
-	 */
-	public function get( $name )
-	{
-		$name = self::escapeValue($name);
-		return isset($this->value[$name]) ? self::escapeValue($this->value[$name]) : false;
-	}
-	
-	public function __toString()
-	{
-		$str = "<-- Content of the cookie '{$this->name}' <br>\n";
-		foreach($this->value as $name => $value )
-		{
-			$str .= $name . " = " . var_export($this->get($name), true) . "<br>\n";
-		}
-		$str .= "--> <br>\n";
-		return $str;
-	}
-	
-	static protected function escapeValue( $value )
-	{
-		return Piwik_Common::sanitizeInputValues($value);
-	}	
-}
-
-//
-//$c = new Piwik_LogStats_Cookie( 'piwik_logstats', 86400);
-//echo $c;
-//$c->set(1,1);
-//$c->set('test',1);
-//$c->set('test2','test=432:gea785');
-//$c->set('test3',array('test=432:gea785'));
-//$c->set('test4',array(array(0=>1),1=>'test'));
-//echo $c;
-//echo "<br>";
-//echo $c->generateContentString();
-//echo "<br>";
-//$v=$c->get('more!');
-//if(empty($v)) $c->set('more!',1);
-//$c->set('more!', array($c->get('more!')));
-//$c->save();
-//$c->deleteCookie();
-
-class Piwik_LogStats_Action
-{
-	
-	 /*
-	  * Specifications
-	  *  
-	  * - External file tracking
-	  * 
-	  *    * MANUAL Download tracking 
-	  *      download = http://piwik.org/hellokity.zip
-	  * 	(name = dir1/file alias name)
-	  *
-	  *    * AUTOMATIC Download tracking for a known list of file extensions. 
-	  *    Make a hit to the piwik.php with the parameter: 
-	  *      download = http://piwik.org/hellokity.zip
-	  *  
-	  *   When 'name' is not specified, 
-	  * 	if AUTOMATIC and if anchor not empty => name = link title anchor
-	  * 	else name = path+query of the URL
-	  *   Ex: myfiles/beta.zip
-	  *
-	  * - External link tracking
-	  * 
-	  *    * MANUAL External link tracking
-	  * 	 outlink = http://amazon.org/test
-	  * 	(name = the big partners / amazon)
-	  * 
-	  *    * AUTOMATIC External link tracking
-	  *      When a link is not detected as being part of the same website 
-	  *     AND when the url extension is not detected as being a file download
-	  * 	 outlink = http://amazon.org/test
-	  * 
-	  *  When 'name' is not specified, 
-	  * 	if AUTOMATIC and if anchor not empty => name = link title anchor
-	  * 	else name = URL
-	  *   Ex: http://amazon.org/test
-	  */
-	private $actionName;
-	private $url;
-	private $defaultActionName;
-	private $nameDownloadOutlink;
-	
-	const TYPE_ACTION   = 1;
-	const TYPE_DOWNLOAD = 3;
-	const TYPE_OUTLINK  = 2;
-	
-	function __construct( $db )
-	{
-		$this->actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string');
-		
-		$downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
-		$this->downloadUrl = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
-		
-		$outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
-		$this->outlinkUrl = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
-		
-		$nameVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_outlink_name_var'];
-		$this->nameDownloadOutlink = Piwik_Common::getRequestVar( $nameVariableName, '', 'string');
-		
-		$this->url = Piwik_Common::getRequestVar( 'url', '', 'string');
-		$this->db = $db;
-		$this->defaultActionName = Piwik_LogStats_Config::getInstance()->LogStats['default_action_name'];
-	}
-	
-	/**
-	 * About the Action concept:
-	 * 
-	 * - An action is defined by a name.
-	 * - The name can be specified in the JS Code in the variable 'action_name'
-	 * - Handling UTF8 in the action name
-	 * PLUGIN_IDEA - An action is associated to URLs and link to the URL from the interface
-	 * PLUGIN_IDEA - An action hit by a visitor is associated to the HTML title of the page that triggered the action
-	 * 
-	 * + If the name is not specified, we use the URL(path+query) to build a default name.
-	 *   For example for "http://piwik.org/test/my_page/test.html" 
-	 *   the name would be "test/my_page/test.html"
-	 * 
-	 * We make sure it is clean and displayable.
-	 * If the name is empty we set it to a default name.
-	 * 
-	 * TODO UTF8 handling to test
-	 * 
-	 */
-	 
-	private function generateInfo()
-	{
-		if(!empty($this->downloadUrl))
-		{
-			$this->actionType = self::TYPE_DOWNLOAD;
-			$url = $this->downloadUrl;
-			$actionName = $this->nameDownloadOutlink;
-		}
-		elseif(!empty($this->outlinkUrl))
-		{
-			$this->actionType = self::TYPE_OUTLINK;
-			$url = $this->outlinkUrl;
-			$actionName = $this->nameDownloadOutlink;
-			if( empty($actionName) )
-			{
-				$actionName = $url;
-			}
-		}
-		else
-		{
-			$this->actionType = self::TYPE_ACTION;
-			$url = $this->url;
-			$actionName = $this->actionName;
-		}		
-		
-		// the ActionName wasn't specified
-		if( empty($actionName) )
-		{
-			$parsedUrl = parse_url( $url );
-			
-			$actionName = '';
-			
-			if(isset($parsedUrl['path']))
-			{
-				$actionName .= substr($parsedUrl['path'], 1);
-			}
-			
-			if(isset($parsedUrl['query']))
-			{
-				$actionName .= '?'.$parsedUrl['query'];
-			}
-		}
-		
-		// clean the name
-		$actionName = str_replace(array("\n", "\r"), '', $actionName);
-		
-		if(empty($actionName))
-		{
-			$actionName = $this->defaultActionName;
-		}
-		
-		$this->finalActionName = $actionName;
-	}
-	
-	/**
-	 * Returns the idaction of the current action name.
-	 * This idaction is used in the visitor logging table to link the visit information 
-	 * (entry action, exit action) to the actions.
-	 * This idaction is also used in the table that links the visits and their actions.
-	 * 
-	 * The methods takes care of creating a new record in the action table if the existing 
-	 * action name doesn't exist yet.
-	 * 
-	 * @return int Id action
-	 */
-	function getActionId()
-	{
-		$this->loadActionId();
-		return $this->idAction;
-	}
-	
-	/**
-	 * @see getActionId()
-	 */
-	private function loadActionId()
-	{		
-		$this->generateInfo();
-		
-		$name = $this->finalActionName;
-		$type = $this->actionType;
-		
-		$idAction = $this->db->fetch("	SELECT idaction 
-							FROM ".$this->db->prefixTable('log_action')
-						."  WHERE name = ? AND type = ?", array($name, $type) );
-		
-		// the action name has not been found, create it
-		if($idAction === false)
-		{
-			$this->db->query("INSERT INTO ". $this->db->prefixTable('log_action'). "( name, type ) 
-								VALUES (?,?)",array($name,$type) );
-			$idAction = $this->db->lastInsertId();
-		}
-		else
-		{
-			$idAction = $idAction['idaction'];
-		}
-		
-		$this->idAction = $idAction;
-	}
-	
-	/**
-	 * Records in the DB the association between the visit and this action.
-	 */
-	 public function record( $idVisit, $idRefererAction, $timeSpentRefererAction)
-	 {
-	 	$this->db->query("INSERT INTO ".$this->db->prefixTable('log_link_visit_action')
-						." (idvisit, idaction, idaction_ref, time_spent_ref_action) VALUES (?,?,?,?)",
-					array($idVisit, $this->idAction, $idRefererAction, $timeSpentRefererAction)
-					);
-	 }
-	 
-}
-
-class Piwik_LogStats_Visit
-{
-	
-	function __construct( $db )
-	{
-		$this->db = $db;
-	}
-	
-	// test if the visitor is excluded because of
-	// - IP
-	// - cookie
-	// - configuration option?
-	private function isExcluded()
-	{
-		$excluded = 0;
-		
-		if($excluded)
-		{
-			printDebug("Visitor excluded.");
-			return true;
-		}
-		
-		return false;
-	}
-	
-	/**
-	 * Handles the visitor.
-	 * 
-	 * We have to split the visitor into one of the category 
-	 * - Known visitor
-	 * - New visitor
-	 * 
-	 * A known visitor is a visitor that has already visited the website in the current month.
-	 * We define a known visitor using (in order of importance):
-	 * 1) A cookie that contains
-	 * 		// a unique id for the visitor
-	 * 		- id_visitor 
-	 * 
-	 * 		// the timestamp of the last action in the most recent visit
-	 * 		- timestamp_last_action 
-	 * 
- 	 *  	// the timestamp of the first action in the most recent visit
-	 * 		- timestamp_first_action
-	 * 
-	 * 		// the ID of the most recent visit (which could be in the past or the current visit)
-	 * 		- id_visit 
-	 * 
-	 * 		// the ID of the most recent action
-	 * 		- id_last_action
-	 * 2) If the visitor doesn't have a cookie, we try to look for a similar visitor configuration.
-	 * 	  We search for a visitor with the same plugins/OS/Browser/Resolution for today for this website.
-	 */
-	private function recognizeTheVisitor()
-	{
-		$this->visitorKnown = false;
-	}
-	
-	private function isVisitorKnown()
-	{
-		return $this->visitorKnown === true;
-	}
-	
-	/**
-	 * Once we have the visitor information, we have to define if the visit is a new or a known visit.
-	 * 
-	 * 1) When the last action was done more than 30min ago, 
-	 * 	  or if the visitor is new, then this is a new visit.
-	 *	
-	 * 2) If the last action is less than 30min ago, then the same visit is going on. 
-	 *	Because the visit goes on, we can get the time spent during the last action.
-	 *
-	 * NB:
-	 *  - In the case of a new visit, then the time spent 
-	 *	during the last action of the previous visit is unknown.
-	 * 
-	 *	- In the case of a new visit but with a known visitor, 
-	 *	we can set the 'returning visitor' flag.
-	 *
-	 */
-	 
-	/**
-	 * In all the cases we set a cookie to the visitor with the new information.
-	 */
-	public function handle()
-	{
-		if(!$this->isExcluded())
-		{
-			$this->recognizeTheVisitor();
-			
-			// known visitor
-			if($this->isVisitorKnown())
-			{
-				if($this->isLastActionInTheSameVisit())
-				{
-					$this->handleKnownVisit();
-				}
-				else
-				{
-					$this->handleNewVisit();
-				}
-			}
-			// new visitor
-			else
-			{
-				$this->handleNewVisit();
-			}
-			
-			$this->updateCookie();
-			
-		}
-	}
-	
-	private function updateCookie()
-	{
-		printDebug("We manage the cookie...");
-	}
-	
-	/**
-	 * In the case of a known visit, we have to do the following actions:
-	 * 
-	 * 1) Insert the new action
-	 * 
-	 * 2) Update the visit information
-	 */
-	private function handleKnownVisit()
-	{
-		printDebug("Visit known.");
-	}
-	
-	/**
-	 * In the case of a new visit, we have to do the following actions:
-	 * 
-	 * 1) Insert the new action
-	 * 
-	 * 2) Insert the visit information
-	 */
-	private function handleNewVisit()
-	{
-		printDebug("New Visit.");
-		
-		/**
-		 * Get the variables from the REQUEST 
-		 */
-
-		/*
-		 * Configuration settings
-		 */
-		$plugin_Flash 			= Piwik_Common::getRequestVar( 'fla', 0, 'int');
-		$plugin_Director 		= Piwik_Common::getRequestVar( 'dir', 0, 'int');
-		$plugin_Quicktime		= Piwik_Common::getRequestVar( 'qt', 0, 'int');
-		$plugin_RealPlayer 		= Piwik_Common::getRequestVar( 'realp', 0, 'int');
-		$plugin_Pdf 			= Piwik_Common::getRequestVar( 'pdf', 0, 'int');
-		$plugin_WindowsMedia 	= Piwik_Common::getRequestVar( 'wma', 0, 'int');
-		$plugin_Java 			= Piwik_Common::getRequestVar( 'java', 0, 'int');
-		$plugin_Cookie 			= Piwik_Common::getRequestVar( 'cookie', 0, 'int');
-		
-		$userAgent		= Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_USER_AGENT']);
-		$aBrowserInfo	= Piwik_Common::getBrowserInfo($userAgent);
-		$browserName	= $aBrowserInfo['name'];
-		$browserVersion	= $aBrowserInfo['version'];
-		
-		$os				= Piwik_Common::getOs($userAgent);
-		
-		$resolution		= Piwik_Common::getRequestVar('res', 'unknown', 'string');
-		$colorDepth		= Piwik_Common::getRequestVar('col', 32, 'numeric');
-		
-
-
-		/*
-		 * General information
-		 */
-		$ip				= Piwik_Common::getIp();
-		$ip 			= ip2long($ip);
-		$localTime				= Piwik_Common::getRequestVar( 'h', date("H"), 'numeric')
-							.':'. Piwik_Common::getRequestVar( 'm', date("i"), 'numeric')
-							.':'. Piwik_Common::getRequestVar( 's', date("s"), 'numeric');
-		
-		$serverDate 	= date("Y-m-d");
-		$serverTime 	= date("H:i:s");
-		
-		$idsite = Piwik_Common::getRequestVar('idsite', 0, 'int');
-		if($idsite <= 0)
-		{
-			throw new Exception("The 'idsite' in the request is not acceptable.");
-		}
-		
-		$idcookie = $this->getVisitorUniqueId();
-		
-		$defaultTimeOnePageVisit = Piwik_LogStats_Config::getInstance()->LogStats['default_time_one_page_visit'];
-		
-		/*
-		 * Location information
-		 */
-		$browserLang	= Piwik_Common::sanitizeInputValues(@$_SERVER['HTTP_ACCEPT_LANGUAGE']);
-		$country 		= Piwik_Common::getCountry($browserLang);
-				
-		$continent		= Piwik_Common::getContinent( $country );
-		
-		/*
-		 * Misc
-		 */
-		$configurationHash = $this->getConfigHash( 
-												$os,
-												$browserName,
-												$browserVersion,
-												$resolution,
-												$colorDepth,
-												$plugin_Flash,
-												$plugin_Director,
-												$plugin_RealPlayer,
-												$plugin_Pdf,
-												$plugin_WindowsMedia,
-												$plugin_Java,
-												$plugin_Cookie,
-												$ip,
-												$browserLang);
-		/**
-		 * Init the action
-		 */
-		$action = new Piwik_LogStats_Action( $this->db );
-		
-		$actionId = $action->getActionId();
-		
-		printDebug("idAction = $actionId");
-		
-		
-		/**
-		 * Save the visitor
-		 */
-		
-		$informationToSave = array(
-			//'idvisit' => ,
-			'idsite' 				=> $idsite,
-			'visitor_localtime' 	=> $localTime,
-			'visitor_idcookie' 		=> $idcookie,
-			'visitor_returning' 	=> 0,
-			'visitor_last_visit_time' => 0,
-			'visit_server_date' 	=> $serverDate,
-			'visit_server_time' 	=> $serverTime,
-			'visit_exit_idaction' 	=> $actionId,
-			'visit_entry_idaction' 	=> $actionId,
-			'visit_total_actions' 	=> 1,
-			'visit_total_time' 		=> $defaultTimeOnePageVisit,
-			'referer_type' 			=> '',
-			'referer_name' 			=> '',
-			'referer_url' 			=> '',
-			'referer_keyword' 		=> '',
-			'config_md5config' 		=> $configurationHash,
-			'config_os' 			=> $os,
-			'config_browser_name' 	=> $browserName,
-			'config_browser_version' => $browserVersion,
-			'config_resolution' 	=> $resolution,
-			'config_color_depth' 	=> $colorDepth,
-			'config_pdf' 			=> $plugin_Pdf,
-			'config_flash' 			=> $plugin_Flash,
-			'config_java' 			=> $plugin_Java,
-			'config_director' 		=> $plugin_Director,
-			'config_quicktime' 		=> $plugin_Quicktime,
-			'config_realplayer' 	=> $plugin_RealPlayer,
-			'config_windowsmedia' 	=> $plugin_WindowsMedia,
-			'config_cookie' 		=> $plugin_RealPlayer,
-			'location_ip' 			=> $ip,
-			'location_browser_lang' => $browserLang,
-			'location_country' 		=> $country,
-			'location_continent' 	=> $continent,
-		);
-		
-		$fields = implode(", ", array_keys($informationToSave));
-		$values = substr(str_repeat( "?,",count($informationToSave)),0,-1);
-		
-		$this->db->query( "INSERT INTO ".$this->db->prefixTable('log_visit').
-						" ($fields) VALUES ($values)", array_values($informationToSave));
-						
-		$idVisit = $this->db->lastInsertId();
-		
-		/**
-		 * Save the action
-		 */
-		$action->record( $idVisit, 0, 0 );
-		
-	}
-	
-	/**
-	 * Compute the following information
-	 * - referer_type
-	 *		- direct			-- absence of referer URL
-	 *		- site				-- based on the referer URL
-	 *		- search_engine		-- based on the referer URL
-	 *		- cpc				-- based on campaign URL parameter
-	 *		- newsletter		-- based on campaign URL parameter
-	 *		- partner			-- based on campaign URL parameter
-	 *
-	 * - referer_name
-	 * 		- piwik.net
-	 * 		- ()
-	 * 		- google.fr
-	 * 		- adwords-search
-	 * 		- beta-release
-	 * 		- my-nice-partner
-	 * 		
-	 * - referer_keyword
-	 * 		- ()
-	 * 		- ()
-	 * 		- my keyword
-	 * 		- my paid keyword
-	 * 		- ()
-	 * 		- ()
-	 *  
-	 * - referer_url : the same for all the referer types
-	 * 
-	 */
-	private function handleReferer()
-	{
-		/*
-		 * Referer analysis
-		 */
-		$refererUrl	= Piwik_Common::getRequestVar( 'urlref', '', 'string');
-		$url		= Piwik_Common::getRequestVar( 'url', '', 'string');
-		
-		
-		 /*
-		  * 
-		  * - Campaign tagging specification
-		  *		* newsletter / beta-release
-		  *     * partner / Amazon / [autofilled by piwik http://amazon.com/refererpage.html]
-		  *   	* CPC / adwords-search / myKeyword
-		  */
-		  
-		  
-	}
-	
-	private function getConfigHash( $os, $browserName, $browserVersion, $resolution, $colorDepth, $plugin_Flash, $plugin_Director, $plugin_RealPlayer, $plugin_Pdf, $plugin_WindowsMedia, $plugin_Java, $plugin_Cookie, $ip, $browserLang)
-	{
-		return md5( $os . $browserName . $browserVersion . $resolution . $colorDepth . $plugin_Flash . $plugin_Director . $plugin_RealPlayer . $plugin_Pdf . $plugin_WindowsMedia . $plugin_Java . $plugin_Cookie . $ip . $browserLang );
-	}
-	
-	private function getVisitorUniqueId()
-	{
-		if($this->isVisitorKnown())
-		{
-			return -1;
-		}
-		else
-		{
-			return Piwik_Common::generateUniqId();
-		}
-	}
-		
-}
-
+ob_start();
 printDebug($_GET);
-
-class Piwik_LogStats
-{	
-	private $stateValid;
-	
-	private $urlToRedirect;
-	
-	private $db = null;
-	
-	const NOTHING_TO_NOTICE = 1;
-	const TO_REDIRECT_URL = 2;
-	const LOGGING_DISABLE = 10;
-	const NO_GET_VARIABLE = 11;
-	
-	public function __construct()
-	{
-		$this->stateValid = self::NOTHING_TO_NOTICE;
-	}
-	
-	// create the database object
-	function connectDatabase()
-	{
-		$configDb = Piwik_LogStats_Config::getInstance()->database;
-		$this->db = new Piwik_LogStats_Db( 	$configDb['host'], 
-										$configDb['username'], 
-										$configDb['password'], 
-										$configDb['dbname']
-							);  
-		$this->db->connect();
-	}
-	
-	private function initProcess()
-	{
-		$saveStats = Piwik_LogStats_Config::getInstance()->LogStats['record_statistics'];
-		
-		if($saveStats == 0)
-		{
-			$this->setState(self::LOGGING_DISABLE);
-		}
-		
-		if( count($_GET) == 0)
-		{
-			$this->setState(self::NO_GET_VARIABLE);			
-		}
-		
-		$downloadVariableName = Piwik_LogStats_Config::getInstance()->LogStats['download_url_var_name'];
-		$urlDownload = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string');
-		
-		if( !empty($urlDownload) )
-		{
-			$this->setState( self::TO_REDIRECT_URL );
-			$this->setUrlToRedirect ( $urlDownload);
-		}
-		
-		$outlinkVariableName = Piwik_LogStats_Config::getInstance()->LogStats['outlink_url_var_name'];
-		$urlOutlink = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string');
-		
-		if( !empty($urlOutlink) )
-		{
-			$this->setState( self::TO_REDIRECT_URL );
-			$this->setUrlToRedirect ( $urlOutlink);
-		}
-	}
-	
-	private function processVisit()
-	{
-		return $this->stateValid !== self::LOGGING_DISABLE
-				&&  $this->stateValid !== self::NO_GET_VARIABLE;
-	}
-	private function getState()
-	{
-		return $this->stateValid;
-	}
-	
-	private function setUrlToRedirect( $url )
-	{
-		$this->urlToRedirect = $url;
-	}
-	private function getUrlToRedirect()
-	{
-		return $this->urlToRedirect;
-	}
-	private function setState( $value )
-	{
-		$this->stateValid = $value;
-	}
-	
-	// main algorithm 
-	// => input : variables filtered
-	// => action : read cookie, read database, database logging, cookie writing
-	function main()
-	{
-		$this->initProcess();
-		
-		if( $this->processVisit() )
-		{
-			$this->connectDatabase();
-			$visit = new Piwik_LogStats_Visit( $this->db );
-			$visit->handle();
-		}
-		$this->endProcess();
-	}	
-	
-	// display the logo or pixel 1*1 GIF
-	// or a marketing page if no parameters in the url
-	// or redirect to a url (transmit the cookie as well)
-	// or load a URL (rss feed) (transmit the cookie as well)
-	private function endProcess()
-	{
-		switch($this->getState())
-		{
-			case self::LOGGING_DISABLE:
-				printDebug("Logging disabled, display transparent logo");
-			break;
-			
-			case self::NO_GET_VARIABLE:
-				printDebug("No get variables => piwik page");
-			break;
-			
-			
-			case self::TO_REDIRECT_URL:
-				header('Location: ' . $this->getUrlToRedirect());
-			break;
-			
-			
-			case self::NOTHING_TO_NOTICE:
-			default:
-				printDebug("Nothing to notice => default behaviour");
-			break;
-		}
-		printDebug("End of the page.");
-		exit;
-	}
-}
-
 $process = new Piwik_LogStats;
+Piwik_PostEvent( 'LogsStats.NewVisitor' );
 $process->main();
 
 // yet to do
 // known visitor test 1h
 // known visitor update 1h
-// referer analysis 3h
 // unit testing the module 7h
 ob_end_flush();
+printDebug($_COOKIE);
 ?>
diff --git a/tests/modules/Common.test.php b/tests/modules/Common.test.php
index 9fc742ecbc..5ac2f591a3 100644
--- a/tests/modules/Common.test.php
+++ b/tests/modules/Common.test.php
@@ -58,6 +58,15 @@ class Test_Piwik_Common extends UnitTestCase
 		$this->assertEqual( $a1OK, Piwik_Common::sanitizeInputValues($a1));
 	}
 	
+	// sanitize a string unicode => no change
+	function test_sanitizeInputValues_arrayBadValueutf8()
+	{
+		$a1 =   " Поиск в Интернете  Поgqegиск страниц на рgeqg8978усском";
+		$a1OK = " Поиск в Интернете  Поgqegиск страниц на рgeqg8978усском";
+
+		$this->assertEqual( $a1OK, Piwik_Common::sanitizeInputValues($a1));
+	}
+	
 	// sanitize a bad string
 	function test_sanitizeInputValues_badString()
 	{
@@ -296,5 +305,91 @@ class Test_Piwik_Common extends UnitTestCase
     	$this->assertEqual( Piwik_Common::getRequestVar('test', array(), 'array'), array());
     	
     }
+    
+    
+    
+    /**
+     * no query string => false
+     */
+    function test_getParameterFromQueryString_noQuerystring()
+    {
+    	$urlQuery = "";
+    	$urlQuery = htmlentities($urlQuery);
+    	$parameter = "test''";
+    	$result = Piwik_Common::getParameterFromQueryString( $urlQuery, $parameter);
+    	$expectedResult = false;
+    	$this->assertEqual($result, $expectedResult);
+    }
+    
+    /**
+     * param not found => false
+     */
+    function test_getParameterFromQueryString_parameternotfound()
+    {
+    	
+    	$urlQuery = "toto=mama&mama=titi";
+    	$urlQuery = htmlentities($urlQuery);
+    	$parameter = "tot";
+    	$result = Piwik_Common::getParameterFromQueryString( $urlQuery, $parameter);
+    	$expectedResult = false;
+    	$this->assertEqual($result, $expectedResult);
+    }
+    
+    /**
+     * empty parameter value => returns empty string
+     */
+    function test_getParameterFromQueryString_emptyParamValue()
+    {
+    	
+    	$urlQuery = "toto=mama&mama=&tuytyt=teaoi";
+    	$urlQuery = htmlentities($urlQuery);
+    	$parameter = "mama";
+    	$result = Piwik_Common::getParameterFromQueryString( $urlQuery, $parameter);
+    	$expectedResult = '';
+    	$this->assertEqual($result, $expectedResult);
+    }
+    
+    /**
+     * twice the parameter => returns the last value in the url
+     */
+    function test_getParameterFromQueryString_twiceTheParameterInQuery()
+    {
+    	
+    	$urlQuery = "toto=mama&mama=&tuytyt=teaoi&toto=mama second value";
+    	$urlQuery = htmlentities($urlQuery);
+    	$parameter = "toto";
+    	$result = Piwik_Common::getParameterFromQueryString( $urlQuery, $parameter);
+    	$expectedResult = 'mama second value';
+    	$this->assertEqual($result, $expectedResult);
+    }
+    
+    /**
+     * normal use case => parameter found
+     */
+    function test_getParameterFromQueryString_normalCase()
+    {
+    	
+    	$urlQuery = "toto=mama&mama=&tuytyt=teaoi&toto=mama second value";
+    	$urlQuery = htmlentities($urlQuery);
+    	$parameter = "tuytyt";
+    	$result = Piwik_Common::getParameterFromQueryString( $urlQuery, $parameter);
+    	$expectedResult = 'teaoi';
+    	$this->assertEqual($result, $expectedResult);
+    }
+    
+    /**
+     * normal use case with a string with many strange characters
+     */
+    function test_getParameterFromQueryString_strangeChars()
+    {
+    	
+    	$urlQuery = 'toto=mama&mama=&tuytyt=Поиск в Интернете  Поиск страниц на русском _*()!$!£$^!£$%&toto=mama second value';
+    	$urlQuery = htmlentities($urlQuery);
+    	$parameter = "tuytyt";
+    	$result = Piwik_Common::getParameterFromQueryString( $urlQuery, $parameter);
+    	$expectedResult = 'Поиск в Интернете  Поиск страниц на русском _*()!$!£$^!£$%';
+    	$expectedResult = htmlentities($expectedResult);
+    	$this->assertEqual($result, $expectedResult);
+    }
 }
 ?>
-- 
GitLab