Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfig/config.ini.php9
-rwxr-xr-xmisc/stress.sh5
-rw-r--r--modules/Common.php53
-rw-r--r--modules/DataFiles/SearchEngines.php1040
-rw-r--r--modules/LogStats.php1454
-rw-r--r--modules/LogStats/Plugins.php52
-rwxr-xr-xmodules/Piwik.php74
-rw-r--r--modules/PluginManager.php158
-rw-r--r--piwik.php1285
-rw-r--r--tests/modules/Common.test.php95
10 files changed, 2908 insertions, 1317 deletions
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 &)
+ * @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 = '&'.trim(str_replace(array('%20'), ' ', '&'.$urlQuery));
+ $word = '&'.$parameter.'=';
+
+ if( $off = strrpos($refererQuery, $word))
+ {
+ $off += strlen($word); // &q=
+ $str = substr($refererQuery, $off);
+ $len = strpos($str, '&');
+ 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);
+ }
}
?>